Load packages + set working directory

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
library(dplyr)
library(tidyr)
library(lme4)
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
library(lmerTest)
## 
## Attaching package: 'lmerTest'
## 
## The following object is masked from 'package:lme4':
## 
##     lmer
## 
## The following object is masked from 'package:stats':
## 
##     step
library(emmeans)
## Welcome to emmeans.
## Caution: You lose important information if you filter this package's results.
## See '? untidy'
library(car)
## Loading required package: carData
## Registered S3 method overwritten by 'car':
##   method           from
##   na.action.merMod lme4
## 
## Attaching package: 'car'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following object is masked from 'package:purrr':
## 
##     some
library(ggplot2)
library(ez)
library(psychReport)


setwd(dirname(rstudioapi::getActiveDocumentContext()$path))

Reading questionnaire data:

This includes only the demographic questions, consent form and debrief questions - not the actual experimental questions.

questionnaire <- read_xlsx("data_exp_274822-v35 2/data_exp_274822-v35_questionnaires.xlsx")

Data wrangling

Selecting relevant columns for questionnaire

questionnaire_filtered <- questionnaire %>%
  select(`Participant Private ID`, Question, Response)

filtering debrief test question to see if any participants failed the debrief test

debrief_test <- questionnaire_filtered %>%
  filter(Question == "What were you asked to do in this study?")
No participants failed the debrief test!

Reading task data - this includes all the experimental questions, including attention checks

tasks <- read_xlsx("data_exp_274822-v35 2/data_exp_274822-v35_tasks.xlsx")

Selecting only the relevant columns for in the task data set

tasks_filtered <- tasks %>%
  select(`Participant Private ID`, Display, Response, Correct, `Object Name`, `Spreadsheet: FileName`, `Spreadsheet: InfluencerType`) %>%
  mutate(Correct = as.numeric(Correct))

tasks_filtered_wide <- tasks_filtered %>%
  pivot_wider(
    id_cols = `Participant Private ID`, 
    names_from = Display,
    values_from = Response
  )
## Warning: Values from `Response` are not uniquely identified; output will contain
## list-cols.
## • Use `values_fn = list` to suppress this warning.
## • Use `values_fn = {summary_fun}` to summarise duplicates.
## • Use the following dplyr code to identify duplicates.
##   {data} |>
##   dplyr::summarise(n = dplyr::n(), .by = c(`Participant Private ID`, Display))
##   |>
##   dplyr::filter(n > 1L)

Filtering out all N/As from Response column to remove all erroneous responses.

The data set includes ‘responses’ for the image stimuli as N/A, which is erroneous data

tasks_filtered_clean <- tasks_filtered %>%
  filter(!is.na(Response))

Creating separate data frames for each display to filter out N/As individually - each display corresponds to either the Instagram task, the isolated faces task, or the attention checks in each of these trials.

instagram <- tasks_filtered_clean %>%
  filter(Display == "Instagram Profiles Trial")

attention_check_instagram <- tasks_filtered_clean %>%
  filter(Display == "Attention Check") 

isolated <- tasks_filtered_clean %>%
  filter(Display == "Isolated Images Trial")

attention_check_isolated <- tasks_filtered_clean %>%
  filter(Display == "Attention Check Isolated")

Combining all data frames back into one

all_tasks_clean <- bind_rows(instagram, attention_check_instagram, isolated, attention_check_isolated)

Figuring out who did not meet attention check requirements

attention_check_score <- all_tasks_clean %>%
  filter(Display %in% c("Attention Check", "Attention Check Isolated")) %>%
  group_by(`Participant Private ID`) %>%
  summarise(attention_check_score = sum(Correct, na.rm = TRUE))

Excluding participants who did not meet attention check requirements

all_tasks_clean <- bind_rows(instagram, attention_check_instagram, isolated, attention_check_isolated, attention_check_score) %>%
  mutate(attention_check_score = as.numeric(attention_check_score)) %>%
  group_by(`Participant Private ID`) %>%
  filter(max(attention_check_score, na.rm = TRUE) >= 6) %>%
  ungroup()

Removing attention check and attention check scores rows for analysis

all_tasks_analysis <- all_tasks_clean %>%
  filter(Display %in% c("Instagram Profiles Trial", "Isolated Images Trial"))

Added condition column to data set to track what type of stimuli corresponded to each response

all_tasks_analysis <- all_tasks_analysis %>%
  mutate(
    condition = case_when(
      str_detect(`Spreadsheet: FileName`, "t70") ~ "High Truncation AI t70",
      str_detect(`Spreadsheet: FileName`, "t30") ~ "Low Truncation AI t30",
      str_detect(`Spreadsheet: FileName`, "chat") ~ "Real Edited",
      str_detect(`Spreadsheet: FileName`, "og") ~ "Real Unedited",
      TRUE ~ "Other"
    ),
    real_or_ai = case_when(
      str_detect(`Spreadsheet: FileName`, "A") ~ "AI",
      str_detect(`Spreadsheet: FileName`, "R") ~ "Real",
      TRUE ~ "Other"
    ),
    Response = as.numeric(Response))

Final line - changing Response to numeric value as it was originally a character

Another way to exclude participants - manually
excluded_ids <- c(16368687, 16368758)

all_tasks_analysis <- all_tasks_analysis %>%
  filter(!`Participant Private ID` %in% excluded_ids)

All_tasks_analysis data frame is ready for analysis!

Creating new dataframe with only trustworthiness question and not the questions about online behaviours (which were only presented in the Instagram)

trustworthiness_rating_analysis = all_tasks_analysis %>% 
  filter(`Object Name` == "Trustworthiness Scale")

Added follower_count column to denote high and low follower count Instagram stimuli

trustworthiness_rating_analysis <- trustworthiness_rating_analysis %>%
  mutate(
    follower_count = case_when(
      str_detect(`Spreadsheet: FileName`, "_h") ~ "High follower count",
      str_detect(`Spreadsheet: FileName`, "_l") ~ "Low follower count",
      TRUE ~ "Other"
    ))

Descriptive analysis

Descriptives for RQ1 - Are AI faces more trustworthy than Real faces

Descriptive statistics for each condition

descriptives <- trustworthiness_rating_analysis %>%
  group_by(real_or_ai, Display) %>%
  summarise(
    mean_trust = mean(Response, na.rm = TRUE),
    sd_trust = sd(Response, na.rm = TRUE),
    n = sum(!is.na(Response)),
    se = sd_trust / sqrt(n),
    ci = qt(0.975, df = n - 1) * se,
    .groups = "drop"
  )

descriptives
## # A tibble: 4 × 7
##   real_or_ai Display                  mean_trust sd_trust     n     se     ci
##   <chr>      <chr>                         <dbl>    <dbl> <int>  <dbl>  <dbl>
## 1 AI         Instagram Profiles Trial       3.99     1.40   980 0.0447 0.0877
## 2 AI         Isolated Images Trial          4.53     1.45   980 0.0464 0.0910
## 3 Real       Instagram Profiles Trial       3.79     1.44   980 0.0461 0.0904
## 4 Real       Isolated Images Trial          4.07     1.43   980 0.0458 0.0899

Calculating mean and standard deviation for real/AI main effect

aggregate(Response ~ real_or_ai, data = trustworthiness_rating_analysis, FUN = mean)
##   real_or_ai Response
## 1         AI 4.257143
## 2       Real 3.934694
aggregate(Response ~ real_or_ai, data = trustworthiness_rating_analysis, FUN = sd)
##   real_or_ai Response
## 1         AI 1.450282
## 2       Real 1.444505

Calculating mean and standard deviation for isolated/Instagram main effect

aggregate(Response ~ Display, data = trustworthiness_rating_analysis, FUN = mean)
##                    Display Response
## 1 Instagram Profiles Trial 3.891327
## 2    Isolated Images Trial 4.300510
aggregate(Response ~ Display, data = trustworthiness_rating_analysis, FUN = sd)
##                    Display Response
## 1 Instagram Profiles Trial 1.423541
## 2    Isolated Images Trial 1.460034

Filtering for only Instagram condition to get follower count descriptives

follower_trustworthiness <- trustworthiness_rating_analysis %>%
  filter(Display == "Instagram Profiles Trial")

Descriptives for follower count and trustworthiness

aggregate(Response ~ follower_count, data = follower_trustworthiness, FUN = mean)
##        follower_count Response
## 1 High follower count 3.915306
## 2  Low follower count 3.867347
aggregate(Response ~ follower_count, data = follower_trustworthiness, FUN = sd)
##        follower_count Response
## 1 High follower count 1.439970
## 2  Low follower count 1.407246

Descriptives for RQ3 What is the effect of truncation on trustworthiness for AI faces

AI truncation descriptives

ai_truncation_descriptives <- trustworthiness_rating_analysis %>%
  filter(real_or_ai == "AI") %>%
  group_by(condition) %>%
  summarise(
    mean_trust = mean(Response, na.rm = TRUE),
    sd_trust = sd(Response, na.rm = TRUE),
    n = sum(!is.na(Response)),
    se = sd_trust / sqrt(n),
    ci = qt(0.975, df = n - 1) * se,
    .groups = "drop"
  )

ai_truncation_descriptives
## # A tibble: 2 × 6
##   condition              mean_trust sd_trust     n     se     ci
##   <chr>                       <dbl>    <dbl> <int>  <dbl>  <dbl>
## 1 High Truncation AI t70       4.04     1.45   980 0.0463 0.0909
## 2 Low Truncation AI t30        4.47     1.42   980 0.0453 0.0889

Descriptive statistics (age) - N = 98

excluded_ids <- c(16368687, 16368758)

age_data <- questionnaire %>%
  select(`Participant Private ID`, Question, Response) %>%
  filter(Question == "Please enter your age in years.") %>%
  filter(!`Participant Private ID` %in% excluded_ids) %>%
  distinct(`Participant Private ID`, .keep_all = TRUE) %>%
  mutate(age = as.numeric(Response))

age_data %>%
  summarise(
    n    = sum(!is.na(age)),
    mean = mean(age, na.rm = TRUE),
    sd   = sd(age, na.rm = TRUE),
    min  = min(age, na.rm = TRUE),
    max  = max(age, na.rm = TRUE)
  )
## # A tibble: 1 × 5
##       n  mean    sd   min   max
##   <int> <dbl> <dbl> <dbl> <dbl>
## 1    98  37.7  11.7    18    63

Descriptive statistics - Gender

gender <- questionnaire %>%
  select(`Participant Private ID`, Question, Response) %>%
  filter(Question == "How would you describe your gender?") %>%
  filter(!`Participant Private ID` %in% excluded_ids) %>%
  distinct(`Participant Private ID`, .keep_all = TRUE) %>%
  mutate(Response = factor(as.numeric(Response),
                           levels = 1:5,
                           labels = c("Man/male", "Woman/female", "Non-binary",
                                      "Prefer not to answer", "I use a different term")))

gender %>%
  count(Response, .drop = FALSE)
## # A tibble: 5 × 2
##   Response                   n
##   <fct>                  <int>
## 1 Man/male                  54
## 2 Woman/female              43
## 3 Non-binary                 1
## 4 Prefer not to answer       0
## 5 I use a different term     0

Descriptive statistics (Instagram use) - adding all the ns up = 98 - makes sense as we have excluded two participants

excluded_ids <- c(16368687, 16368758)

social_media_use <- questionnaire %>%
  select(`Participant Private ID`, Question, Response) %>%
  filter(Question == "How often do you use social media platforms such as Instagram, TikTok, Facebook, X/Twitter, or similar platforms?") %>%
  filter(!`Participant Private ID` %in% excluded_ids) %>%
  distinct(`Participant Private ID`, .keep_all = TRUE) %>%
  mutate(Response = factor(as.numeric(Response),
                           levels = 1:6,
                           labels = c("Never", "Less than once a week", "1-2 days per week",
                                      "3-5 days per week", "Daily", "Multiple times per day")))

social_media_use %>%
  count(Response, .drop = FALSE)
## # A tibble: 6 × 2
##   Response                   n
##   <fct>                  <int>
## 1 Never                      0
## 2 Less than once a week      3
## 3 1-2 days per week          5
## 4 3-5 days per week          8
## 5 Daily                     31
## 6 Multiple times per day    51

Ethnicity descriptives - IMPORTANT NOTE. PEOPLE CAN SELECT MULTIPLE OPTIONS

excluded_ids <- c(16368687, 16368758)

ethnicity <- questionnaire_filtered %>%
  select(`Participant Private ID`, Question, Response) %>%
  filter(Question == "What is your family background with respect to ethnicity? You may select more than one option.")

Checking how many people chose ‘other’ - which prompts them with their own response, adding a 9th row

ethnicity %>%
  count(`Participant Private ID`, sort = TRUE) %>%
  count(n)
## Storing counts in `nn`, as `n` already present in input
## ℹ Use `name = "new_name"` to pick a new name.
## # A tibble: 2 × 2
##       n    nn
##   <int> <int>
## 1     8    87
## 2     9    13

Row 1 - Aboriginal, row 2 - European… etc. you can see by the lines of code what each line corresponds to

How many people are Aboriginal? - 1

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice_head(n = 1) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           99
## 2 What is your family background with respect to ethnicity? You … 1            1

How many people are European? - 65

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice(2) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           35
## 2 What is your family background with respect to ethnicity? You … 1           65

How many people are East Asian? - 3

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice(3) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           97
## 2 What is your family background with respect to ethnicity? You … 1            3

How many people are South Asian? - 4

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice(4) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           96
## 2 What is your family background with respect to ethnicity? You … 1            4

How many people are Southeast Asian? - 10

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice(5) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           90
## 2 What is your family background with respect to ethnicity? You … 1           10

How many people are Middle Eastern/North African? - 3

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice(6) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           97
## 2 What is your family background with respect to ethnicity? You … 1            3

How many people Prefer not to say? - 4

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice(7) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           96
## 2 What is your family background with respect to ethnicity? You … 1            4

How many people are ‘other’ - 13

ethnicity %>%
  group_by(`Participant Private ID`) %>%
  slice(8) %>%
  ungroup() %>%
  count(Question, Response)
## # A tibble: 2 × 3
##   Question                                                        Response     n
##   <chr>                                                           <chr>    <int>
## 1 What is your family background with respect to ethnicity? You … 0           87
## 2 What is your family background with respect to ethnicity? You … 1           13
Note for us: THE TOTAL AFTER ADDING EVERYTHING UP IS 103 > 98. THIS IS FINE BECAUSE PEOPLE CAN SELECT MORE THAN ONE OPTION

Main replication and extension analysis:

Two-way ANOVA - are AI faces more trustworthy than real faces (replication)? and does it differ for isolated and Instagram (extension)?

Defining factors for ANOVA

trustworthiness_rating_analysis <- trustworthiness_rating_analysis %>%
  mutate(
    real_or_ai = as.factor(real_or_ai),
    Display = as.factor(Display)
  )

Running ANOVA1. We are using the ezANOVA function because the the default for base R is independent samples, adn this function works better for paired samples (James’ suggestion).

Before we could run the ANOVA, R was having trouble reading the participant column because of the spaces, so we had to change the object name to something R-friendly.
names(trustworthiness_rating_analysis)[
  names(trustworthiness_rating_analysis) == grep("Participant", names(trustworthiness_rating_analysis), value = TRUE)] = "participant_id"

Now, running ANOVA1:

ANOVA_1.EZ <- ezANOVA(data = trustworthiness_rating_analysis, dv = .(Response), 
                      wid = .(`participant_id`), 
                      within = .(real_or_ai, Display), 
                      detailed = TRUE, type = 3, return_aov = TRUE)
## Warning: Converting "participant_id" to factor for ANOVA.
## Warning: Collapsing data to cell means. *IF* the requested effects are a subset
## of the full design, you must use the "within_full" argument, else results may
## be inaccurate.
aovDispTable(ANOVA_1.EZ)
## ══════════════════════════════════════ ANOVA:ANOVA_1.EZ ═════════════════════════════════════
##              Effect DFn DFd         SSn        SSd          F            p
##         (Intercept)   1  97 6576.406531 200.993469 3173.79184 6.480393e-76
##          real_or_ai   1  97   10.189388  20.170612   49.00053 3.337766e-10
##             Display   1  97   16.408265  55.741735   28.55314 6.032430e-07
##  real_or_ai:Display   1  97    1.645816   9.244184   17.26969 6.992446e-05
##  p<.05         ges
##      * 0.958302711
##      * 0.034384183
##      * 0.054231754
##      * 0.005718694
## ─────────────────────────────────────────────────────────────────────────────────────────────

Follow-up tests for significant main effects.

Simple effect, paired samples t-test for real faces and Instagram/isolated
real_filtered_data <- trustworthiness_rating_analysis %>%
  filter(real_or_ai == "Real")

real_mean_display_trustworthiness_analysis <- real_filtered_data %>%
  group_by(participant_id, Display) %>%
  summarise(mean_response_per_participant = mean(Response, na.rm = TRUE)) %>%
  pivot_wider(names_from = Display, values_from = mean_response_per_participant)
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by participant_id and Display.
## ℹ Output is grouped by participant_id.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(participant_id, Display))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
t1_ANOVA1 <- t.test(
  real_mean_display_trustworthiness_analysis$`Instagram Profiles Trial`,
  real_mean_display_trustworthiness_analysis$`Isolated Images Trial`,
  paired = TRUE)
Simple effect, paired samples t-test for AI faces and Instagram/isolated
ai_filtered_data <- trustworthiness_rating_analysis %>%
  filter(real_or_ai == "AI")

ai_mean_display_trustworthiness_analysis <- ai_filtered_data %>%
  group_by(participant_id, Display) %>%
  summarise(mean_response_per_participant = mean(Response, na.rm = TRUE)) %>%
  pivot_wider(names_from = Display, values_from = mean_response_per_participant)
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by participant_id and Display.
## ℹ Output is grouped by participant_id.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(participant_id, Display))` for per-operation grouping
##   (`?dplyr::dplyr_by`) instead.
t2_ANOVA1 <- t.test(
  ai_mean_display_trustworthiness_analysis$`Instagram Profiles Trial`,
  ai_mean_display_trustworthiness_analysis$`Isolated Images Trial`,
  paired = TRUE)

Simple effect, paired samples t-test for Instagram trial and AI/real faces

instagram_filtered_data <- trustworthiness_rating_analysis %>%
  filter(Display == "Instagram Profiles Trial")

instagram_mean_display_trustworthiness_analysis <- instagram_filtered_data %>%
  group_by(participant_id, real_or_ai) %>%
  summarise(mean_response_per_participant = mean(Response, na.rm = TRUE)) %>%
  pivot_wider(names_from = real_or_ai, values_from = mean_response_per_participant)
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by participant_id and real_or_ai.
## ℹ Output is grouped by participant_id.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(participant_id, real_or_ai))` for per-operation
##   grouping (`?dplyr::dplyr_by`) instead.
t3_ANOVA1 <- t.test(
  instagram_mean_display_trustworthiness_analysis$`Real`,
  instagram_mean_display_trustworthiness_analysis$`AI`,
  paired = TRUE)

Simple effect, paired samples t-test for isolated trial and AI/real faces

isolated_filtered_data <- trustworthiness_rating_analysis %>%
  filter(Display == "Isolated Images Trial")

isolated_mean_display_trustworthiness_analysis <- isolated_filtered_data %>%
  group_by(participant_id, real_or_ai) %>%
  summarise(mean_response_per_participant = mean(Response, na.rm = TRUE)) %>%
  pivot_wider(names_from = real_or_ai, values_from = mean_response_per_participant)
## `summarise()` has regrouped the output.
## ℹ Summaries were computed grouped by participant_id and real_or_ai.
## ℹ Output is grouped by participant_id.
## ℹ Use `summarise(.groups = "drop_last")` to silence this message.
## ℹ Use `summarise(.by = c(participant_id, real_or_ai))` for per-operation
##   grouping (`?dplyr::dplyr_by`) instead.
t4_ANOVA1 <- t.test(
  isolated_mean_display_trustworthiness_analysis$`Real`,
  isolated_mean_display_trustworthiness_analysis$`AI`,
  paired = TRUE)

making Bonferroni adjustments to p-values

p_adjustment <- c(
  t1_ANOVA1$p.value,
  t2_ANOVA1$p.value,
  t3_ANOVA1$p.value,
  t4_ANOVA1$p.value
)

p.adjust(p_adjustment, method = "bonferroni")
## [1] 7.283087e-03 1.988361e-09 1.911213e-04 1.168133e-09

Extended research question 1 - t-test for real edited and real unedited as paired-samples t-test

Calculation of one mean per participant per condition - new dataframe

real_participant_means <- trustworthiness_rating_analysis %>%
  filter(real_or_ai == "Real") %>%
  group_by(`participant_id`, condition) %>%
  summarise(
    mean_response = mean(Response, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  pivot_wider(
    names_from = condition,
    values_from = mean_response
  ) %>%
  mutate(
    difference = `Real Edited` - `Real Unedited`
  )

Checking to see distribution

par(mfrow = c(1, 2))

hist(
  real_participant_means$difference,
  main = "Difference Scores",
  xlab = "Edited minus Unedited"
)

qqnorm(real_participant_means$difference)
qqline(real_participant_means$difference)

par(mfrow = c(1, 1))

shapiro.test(real_participant_means$difference)
## 
##  Shapiro-Wilk normality test
## 
## data:  real_participant_means$difference
## W = 0.98151, p-value = 0.184

Points roughly follow q-q plot, histogram looks okay and Shapiro test shows that p>0.05 –> can assume normality

t-test for real edited and real unedited stimuli

t.test
## function (x, ...) 
## UseMethod("t.test")
## <bytecode: 0x8718f2708>
## <environment: namespace:stats>
realedits_t_test <- t.test(
  x = real_participant_means$'Real Edited',
  y = real_participant_means$'Real Unedited',
  paired = TRUE
)

realedits_t_test
## 
##  Paired t-test
## 
## data:  real_participant_means$"Real Edited" and real_participant_means$"Real Unedited"
## t = 1.2326, df = 97, p-value = 0.2207
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
##  -0.04857138  0.20775505
## sample estimates:
## mean difference 
##      0.07959184

Extended research question 2 - t-test for truncation as paired samples t-test

t-test for truncation 30 and truncation 70 as paired-samples t-test

Calculate one mean per participant at each truncation level

ai_participant_means <- trustworthiness_rating_analysis %>%
  filter(real_or_ai == "AI") %>%
  group_by(`participant_id`, condition) %>%
  summarise(
    mean_response = mean(Response, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  pivot_wider(
    names_from = condition,
    values_from = mean_response
  ) %>%
  mutate(
    difference = `High Truncation AI t70` - `Low Truncation AI t30`
  )

Checking to see distribution

par(mfrow = c(1, 2))

hist(
  ai_participant_means$difference,
  main = "Difference Scores",
  xlab = "t70 minus t30"
)

qqnorm(ai_participant_means$difference)
qqline(ai_participant_means$difference)

par(mfrow = c(1, 1))

shapiro.test(ai_participant_means$difference)
## 
##  Shapiro-Wilk normality test
## 
## data:  ai_participant_means$difference
## W = 0.97009, p-value = 0.02467

Points roughly follow q-q plot, histogram looks negatively skewed and shapiro test shows that p<0.05 –> may be mild non-normality but our sample size is 98.

Using Wilcoxon signed rank test for sensitivity

wilcox.test(
  ai_participant_means$'High Truncation AI t70',
  ai_participant_means$'Low Truncation AI t30',
  paired = TRUE,
  exact = FALSE
)
## 
##  Wilcoxon signed rank test with continuity correction
## 
## data:  ai_participant_means$"High Truncation AI t70" and ai_participant_means$"Low Truncation AI t30"
## V = 448.5, p-value = 3.992e-12
## alternative hypothesis: true location shift is not equal to 0

t test for the two truncation levels

t.test
## function (x, ...) 
## UseMethod("t.test")
## <bytecode: 0x8718f2708>
## <environment: namespace:stats>
truncation_t_test <- t.test(
  x = ai_participant_means$'High Truncation AI t70',
  y = ai_participant_means$'Low Truncation AI t30',
  paired = TRUE
)

truncation_t_test
## 
##  Paired t-test
## 
## data:  ai_participant_means$"High Truncation AI t70" and ai_participant_means$"Low Truncation AI t30"
## t = -9.3953, df = 97, p-value = 2.713e-15
## alternative hypothesis: true mean difference is not equal to 0
## 95 percent confidence interval:
##  -0.5191059 -0.3380369
## sample estimates:
## mean difference 
##      -0.4285714

Extended research question 3 - two-way ANOVA - Does follower count affect trustworthiness?

Defining factors before running ANOVA

trustworthiness_rating_analysis <- trustworthiness_rating_analysis %>%
  mutate(
    follower_count = as.factor(follower_count)
  )

Filtering for only the instagram task - as that is where the follower count condition applies

instagram_data_all_tasks <- trustworthiness_rating_analysis %>%
  filter(Display == "Instagram Profiles Trial") 

names(instagram_data_all_tasks)[
  names(instagram_data_all_tasks) == grep("Participant", names(instagram_data_all_tasks), value = TRUE)] = "participant_id"

Running ANOVA2 (follower count ANOVA)

ANOVA_2.EZ <- ezANOVA(data = instagram_data_all_tasks, dv = .(Response), 
                      wid = .(`participant_id`), 
                      within = .(real_or_ai, follower_count), 
                      detailed = TRUE, type = 3, return_aov = TRUE)
## Warning: Converting "participant_id" to factor for ANOVA.
## Warning: You have removed one or more levels from variable "follower_count".
## Refactoring for ANOVA.
## Warning: Collapsing data to cell means. *IF* the requested effects are a subset
## of the full design, you must use the "within_full" argument, else results may
## be inaccurate.
aovDispTable(ANOVA_2.EZ)
## ═══════════════════════════════════════════ ANOVA:ANOVA_2.EZ ══════════════════════════════════════════
##                     Effect DFn DFd          SSn       SSd            F
##                (Intercept)   1  97 5935.8294898 316.80051 1.817470e+03
##                 real_or_ai   1  97    3.6450000  19.50500 1.812689e+01
##             follower_count   1  97    0.2254082  35.04459 6.239077e-01
##  real_or_ai:follower_count   1  97    0.0050000  23.54500 2.059885e-02
##             p p<.05          ges
##  1.251884e-64     * 9.376224e-01
##  4.778034e-05     * 9.145880e-03
##  4.315255e-01       5.704795e-04
##  8.861749e-01       1.266143e-05
## ───────────────────────────────────────────────────────────────────────────────────────────────────────

Not conducting any follow-up t-tests for ANOVA2 as there was no significant interaction - and the only significant main effect was real/AI, for which we have already conducted t-tests.

Checking for balancing

trustworthiness_rating_analysis %>%
  count(real_or_ai, Display)
## # A tibble: 4 × 3
##   real_or_ai Display                      n
##   <fct>      <fct>                    <int>
## 1 AI         Instagram Profiles Trial   980
## 2 AI         Isolated Images Trial      980
## 3 Real       Instagram Profiles Trial   980
## 4 Real       Isolated Images Trial      980

Extended research question 4 - do trustworthiness and AI/real faces predict online behaviours?

Changing dataframe to wide for regression model

instagram_analysis_wide = all_tasks_analysis %>% 
  pivot_wider(names_from = `Object Name`, values_from = Response) %>% 
  mutate(`Participant Private ID` = factor(`Participant Private ID`), real_or_ai = factor(real_or_ai), `Trustworthiness Scale` = scale(`Trustworthiness Scale`, center = TRUE, scale = FALSE)) %>% 
  filter(`Display` == "Instagram Profiles Trial")

Creating linear fixed-effects regression model

model = lmer(
  `Follow Request Answer` ~ real_or_ai * `Trustworthiness Scale` + (1 | `Participant Private ID`), 
  data = instagram_analysis_wide, REML = TRUE
)

summary(model)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: `Follow Request Answer` ~ real_or_ai * `Trustworthiness Scale` +  
##     (1 | `Participant Private ID`)
##    Data: instagram_analysis_wide
## 
## REML criterion at convergence: 6101.4
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.6499 -0.5882 -0.0513  0.6005  4.2208 
## 
## Random effects:
##  Groups                 Name        Variance Std.Dev.
##  Participant Private ID (Intercept) 0.9837   0.9918  
##  Residual                           1.1303   1.0631  
## Number of obs: 1960, groups:  Participant Private ID, 98
## 
## Fixed effects:
##                                          Estimate Std. Error         df t value
## (Intercept)                               3.18087    0.10583  107.97297  30.056
## real_or_aiReal                           -0.13705    0.04869 1859.95706  -2.815
## `Trustworthiness Scale`                   0.64387    0.02805 1926.85617  22.956
## real_or_aiReal:`Trustworthiness Scale`   -0.05762    0.03426 1864.43428  -1.682
##                                        Pr(>|t|)    
## (Intercept)                             < 2e-16 ***
## real_or_aiReal                          0.00493 ** 
## `Trustworthiness Scale`                 < 2e-16 ***
## real_or_aiReal:`Trustworthiness Scale`  0.09275 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) rl_r_R `TScl`
## real_or_aRl -0.225              
## `TrstwrScl`  0.029 -0.025       
## rl_r_R:`TS` -0.018  0.140 -0.644

Follow Request regression - do trustworthiness and AI/real faces predict purchase intention?

model1 = lmer(
  `Follow Request Answer` ~ real_or_ai * `Trustworthiness Scale` + (1 | `Participant Private ID`), 
  data = instagram_analysis_wide, REML = TRUE
)

summary(model1)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: `Follow Request Answer` ~ real_or_ai * `Trustworthiness Scale` +  
##     (1 | `Participant Private ID`)
##    Data: instagram_analysis_wide
## 
## REML criterion at convergence: 6101.4
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.6499 -0.5882 -0.0513  0.6005  4.2208 
## 
## Random effects:
##  Groups                 Name        Variance Std.Dev.
##  Participant Private ID (Intercept) 0.9837   0.9918  
##  Residual                           1.1303   1.0631  
## Number of obs: 1960, groups:  Participant Private ID, 98
## 
## Fixed effects:
##                                          Estimate Std. Error         df t value
## (Intercept)                               3.18087    0.10583  107.97297  30.056
## real_or_aiReal                           -0.13705    0.04869 1859.95706  -2.815
## `Trustworthiness Scale`                   0.64387    0.02805 1926.85617  22.956
## real_or_aiReal:`Trustworthiness Scale`   -0.05762    0.03426 1864.43428  -1.682
##                                        Pr(>|t|)    
## (Intercept)                             < 2e-16 ***
## real_or_aiReal                          0.00493 ** 
## `Trustworthiness Scale`                 < 2e-16 ***
## real_or_aiReal:`Trustworthiness Scale`  0.09275 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) rl_r_R `TScl`
## real_or_aRl -0.225              
## `TrstwrScl`  0.029 -0.025       
## rl_r_R:`TS` -0.018  0.140 -0.644

Product Answer regression - do trustworthiness and AI/real faces predict purchase intention?

model2 = lmer(
  `Product Answer` ~ real_or_ai * `Trustworthiness Scale` + (1 | `Participant Private ID`), 
  data = instagram_analysis_wide, REML = TRUE
)

summary(model2)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: `Product Answer` ~ real_or_ai * `Trustworthiness Scale` + (1 |  
##     `Participant Private ID`)
##    Data: instagram_analysis_wide
## 
## REML criterion at convergence: 5166.7
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.9537 -0.6056 -0.0394  0.5586  4.9657 
## 
## Random effects:
##  Groups                 Name        Variance Std.Dev.
##  Participant Private ID (Intercept) 1.0224   1.0111  
##  Residual                           0.6831   0.8265  
## Number of obs: 1960, groups:  Participant Private ID, 98
## 
## Fixed effects:
##                                          Estimate Std. Error         df t value
## (Intercept)                               2.64087    0.10552  103.34555  25.027
## real_or_aiReal                           -0.03840    0.03785 1859.47251  -1.014
## `Trustworthiness Scale`                   0.47221    0.02191 1904.40496  21.556
## real_or_aiReal:`Trustworthiness Scale`   -0.01653    0.02664 1862.14893  -0.621
##                                        Pr(>|t|)    
## (Intercept)                              <2e-16 ***
## real_or_aiReal                            0.310    
## `Trustworthiness Scale`                  <2e-16 ***
## real_or_aiReal:`Trustworthiness Scale`    0.535    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##             (Intr) rl_r_R `TScl`
## real_or_aRl -0.175              
## `TrstwrScl`  0.022 -0.024       
## rl_r_R:`TS` -0.014  0.140 -0.642

Plots for RQ1 - Are AI faces more trustworthy than Real faces?

Interaction for AI vs Real

ggplot(
  descriptives,
  aes(
    x = real_or_ai,
    y = mean_trust,
    group = Display,
    linetype = Display,
    shape = Display
    )
  ) +
  geom_line(
    linewidth = 0.9
    ) +
  geom_point(
    size = 3.5
    ) +
  geom_errorbar(
    aes(
      ymin = mean_trust - ci,
      ymax = mean_trust + ci
      ),
    width = 0.08
    ) +
  scale_y_continuous(
    limits = c(1, 7),
    breaks = 1:7
    ) +
  labs(
    x = "Face type",
    y = "Mean trustworthiness rating",
    linetype = "Presentation condition",
    shape = "Presentation condition"
    ) +
  theme_classic()

Plots for RQ3: What is the effect of truncation on trustworthiness for AI faces?

Truncation levels on AI trustworthiness ratings

AI truncation column graph
ggplot(
  ai_truncation_descriptives,
  aes(
    x = condition,
    y = mean_trust,
    fill = condition
    )
  ) +
  geom_col(
    width = 0.60
    ) +
  geom_errorbar(
    aes(
      ymin = mean_trust - ci,
      ymax = mean_trust + ci
      ),
    width = 0.12
    ) +
  scale_y_continuous(
    limits = c(0, 7),
    breaks = 0:7
    ) +
  labs(
    x = "Truncation condition",
    y = "Mean trustworthiness rating"
    ) +
  guides(fill = "none") +
  theme_classic()