This file includes code used to clean, analyze data, and draw plots from the study’s experiment. The source data is also stored in this repository.
renv.lock
lists all package versions used for the analysis; (https://rstudio.github.io/renv/articles/renv.html)[renv] can be used to install them.
The dataset has one row per participant-article combination. Each participant saw eight articles. The dataset is joined with the participant’s pre-survey data collected four months previously.
As part of the data cleaning, friendly labels are assigned to some raw qualtrics pre-survey responses.
## Read in the baseline info and the new survey info
path <- c("")
paired_data <-
read.csv(paste(path, "input/experiment-data-paired-deidentified.csv", sep = ""))
## Drop participants with no set political affiliation -- data collection error
paired_data <- paired_data %>% filter(!is.na(POL_AFFIL_updated))
## Drop the example article from the dataset
paired_data <- paired_data %>% filter(article_true_leaning != "neither")
paired_data$POL_AFFIL_updated[which(paired_data$POL_AFFIL_updated ==
1)] <- 0
paired_data$POL_AFFIL_updated[which(paired_data$POL_AFFIL_updated ==
4)] <- 1
paired_data <-
paired_data %>%
mutate(political_affiliation = case_when(
POL_AFFIL_updated == 0 ~ 'Liberal',
POL_AFFIL_updated == 1 ~ 'Conservative'))
paired_data <- paired_data %>% mutate(
political_affiliation = as.factor(political_affiliation),
label_type = as.factor(label_type),
article_user_is_opposed_content = as.factor(article_user_is_opposed_content),
article_user_is_opposed_source = as.factor(article_user_is_opposed_source),
condition = as.numeric(condition),
num_decetively_seen = as.numeric(num_decetively_seen),
actual_article_presentation_index = as.numeric(actual_article_presentation_index),
DEMOG_Age = as.numeric(DEMOG_Age),
DEMOG_Gender = as.factor(DEMOG_Gender),
DEMOG_White = as.factor(DEMOG_White),
DEMOG_College_Degree = as.factor(DEMOG_College_Degree),
all_cum_lib_deceptive = as.numeric(all_cum_lib_deceptive),
all_cum_cons_deceptive = as.numeric(all_cum_cons_deceptive),
article_is_content_blended = as.factor(article_is_content_blended),
num_content_blended_seen = as.numeric(num_content_blended_seen),
article_is_source_blended = as.factor(article_is_source_blended)
)
loadings = function(res.pca){
# <http://factominer.free.fr/question/FAQ.html>
#
# Loadings (i.e. standard coordinates) are not given by FactoMineR's methods. They return principal coordinates.
# You can calculate them by dividing variables' coordinates on a dimension by this dimension's eigenvalue's square root.
# Just type:
# sweep(res.pca$var$coord,2,sqrt(res.pca$eig[1:ncol(res.pca$var$coord),
return(sweep(res.pca$var$coord,2,sqrt(res.pca$eig[1:ncol(res.pca$var$coord),1]),FUN="/"))
}
Items:
DV_agreeable.items = c(
"article_survey_q_find_issue_agreeable",
"article_survey_q_find_issue_confirming",
"article_survey_q_find_issue_rational",
"article_survey_q_find_issue_supporting_my_views",
"article_survey_q_find_issue_meeting_my_expectations"
)
pca.tmp = PCA(paired_data[, DV_agreeable.items], graph = FALSE)
as.data.frame(pca.tmp$eig)
eigenvalue | percentage of variance | cumulative percentage of variance | |
---|---|---|---|
comp 1 | 4.11 | 82.24 | 82.24 |
comp 2 | 0.31 | 6.15 | 88.39 |
comp 3 | 0.22 | 4.43 | 92.83 |
comp 4 | 0.21 | 4.21 | 97.03 |
comp 5 | 0.15 | 2.97 | 100.00 |
as.data.frame(loadings(pca.tmp))
Dim.1 | Dim.2 | Dim.3 | Dim.4 | Dim.5 | |
---|---|---|---|---|---|
article_survey_q_find_issue_agreeable | 0.44 | -0.51 | 0.66 | -0.31 | -0.10 |
article_survey_q_find_issue_confirming | 0.45 | -0.30 | -0.13 | 0.76 | 0.32 |
article_survey_q_find_issue_rational | 0.45 | -0.03 | -0.52 | -0.56 | 0.45 |
article_survey_q_find_issue_supporting_my_views | 0.46 | 0.07 | -0.35 | 0.04 | -0.81 |
article_survey_q_find_issue_meeting_my_expectations | 0.43 | 0.80 | 0.38 | 0.07 | 0.15 |
paired_data = paired_data %>%
mutate(DV_agreeable = rowMeans(select(., DV_agreeable.items)))
Items:
DV_credible.items = c(
"article_survey_q_believable",
"article_survey_q_truthful" ,
"article_survey_q_credible"
)
pca.tmp = PCA(paired_data[, DV_credible.items], graph = FALSE)
as.data.frame(pca.tmp$eig)
eigenvalue | percentage of variance | cumulative percentage of variance | |
---|---|---|---|
comp 1 | 2.87 | 95.75 | 95.75 |
comp 2 | 0.08 | 2.53 | 98.28 |
comp 3 | 0.05 | 1.72 | 100.00 |
as.data.frame(loadings(pca.tmp))
Dim.1 | Dim.2 | Dim.3 | |
---|---|---|---|
article_survey_q_believable | 0.57 | 0.82 | 0.06 |
article_survey_q_truthful | 0.58 | -0.35 | -0.74 |
article_survey_q_credible | 0.58 | -0.46 | 0.67 |
paired_data = paired_data %>%
mutate(DV_credible = rowMeans(select(., DV_credible.items)))
Items:
DV_sharing_nonnegative.items = c(
"article_survey_q_like_article",
"article_survey_q_post_support_comment",
"article_survey_q_share_without_comment",
"article_survey_q_share_with_quote",
"article_survey_q_share_with_supporting_comment"
)
pca.tmp = PCA(paired_data[, DV_sharing_nonnegative.items], graph = FALSE)
as.data.frame(pca.tmp$eig)
eigenvalue | percentage of variance | cumulative percentage of variance | |
---|---|---|---|
comp 1 | 4.02 | 80.35 | 80.35 |
comp 2 | 0.43 | 8.69 | 89.04 |
comp 3 | 0.28 | 5.58 | 94.62 |
comp 4 | 0.17 | 3.38 | 98.00 |
comp 5 | 0.10 | 2.00 | 100.00 |
as.data.frame(loadings(pca.tmp))
Dim.1 | Dim.2 | Dim.3 | Dim.4 | Dim.5 | |
---|---|---|---|---|---|
article_survey_q_like_article | 0.41 | 0.85 | -0.24 | 0.24 | -0.04 |
article_survey_q_post_support_comment | 0.47 | -0.15 | -0.36 | -0.53 | 0.59 |
article_survey_q_share_without_comment | 0.44 | 0.09 | 0.87 | -0.18 | 0.05 |
article_survey_q_share_with_quote | 0.45 | -0.43 | -0.04 | 0.75 | 0.19 |
article_survey_q_share_with_supporting_comment | 0.47 | -0.26 | -0.21 | -0.24 | -0.78 |
paired_data = paired_data %>%
mutate(DV_sharing_nonnegative = rowMeans(select(., DV_sharing_nonnegative.items)))
Items:
DV_sharing_negative.items = c(
"article_survey_q_post_opposing_comment",
"article_survey_q_share_with_opposing_comment"
)
pca.tmp = PCA(paired_data[, DV_sharing_negative.items], graph = FALSE)
as.data.frame(pca.tmp$eig)
eigenvalue | percentage of variance | cumulative percentage of variance | |
---|---|---|---|
comp 1 | 1.87 | 93.49 | 93.49 |
comp 2 | 0.13 | 6.51 | 100.00 |
as.data.frame(loadings(pca.tmp))
Dim.1 | Dim.2 | |
---|---|---|
article_survey_q_post_opposing_comment | 0.71 | -0.71 |
article_survey_q_share_with_opposing_comment | 0.71 | 0.71 |
paired_data = paired_data %>%
mutate(DV_sharing_negative = rowMeans(select(., DV_sharing_negative.items)))
A measure of confirmation bias is suggested by (Kim et al., 2019); Modified from: (Brown et al., 2014):
Confirmation_Bias = ("Position on Article" x "Importance") / 100
Divided by 100 to keep on the same scale as all other DVs.
paired_data = paired_data %>%
mutate(DV_confirmation_bias = article_survey_q_find_issue_important * article_survey_q_article_position / 100)
An article is “unexpected” if a friendly source presents unfriendly content, and vice versa.
paired_data = paired_data %>%
mutate( unexpected = ( article_user_is_opposed_content != article_user_is_opposed_source ) )
Correlations between each dependent variable and confirmation bias.
correlation_data <-
paired_data[, c(
"DV_confirmation_bias",
"DV_sharing_nonnegative",
"DV_sharing_negative",
"DV_agreeable",
"DV_credible"
)]
correlations = rcorr(as.matrix(correlation_data))
correlations.r = as.data.frame(correlations$r)['DV_confirmation_bias']
knitr::kable(correlations.r, caption="Pearson's `r`")
DV_confirmation_bias | |
---|---|
DV_confirmation_bias | 1.0000000 |
DV_sharing_nonnegative | 0.5549678 |
DV_sharing_negative | 0.0725429 |
DV_agreeable | 0.6675339 |
DV_credible | 0.5448545 |
correlations.P = as.data.frame(correlations$P)['DV_confirmation_bias']
knitr::kable(correlations.P, caption='p-values')
DV_confirmation_bias | |
---|---|
DV_confirmation_bias | NA |
DV_sharing_nonnegative | 0.0000000 |
DV_sharing_negative | 0.0183895 |
DV_agreeable | 0.0000000 |
DV_credible | 0.0000000 |
This section shows the fitting of each model included in the paper.
All models used the following independent variables:
Three-way interaction term:
article_user_is_opposed_content * article_user_is_opposed_source * political_affiliation
This is a full three-way interaction of three factors that specifies all possible main effects, two-way, and three-way interactions between the factors.
Repeated measures:
Repeated measures were accounted for by specifying a random intercept per participant:
(1 | uid)
Control variables:
All models used the following control variables:
Count_TRUST
: The number of publication sources the given participant said they “trusted” in the presurvey.condition
: The randomly assigned condition.all_cum_lib_deceptive
: For liberals, how many “unexpected” articles had been seen so far.all_cum_cons_deceptive
: For conservatives, how many “unexpected” articles had been seen so far.actual_article_presentation_index
: How many articles the participant had seen so far.DEMOG_Age
: A participant’s age.DEMOG_Gender
: A participant’s gender.DEMOG_White
: Whether a participant self-identified as white.DEMOG_College_Degree
: Whether a participant had a college degree.formula.base =
" ~ article_user_is_opposed_content * article_user_is_opposed_source * political_affiliation +
(1 | UID) +
Count_TRUST + condition +
all_cum_lib_deceptive + all_cum_cons_deceptive + actual_article_presentation_index +
DEMOG_Age + DEMOG_Gender + DEMOG_White + DEMOG_College_Degree"
This section describes the two attitudes and beliefs-related dvs: “Agreeable” and “Credible”
Agreeable.formula = formula(paste0('DV_agreeable', formula.base))
Agreeable.model <-
lmer(
Agreeable.formula,
data = paired_data
)
# Agreeable -- pairwise contrasts ----------------------------------------
Agreeable.model.pairwise = do_pairwise_content_source(Agreeable.model)
# Agreeable.model.pairwise$emm
as.data.frame(Agreeable.model.pairwise$contrasts) %>%
mutate(across(where(is.numeric), ~as.numeric(number(., accuracy=0.01))))
contrast | estimate | SE | df | t.ratio | p.value |
---|---|---|---|---|---|
Fully friendly vs Friendly-source/unfriendly-content | 18.02 | 2.77 | 937.43 | 6.51 | 0.00 |
Fully friendly vs Unfriendly-source/friendly-content | 8.75 | 2.76 | 937.19 | 3.17 | 0.00 |
Fully friendly vs Fully unfriendly | 25.40 | 2.06 | 908.32 | 12.34 | 0.00 |
Friendly-source/unfriendly-content vs Unfriendly-source/friendly-content | -9.27 | 2.79 | 908.18 | -3.32 | 0.00 |
Friendly-source/unfriendly-content vs Fully unfriendly | 7.38 | 2.76 | 937.43 | 2.68 | 0.01 |
Unfriendly source-friendly content vs Fully unfriendly | 16.65 | 2.75 | 937.23 | 6.05 | 0.00 |
Friendly source vs Unfriendly source | 8.07 | 1.73 | 908.19 | 4.65 | 0.00 |
Friendly content vs Unfriendly content | 17.33 | 1.73 | 908.28 | 10.00 | 0.00 |
# Agreeable -- plots ----
plot_agreeable(Agreeable.model,custom.ylim = c(40,85))
plot_opposed_vs_blended_content(Agreeable.model, 'Agreeable', custom.ylim = c(40,80))
Credible.formula = formula(paste0('DV_credible', formula.base))
Credible.model <-
lmer(
Credible.formula,
data = paired_data
)
# Credible -- contrasts ----------------------------------------------
Credible.model.pairwise = do_pairwise_content_source(Credible.model)
# Credible.model.pairwise$emm
as.data.frame(Credible.model.pairwise$contrasts) %>%
mutate(across(where(is.numeric), ~as.numeric(number(., accuracy=0.01))))
contrast | estimate | SE | df | t.ratio | p.value |
---|---|---|---|---|---|
Fully friendly vs Friendly-source/unfriendly-content | 13.62 | 2.84 | 942.09 | 4.81 | 0.00 |
Fully friendly vs Unfriendly-source/friendly-content | 6.93 | 2.83 | 941.77 | 2.45 | 0.01 |
Fully friendly vs Fully unfriendly | 25.71 | 2.11 | 908.40 | 12.17 | 0.00 |
Friendly-source/unfriendly-content vs Unfriendly-source/friendly-content | -6.70 | 2.86 | 908.24 | -2.34 | 0.02 |
Friendly-source/unfriendly-content vs Fully unfriendly | 12.09 | 2.83 | 942.00 | 4.28 | 0.00 |
Unfriendly source-friendly content vs Fully unfriendly | 18.79 | 2.82 | 941.73 | 6.66 | 0.00 |
Friendly source vs Unfriendly source | 9.51 | 1.78 | 908.24 | 5.34 | 0.00 |
Friendly content vs Unfriendly content | 16.21 | 1.78 | 908.35 | 9.10 | 0.00 |
# Credible -- plots ----------------------------------------------
plot_credible(Credible.model, custom.ylim = c(40,85))
plot_opposed_vs_blended_content(Credible.model, 'Credible', custom.ylim = c(40,80))
Model output for Credible and Agreeable Attitudes and Beliefs models.
tab_model(Credible.model, Agreeable.model)
DV_credible | DV_agreeable | |||||
---|---|---|---|---|---|---|
Predictors | Estimates | CI | p | Estimates | CI | p |
(Intercept) | 77.81 | 65.07 – 90.54 | <0.001 | 69.91 | 56.32 – 83.50 | <0.001 |
article_user_is_opposed_content [TRUE] |
-14.38 | -23.43 – -5.32 | 0.002 | -22.28 | -31.17 – -13.40 | <0.001 |
article_user_is_opposed_source [TRUE] |
-5.76 | -14.88 – 3.36 | 0.216 | -8.87 | -17.83 – 0.09 | 0.052 |
political_affiliation [Liberal] |
0.79 | -6.28 – 7.86 | 0.827 | -6.91 | -14.23 – 0.42 | 0.065 |
Count_TRUST | 0.56 | 0.28 – 0.85 | <0.001 | 0.66 | 0.35 – 0.97 | <0.001 |
condition | 1.23 | -1.83 – 4.30 | 0.430 | -0.07 | -3.22 – 3.08 | 0.965 |
all_cum_lib_deceptive | 2.23 | -1.23 – 5.69 | 0.206 | 2.16 | -1.32 – 5.64 | 0.223 |
all_cum_cons_deceptive | 0.58 | -2.83 – 3.99 | 0.738 | -2.50 | -5.91 – 0.91 | 0.151 |
actual_article_presentation_index | -0.01 | -1.02 – 1.00 | 0.986 | 0.38 | -0.61 – 1.37 | 0.449 |
DEMOG_Age | -0.15 | -0.34 – 0.03 | 0.099 | -0.06 | -0.26 – 0.14 | 0.566 |
DEMOG_Gender [Male] | -4.11 | -8.57 – 0.36 | 0.071 | 0.27 | -4.59 – 5.13 | 0.913 |
DEMOG_White [1] | -2.17 | -7.90 – 3.55 | 0.457 | -5.85 | -12.07 – 0.38 | 0.066 |
DEMOG_College_Degree [Yes] |
1.12 | -3.76 – 6.00 | 0.654 | -2.99 | -8.30 – 2.32 | 0.270 |
article_user_is_opposed_content [TRUE] * article_user_is_opposed_source [TRUE] |
-5.43 | -18.95 – 8.09 | 0.431 | 7.36 | -5.99 – 20.70 | 0.280 |
article_user_is_opposed_content [TRUE] * political_affiliation [Liberal] |
1.51 | -9.13 – 12.14 | 0.781 | 8.54 | -1.95 – 19.02 | 0.110 |
article_user_is_opposed_source [TRUE] * political_affiliation [Liberal] |
-2.34 | -13.11 – 8.44 | 0.671 | 0.24 | -10.41 – 10.89 | 0.964 |
(article_user_is_opposed_content [TRUE] article_user_is_opposed_source [TRUE]) political_affiliation [Liberal] |
0.53 | -14.76 – 15.83 | 0.946 | -11.98 | -27.18 – 3.21 | 0.122 |
Random Effects | ||||||
σ2 | 623.21 | 590.85 | ||||
τ00 | 77.16 UID | 110.03 UID | ||||
ICC | 0.11 | 0.16 | ||||
N | 131 UID | 131 UID | ||||
Observations | 1048 | 1048 | ||||
Marginal R2 / Conditional R2 | 0.186 / 0.275 | 0.185 / 0.313 |
Brown, S. A., Venkatesh, V., & Goyal, S. (2014). Expectation Confirmation in Information Systems Research. MIS Quarterly, 38(3), 729-A729.
Kim, A., & Dennis, A. R. (2019). SAYS WHO? THE EFFECTS OF PRESENTATION FORMAT AND SOURCE RATING ON FAKE NEWS IN SOCIAL MEDIA. MIS Quarterly, 43(3).
Kim, A., Moravec, P. L., & Dennis, A. R. (2019). Combating Fake News on Social Media with Source Ratings: The Effects of User and Expert Reputation Ratings. Journal of Management Information Systems, 36(3), 931-968.
Moravec, P., Minas, R., & Dennis, A. (2019). Fake News on Social Media: People Believe what They want to Believe when it Makes no Sense at All. MIS Quarterly, 43(4).
Social Media Engagement Intentions
First, plots for positive social media engagement intentions are shown; then, plots negative ones; then, full model output is shown.
Positive Social Media Engagement Intentions
Negative Intentions
[TRUE]
[TRUE]
[Liberal]
[Yes]
[TRUE] *
article_user_is_opposed_source
[TRUE]
[TRUE] *
political_affiliation
[Liberal]
[TRUE] *
political_affiliation
[Liberal]
[TRUE]
article_user_is_opposed_source
[TRUE])
political_affiliation
[Liberal]
Negative Intentions - expected vs unexpected
A follow-up test prompted by unexpected results for DV_sharing_negative explored whether differences existed between expected and unepected articles.
Model Output
[TRUE]
[TRUE]
[Liberal]
[Yes]
[TRUE] *
article_user_is_opposed_source
[TRUE]
[TRUE] *
political_affiliation
[Liberal]
[TRUE] *
political_affiliation
[Liberal]
[TRUE]
article_user_is_opposed_source
[TRUE])
political_affiliation
[Liberal]