---
title: "Orthogonality Study 3"
author: "Jim A.C. Everett & Simon Myers"
date: "`r format(Sys.time(), '%B %d, %Y')`"
output:
  html_document:
    theme: flatly
    code_folding: hide
    toc: true
    toc_depth: 3
    toc_float: 
      collapsed: TRUE
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
options(knitr.kable.NA = '')
```

***

# Description

***

In Study 3 we used a fully between subjects design in which participants were randomly assigned to read one of eight vignettes, reading either about an AI agent called OmegaAI or a human called Tom; who has high (vs. low) intelligence or high (vs. low) morality. Participants were asked to think about OmegaAI [Tom], and told that “Experts have used a wide array of tests to assess OmegaAI’s [Tom’s] morality [intelligence] and across all the tests this [person/AI] has been rated as extremely high in morality [intelligence]. This matches with how [Tom’s friends and acquaintances / independent testers of OmegaAI] describe [him/it].”. We then again looked at ratings of intelligence, morality, trust, and danger.

***


# Setup

***

Before doing any analyses, we just need to load the data file and do some initial basis preparation.

```{r, results='hide', message=FALSE, warning=FALSE}
library(readr)
library(psych)
library(car)
library(tidyverse)
library(knitr)
library(kableExtra)
library(jmv)
library(BayesFactor)
library(effectsize)
library(gtsummary)
library(rempsyc)
library(see)
library(correlation)
library(lavaan)
library(emmeans)
library(ggnewscale)
library(patchwork)
```

We'll load the data and make sure the conditions are set as factors.

```{r}
# Load in data
df <- read.csv("Study_3_Data.csv", header=TRUE, stringsAsFactors=FALSE)

# set conditions as factors
df$Agent <- as.factor(df$Agent)
df$Level <- as.factor(df$Level)
df$Trait <- as.factor(df$Trait)

# relevel factors

df$Level <- 
  factor(df$Level, 
         levels = c("Low", "High"))

df$Trait <- 
  factor(df$Trait, 
         levels = c("Morality", "Intelligence"))

df$Agent <- 
  factor(df$Agent, 
         levels = c("Human", "AI"))
```

We can just see the numbers per condition. In total, we have `r nrow(df)` participants.

```{r}
condition_table <- df %>%
  count(Agent, Trait, Level) %>%
  arrange(Agent, Trait, Level)


kable(condition_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

Sometimes we'll need to just look at participants in the morality conditions, or the intelligence conditions, so we'll create dataframes for this. We'll actually do this for different "levels" as well as the full 8 condition breakdown, which might be interesting for the correlations.

```{r}
# First we break down just whether it is a human or an AI condition.
df_human <- df  %>% filter(Agent == "Human")
df_AI <- df  %>% filter(Agent == "AI")

# Second we break down just whether it is a morality or intelligence condition.
df_morality <- df %>% filter(Trait == "Morality")
df_intelligence <- df %>% filter(Trait == "Intelligence")

# Third we break down by both agent AND trait
df_intelligence_AI <- df %>% filter(Trait == "Intelligence" & Agent == "AI")
df_morality_AI <- df %>% filter(Trait == "Morality" & Agent == "AI")
df_intelligence_human <- df %>% filter(Trait == "Intelligence" & Agent == "Human")
df_morality_human <- df %>% filter(Trait == "Morality" & Agent == "Human")

# Fourth we break down by both trait AND level
df_intelligence_low <- df %>% filter(Trait == "Intelligence" & Level == "Low")
df_intelligence_high <- df %>% filter(Trait == "Intelligence" & Level == "High")
df_morality_low <- df %>% filter(Trait == "Morality" & Level == "Low")
df_morality_high <- df %>% filter(Trait == "Morality" & Level == "High")

# Finally we do the full breakdown, also with levels.
df_intelligence_AI_high <- df %>%
  filter(Trait == "Intelligence", Agent == "AI", Level == "High")

df_intelligence_AI_low <- df %>%
  filter(Trait == "Intelligence", Agent == "AI", Level == "Low")

df_morality_AI_high <- df %>%
  filter(Trait == "Morality", Agent == "AI", Level == "High")

df_morality_AI_low <- df %>%
  filter(Trait == "Morality", Agent == "AI", Level == "Low")

df_intelligence_human_high <- df %>%
  filter(Trait == "Intelligence", Agent == "Human", Level == "High")

df_intelligence_human_low <- df %>%
  filter(Trait == "Intelligence", Agent == "Human", Level == "Low")

df_morality_human_high <- df %>%
  filter(Trait == "Morality", Agent == "Human", Level == "High")

df_morality_human_low <- df %>%
  filter(Trait == "Morality", Agent == "Human", Level == "Low")
```

***

We can get participant age for the reporting.

```{r}
# Summarize age
age_table <- df %>%
  summarise(
    mean_age = mean(Age, na.rm = TRUE),
    sd_age = sd(Age, na.rm = TRUE),
    min_age = min(Age, na.rm = TRUE),
    max_age = max(Age, na.rm = TRUE)
  )

kable(age_table, format = "html",
            row.names = FALSE, digits = 2) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

And report gender.

```{r}
df$Gender <- as.factor(df$Gender)

kable(table(df$Gender), format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

# Manipulation Check

We start looking at our manipulation check,  looking at ratings of morality (or intelligence) by the level condition (high vs low), but also look at whether this depends on the type of Agent. We do this seperately for our two trait conditions.

***

## Morality Conditions

```{r, message=FALSE}
# Compute model
contrasts(df$Level) <- contr.sum
contrasts(df$Agent) <- contr.sum
raw.model <- lm(Moral ~ Level * Agent, data = df_morality)

# Extract ANOVA into table
model <- car::Anova(raw.model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***


## Intelligence Conditions

```{r, message=FALSE}
# Compute model
contrasts(df$Level) <- contr.sum
contrasts(df$Agent) <- contr.sum
raw.model <- lm(Intelligent ~ Level * Agent, data = df_intelligence)

# Extract ANOVA into table
model <- car::Anova(raw.model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

# Effects by Condition

***

## Descriptives & Simple Effects

Now we will turn to look at different effects by condition. First, we will look at descriptives, and we'll do this seperately for the different conditions by agent (human vs AI) and trait (morality vs intelligence). We'll also report here the simple effects of level (high vs low), since we'll need those.

***

### AI + Intelligence Conditions

```{r}
summary_table_intelligence_AI <- df %>%
  filter(Trait == "Intelligence" & Agent == "AI") %>%
  select(Level, Intelligent, Intelligent_Comparison, 
         Moral, Moral_Comparison, Trust, Danger) %>%
  pivot_longer(-Level, names_to = "Variable", values_to = "Value") %>%
  mutate(Variable = factor(Variable, levels = c("Intelligent", "Intelligent_Comparison", "Moral", "Moral_Comparison", "Trust", "Danger"))) %>%
  group_by(Variable, Level) %>%
  summarise(
    min = min(Value, na.rm = TRUE),
    max = max(Value, na.rm = TRUE),
    mean = round(mean(Value, na.rm = TRUE), 2),
    sd = round(sd(Value, na.rm = TRUE), 2),
    .groups = "drop"
  )

summary_table_intelligence_AI <- summary_table_intelligence_AI %>%
  mutate(Variable = dplyr::recode(
    Variable,
    "Intelligent" = "Intelligence",
    "Intelligent_Comparison" = "Intelligence (Compared to Human)",
    "Moral" = "Morality",
    "Moral_Comparison" = "Morality (Compared to Human)"
  ))

kable(summary_table_intelligence_AI, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

First, like in Studies 1a and 1b, we'll do the Welch's t-test.

```{r}
# T-test
results <- t.test(Intelligent ~ Level, data = df_intelligence_AI, var.equal = FALSE)

# Rounding
t_val <- round(results$statistic, 2)
df_val <- sprintf("%.2f", results$parameter)  # force 2 decimal places
p_val <- if (results$p.value < 0.001) "<0.001" else sprintf("%.3f", results$p.value)

# Effect size
effect_size <- cohens_d(Intelligent ~ Level, data = df_intelligence_AI)
d_val <- round(effect_size$Cohens_d, 2)

# Bayes Factor
bf_result <- ttestBF(formula = Intelligent ~ Level, data = df_intelligence_AI)
bf_numeric <- as.numeric(exp(bf_result@bayesFactor$bf))
bf_exponent <- floor(log10(bf_numeric))
bf_mantissa <- bf_numeric / (10^bf_exponent)
bf_apa <- paste0(sprintf("%.2f", bf_mantissa), " × 10^", bf_exponent)

# Combine into a table
final_table <- data.frame(
  `_t_` = t_val,
  `df` = df_val,
  `_p_` = p_val,
  `Bayes Factor` = format(bf_numeric, scientific = TRUE, digits = 6),
  `Bayes Factor APA` = bf_apa,
  `Cohen's _d_` = d_val,
  check.names = FALSE
)

kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

To save time for other analyses we create a function to do produce the output for the variables which we then pull into a table.

```{r}
welch.t.test.bf <- function(DV, IV, data, labeliv = "IV", labeldv = "DV") {
  # Build formula from strings
  formula <- as.formula(paste(DV, "~", IV))

  # Welch's t-test
  results <- t.test(formula, data = data, var.equal = FALSE)
  
  # Format test results
  results_table <- data.frame(
    t = round(results$statistic, 2),
    df = sprintf("%.2f", results$parameter),
    p <- if (results$p.value < 0.001) "<0.001" else sprintf("%.3f", results$p.value)
  )

  # Effect size
  effect_size <- cohens_d(formula, data = data)
  cohen_d <- round(effect_size$Cohens_d, 2)

  # Bayes Factor
  bf_result <- ttestBF(formula = formula, data = data)
  bf_numeric <- exp(bf_result@bayesFactor$bf)
  bf_numeric <- as.numeric(exp(bf_result@bayesFactor$bf))
  bf_exponent <- floor(log10(bf_numeric))
  bf_mantissa <- bf_numeric / (10^bf_exponent)
  bf_apa <- paste0(sprintf("%.2f", bf_mantissa), " × 10^", bf_exponent)

  # Combine into final table
  final_table <- data.frame(
    "_t_" = results_table$t,
    "df" = results_table$df,
    "_p_" = results_table$p,
    "Bayes Factor" = bf_numeric,
    "Bayes Factor APA" = bf_apa,
    "Cohen's _d_" = cohen_d,
    check.names = FALSE
  )

  return(final_table)
}
```

After creating the function we can check it shows the same results.

```{r}
intelligence <- welch.t.test.bf(data = df_intelligence_AI, DV = "Intelligent", IV = "Level", labeliv = "Level", labeldv = "Intelligence")
kable(intelligence, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

Now we know it works, we can easily do the same for future analyses and create tables showing variables together.

```{r}
intelligence <- welch.t.test.bf(data = df_intelligence_AI, 
                                DV = "Intelligent", IV = "Level", 
                                labeliv = "Level", labeldv = "Intelligence")

intelligence_comparison <- welch.t.test.bf(data = df_intelligence_AI, 
                                           DV = "Intelligent_Comparison", IV = "Level", 
                                           labeliv = "Level", labeldv = "Intelligence (Compared to Human)")

morality <- welch.t.test.bf(data = df_intelligence_AI, 
                            DV = "Moral", IV = "Level", 
                            labeliv = "Level", labeldv = "Morality")

morality_comparison <- welch.t.test.bf(data = df_intelligence_AI, 
                                       DV = "Moral_Comparison", IV = "Level", 
                                       labeliv = "Level", labeldv = "Morality (Compared to Human)")

trust <- welch.t.test.bf(data = df_intelligence_AI, 
                         DV = "Trust", IV = "Level", 
                         labeliv = "Level", labeldv = "Trust")

danger <- welch.t.test.bf(data = df_intelligence_AI, 
                          DV = "Danger", IV = "Level", 
                          labeliv = "Level", labeldv = "Danger")

# Add a DV label column to each
intelligence$DV <- "Intelligence"
intelligence_comparison$DV <- "Intelligence (Compared to Human)"
morality$DV <- "Morality"
morality_comparison$DV <- "Morality (Compared to Human)"
trust$DV <- "Trust"
danger$DV <- "Danger"

# Combine into a single data frame
combined_results <- rbind(intelligence,intelligence_comparison, morality, morality_comparison, trust, danger)

# Move DV column to the front
combined_results <- combined_results[, c("DV", setdiff(names(combined_results), "DV"))]

kable(combined_results, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")

```

***

### AI + Morality Conditions

```{r}
summary_table_morality_AI <- df %>%
  filter(Trait == "Morality" & Agent == "AI") %>%
  select(Level, Intelligent, Intelligent_Comparison, 
         Moral, Moral_Comparison, Trust, Danger) %>%
  pivot_longer(-Level, names_to = "Variable", values_to = "Value") %>%
  mutate(Variable = factor(Variable, levels = c("Intelligent", "Intelligent_Comparison", "Moral", "Moral_Comparison", "Trust", "Danger"))) %>%
  group_by(Variable, Level) %>%
  summarise(
    min = min(Value, na.rm = TRUE),
    max = max(Value, na.rm = TRUE),
    mean = round(mean(Value, na.rm = TRUE), 2),
    sd = round(sd(Value, na.rm = TRUE), 2),
    .groups = "drop"
  )

summary_table_morality_AI <- summary_table_morality_AI %>%
  mutate(Variable = dplyr::recode(
    Variable,
    "Intelligent" = "Intelligence",
    "Intelligent_Comparison" = "Intelligence (Compared to Human)",
    "Moral" = "Morality",
    "Moral_Comparison" = "Morality (Compared to Human)"
  ))

kable(summary_table_morality_AI, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

```{r}
intelligence <- welch.t.test.bf(data = df_morality_AI, 
                                DV = "Intelligent", IV = "Level", 
                                labeliv = "Level", labeldv = "Intelligence")

intelligence_comparison <- welch.t.test.bf(data = df_morality_AI, 
                                           DV = "Intelligent_Comparison", IV = "Level", 
                                           labeliv = "Level", labeldv = "Intelligence (Compared to Human)")

morality <- welch.t.test.bf(data = df_morality_AI, 
                            DV = "Moral", IV = "Level", 
                            labeliv = "Level", labeldv = "Morality")

morality_comparison <- welch.t.test.bf(data = df_morality_AI, 
                                       DV = "Moral_Comparison", IV = "Level", 
                                       labeliv = "Level", labeldv = "Morality (Compared to Human)")

trust <- welch.t.test.bf(data = df_morality_AI, 
                         DV = "Trust", IV = "Level", 
                         labeliv = "Level", labeldv = "Trust")

danger <- welch.t.test.bf(data = df_morality_AI, 
                          DV = "Danger", IV = "Level", 
                          labeliv = "Level", labeldv = "Danger")

# Add a DV label column to each
intelligence$DV <- "Intelligence"
intelligence_comparison$DV <- "Intelligence (Compared to Human)"
morality$DV <- "Morality"
morality_comparison$DV <- "Morality (Compared to Human)"
trust$DV <- "Trust"
danger$DV <- "Danger"

# Combine into a single data frame
combined_results <- rbind(intelligence,intelligence_comparison, morality, morality_comparison, trust, danger)

# Move DV column to the front
combined_results <- combined_results[, c("DV", setdiff(names(combined_results), "DV"))]

kable(combined_results, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")

```


***

### Human + Intelligence Conditions

```{r}
summary_table_intelligence_human <- df %>%
  filter(Trait == "Intelligence" & Agent == "Human") %>%
  select(Level, Intelligent, Intelligent_Comparison, 
         Moral, Moral_Comparison, Trust, Danger) %>%
  pivot_longer(-Level, names_to = "Variable", values_to = "Value") %>%
  mutate(Variable = factor(Variable, levels = c("Intelligent", "Intelligent_Comparison", "Moral", "Moral_Comparison", "Trust", "Danger"))) %>%
  group_by(Variable, Level) %>%
  summarise(
    min = min(Value, na.rm = TRUE),
    max = max(Value, na.rm = TRUE),
    mean = round(mean(Value, na.rm = TRUE), 2),
    sd = round(sd(Value, na.rm = TRUE), 2),
    .groups = "drop"
  )

summary_table_intelligence_human <- summary_table_intelligence_human %>%
  mutate(Variable = dplyr::recode(
    Variable,
    "Intelligent" = "Intelligence",
    "Intelligent_Comparison" = "Intelligence (Compared to Human)",
    "Moral" = "Morality",
    "Moral_Comparison" = "Morality (Compared to Human)"
  ))

kable(summary_table_intelligence_human, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```
***

```{r}
intelligence <- welch.t.test.bf(data = df_intelligence_human, 
                                DV = "Intelligent", IV = "Level", 
                                labeliv = "Level", labeldv = "Intelligence")

intelligence_comparison <- welch.t.test.bf(data = df_intelligence_human, 
                                           DV = "Intelligent_Comparison", IV = "Level", 
                                           labeliv = "Level", labeldv = "Intelligence (Compared to Human)")

morality <- welch.t.test.bf(data = df_intelligence_human, 
                            DV = "Moral", IV = "Level", 
                            labeliv = "Level", labeldv = "Morality")

morality_comparison <- welch.t.test.bf(data = df_intelligence_human, 
                                       DV = "Moral_Comparison", IV = "Level", 
                                       labeliv = "Level", labeldv = "Morality (Compared to Human)")

trust <- welch.t.test.bf(data = df_intelligence_human, 
                         DV = "Trust", IV = "Level", 
                         labeliv = "Level", labeldv = "Trust")

danger <- welch.t.test.bf(data = df_intelligence_human, 
                          DV = "Danger", IV = "Level", 
                          labeliv = "Level", labeldv = "Danger")

# Add a DV label column to each
intelligence$DV <- "Intelligence"
intelligence_comparison$DV <- "Intelligence (Compared to Human)"
morality$DV <- "Morality"
morality_comparison$DV <- "Morality (Compared to Human)"
trust$DV <- "Trust"
danger$DV <- "Danger"

# Combine into a single data frame
combined_results <- rbind(intelligence,intelligence_comparison, morality, morality_comparison, trust, danger)

# Move DV column to the front
combined_results <- combined_results[, c("DV", setdiff(names(combined_results), "DV"))]

kable(combined_results, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")

```

***

### Human + Morality Conditions

```{r}
summary_table_morality_human <- df %>%
  filter(Trait == "Morality" & Agent == "Human") %>%
  select(Level, Intelligent, Intelligent_Comparison, 
         Moral, Moral_Comparison, Trust, Danger) %>%
  pivot_longer(-Level, names_to = "Variable", values_to = "Value") %>%
  mutate(Variable = factor(Variable, levels = c("Intelligent", "Intelligent_Comparison", "Moral", "Moral_Comparison", "Trust", "Danger"))) %>%
  group_by(Variable, Level) %>%
  summarise(
    min = min(Value, na.rm = TRUE),
    max = max(Value, na.rm = TRUE),
    mean = round(mean(Value, na.rm = TRUE), 2),
    sd = round(sd(Value, na.rm = TRUE), 2),
    .groups = "drop"
  )

summary_table_morality_human <- summary_table_morality_human %>%
  mutate(Variable = dplyr::recode(
    Variable,
    "Intelligent" = "Intelligence",
    "Intelligent_Comparison" = "Intelligence (Compared to Human)",
    "Moral" = "Morality",
    "Moral_Comparison" = "Morality (Compared to Human)"
  ))

kable(summary_table_morality_human, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

```{r}
intelligence <- welch.t.test.bf(data = df_morality_human, 
                                DV = "Intelligent", IV = "Level", 
                                labeliv = "Level", labeldv = "Intelligence")

intelligence_comparison <- welch.t.test.bf(data = df_morality_human, 
                                           DV = "Intelligent_Comparison", IV = "Level", 
                                           labeliv = "Level", labeldv = "Intelligence (Compared to Human)")

morality <- welch.t.test.bf(data = df_morality_human, 
                            DV = "Moral", IV = "Level", 
                            labeliv = "Level", labeldv = "Morality")

morality_comparison <- welch.t.test.bf(data = df_morality_human, 
                                       DV = "Moral_Comparison", IV = "Level", 
                                       labeliv = "Level", labeldv = "Morality (Compared to Human)")

trust <- welch.t.test.bf(data = df_morality_human, 
                         DV = "Trust", IV = "Level", 
                         labeliv = "Level", labeldv = "Trust")

danger <- welch.t.test.bf(data = df_morality_human, 
                          DV = "Danger", IV = "Level", 
                          labeliv = "Level", labeldv = "Danger")

# Add a DV label column to each
intelligence$DV <- "Intelligence"
intelligence_comparison$DV <- "Intelligence (Compared to Human)"
morality$DV <- "Morality"
morality_comparison$DV <- "Morality (Compared to Human)"
trust$DV <- "Trust"
danger$DV <- "Danger"

# Combine into a single data frame
combined_results <- rbind(intelligence,intelligence_comparison, morality, morality_comparison, trust, danger)

# Move DV column to the front
combined_results <- combined_results[, c("DV", setdiff(names(combined_results), "DV"))]

kable(combined_results, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")

```

***

# ANOVAs

***

## Intelligence

Here, we ask whether high or low morality predicts rated intelligence for humans and AI. We therefore look only at the conditions where morality was manipulated/described, and look at ratings of *intelligence*.

***

### General Ratings

We look at the conditions where morality was manipulated, and look at ratings of general *intelligence*.

```{r, message=FALSE, warning=FALSE}
# Compute model
contrasts(df_morality$Level) <- contr.sum
contrasts(df_morality$Agent) <- contr.sum
model <- lm(Intelligent ~ Level * Agent, data = df_morality)

# Extract ANOVA into table
model <- car::Anova(model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Remove intercept row
anova_df <- anova_df[-1, ]

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

# Rename the rows 
final_table <- final_table %>%
  mutate(
    Term = case_when(
      Term == "Level" ~ "Level",
      Term == "Agent" ~ "Agent",
      Term == "Level:Agent" ~ "Level × Agent",
      TRUE ~ Term
    )
  )


kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

We now break down this interaction effect; above we have looked at the difference between high and low for humans and AI separately, here we are looking at the difference between human and AI for high and low separately.

```{r}
simple_effect_low <- welch.t.test.bf(data = df_morality_low, DV = "Intelligent", IV = "Agent", labeliv = "Agent", labeldv = "Intelligence")

kable(simple_effect_low, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

```{r}
simple_effect_high <- welch.t.test.bf(data = df_morality_high, DV = "Intelligent", IV = "Agent", labeliv = "Agent", labeldv = "Intelligence")

kable(simple_effect_high, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

We'll then make the figure that we will combine and show in a minute.

```{r}
# Fit the model
model <- lm(Intelligent ~ Level * Agent, data = df_morality) # model
emm <- emmeans(model, ~ Level * Agent) # Estimated marginal means
emm_df <- as.data.frame(emm) # Convert to data frame for plotting

# Define consistent colors
fill_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")
line_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")

# Ensure correct factor order
df_morality$Level <- factor(df_morality$Level, levels = c("Low", "High"))
emm_df$Level <- factor(emm_df$Level, levels = c("Low", "High"))

# Create the plot
intelligence_plot <- ggplot() +

  # Jittered individual data points (filled circles, no outline)
geom_jitter(
  data = df_morality,
  aes(x = Level, y = Intelligent, fill = Agent),
  shape = 21,
  size = 2,
  stroke = 0.1,           # tiny border just to make visible
  color = "gray80",       # light neutral outline
  alpha = 0.15,
  position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.35, jitter.height = 0.35)
)+
  
  # Estimated marginal means with visible outline
geom_pointrange(
  data = emm_df,
  aes(
    x = Level,
    y = emmean,
    ymin = lower.CL,
    ymax = upper.CL,
    group = Agent,
    fill = Agent,
    colour = Agent
  ),
  position = position_dodge(width = 0.7),
  shape = 21,         # Fillable circle
  size = 1.2,         # Thicker CI line
  stroke = 1.2,       # Thicker point border
  fatten = 3,         # Large mean point
  show.legend = TRUE
)+
  

  # Color scales
  scale_color_manual(values = line_colors) +
  scale_fill_manual(values = fill_colors) +

  # Axis labels and limits
  labs(
    title = "Perceived Intelligence from Morality Level",
    x = "Morality Level",
    y = "Perceived Intelligence",
    fill = NULL,
    colour = NULL
  ) +
  coord_cartesian(ylim = c(1, 7)) +
  scale_y_continuous(breaks = 1:7) +

  # Theme and legend styling
  theme_classic() +
  theme(
    text = element_text(size = 12),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12),
    axis.title.y = element_text(margin = margin(r = 10)),
    axis.title.x = element_text(margin = margin(t = 10)),
    plot.title = element_text(size = 12, hjust = 0.5, margin = margin(b=10)),  # Centered title
    legend.position = "bottom",
    legend.direction = "horizontal",
    legend.justification = "center",
    legend.box.margin = margin(t = -5),
    legend.key.height = unit(1.2, "lines"),
    legend.key.width = unit(1.2, "lines"),
    legend.text = element_text(size = 12)
  )

intelligence_plot
```

***

### Comparison Question

***

Now we ask whether high or low morality predicts rated *intelligence* for humans and AI on the comparison question.

```{r, message=FALSE, warning=FALSE}
# Compute model
contrasts(df_morality$Level) <- contr.sum
contrasts(df_morality$Agent) <- contr.sum
model <- lm(Intelligent_Comparison ~ Level * Agent, data = df_morality)

# Extract ANOVA into table
model <- car::Anova(model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Remove intercept row
anova_df <- anova_df[-1, ]

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

# Rename the rows 
final_table <- final_table %>%
  mutate(
    Term = case_when(
      Term == "Level" ~ "Level",
      Term == "Agent" ~ "Agent",
      Term == "Level:Agent" ~ "Level × Agent",
      TRUE ~ Term
    )
  )


kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

```{r}
simple_effect_low <- welch.t.test.bf(data = df_morality_low, DV = "Intelligent_Comparison", IV = "Agent", labeliv = "Agent", labeldv = "Intelligence (Comparison)")

kable(simple_effect_low, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

```{r}
simple_effect_high <- welch.t.test.bf(data = df_morality_high, DV = "Intelligent_Comparison", IV = "Agent", labeliv = "Agent", labeldv = "Intelligence (Comparison)")

kable(simple_effect_high, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

Again, we'll make a figure that we will combine in a minute.

```{r}
# Fit the model
model <- lm(Intelligent_Comparison ~ Level * Agent, data = df_morality)
emm <- emmeans(model, ~ Level * Agent)
emm_df <- as.data.frame(emm)

# Define consistent colors
fill_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")
line_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")

# Ensure correct factor order
df_morality$Level <- factor(df_morality$Level, levels = c("Low", "High"))
emm_df$Level <- factor(emm_df$Level, levels = c("Low", "High"))

# Create the plot
intelligence_comparison_plot <- ggplot() +

  # Jittered individual data points (filled circles, no outline)
geom_jitter(
  data = df_morality,
  aes(x = Level, y = Intelligent_Comparison, fill = Agent),
  shape = 21,
  size = 2,
  stroke = 0.1,           # tiny border just to make visible
  color = "gray80",       # light neutral outline
  alpha = 0.15,
  position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.35, jitter.height = 0.35)
)+
  
# Reference line for comparison baseline
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray60", linewidth = 0.7) +
  
  # Estimated marginal means with visible outline
geom_pointrange(
  data = emm_df,
  aes(
    x = Level,
    y = emmean,
    ymin = lower.CL,
    ymax = upper.CL,
    group = Agent,
    fill = Agent,
    colour = Agent
  ),
  position = position_dodge(width = 0.7),
  shape = 21,         # Fillable circle
  size = 1.2,         # Thicker CI line
  stroke = 1.2,       # Thicker point border
  fatten = 3,         # Large mean point
  show.legend = TRUE
)+
  

  # Color scales
  scale_color_manual(values = line_colors) +
  scale_fill_manual(values = fill_colors) +

  # Axis labels and limits
  labs(
    title = "Perceived Intelligence from Morality",
    x = "Morality Level",
    y = expression(atop("Intelligence", "(Comparison to Average Person)")),
    fill = NULL,
    colour = NULL
  ) +
  coord_cartesian(ylim = c(-3, 3)) +
  scale_y_continuous(breaks = -3:3) +

  # Theme and legend styling
  theme_classic() +
  theme(
    text = element_text(size = 12),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12),
    axis.title.y = element_text(margin = margin(r = 10)),
    axis.title.x = element_text(margin = margin(t = 10)),
    plot.title = element_text(size = 12, hjust = 0.5, margin = margin(b=10)),  # Centered title
    legend.position = "bottom",
    legend.direction = "horizontal",
    legend.justification = "center",
    legend.box.margin = margin(t = -5),
    legend.key.height = unit(1.2, "lines"),
    legend.key.width = unit(1.2, "lines"),
    legend.text = element_text(size = 12)
  )


intelligence_comparison_plot
```

***

## Morality

***

### General Ratings

Here we look at the conditions where intelligence was manipulated, and look at ratings of general *morality*.

```{r, message=FALSE, warning=FALSE}
# Compute model
contrasts(df_intelligence$Level) <- contr.sum
contrasts(df_intelligence$Agent) <- contr.sum
model <- lm(Moral ~ Level * Agent, data = df_intelligence)

# Extract ANOVA into table
model <- car::Anova(model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Remove intercept row
anova_df <- anova_df[-1, ]

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

# Rename the rows 
final_table <- final_table %>%
  mutate(
    Term = case_when(
      Term == "Level" ~ "Level",
      Term == "Agent" ~ "Agent",
      Term == "Level:Agent" ~ "Level × Agent",
      TRUE ~ Term
    )
  )

kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***


We'll then make the figure that we will combine and show in a minute.

```{r}
# Fit the model
model <- lm(Moral ~ Level * Agent, data = df_intelligence) # model
emm <- emmeans(model, ~ Level * Agent) # Estimated marginal means
emm_df <- as.data.frame(emm) # Convert to data frame for plotting

# Define consistent colors
fill_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")
line_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")

# Ensure correct factor order
df_intelligence$Level <- factor(df_intelligence$Level, levels = c("Low", "High"))
emm_df$Level <- factor(emm_df$Level, levels = c("Low", "High"))

# Create the plot
morality_plot <- ggplot() +

  # Jittered individual data points (filled circles, no outline)
geom_jitter(
  data = df_intelligence,
  aes(x = Level, y = Moral, fill = Agent),
  shape = 21,
  size = 2,
  stroke = 0.1,           # tiny border just to make visible
  color = "gray80",       # light neutral outline
  alpha = 0.15,
  position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.35, jitter.height = 0.35)
)+
  
  # Estimated marginal means with visible outline
geom_pointrange(
  data = emm_df,
  aes(
    x = Level,
    y = emmean,
    ymin = lower.CL,
    ymax = upper.CL,
    group = Agent,
    fill = Agent,
    colour = Agent
  ),
  position = position_dodge(width = 0.7),
  shape = 21,         # Fillable circle
  size = 1.2,         # Thicker CI line
  stroke = 1.2,       # Thicker point border
  fatten = 3,         # Large mean point
  show.legend = TRUE
)+
  

  # Color scales
  scale_color_manual(values = line_colors) +
  scale_fill_manual(values = fill_colors) +

  # Axis labels and limits
  labs(
    title = "Perceived Morality from Intelligence Level",
    x = "Intelligence Level",
    y = "Perceived Morality",
    fill = NULL,
    colour = NULL
  ) +
  coord_cartesian(ylim = c(1, 7)) +
  scale_y_continuous(breaks = 1:7) +

  # Theme and legend styling
  theme_classic() +
  theme(
    text = element_text(size = 12),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12),
    axis.title.y = element_text(margin = margin(r = 10)),
    axis.title.x = element_text(margin = margin(t = 10)),
    plot.title = element_text(size = 12, hjust = 0.5, margin = margin(b=10)),  # Centered title
    legend.position = "bottom",
    legend.direction = "horizontal",
    legend.justification = "center",
    legend.box.margin = margin(t = -5),
    legend.key.height = unit(1.2, "lines"),
    legend.key.width = unit(1.2, "lines"),
    legend.text = element_text(size = 12)
  )

morality_plot
```

***

### Comparison Question

Now we look at the question asking about morality in comparison to the average person, again only for participants who saw information about intelligence.

```{r, message=FALSE, warning=FALSE}
# Compute model
contrasts(df_intelligence$Level) <- contr.sum
contrasts(df_intelligence$Agent) <- contr.sum
model <- lm(Moral_Comparison ~ Level * Agent, data = df_intelligence)

# Extract ANOVA into table
model <- car::Anova(model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Remove intercept row
anova_df <- anova_df[-1, ]

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

# Rename the rows 
final_table <- final_table %>%
  mutate(
    Term = case_when(
      Term == "Level" ~ "Level",
      Term == "Agent" ~ "Agent",
      Term == "Level:Agent" ~ "Level × Agent",
      TRUE ~ Term
    )
  )

kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

Again, we'll make a figure that we will combine in a minute.


```{r}
# Fit the model
model <- lm(Moral_Comparison ~ Level * Agent, data = df_intelligence)
emm <- emmeans(model, ~ Level * Agent)
emm_df <- as.data.frame(emm)

# Define consistent colors
fill_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")
line_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")

# Ensure correct factor order
df_intelligence$Level <- factor(df_intelligence$Level, levels = c("Low", "High"))
emm_df$Level <- factor(emm_df$Level, levels = c("Low", "High"))

# Create the plot
morality_comparison_plot <- ggplot() +

  # Jittered individual data points (filled circles, no outline)
geom_jitter(
  data = df_morality,
  aes(x = Level, y = Moral_Comparison, fill = Agent),
  shape = 21,
  size = 2,
  stroke = 0.1,           # tiny border just to make visible
  color = "gray80",       # light neutral outline
  alpha = 0.15,
  position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.35, jitter.height = 0.35)
)+
  
# Reference line for comparison baseline
  geom_hline(yintercept = 0, linetype = "dashed", color = "gray60", linewidth = 0.7) +

  
  # Estimated marginal means with visible outline
geom_pointrange(
  data = emm_df,
  aes(
    x = Level,
    y = emmean,
    ymin = lower.CL,
    ymax = upper.CL,
    group = Agent,
    fill = Agent,
    colour = Agent
  ),
  position = position_dodge(width = 0.7),
  shape = 21,         # Fillable circle
  size = 1.2,         # Thicker CI line
  stroke = 1.2,       # Thicker point border
  fatten = 3,         # Large mean point
  show.legend = TRUE
)+

  # Color scales
  scale_color_manual(values = line_colors) +
  scale_fill_manual(values = fill_colors) +

  # Axis labels and limits
  labs(
    title = "Perceived Morality from Intelligence",
    x = "Intelligence Level",
    y = expression(atop("Morality", "(Comparison to Average Person)")),
    fill = NULL,
    colour = NULL
  ) +
  coord_cartesian(ylim = c(-3, 3)) +
  scale_y_continuous(breaks = -3:3) +

  # Theme and legend styling
  theme_classic() +
  theme(
    text = element_text(size = 12),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12),
    axis.title.y = element_text(margin = margin(r = 10)),
    axis.title.x = element_text(margin = margin(t = 10)),
    plot.title = element_text(size = 12, hjust = 0.5, margin = margin(b=10)),  # Centered title
    legend.position = "bottom",
    legend.direction = "horizontal",
    legend.justification = "center",
    legend.box.margin = margin(t = -5),
    legend.key.height = unit(1.2, "lines"),
    legend.key.width = unit(1.2, "lines"),
    legend.text = element_text(size = 12)
  )

morality_comparison_plot
```

***

## Trust

Here we look at the effects of level (high vs. low), described trait (intelligence vs morality) and agent (human vs. AI) on perceived trustworthiness.
 
```{r, message=FALSE, warning=FALSE}
# Compute model
contrasts(df$Level) <- contr.sum
contrasts(df$Agent) <- contr.sum
contrasts(df$Trait) <- contr.sum
model <- lm(Trust ~ Level * Agent * Trait, data = df)

# Extract ANOVA into table
model <- car::Anova(model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Remove intercept row
anova_df <- anova_df[-1, ]

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

# Rename the rows 
final_table <- final_table %>%
  mutate(
    Term = case_when(
      Term == "Level" ~ "Level",
      Term == "Agent" ~ "Agent",
      Term == "Trait" ~ "Trait",
      Term == "Level:Agent" ~ "Level × Agent",
      Term == "Level:Trait" ~ "Level × Trait",
      Term == "Agent:Trait" ~ "Agent × Trait",
      Term == "Level:Agent:Trait" ~ "Level × Agent × Trait",
      TRUE ~ Term
    )
  )


kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

```{r}
trust_table <- df %>%
  group_by(Agent, Trait, Level) %>%
  summarise(
    "Mean Trust" = mean(Trust),
    "SD" = sd(Trust),
    n = n(),
    .groups = "drop"
  )

kable(trust_table, format = "html", digits = 2,
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

```{r}
figure_df <- df

# Fit the model
model <- lm(Trust ~ Level * Agent * Trait, data = figure_df)

# Get estimated marginal means
emm <- emmeans(model, ~ Level * Agent * Trait)
emm_df <- as.data.frame(emm)

# Define consistent colors
fill_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")
line_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")

# Ensure correct factor ordering
figure_df$Level <- factor(figure_df$Level, levels = c("Low", "High"))
emm_df$Level <- factor(emm_df$Level, levels = c("Low", "High"))
emm_df$Trait <- factor(emm_df$Trait, levels = c("Intelligence", "Morality")) # Reorder Trait in the plotting dataframe only
figure_df$Trait <- factor(figure_df$Trait, levels = c("Intelligence", "Morality")) # Reorder Trait in the plotting dataframe only

# Create faceted plot
trust_plot <- ggplot() +

  # Jittered individual data points
  geom_jitter(
    data = figure_df,
    aes(x = Level, y = Trust, fill = Agent),
    shape = 21,
    size = 2,
    stroke = 0.1,
    color = "gray80",
    alpha = 0.15,
    position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.35, jitter.height = 0.35)
  ) +

  # Estimated marginal means
  geom_pointrange(
    data = emm_df,
    aes(
      x = Level,
      y = emmean,
      ymin = lower.CL,
      ymax = upper.CL,
      group = Agent,
      fill = Agent,
      colour = Agent
    ),
    position = position_dodge(width = 0.7),
    shape = 21,
    size = 1.2,
    stroke = 1.2,
    fatten = 3,
    show.legend = TRUE
  ) +

  # Color scales
  scale_color_manual(values = line_colors) +
  scale_fill_manual(values = fill_colors) +

  # Axis labels and limits
  labs(
    x = "Level",
    y = "Trustworthiness",
    fill = NULL,
    colour = NULL
  ) +
  coord_cartesian(ylim = c(1, 7)) +
  scale_y_continuous(breaks = 1:7) +

  # Facet by Trait
  facet_wrap(~ Trait) +

  # Theme and legend styling
  theme_classic() +
  theme(
    text = element_text(size = 12),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12),
    axis.title.y = element_text(margin = margin(r = 10)),
    legend.position = "bottom",
    legend.direction = "horizontal",
    legend.justification = "center",
    legend.box.margin = margin(t = -5),
    legend.key.height = unit(1.2, "lines"),
    legend.key.width = unit(1.2, "lines"),
    legend.text = element_text(size = 12),
    strip.text = element_text(size = 13)  # Facet labels
  )

# Define the subfolder name for figure saving
folder <- "figures"

# Create the folder if it doesn't already exist
if (!dir.exists(folder)) {
  dir.create(folder)
}

# Save
ggsave((file.path(folder, "Study 3 Trust.png")), plot = trust_plot,
       width = 5, height = 4, units = "in", dpi = 300)

trust_plot
```


***

## Danger

Here we look at the effects of level (high vs. low), described trait (intelligence vs morality) and agent (human vs. AI) on perceived danger

```{r, message=FALSE, warning=FALSE}
# Compute model
contrasts(df$Level) <- contr.sum
contrasts(df$Agent) <- contr.sum
contrasts(df$Trait) <- contr.sum
model <- lm(Danger ~ Level * Agent * Trait, data = df)

# Extract ANOVA into table
model <- car::Anova(model, type = 3)

# Get effect size
eta <- eta_squared(model, partial = TRUE)

# Get tidy ANOVA
anova_df <- as.data.frame(model)

# Remove intercept row
anova_df <- anova_df[-1, ]

# Rename rows from ANOVA table
anova_df <- anova_df %>%
  rownames_to_column("Term") %>%
  rename(
    SS = `Sum Sq`,
    df = Df,
    F = `F value`,
    p = `Pr(>F)`
  )

# Combine with partial eta square
combined_table <- left_join(anova_df, eta, by = c("Term" = "Parameter"))

# Select ones we want for APA and rename
final_table <- combined_table %>% 
  select(Term, df, F, p, Eta2_partial) %>% 
  rename(
    `ηp²` = Eta2_partial
  )

# Format test results

final_table <- final_table %>%
  mutate(
    F = round(F, 2),
    `ηp²` = round(`ηp²`, 2),
    p = case_when(
      is.na(p) ~ NA_character_,
      as.numeric(p) < 0.001 ~ "<.001",
      TRUE ~ sprintf("%.3f", as.numeric(p))
    )
  )

# Rename the rows 
final_table <- final_table %>%
  mutate(
    Term = case_when(
      Term == "Level" ~ "Level",
      Term == "Agent" ~ "Agent",
      Term == "Trait" ~ "Trait",
      Term == "Level:Agent" ~ "Level × Agent",
      Term == "Level:Trait" ~ "Level × Trait",
      Term == "Agent:Trait" ~ "Agent × Trait",
      Term == "Level:Agent:Trait" ~ "Level × Agent × Trait",
      TRUE ~ Term
    )
  )


kable(final_table, format = "html",
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

```{r}
danger_table <- df %>%
  group_by(Agent, Trait, Level) %>%
  summarise(
    "Mean Danger" = mean(Danger),
    "SD" = sd(Danger),
    n = n(),
    .groups = "drop"
  )

kable(danger_table, format = "html", digits = 2,
            row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

```{r}
figure_df <- df

# Fit the model
model <- lm(Danger ~ Level * Agent * Trait, data = figure_df)

# Get estimated marginal means
emm <- emmeans(model, ~ Level * Agent * Trait)
emm_df <- as.data.frame(emm)

# Define consistent colors
fill_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")
line_colors <- c("Human" = "#E69F00", "AI" = "#56B4E9")

# Ensure correct factor ordering
figure_df$Level <- factor(figure_df$Level, levels = c("Low", "High"))
emm_df$Level <- factor(emm_df$Level, levels = c("Low", "High"))
emm_df$Trait <- factor(emm_df$Trait, levels = c("Intelligence", "Morality")) # Reorder Trait in the plotting dataframe only
figure_df$Trait <- factor(figure_df$Trait, levels = c("Intelligence", "Morality")) # Reorder Trait in the plotting dataframe only

# Create faceted plot
danger_plot <- ggplot() +

  # Jittered individual data points
  geom_jitter(
    data = figure_df,
    aes(x = Level, y = Danger, fill = Agent),
    shape = 21,
    size = 2,
    stroke = 0.1,
    color = "gray80",
    alpha = 0.15,
    position = position_jitterdodge(dodge.width = 0.7, jitter.width = 0.35, jitter.height = 0.35)
  ) +

  # Estimated marginal means
  geom_pointrange(
    data = emm_df,
    aes(
      x = Level,
      y = emmean,
      ymin = lower.CL,
      ymax = upper.CL,
      group = Agent,
      fill = Agent,
      colour = Agent
    ),
    position = position_dodge(width = 0.7),
    shape = 21,
    size = 1.2,
    stroke = 1.2,
    fatten = 3,
    show.legend = TRUE
  ) +

  # Color scales
  scale_color_manual(values = line_colors) +
  scale_fill_manual(values = fill_colors) +

  # Axis labels and limits
  labs(
    x = "Level",
    y = "Danger",
    fill = NULL,
    colour = NULL
  ) +
  coord_cartesian(ylim = c(1, 7)) +
  scale_y_continuous(breaks = 1:7) +

  # Facet by Trait
  facet_wrap(~ Trait) +

  # Theme and legend styling
  theme_classic() +
  theme(
    text = element_text(size = 12),
    axis.title = element_text(size = 13),
    axis.text = element_text(size = 12),
    axis.title.y = element_text(margin = margin(r = 10)),
    legend.position = "bottom",
    legend.direction = "horizontal",
    legend.justification = "center",
    legend.box.margin = margin(t = -5),
    legend.key.height = unit(1.2, "lines"),
    legend.key.width = unit(1.2, "lines"),
    legend.text = element_text(size = 12),
    strip.text = element_text(size = 13)  # Facet labels
  )

# Define the subfolder name for figure saving
folder <- "figures"

# Create the folder if it doesn't already exist
if (!dir.exists(folder)) {
  dir.create(folder)
}

# Save
ggsave((file.path(folder, "Study 3 Danger.png")), plot = danger_plot,
       width = 5, height = 5, units = "in", dpi = 300)

danger_plot
```

***

# Figures

We will combine the individual plots from above to create some figures plotting rated intelligence from manipulated morality, and rated morality from manipulated intelligence. 

***

## General Ratings

```{r}
# Remove legend from the first plot
intelligence_plot_clean <- intelligence_plot +
  theme(legend.position = "none")

# Combine
combined_int_morality_plot <- morality_plot + intelligence_plot_clean +
  plot_layout(ncol = 2, guides = "collect") &
  theme(legend.position = "bottom")

# Define the subfolder name
folder <- "figures"

# Create the folder if it doesn't already exist
if (!dir.exists(folder)) {
  dir.create(folder)
}

# Save both versions
ggsave((file.path(folder, "Study 3 Intelligence and Morality.png")), plot = combined_int_morality_plot,
       width = 8, height = 4.5, units = "in", dpi = 300)

combined_int_morality_plot

```


## Comparison Question


```{r}
# Remove legend from the first plot
intelligence_comparison_plot_clean <- intelligence_comparison_plot +
  theme(legend.position = "none")

# Combine
combined_int_morality_comparison_plot <- morality_comparison_plot + intelligence_comparison_plot_clean +
  plot_layout(ncol = 2, guides = "collect") &
  theme(legend.position = "bottom")

# Define the subfolder name for figure saving
folder <- "figures"

# Create the folder if it doesn't already exist
if (!dir.exists(folder)) {
  dir.create(folder)
}

# Save
ggsave((file.path(folder, "Study 3 Intelligence and Morality Comparison.png")), plot = combined_int_morality_comparison_plot,
       width = 8, height = 4.5, units = "in", dpi = 300)

combined_int_morality_comparison_plot

```


***


***

# Correlations

***

## Overall

We'll look, across conditions, the correlation between participants ratings of intelligence and morality. We can use a quick custom function to make this easy.

```{r}
# Individual correlation 
jim.correlation <- function(DV_A, DV_B, Item) {
  
test <- cor.test(DV_A, DV_B, method = "pearson")

correlation <- test$estimate
correlation <- round(correlation, digits = 2)
df_val <- sprintf("%.0f", test$parameter)  # APA style: no decimals for df

p_raw <- test$p.value
p_formatted <- ifelse(
  is.na(p_raw), NA,
  ifelse(p_raw < .001, "< .001", paste0("= ", sub("^0", "", sprintf("%.3f", p_raw))))
)

combined <- data.frame(
    df = df_val,
    r = correlation,
    p = p_formatted,
    row.names = Item,
    check.names = FALSE
  )

  return(combined)
}

```

***

We first look at the highest level with the correlation between general intelligence and general morality.

```{r}
intelligence_morality_corr <- jim.correlation(DV_A = df$Intelligent, 
                                              DV_B = df$Moral,
                                              Item = "Intelligence + Morality")


kable(intelligence_morality_corr, format = "html",
            row.names = TRUE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

And the same for the relationship between intelligence compared to a human and morality compared to a human.

```{r}
intelligence_morality_comparison_corr <- jim.correlation(DV_A = df$Intelligent_Comparison, 
                                                DV_B = df$Moral_Comparison, 
                                                Item = "Intelligence (Comparison) + Morality (Comparison)")


kable(intelligence_morality_comparison_corr, format = "html",
            row.names = TRUE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

## By Conditions

We'll also look at this main correlation broken down by conditions.

```{r}
# Intelligence condition, AI agent
corr_intelligence_AI_high <- jim.correlation(
  DV_A = df_intelligence_AI_high$Intelligent,
  DV_B = df_intelligence_AI_high$Moral,
  Item = "Intelligence + Morality (AI + Intelligence + High)"
)

corr_intelligence_AI_low <- jim.correlation(
  DV_A = df_intelligence_AI_low$Intelligent,
  DV_B = df_intelligence_AI_low$Moral,
  Item = "Intelligence + Morality (AI + Intelligence + Low)"
)

# Morality condition, AI agent
corr_morality_AI_high <- jim.correlation(
  DV_A = df_morality_AI_high$Intelligent,
  DV_B = df_morality_AI_high$Moral,
  Item = "Intelligence + Morality (AI + Morality + High)"
)

corr_morality_AI_low <- jim.correlation(
  DV_A = df_morality_AI_low$Intelligent,
  DV_B = df_morality_AI_low$Moral,
  Item = "Intelligence + Morality (AI + Morality + Low)"
)

# Intelligence condition, Human agent
corr_intelligence_human_high <- jim.correlation(
  DV_A = df_intelligence_human_high$Intelligent,
  DV_B = df_intelligence_human_high$Moral,
  Item = "Intelligence + Morality (Human + Intelligence + High)"
)

corr_intelligence_human_low <- jim.correlation(
  DV_A = df_intelligence_human_low$Intelligent,
  DV_B = df_intelligence_human_low$Moral,
  Item = "Intelligence + Morality (Human + Intelligence + Low)"
)

# Morality condition, Human agent
corr_morality_human_high <- jim.correlation(
  DV_A = df_morality_human_high$Intelligent,
  DV_B = df_morality_human_high$Moral,
  Item = "Intelligence + Morality (Human + Morality + High)"
)

corr_morality_human_low <- jim.correlation(
  DV_A = df_morality_human_low$Intelligent,
  DV_B = df_morality_human_low$Moral,
  Item = "Intelligence + Morality (Human + Morality + Low)"
)


# Bind together
correlation_summary <- bind_rows(
  corr_intelligence_AI_high,
  corr_intelligence_AI_low,
  corr_morality_AI_high,
  corr_morality_AI_low,
  corr_intelligence_human_high,
  corr_intelligence_human_low,
  corr_morality_human_high,
  corr_morality_human_low
)

kable(correlation_summary, format = "html",
            row.names = TRUE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")

```

***

# Familiarity

***

```{r, fig.width=4, fig.height=4}
ggplot(df, aes(x = Familiarity)) +
  geom_histogram(binwidth = 1, fill = "#0072B2", color = "white") +
  scale_x_continuous(breaks = 0:6) +
  labs(
    title = "Distribution of Familiarity Ratings",
    x = "Familiarity",
    y = "Number of Participants"
  ) +
  theme_minimal(base_size = 14) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5)
  )
```

***

## Intelligence

In this study, familiarity was only asked for participants in the AI conditions, not the human conditions. Therefore we're only looking at the interactive effect with level and trait, for the AI conditions only.

***

First we'll ask for the general question.

```{r, message=FALSE}
# Centre familiarity
df <- df %>%
  mutate(
    Familiarity_c = scale(Familiarity, center = TRUE, scale = FALSE) %>% as.numeric(),
    Level = factor(Level, levels = c("Low", "High")),
    Trait = factor(Trait, levels = c("Morality", "Intelligence"))
    )
                   
# Set contrasts
contrasts(df$Level) <- contr.treatment
contrasts(df$Trait) <- contr.treatment

# Fit model
model <- lm(Intelligent ~ Familiarity_c * Level * Trait, data = df)

# Get degrees of freedom
df_resid <- df.residual(model)

# Format nicely
formatted_table <- broom::tidy(model) %>%
  mutate(
    Term = case_when(
      term == "(Intercept)" ~ "Intercept",
      term == "Familiarity_c" ~ "Familiarity (Centred)",
      term == "LevelHigh" ~ "Level [High]",
      term == "TraitIntelligence" ~ "Trait [Intelligence]",
      term == "Familiarity_c:LevelHigh" ~ "Familiarity × Level",
      term == "Familiarity_c:TraitIntelligence" ~ "Familiarity × Trait",
      term == "LevelHigh:TraitIntelligence" ~ "Trait × Level",
      term == "Familiarity_c:LevelHigh:TraitIntelligence" ~ "Familiarity x Trait × Level",
      TRUE ~ term
    ),
    Estimate = round(estimate, 2),
    SE = round(std.error, 2),
    df = df_resid,
    t = round(statistic, 2),
    p = ifelse(
      is.na(p.value), NA,
      ifelse(p.value < .001, "<.001", paste0("= ", sub("^0", "", sprintf("%.3f", p.value))))
    ),
    order = factor(Term, levels = c(
      "Intercept", 
      "Familiarity (Centred)", 
      "Level [High]", 
      "Trait [Intelligence]",
      "Trait × Level",
      "Familiarity × Level",
      "Familiarity × Trait",
      "Familiarity x Trait × Level"))
  ) %>%
  arrange(order) %>%
  select(Term, Estimate, SE, df, t, p)


kable(formatted_table,
      format = "html", 
      caption = "<span style='color:black; font-weight:bold;
      '>Perceived intelligence by participant AI familiarity, level, and trait type (AI conditions only)</span>",
       row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")


```

***

Then we'll ask for the comparison question.

```{r, message=FALSE}
# Centre familiarity
df <- df %>%
  mutate(
    Familiarity_c = scale(Familiarity, center = TRUE, scale = FALSE) %>% as.numeric(),
    Level = factor(Level, levels = c("Low", "High")),
    Trait = factor(Trait, levels = c("Morality", "Intelligence"))
    )
                   
# Set contrasts
contrasts(df$Level) <- contr.treatment
contrasts(df$Trait) <- contr.treatment

# Fit model
model <- lm(Intelligent_Comparison ~ Familiarity_c * Level * Trait, data = df)

# Get degrees of freedom
df_resid <- df.residual(model)

# Format nicely
formatted_table <- broom::tidy(model) %>%
  mutate(
    Term = case_when(
      term == "(Intercept)" ~ "Intercept",
      term == "Familiarity_c" ~ "Familiarity (Centred)",
      term == "LevelHigh" ~ "Level [High]",
      term == "TraitIntelligence" ~ "Trait [Intelligence]",
      term == "Familiarity_c:LevelHigh" ~ "Familiarity × Level",
      term == "Familiarity_c:TraitIntelligence" ~ "Familiarity × Trait",
      term == "LevelHigh:TraitIntelligence" ~ "Trait × Level",
      term == "Familiarity_c:LevelHigh:TraitIntelligence" ~ "Familiarity x Trait × Level",
      TRUE ~ term
    ),
    Estimate = round(estimate, 2),
    SE = round(std.error, 2),
    df = df_resid,
    t = round(statistic, 2),
    p = ifelse(
      is.na(p.value), NA,
      ifelse(p.value < .001, "<.001", paste0("= ", sub("^0", "", sprintf("%.3f", p.value))))
    ),
    order = factor(Term, levels = c(
      "Intercept", 
      "Familiarity (Centred)", 
      "Level [High]", 
      "Trait [Intelligence]",
      "Trait × Level",
      "Familiarity × Level",
      "Familiarity × Trait",
      "Familiarity x Trait × Level"))
  ) %>%
  arrange(order) %>%
  select(Term, Estimate, SE, df, t, p)


kable(formatted_table,
      format = "html", 
      caption = "<span style='color:black; font-weight:bold;
      '>Perceived intelligence (in comparison to average person) by participant AI familiarity, level, and trait type (AI conditions only)</span>",
       row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```

***

## Morality

In this study, familiarity was only asked for participants in the AI conditions, not the human conditions. Therefore we're only looking at the interactive effect with level and trait, for the AI conditions only.

***

First we ask for the general ratings.

```{r, message=FALSE}
# Centre familiarity
df <- df %>%
  mutate(
    Familiarity_c = scale(Familiarity, center = TRUE, scale = FALSE) %>% as.numeric(),
    Level = factor(Level, levels = c("Low", "High")),
    Trait = factor(Trait, levels = c("Morality", "Intelligence"))
    )
                   
# Set contrasts
contrasts(df$Level) <- contr.treatment
contrasts(df$Trait) <- contr.treatment

# Fit model
model <- lm(Moral ~ Familiarity_c * Level * Trait, data = df)

# Get degrees of freedom
df_resid <- df.residual(model)

# Format nicely
formatted_table <- broom::tidy(model) %>%
  mutate(
    Term = case_when(
      term == "(Intercept)" ~ "Intercept",
      term == "Familiarity_c" ~ "Familiarity (Centred)",
      term == "LevelHigh" ~ "Level [High]",
      term == "TraitIntelligence" ~ "Trait [Intelligence]",
      term == "Familiarity_c:LevelHigh" ~ "Familiarity × Level",
      term == "Familiarity_c:TraitIntelligence" ~ "Familiarity × Trait",
      term == "LevelHigh:TraitIntelligence" ~ "Trait × Level",
      term == "Familiarity_c:LevelHigh:TraitIntelligence" ~ "Familiarity x Trait × Level",
      TRUE ~ term
    ),
    Estimate = round(estimate, 2),
    SE = round(std.error, 2),
    df = df_resid,
    t = round(statistic, 2),
    p = ifelse(
      is.na(p.value), NA,
      ifelse(p.value < .001, "<.001", paste0("= ", sub("^0", "", sprintf("%.3f", p.value))))
    ),
    order = factor(Term, levels = c(
      "Intercept", 
      "Familiarity (Centred)", 
      "Level [High]", 
      "Trait [Intelligence]",
      "Trait × Level",
      "Familiarity × Level",
      "Familiarity × Trait",
      "Familiarity x Trait × Level"))
  ) %>%
  arrange(order) %>%
  select(Term, Estimate, SE, df, t, p)


kable(formatted_table,
      format = "html", 
      caption = "<span style='color:black; font-weight:bold;
      '>Perceived morality by participant AI familiarity, level, and trait type (AI conditions only)</span>",
       row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")


```

***

Then we'll ask for the comparison question.

```{r, message=FALSE}
# Centre familiarity
df <- df %>%
  mutate(
    Familiarity_c = scale(Familiarity, center = TRUE, scale = FALSE) %>% as.numeric(),
    Level = factor(Level, levels = c("Low", "High")),
    Trait = factor(Trait, levels = c("Morality", "Intelligence"))
    )
                   
# Set contrasts
contrasts(df$Level) <- contr.treatment
contrasts(df$Trait) <- contr.treatment

# Fit model
model <- lm(Moral_Comparison ~ Familiarity_c * Level * Trait, data = df)

# Get degrees of freedom
df_resid <- df.residual(model)

# Format nicely
formatted_table <- broom::tidy(model) %>%
  mutate(
    Term = case_when(
      term == "(Intercept)" ~ "Intercept",
      term == "Familiarity_c" ~ "Familiarity (Centred)",
      term == "LevelHigh" ~ "Level [High]",
      term == "TraitIntelligence" ~ "Trait [Intelligence]",
      term == "Familiarity_c:LevelHigh" ~ "Familiarity × Level",
      term == "Familiarity_c:TraitIntelligence" ~ "Familiarity × Trait",
      term == "LevelHigh:TraitIntelligence" ~ "Trait × Level",
      term == "Familiarity_c:LevelHigh:TraitIntelligence" ~ "Familiarity x Trait × Level",
      TRUE ~ term
    ),
    Estimate = round(estimate, 2),
    SE = round(std.error, 2),
    df = df_resid,
    t = round(statistic, 2),
    p = ifelse(
      is.na(p.value), NA,
      ifelse(p.value < .001, "<.001", paste0("= ", sub("^0", "", sprintf("%.3f", p.value))))
    ),
    order = factor(Term, levels = c(
      "Intercept", 
      "Familiarity (Centred)", 
      "Level [High]", 
      "Trait [Intelligence]",
      "Trait × Level",
      "Familiarity × Level",
      "Familiarity × Trait",
      "Familiarity x Trait × Level"))
  ) %>%
  arrange(order) %>%
  select(Term, Estimate, SE, df, t, p)


kable(formatted_table,
      format = "html", 
      caption = "<span style='color:black; font-weight:bold;
      '>Perceived morality (in comparison to average person) by participant AI familiarity, level, and trait type (AI conditions only)</span>",
       row.names = FALSE) %>%
        kable_styling(bootstrap_options = "striped", 
                      full_width = FALSE, position = "left")
```
