library(brms)
library(ggplot2)
library(kableExtra)
library(lme4)
library(lmerTest)
library(rmarkdown)
library(naniar)
library(performance)
library(see)
library(sjmisc)
library(tidyverse)
options(
digits = 3
)set.seed(170819)
Analyses
Set-up
Packages
Custom functions
# function to silence brms output
<-
hush function(
code
){sink("/dev/null")
= code
tmp sink()
return(tmp)
}
Data
<- read_csv("data/data.csv")
d
# same as above; but original file name:
# d <- read_csv("data/DataAggregated_T1T2_costsbenefits.csv")
# load image for work in IDE
# load("data/image.RData")
<- d |>
d rename(
group = roles,
op_expr = n_OE,
gender = DE01_T1,
age = DE02_01_T1,
pol_stance = DE06_01_T1
|>
) mutate(
female = as.logical(2 - gender),
gender = factor(gender, labels = c("female", "male"))
)
# recode to make as sum coding
$anonymity_dev <- factor(d$anonymity)
dcontrasts(d$anonymity_dev) <- contr.sum(2)
$persistence_dev <- factor(d$persistence)
dcontrasts(d$persistence_dev) <- contr.sum(2)
Missing data
Let’s first inspect how much data is missing
# Filter variables used for analyses
<- d |>
d_filt select(age, gender, pol_stance, topic, persistence, persistence_dev, anonymity, anonymity_dev, op_expr)
# Summary
miss_var_summary(d_filt) # % missing per variable
# A tibble: 9 × 3
variable n_miss pct_miss
<chr> <int> <num>
1 age 0 0
2 gender 0 0
3 pol_stance 0 0
4 topic 0 0
5 persistence 0 0
6 persistence_dev 0 0
7 anonymity 0 0
8 anonymity_dev 0 0
9 op_expr 0 0
miss_case_summary(d_filt) # % missing per row
# A tibble: 960 × 3
case n_miss pct_miss
<int> <int> <dbl>
1 1 0 0
2 2 0 0
3 3 0 0
4 4 0 0
5 5 0 0
6 6 0 0
7 7 0 0
8 8 0 0
9 9 0 0
10 10 0 0
# ℹ 950 more rows
# Visualization
vis_miss(d_filt) # Heatmap of missingness
gg_miss_var(d_filt) # Bar plot: missing per variable
gg_miss_case(d_filt) # Bar plot: missing per case
Shows that there are nore missing data for the variables we’ll analyse. Hence, no imputing necessary.
Descriptives
Let’s inspect distribution of opinion expressions.
ggplot(d, aes(op_expr)) +
geom_histogram(binwidth = 1)
Looks like a zero-inflated poisson distribution. Confirms our preregistered approach to analyze data using zero-inflated Poisson approach.
nrow(d[d$op_expr == 0, ]) / nrow(d)
[1] 0.214
Overall, 21% of participants without any opinion expressions.
Let’s look at distribution of experimental groups.
|>
d select(persistence, anonymity) |>
table()
anonymity
persistence high low
high 240 240
low 240 240
Distribution among groups perfect.
|>
d select(topic) |>
table()
topic
climate gender migration
320 320 320
Distribution of topics also perfect.
Let’s check if groups are nested in topics:
is_nested(d$topic, d$group)
'f2' is nested within 'f1'
[1] TRUE
Indeed the case.
We first look at the experimental group’s descriptives
|>
d group_by(persistence) |>
summarize(op_expr_m = mean(op_expr)) |>
as.data.frame() |>
kable()
persistence | op_expr_m |
---|---|
high | 9.26 |
low | 9.29 |
Looking at persistence, we see there’s virtually no difference among groups.
|>
d group_by(anonymity) |>
summarize(op_expr_m = mean(op_expr)) |>
as.data.frame() |>
kable()
anonymity | op_expr_m |
---|---|
high | 9.03 |
low | 9.52 |
People who with less anonymity communicated more. But the difference isn’t particularly large.
|>
d group_by(persistence, anonymity) |>
summarize(op_expr_m = mean(op_expr)) |>
as.data.frame() |>
kable()
`summarise()` has grouped output by 'persistence'. You can override using the
`.groups` argument.
persistence | anonymity | op_expr_m |
---|---|---|
high | high | 9.27 |
high | low | 9.25 |
low | high | 8.79 |
low | low | 9.78 |
Looking at both groups combined, we see that low anonymity and low persistence created highest participation. But differences among groups aren’t large.
|>
d group_by(group) |>
summarize(
anonymity = anonymity[1],
persistence = persistence[1],
topic = topic[1],
op_expr_m = mean(op_expr)
|>
) ::paged_table() rmarkdown
Looking at the various individual groups, we do see some difference. Generally, this shows that communication varied across groups.
|>
d group_by(topic) |>
summarize(op_expr_m = mean(op_expr)) |>
as.data.frame() |>
kable()
topic | op_expr_m |
---|---|
climate | 9.63 |
gender | 9.96 |
migration | 8.22 |
Looking at topics specifically, we also see that there’s some variance.
Opinion expressions
Let’s look at the distribution of communicated number of words.
ggplot(d, aes(op_expr)) +
geom_histogram(bins = 50)
Let’s also look at how many did not express any opinions at all.
Number:
length(which(d$op_expr == 0))
[1] 205
Percentage:
length(which(d$op_expr == 0)) / length(d$op_expr)
[1] 0.214
Stats:
summary(d$op_expr)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 1.0 6.0 9.3 12.0 186.0
Communicated words
Let’s look at the distribution of communicated number of words.
ggplot(d, aes(n_Words)) +
geom_histogram(bins = 50)
Warning: Removed 198 rows containing non-finite outside the scale range
(`stat_bin()`).
Let’s also look at how many did not write any words at all.
length(which(d$n_Words == 0))
[1] 0
Everyone wrote at least one word.
Stats:
summary(d$n_Words)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
1 149 336 496 602 8843 198
Manipulation Check
Let’s see if respondents indeed perceived the experimental manipulations. Let’s first look at descriptives.
|>
d group_by(persistence) |>
summarize(
"Perceived persistence" = mean(per_persistence, na.rm = TRUE)
|>
) as.data.frame() |>
kable()
persistence | Perceived persistence |
---|---|
high | 1.78 |
low | 4.19 |
The experimental manipulation worked.
<- lm(
model_pers ~ persistence_dev,
per_persistence
d
)
summary(model_pers)
Call:
lm(formula = per_persistence ~ persistence_dev, data = d)
Residuals:
Min 1Q Median 3Q Max
-3.194 -0.777 -0.110 0.806 3.223
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.9854 0.0378 79.1 <2e-16 ***
persistence_dev1 -1.2085 0.0378 -32.0 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1 on 703 degrees of freedom
(255 observations deleted due to missingness)
Multiple R-squared: 0.593, Adjusted R-squared: 0.592
F-statistic: 1.02e+03 on 1 and 703 DF, p-value: <2e-16
The difference was statistically significant. Let’s now look at anonymity.
|>
d group_by(anonymity) |>
summarize(
"Perceived anonymity" = mean(per_anonymity, na.rm = TRUE)
|>
) as.data.frame() |>
kable()
anonymity | Perceived anonymity |
---|---|
high | 1.22 |
low | 4.42 |
The experimental manipulation worked.
<- lm(
model_anon ~ anonymity_dev,
per_anonymity
d
)summary(model_anon)
Call:
lm(formula = per_anonymity ~ anonymity_dev, data = d)
Residuals:
Min 1Q Median 3Q Max
-3.425 -0.216 -0.216 0.575 3.784
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.8204 0.0348 81.0 <2e-16 ***
anonymity_dev1 -1.6042 0.0348 -46.1 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.924 on 703 degrees of freedom
(255 observations deleted due to missingness)
Multiple R-squared: 0.751, Adjusted R-squared: 0.751
F-statistic: 2.12e+03 on 1 and 703 DF, p-value: <2e-16
The experimental manipulation worked.
Cross contamination
Let’s look at cross contamination, i.e. if anonymity affected perceived persistence and if persistence affects perceived anonymity.
|>
d group_by(anonymity) |>
summarize(
"Perceived persistence" = mean(per_persistence, na.rm = TRUE)
|>
) as.data.frame() |>
kable()
anonymity | Perceived persistence |
---|---|
high | 2.89 |
low | 3.00 |
There was next to no difference among groups.
<- lm(
model_pers ~ anonymity_dev,
per_persistence
d
)
summary(model_pers)
Call:
lm(formula = per_persistence ~ anonymity_dev, data = d)
Residuals:
Min 1Q Median 3Q Max
-2.005 -1.671 0.108 1.662 2.108
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.9485 0.0591 49.86 <2e-16 ***
anonymity_dev1 -0.0561 0.0591 -0.95 0.34
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.57 on 703 degrees of freedom
(255 observations deleted due to missingness)
Multiple R-squared: 0.00128, Adjusted R-squared: -0.000141
F-statistic: 0.901 on 1 and 703 DF, p-value: 0.343
The difference was not statistically significant. No cross contamination re. anonymity.
|>
d group_by(persistence) |>
summarize(
"Perceived anonymity" = mean(per_anonymity, na.rm = TRUE)
|>
) as.data.frame() |>
kable()
persistence | Perceived anonymity |
---|---|
high | 2.76 |
low | 2.93 |
There was next to no difference in the groups’ means.
<- lm(
model_anon ~ persistence_dev,
per_anonymity
d
)summary(model_anon)
Call:
lm(formula = per_anonymity ~ persistence_dev, data = d)
Residuals:
Min 1Q Median 3Q Max
-1.933 -1.763 -0.763 2.067 2.237
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.8479 0.0698 40.83 <2e-16 ***
persistence_dev1 -0.0848 0.0698 -1.22 0.22
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.85 on 703 degrees of freedom
(255 observations deleted due to missingness)
Multiple R-squared: 0.0021, Adjusted R-squared: 0.00068
F-statistic: 1.48 on 1 and 703 DF, p-value: 0.224
Again, no significant difference. In conclusion, no cross-contamination.
Random Allocation
Let’s check if random allocation across experimental conditions worked alright. Let’s first look at descriptives.
|>
d group_by(persistence, anonymity) |>
summarize(
female_m = mean(female)
age_m = mean(age)
, pol_stance_m = mean(pol_stance)
, |>
) as.data.frame() |>
kable()
`summarise()` has grouped output by 'persistence'. You can override using the
`.groups` argument.
persistence | anonymity | female_m | age_m | pol_stance_m |
---|---|---|---|---|
high | high | 0.613 | 43.2 | 5.57 |
high | low | 0.662 | 39.6 | 5.44 |
low | high | 0.558 | 41.7 | 5.76 |
low | low | 0.683 | 40.8 | 5.15 |
There seem to be some differences. Let’s inspect manually.
<- lm(
model_persis as.integer(persistence_dev) ~ age + gender + pol_stance,
d
)summary(model_persis)
Call:
lm(formula = as.integer(persistence_dev) ~ age + gender + pol_stance,
data = d)
Residuals:
Min 1Q Median 3Q Max
-0.5352 -0.4994 -0.0006 0.4994 0.5385
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.536577 0.072667 21.15 <2e-16 ***
age -0.000632 0.001402 -0.45 0.65
gendermale 0.022885 0.034672 0.66 0.51
pol_stance -0.003460 0.008089 -0.43 0.67
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.501 on 956 degrees of freedom
Multiple R-squared: 0.000702, Adjusted R-squared: -0.00243
F-statistic: 0.224 on 3 and 956 DF, p-value: 0.88
Allocation of gender, age, and political stance on the two persistence groups was successful.
<- lm(
model_anon as.integer(anonymity_dev) ~ age + gender + pol_stance,
d
)summary(model_anon)
Call:
lm(formula = as.integer(anonymity_dev) ~ age + gender + pol_stance,
data = d)
Residuals:
Min 1Q Median 3Q Max
-0.6702 -0.4902 0.0125 0.4874 0.7263
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.77558 0.07192 24.69 <2e-16 ***
age -0.00323 0.00139 -2.33 0.0201 *
gendermale -0.06686 0.03432 -1.95 0.0517 .
pol_stance -0.02142 0.00801 -2.68 0.0076 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.496 on 956 degrees of freedom
Multiple R-squared: 0.0211, Adjusted R-squared: 0.018
F-statistic: 6.87 on 3 and 956 DF, p-value: 0.00014
However, allocation across anonymity groups wasn’t successful. In the subsequent analyses, let’s hence control for these sociodemographic variables.
Bayesian mixed effects modeling
We analyze the data using Bayesian modelling.
We use deviation/sum contrast coding (-.1, .1). Meaning, contrasts measure main effects of independent variables.
Fixed effects
We preregistered to analyze fixed effects.
<-
fit_fe_1 hush(
brm(
~
op_expr 1 + persistence_dev * anonymity_dev + age + female + pol_stance +
1 | topic/group)
(data = d
, chains = 4
, cores = 4
, iter = 6000
, warmup = 2000
, family = zero_inflated_poisson()
, control = list(
, adapt_delta = .95
max_treedepth = 12
,
)save_pars = save_pars(all = TRUE)
, silent = 2
,
) )
Warning: There were 214 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems
Shows some convergence warnings. Let’s inspect model.
plot(fit_fe_1, ask = FALSE)
Trace-plots look alright.
Let’s look at results.
summary(fit_fe_1)
Warning: There were 214 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Family: zero_inflated_poisson
Links: mu = log; zi = identity
Formula: op_expr ~ 1 + persistence_dev * anonymity_dev + age + female + pol_stance + (1 | topic/group)
Data: d (Number of observations: 960)
Draws: 4 chains, each with iter = 6000; warmup = 2000; thin = 1;
total post-warmup draws = 16000
Multilevel Hyperparameters:
~topic (Number of levels: 3)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.27 0.31 0.01 1.18 1.00 1935 4004
~topic:group (Number of levels: 48)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.40 0.05 0.32 0.50 1.00 2863 4835
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat
Intercept 1.99 0.20 1.56 2.43 1.00
persistence_dev1 -0.01 0.06 -0.12 0.11 1.00
anonymity_dev1 -0.01 0.06 -0.13 0.10 1.00
age 0.01 0.00 0.01 0.01 1.00
femaleTRUE -0.00 0.02 -0.05 0.05 1.00
pol_stance -0.02 0.01 -0.03 -0.01 1.00
persistence_dev1:anonymity_dev1 0.02 0.06 -0.09 0.14 1.00
Bulk_ESS Tail_ESS
Intercept 3751 2124
persistence_dev1 2818 4655
anonymity_dev1 2685 5035
age 22907 13685
femaleTRUE 9989 10431
pol_stance 11560 10620
persistence_dev1:anonymity_dev1 2773 4109
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
zi 0.21 0.01 0.19 0.24 1.00 10401 9549
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
No significant effect emerged.
Let’s inspect ICC
<- performance::variance_decomposition(
var_ratio_fe
fit_fe_1by_group = TRUE)
, var_ratio_fe
# Random Effect Variances and ICC
Conditioned on: all random effects
## Variance Ratio (comparable to ICC)
Ratio: 0.42 CI 95%: [-0.24 0.72]
## Variances of Posterior Predicted Distribution
Conditioned on fixed effects: 31.94 CI 95%: [15.38 68.22]
Conditioned on rand. effects: 55.01 CI 95%: [49.75 60.91]
## Difference in Variances
Difference: 22.97 CI 95%: [-13.03 40.39]
41.886 percent of variance in opinion expressions explained by both topics and groups.
Let’s visualize results to see what they exactly mean.
<- plot(
p conditional_effects(
fit_fe_1
), ask = FALSE,
plot = FALSE
)
<-
p_anon "anonymity_dev"]] +
p[[xlab("Anonymity") +
ylab("Opinion expression") +
scale_x_discrete(
limits = rev
+
) scale_y_continuous(
limits = c(5, 14)
breaks = c(6, 8, 10, 12, 14)
,
)
<-
p_pers "persistence_dev"]] +
p[[xlab("Persistence") +
ylab("Opinion expression") +
scale_x_discrete(
limits = rev
+
) scale_y_continuous(
limits = c(5, 14)
breaks = c(6, 8, 10, 12, 14)
, +
) theme(
axis.title.y = element_blank()
)
<-
p_int "persistence_dev:anonymity_dev"]] +
p[[xlab("Persistence") +
scale_x_discrete(
limits = rev
+
) scale_color_discrete(
labels = c("low", "high")
+
) guides(
fill = "none",
color = guide_legend(
title = "Anonymity"
)+
) theme(
axis.title.y = element_blank()
+
) scale_y_continuous(
limits = c(5, 14)
breaks = c(6, 8, 10, 12, 14)
,
)
<- cowplot::plot_grid(
plot
p_anon, p_pers, p_int, labels = c('A', 'B', "C"),
nrow = 1,
rel_widths = c(2, 2, 3)
)
plot
ggsave("figures/results.png", plot, width = 8, height = 4)
Shows that there are no main effects. There seems to be a (nonsignificant) interaction effect. In low persistence environment, anonymity is conducive to communication; in high it’s the opposite.
Let’s look at posteriors
<-
p_1 pp_check(fit_fe_1) +
labs(title = "Zero-inflated poisson")
Using 10 posterior draws for ppc type 'dens_overlay' by default.
p_1
The actual distribution cannot be precisely reproduced, but it’s also not too far off.
Random effects
We preregistered to explore and compare models with random effects. So let’s model how the experimental conditions affect the outcomes differently depending on topic.
<-
fit_re_1 hush(
brm(
~
op_expr 1 + persistence_dev * anonymity_dev + age + female + pol_stance +
1 + persistence_dev * anonymity_dev | topic) +
(1 | topic:group)
(data = d
, chains = 4
, cores = 4
, iter = 6000
, warmup = 2000
, family = zero_inflated_poisson()
, control = list(
, adapt_delta = .95
max_treedepth = 15
,
)save_pars = save_pars(all = TRUE)
,
) )
Warning: There were 757 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems
Warning: Tail Effective Samples Size (ESS) is too low, indicating posterior variances and tail quantiles may be unreliable.
Running the chains for more iterations may help. See
https://mc-stan.org/misc/warnings.html#tail-ess
Shows some convergence warnings.
Let’s inspect model.
plot(fit_re_1, ask = FALSE)
Traceplots look alright.
Let’s look at results.
summary(fit_re_1)
Warning: There were 757 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Family: zero_inflated_poisson
Links: mu = log; zi = identity
Formula: op_expr ~ 1 + persistence_dev * anonymity_dev + age + female + pol_stance + (1 + persistence_dev * anonymity_dev | topic) + (1 | topic:group)
Data: d (Number of observations: 960)
Draws: 4 chains, each with iter = 6000; warmup = 2000; thin = 1;
total post-warmup draws = 16000
Multilevel Hyperparameters:
~topic (Number of levels: 3)
Estimate Est.Error
sd(Intercept) 0.35 0.45
sd(persistence_dev1) 0.25 0.38
sd(anonymity_dev1) 0.25 0.40
sd(persistence_dev1:anonymity_dev1) 0.44 0.53
cor(Intercept,persistence_dev1) -0.01 0.46
cor(Intercept,anonymity_dev1) -0.01 0.46
cor(persistence_dev1,anonymity_dev1) -0.01 0.46
cor(Intercept,persistence_dev1:anonymity_dev1) 0.06 0.46
cor(persistence_dev1,persistence_dev1:anonymity_dev1) 0.01 0.46
cor(anonymity_dev1,persistence_dev1:anonymity_dev1) -0.01 0.46
l-95% CI u-95% CI Rhat
sd(Intercept) 0.01 1.64 1.00
sd(persistence_dev1) 0.00 1.34 1.00
sd(anonymity_dev1) 0.00 1.36 1.00
sd(persistence_dev1:anonymity_dev1) 0.02 2.00 1.00
cor(Intercept,persistence_dev1) -0.83 0.82 1.00
cor(Intercept,anonymity_dev1) -0.83 0.82 1.00
cor(persistence_dev1,anonymity_dev1) -0.83 0.84 1.00
cor(Intercept,persistence_dev1:anonymity_dev1) -0.79 0.86 1.00
cor(persistence_dev1,persistence_dev1:anonymity_dev1) -0.83 0.84 1.00
cor(anonymity_dev1,persistence_dev1:anonymity_dev1) -0.84 0.84 1.00
Bulk_ESS Tail_ESS
sd(Intercept) 3999 7589
sd(persistence_dev1) 3022 1235
sd(anonymity_dev1) 2973 4156
sd(persistence_dev1:anonymity_dev1) 812 1075
cor(Intercept,persistence_dev1) 18914 11709
cor(Intercept,anonymity_dev1) 11052 10770
cor(persistence_dev1,anonymity_dev1) 14598 12328
cor(Intercept,persistence_dev1:anonymity_dev1) 4205 1403
cor(persistence_dev1,persistence_dev1:anonymity_dev1) 13374 12801
cor(anonymity_dev1,persistence_dev1:anonymity_dev1) 3222 1168
~topic:group (Number of levels: 48)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.40 0.05 0.32 0.50 1.00 1232 2167
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat
Intercept 2.00 0.29 1.36 2.59 1.00
persistence_dev1 -0.00 0.22 -0.43 0.47 1.00
anonymity_dev1 -0.01 0.25 -0.45 0.45 1.00
age 0.01 0.00 0.01 0.01 1.00
femaleTRUE -0.00 0.02 -0.05 0.04 1.00
pol_stance -0.02 0.01 -0.03 -0.01 1.00
persistence_dev1:anonymity_dev1 0.05 0.39 -0.69 1.38 1.01
Bulk_ESS Tail_ESS
Intercept 5679 5933
persistence_dev1 7250 6100
anonymity_dev1 4316 4669
age 17356 12335
femaleTRUE 25008 9823
pol_stance 14780 10789
persistence_dev1:anonymity_dev1 681 124
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
zi 0.21 0.01 0.19 0.24 1.00 16420 10765
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Again, no main or interaction effects.
Let’s see if the random effects model fits better
<- parallel::detectCores()
cores
<- add_criterion(
fit_fe_1
fit_fe_1"kfold"
, K = 5
, cores = cores
, )
Fitting model 1 out of 5
Fitting model 2 out of 5
Fitting model 3 out of 5
Fitting model 4 out of 5
Fitting model 5 out of 5
<- add_criterion(
fit_re_1
fit_re_1"kfold"
, K = 5
, cores = cores
, )
Fitting model 1 out of 5
Start sampling
Fitting model 2 out of 5
Start sampling
Fitting model 3 out of 5
Start sampling
Fitting model 4 out of 5
Start sampling
Fitting model 5 out of 5
Start sampling
<- loo_compare(fit_fe_1, fit_re_1, criterion = "kfold")
comp_1 comp_1
elpd_diff se_diff
fit_re_1 0.0 0.0
fit_fe_1 -149.4 69.3
Although model comparisons showed that the model with random effects fitted better, the difference was not significant (Δ ELPD = -149.36, 95% CI [-285.246, -13.467]. Hence, for reasons of parsimony the model with fixed effects is preferred.
Null-Model
Let’s also inspect a model without random intercepts to see if including random intercepts is worthwhile.
<-
fit_nm_1 hush(
brm(
~
op_expr 1 + persistence_dev * anonymity_dev + age + female + pol_stance
data = d
, chains = 4
, cores = 4
, iter = 6000
, warmup = 2000
, family = zero_inflated_poisson()
, control = list(
, adapt_delta = .95
max_treedepth = 12
,
)save_pars = save_pars(all = TRUE)
, silent = 2
,
)
)
summary(fit_nm_1)
Family: zero_inflated_poisson
Links: mu = log; zi = identity
Formula: op_expr ~ 1 + persistence_dev * anonymity_dev + age + female + pol_stance
Data: d (Number of observations: 960)
Draws: 4 chains, each with iter = 6000; warmup = 2000; thin = 1;
total post-warmup draws = 16000
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat
Intercept 1.96 0.06 1.85 2.06 1.00
persistence_dev1 0.00 0.01 -0.02 0.02 1.00
anonymity_dev1 -0.04 0.01 -0.06 -0.02 1.00
age 0.01 0.00 0.01 0.01 1.00
femaleTRUE 0.02 0.02 -0.02 0.07 1.00
pol_stance -0.01 0.01 -0.02 0.01 1.00
persistence_dev1:anonymity_dev1 0.02 0.01 -0.00 0.04 1.00
Bulk_ESS Tail_ESS
Intercept 20193 13428
persistence_dev1 20156 11001
anonymity_dev1 20783 10932
age 19306 11896
femaleTRUE 20703 11806
pol_stance 22715 12379
persistence_dev1:anonymity_dev1 20156 12002
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
zi 0.21 0.01 0.19 0.24 1.00 20932 11802
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Shows significant effect of anonymity on opinion expression.
Let’s compare models.
<- add_criterion(
fit_nm_1
fit_nm_1"kfold"
, K = 5
, cores = cores
, )
Fitting model 1 out of 5
Fitting model 2 out of 5
Fitting model 3 out of 5
Fitting model 4 out of 5
Fitting model 5 out of 5
<- loo_compare(fit_fe_1, fit_nm_1, criterion = "kfold")
comp_2 comp_2
elpd_diff se_diff
fit_fe_1 0.0 0.0
fit_nm_1 -364.9 134.0
The model comparisons showed that the model with random intercepts fitted significantly better than the null model with fixed intercepts (Δ ELPD = -364.85, 95% CI [-627.549, -102.152].
Hurdle
Let’s now estimate a fixed effects model with hurdles.
<-
fit_hrdl_1 hush(
brm(
bf(
~
op_expr 1 + persistence_dev * anonymity_dev + age + female + pol_stance +
1 | topic) +
(1 | topic:group),
(~
zi 1 + persistence_dev * anonymity_dev + age + female + pol_stance +
1 | topic) +
(1 | topic:group)
(
)data = d
, chains = 4
, cores = 4
, iter = 6000
, warmup = 2000
, family = zero_inflated_poisson()
, control = list(
, adapt_delta = .95
max_treedepth = 15
,
)
) )
Warning: There were 422 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems
Warning: Tail Effective Samples Size (ESS) is too low, indicating posterior variances and tail quantiles may be unreliable.
Running the chains for more iterations may help. See
https://mc-stan.org/misc/warnings.html#tail-ess
Again, some warnings.
Let’s inspect model.
plot(fit_hrdl_1, ask = FALSE)
Trace-plots look alright.
summary(fit_hrdl_1)
Warning: There were 422 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Family: zero_inflated_poisson
Links: mu = log; zi = logit
Formula: op_expr ~ 1 + persistence_dev * anonymity_dev + age + female + pol_stance + (1 | topic) + (1 | topic:group)
zi ~ 1 + persistence_dev * anonymity_dev + age + female + pol_stance + (1 | topic) + (1 | topic:group)
Data: d (Number of observations: 960)
Draws: 4 chains, each with iter = 6000; warmup = 2000; thin = 1;
total post-warmup draws = 16000
Multilevel Hyperparameters:
~topic (Number of levels: 3)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.26 0.31 0.01 1.20 1.00 1220 292
sd(zi_Intercept) 0.28 0.42 0.01 1.52 1.00 3380 4173
~topic:group (Number of levels: 48)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.40 0.05 0.32 0.50 1.00 2278 1113
sd(zi_Intercept) 0.24 0.14 0.02 0.52 1.00 3893 7197
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat
Intercept 2.00 0.18 1.60 2.39 1.01
zi_Intercept -1.69 0.49 -2.61 -0.72 1.00
persistence_dev1 -0.01 0.06 -0.13 0.11 1.00
anonymity_dev1 -0.01 0.06 -0.13 0.11 1.00
age 0.01 0.00 0.01 0.01 1.00
femaleTRUE -0.00 0.02 -0.05 0.05 1.00
pol_stance -0.02 0.01 -0.03 -0.01 1.00
persistence_dev1:anonymity_dev1 0.02 0.06 -0.09 0.16 1.01
zi_persistence_dev1 0.03 0.09 -0.15 0.20 1.00
zi_anonymity_dev1 0.01 0.09 -0.17 0.18 1.00
zi_age 0.01 0.01 -0.00 0.03 1.00
zi_femaleTRUE 0.19 0.17 -0.15 0.53 1.00
zi_pol_stance -0.05 0.04 -0.12 0.04 1.00
zi_persistence_dev1:anonymity_dev1 -0.01 0.09 -0.18 0.17 1.00
Bulk_ESS Tail_ESS
Intercept 4517 2523
zi_Intercept 8977 5131
persistence_dev1 3100 2940
anonymity_dev1 2296 5882
age 17399 12354
femaleTRUE 19167 11310
pol_stance 21511 10355
persistence_dev1:anonymity_dev1 1098 208
zi_persistence_dev1 12625 3777
zi_anonymity_dev1 15515 11855
zi_age 17492 11736
zi_femaleTRUE 19399 11393
zi_pol_stance 19998 11223
zi_persistence_dev1:anonymity_dev1 15922 10687
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Same results, no main effects, slightly larger but still non-significant interaction effect.
Exploratory Analyses
Frequentist
Look at results from a frequentist perspective.
Fixed effects
Estimate nested model.
<-
fit_fe_1_frq lmer(
~
op_expr 1 +
1 | topic/group) +
(* anonymity_dev + age + female + pol_stance
persistence_dev data = d
,
)
summary(fit_fe_1_frq)
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: op_expr ~ 1 + (1 | topic/group) + persistence_dev * anonymity_dev +
age + female + pol_stance
Data: d
REML criterion at convergence: 7604
Scaled residuals:
Min 1Q Median 3Q Max
-1.484 -0.555 -0.196 0.264 13.549
Random effects:
Groups Name Variance Std.Dev.
group:topic (Intercept) 1.08e+01 3.28e+00
topic (Intercept) 8.92e-08 2.99e-04
Residual 1.55e+02 1.25e+01
Number of obs: 960, groups: group:topic, 48; topic, 3
Fixed effects:
Estimate Std. Error df t value Pr(>|t|)
(Intercept) 6.0450 2.1478 885.1185 2.81 0.005
persistence_dev1 -0.0197 0.6214 43.8644 -0.03 0.975
anonymity_dev1 -0.3437 0.6242 44.6401 -0.55 0.585
age 0.0860 0.0357 943.4320 2.41 0.016
femaleTRUE -0.2434 0.8783 939.5946 -0.28 0.782
pol_stance -0.0319 0.2058 942.6445 -0.16 0.877
persistence_dev1:anonymity_dev1 0.1953 0.6226 44.1939 0.31 0.755
(Intercept) **
persistence_dev1
anonymity_dev1
age *
femaleTRUE
pol_stance
persistence_dev1:anonymity_dev1
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Correlation of Fixed Effects:
(Intr) prss_1 anny_1 age fmTRUE pl_stn
prsstnc_dv1 0.015
annymty_dv1 0.053 0.000
age -0.750 -0.010 -0.049
femaleTRUE -0.463 -0.014 0.041 0.252
pol_stance -0.538 -0.009 -0.057 -0.003 0.062
prsstn_1:_1 0.019 0.000 -0.002 -0.045 -0.034 0.038
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
Quite weird that topic doesn’t get any variance at all. Perhaps due to small cluster size? With Bayesian estimation, it worked alright. Also, again no significant effects.
Estimate without nesting.
<-
fit_fe_2_frq lmer(
~
op_expr 1 +
1 | group) +
(* anonymity_dev + age + female + pol_stance + topic
persistence_dev data = d
,
)
summary(fit_fe_2_frq)
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: op_expr ~ 1 + (1 | group) + persistence_dev * anonymity_dev +
age + female + pol_stance + topic
Data: d
REML criterion at convergence: 7598
Scaled residuals:
Min 1Q Median 3Q Max
-1.513 -0.548 -0.188 0.265 13.577
Random effects:
Groups Name Variance Std.Dev.
group (Intercept) 11 3.32
Residual 155 12.46
Number of obs: 960, groups: group, 48
Fixed effects:
Estimate Std. Error df t value Pr(>|t|)
(Intercept) 6.3760 2.3261 511.8117 2.74 0.0063
persistence_dev1 -0.0195 0.6257 41.8742 -0.03 0.9753
anonymity_dev1 -0.3439 0.6284 42.6053 -0.55 0.5871
age 0.0854 0.0357 942.0312 2.39 0.0169
femaleTRUE -0.2641 0.8788 938.1103 -0.30 0.7638
pol_stance -0.0324 0.2058 941.3972 -0.16 0.8751
topicgender 0.4373 1.5334 41.9702 0.29 0.7769
topicmigration -1.3078 1.5329 41.9180 -0.85 0.3984
persistence_dev1:anonymity_dev1 0.1960 0.6268 42.1850 0.31 0.7561
(Intercept) **
persistence_dev1
anonymity_dev1
age *
femaleTRUE
pol_stance
topicgender
topicmigration
persistence_dev1:anonymity_dev1
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Correlation of Fixed Effects:
(Intr) prss_1 anny_1 age fmTRUE pl_stn tpcgnd tpcmgr
prsstnc_dv1 0.014
annymty_dv1 0.049 0.000
age -0.701 -0.010 -0.049
femaleTRUE -0.423 -0.014 0.041 0.252
pol_stance -0.489 -0.009 -0.057 -0.004 0.063
topicgender -0.325 0.000 -0.001 0.017 -0.024 -0.020
topicmigrtn -0.337 0.000 -0.001 0.024 0.000 -0.016 0.500
prsstn_1:_1 0.018 0.000 -0.001 -0.045 -0.033 0.038 -0.001 -0.002
Also shows no significant effects.
For curiosity, estimate also without hierarchical structure.
<-
fit_fe_3_frq lm(
~
op_expr 1 +
* anonymity_dev + topic + age + female + pol_stance
persistence_dev data = d
,
)
summary(fit_fe_3_frq)
Call:
lm(formula = op_expr ~ 1 + persistence_dev * anonymity_dev +
topic + age + female + pol_stance, data = d)
Residuals:
Min 1Q Median 3Q Max
-12.95 -7.46 -2.92 3.00 177.59
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.7503 2.2095 2.60 0.0094 **
persistence_dev1 -0.0223 0.4148 -0.05 0.9571
anonymity_dev1 -0.3598 0.4191 -0.86 0.3908
topicgender 0.4291 1.0174 0.42 0.6733
topicmigration -1.3114 1.0166 -1.29 0.1974
age 0.0901 0.0362 2.49 0.0129 *
femaleTRUE -0.2010 0.8932 -0.23 0.8220
pol_stance 0.0398 0.2087 0.19 0.8489
persistence_dev1:anonymity_dev1 0.2004 0.4166 0.48 0.6306
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 12.8 on 951 degrees of freedom
Multiple R-squared: 0.0115, Adjusted R-squared: 0.00315
F-statistic: 1.38 on 8 and 951 DF, p-value: 0.202
Also here, no significant effects.
Gender
As preregistered, let’s see if effects differ across genders.
<-
fit_fe_gen hush(
brm(
~
op_expr 1 + persistence_dev * anonymity_dev * gender + age + pol_stance +
1 | topic/group)
(data = d
, chains = 4
, cores = 4
, iter = 8000
, warmup = 2000
, family = zero_inflated_poisson()
, control = list(
, adapt_delta = .95
max_treedepth = 12
,
)
) )
Warning: There were 603 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems
Again, some warnings.
Let’s inspect model.
plot(fit_fe_gen, ask = FALSE)
Traceplots look alright.
Let’s look at results.
summary(fit_fe_gen)
Warning: There were 603 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Family: zero_inflated_poisson
Links: mu = log; zi = identity
Formula: op_expr ~ 1 + persistence_dev * anonymity_dev * gender + age + pol_stance + (1 | topic/group)
Data: d (Number of observations: 960)
Draws: 4 chains, each with iter = 8000; warmup = 2000; thin = 1;
total post-warmup draws = 24000
Multilevel Hyperparameters:
~topic (Number of levels: 3)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.21 0.21 0.01 0.84 1.00 3091 4051
~topic:group (Number of levels: 48)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.40 0.05 0.32 0.50 1.00 3874 8425
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI
Intercept 1.99 0.15 1.67 2.32
persistence_dev1 0.02 0.06 -0.10 0.14
anonymity_dev1 -0.04 0.06 -0.16 0.08
gendermale -0.00 0.02 -0.05 0.04
age 0.01 0.00 0.01 0.01
pol_stance -0.02 0.01 -0.03 -0.01
persistence_dev1:anonymity_dev1 0.01 0.06 -0.11 0.12
persistence_dev1:gendermale -0.08 0.02 -0.12 -0.03
anonymity_dev1:gendermale 0.07 0.02 0.03 0.11
persistence_dev1:anonymity_dev1:gendermale 0.05 0.02 0.00 0.09
Rhat Bulk_ESS Tail_ESS
Intercept 1.00 5113 5226
persistence_dev1 1.00 3885 6451
anonymity_dev1 1.00 4466 6784
gendermale 1.00 12495 8754
age 1.00 16372 7194
pol_stance 1.00 15721 15008
persistence_dev1:anonymity_dev1 1.00 4269 7151
persistence_dev1:gendermale 1.00 13393 12807
anonymity_dev1:gendermale 1.00 15059 13599
persistence_dev1:anonymity_dev1:gendermale 1.00 14762 13666
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
zi 0.21 0.01 0.19 0.24 1.00 14835 13287
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Indeed, several gender effects.
- For females, the effect of persistence is larger, that is more positive.
- For females, the effect of anonymity is smaller, that is more negative.
- For females, the interaction effect is also a bit smaller, that is more negative.
Let’s visualize results.
<- plot(
p_gen conditional_effects(
fit_fe_gen
), ask = FALSE,
plot = FALSE
)
<-
p_gen_pers "persistence_dev:gender"]] +
p_gen[[xlab("Persistence") +
ylab("Opinion expression") +
scale_y_continuous(
limits = c(4, 15),
breaks = c(5, 7.5, 10, 12.5, 15)
+
) scale_x_discrete(
limits = rev
+
) guides(
fill = "none"
color = "none"
,
)
<-
p_gen_anon "anonymity_dev:gender"]] +
p_gen[[xlab("Anonymity") +
ylab("Opinion expression") +
scale_y_continuous(
limits = c(3.5, 15),
breaks = c(5, 7.5, 10, 12.5, 15)
+
) theme(
axis.title.y = element_blank()
+
) guides(
fill = "none"
+
) scale_x_discrete(
limits = rev
+
) scale_color_discrete(
name = "Gender"
)
<- cowplot::plot_grid(
plot_gen
p_gen_pers, p_gen_anon, labels = c('A', 'B'),
nrow = 1,
rel_widths = c(4, 5)
)
plot_gen
ggsave("figures/results_gen.png", plot_gen, width = 8, height = 4)
Benefits
Let’s see if benefits differ across experimental groups.
We first look at the experimental group’s descriptives
|>
d group_by(persistence) |>
summarize(benefits_m = mean(benefits, na.rm = TRUE)) |>
as.data.frame() |>
kable()
persistence | benefits_m |
---|---|
high | 3.12 |
low | 3.23 |
Looking at persistence, we see people with lower persistence reporting slightly higher benefits.
|>
d group_by(anonymity) |>
summarize(benefits_m = mean(benefits, na.rm = TRUE)) |>
as.data.frame() |>
kable()
anonymity | benefits_m |
---|---|
high | 3.15 |
low | 3.20 |
Looking at anonymity, we see people with low anonymity reporting marginally higher benefits.
|>
d group_by(persistence, anonymity) |>
summarize(benefits_m = mean(benefits, na.rm = T)) |>
as.data.frame() |>
kable()
`summarise()` has grouped output by 'persistence'. You can override using the
`.groups` argument.
persistence | anonymity | benefits_m |
---|---|---|
high | high | 3.07 |
high | low | 3.18 |
low | high | 3.22 |
low | low | 3.23 |
Looking at both groups combined, we see that low anonymity and low persistence yielded highest benefits.
Let’s look if effects are significant.
<-
fit_fe_ben_1 hush(
brm(
~
benefits 1 + persistence_dev * anonymity_dev + age + female + pol_stance +
1 | topic/group)
(data = d
, chains = 4
, cores = 4
, iter = 6000
, warmup = 2000
, control = list(
, adapt_delta = .95
max_treedepth = 12
,
)
) )
Warning: Rows containing NAs were excluded from the model.
Warning: There were 160 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems
Let’s inspect model.
plot(fit_fe_ben_1, ask = FALSE)
Traceplots look alright.
Let’s look at results.
summary(fit_fe_ben_1)
Warning: There were 160 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Family: gaussian
Links: mu = identity; sigma = identity
Formula: benefits ~ 1 + persistence_dev * anonymity_dev + age + female + pol_stance + (1 | topic/group)
Data: d (Number of observations: 705)
Draws: 4 chains, each with iter = 6000; warmup = 2000; thin = 1;
total post-warmup draws = 16000
Multilevel Hyperparameters:
~topic (Number of levels: 3)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.13 0.17 0.00 0.63 1.00 2141 2071
~topic:group (Number of levels: 48)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.08 0.05 0.00 0.17 1.00 3801 6162
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat
Intercept 3.21 0.17 2.85 3.55 1.00
persistence_dev1 -0.05 0.03 -0.11 0.01 1.00
anonymity_dev1 -0.03 0.03 -0.09 0.03 1.00
age -0.00 0.00 -0.01 0.00 1.00
femaleTRUE -0.07 0.06 -0.19 0.04 1.00
pol_stance 0.02 0.01 -0.01 0.04 1.00
persistence_dev1:anonymity_dev1 -0.02 0.03 -0.08 0.04 1.00
Bulk_ESS Tail_ESS
Intercept 2614 903
persistence_dev1 10311 8192
anonymity_dev1 9308 9903
age 18813 12042
femaleTRUE 13006 9174
pol_stance 10421 10613
persistence_dev1:anonymity_dev1 10541 7780
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 0.74 0.02 0.70 0.78 1.00 12561 9668
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
No significant effects. But note that effect of persistence on perceived benefits only marginally not significant.
Costs
Let’s see if perceived differed across experimental groups.
We first look at the experimental group’s descriptives
|>
d group_by(persistence) |>
summarize(costs = mean(costs, na.rm = TRUE)) |>
as.data.frame() |>
kable()
persistence | costs |
---|---|
high | 1.99 |
low | 1.99 |
Looking at persistence, we see both groups report equal costs.
|>
d group_by(anonymity) |>
summarize(costs = mean(costs, na.rm = TRUE)) |>
as.data.frame() |>
kable()
anonymity | costs |
---|---|
high | 1.89 |
low | 2.09 |
Looking at anonymity, we see people with low anonymity report slightly higher costs.
|>
d group_by(persistence, anonymity) |>
summarize(costs = mean(costs, na.rm = TRUE)) |>
as.data.frame() |>
kable()
`summarise()` has grouped output by 'persistence'. You can override using the
`.groups` argument.
persistence | anonymity | costs |
---|---|---|
high | high | 1.90 |
high | low | 2.07 |
low | high | 1.87 |
low | low | 2.11 |
Looking at both groups combined, we see that highest costs were reported by group with low anonymity and low persistence.
Let’s look if effects are significant.
<-
fit_fe_costs_1 hush(
brm(
~
costs 1 + persistence_dev * anonymity_dev + age + female + pol_stance +
1 | topic/group)
(data = d
, chains = 4
, cores = 4
, iter = 8000
, warmup = 2000
, control = list(
, adapt_delta = .95
max_treedepth = 12
,
)
) )
Warning: Rows containing NAs were excluded from the model.
Warning: There were 186 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems
Let’s inspect model.
plot(fit_fe_costs_1, ask = FALSE)
Traceplots look alright.
Let’s look at results.
summary(fit_fe_costs_1)
Warning: There were 186 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Family: gaussian
Links: mu = identity; sigma = identity
Formula: costs ~ 1 + persistence_dev * anonymity_dev + age + female + pol_stance + (1 | topic/group)
Data: d (Number of observations: 705)
Draws: 4 chains, each with iter = 8000; warmup = 2000; thin = 1;
total post-warmup draws = 24000
Multilevel Hyperparameters:
~topic (Number of levels: 3)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.13 0.19 0.00 0.66 1.00 3819 2147
~topic:group (Number of levels: 48)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.07 0.05 0.00 0.17 1.00 7146 9973
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat
Intercept 2.48 0.19 2.11 2.86 1.00
persistence_dev1 0.00 0.03 -0.07 0.07 1.00
anonymity_dev1 -0.08 0.03 -0.15 -0.02 1.00
age -0.01 0.00 -0.02 -0.01 1.00
femaleTRUE 0.01 0.07 -0.12 0.14 1.00
pol_stance -0.00 0.02 -0.04 0.03 1.00
persistence_dev1:anonymity_dev1 0.02 0.03 -0.05 0.09 1.00
Bulk_ESS Tail_ESS
Intercept 5196 2440
persistence_dev1 26569 16662
anonymity_dev1 25889 17303
age 34343 18126
femaleTRUE 25883 16648
pol_stance 34809 17089
persistence_dev1:anonymity_dev1 24338 13993
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 0.84 0.02 0.80 0.89 1.00 30590 16079
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
We find that anonymity does reduce costs.
Mediation
Let’s see if perceived benefits and costs were associated with increased opinion expressions.
<-
fit_fe_med hush(
brm(
~
op_expr 1 + persistence_dev * anonymity_dev + benefits + costs + age + female + pol_stance +
1 | topic/group)
(data = d
, chains = 4
, cores = 4
, iter = 6000
, warmup = 2000
, family = zero_inflated_poisson()
, control = list(
, adapt_delta = .95
max_treedepth = 12
,
)
) )
Warning: Rows containing NAs were excluded from the model.
Warning: There were 195 divergent transitions after warmup. See
https://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
to find out why this is a problem and how to eliminate them.
Warning: Examine the pairs() plot to diagnose sampling problems
Let’s look at results.
summary(fit_fe_med)
Warning: There were 195 divergent transitions after warmup. Increasing
adapt_delta above 0.95 may help. See
http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup
Family: zero_inflated_poisson
Links: mu = log; zi = identity
Formula: op_expr ~ 1 + persistence_dev * anonymity_dev + benefits + costs + age + female + pol_stance + (1 | topic/group)
Data: d (Number of observations: 705)
Draws: 4 chains, each with iter = 6000; warmup = 2000; thin = 1;
total post-warmup draws = 16000
Multilevel Hyperparameters:
~topic (Number of levels: 3)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.19 0.23 0.01 0.86 1.00 2218 4466
~topic:group (Number of levels: 48)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.41 0.05 0.32 0.51 1.00 3002 4917
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat
Intercept 1.96 0.16 1.63 2.30 1.00
persistence_dev1 -0.03 0.06 -0.14 0.09 1.00
anonymity_dev1 0.00 0.06 -0.12 0.12 1.00
benefits 0.11 0.02 0.08 0.14 1.00
costs -0.09 0.01 -0.12 -0.07 1.00
age 0.01 0.00 0.01 0.01 1.00
femaleTRUE 0.00 0.02 -0.05 0.05 1.00
pol_stance -0.02 0.01 -0.03 -0.00 1.00
persistence_dev1:anonymity_dev1 0.02 0.06 -0.10 0.14 1.00
Bulk_ESS Tail_ESS
Intercept 4690 4632
persistence_dev1 2325 3690
anonymity_dev1 2431 3821
benefits 12168 11167
costs 11977 10166
age 21747 12851
femaleTRUE 12484 10183
pol_stance 11991 9162
persistence_dev1:anonymity_dev1 2662 4144
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
zi 0.08 0.01 0.06 0.10 1.00 12415 10719
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
We find that increased perceived costs are associated with decreased opinion expressions. Increased benefits are associated with increased opinion expressions. Let’s check if overall effect is significant.
<- fixef(fit_fe_costs_1)["anonymity_dev1", "Estimate"]
anon_costs_a_b <- fixef(fit_fe_costs_1)["anonymity_dev1", "Est.Error"]
anon_costs_a_se <- rnorm(10000, anon_costs_a_b, anon_costs_a_se)
anon_costs_a_dis
<- fixef(fit_fe_med)["benefits", "Estimate"]
anon_costs_b_b <- fixef(fit_fe_med)["benefits", "Est.Error"]
anon_costs_b_se <- rnorm(10000, anon_costs_b_b, anon_costs_b_se)
anon_costs_b_dis
<- anon_costs_a_dis * anon_costs_b_dis
anon_costs_ab_dis <- median(anon_costs_ab_dis)
anon_costs_ab_m <- quantile(anon_costs_ab_dis, .025)
anon_costs_ab_ll <- quantile(anon_costs_ab_dis, .975) anon_costs_ab_ul
The effect is significant (b = -0.01, 95% MC CI [-0.02, 0]).
Save
save.image("data/image.RData")