Author

Marcus, Dayo

Published

October 23, 2024

Setup

Libraries and functions

Code
rm(list = ls())

knitr::opts_chunk$set(warning = FALSE, message = FALSE) 

Mypackages <-
  c("lme4","tidyverse","effects","ggplot2","psych",
    "MASS","Rmisc","lmerTest","ggthemes", "knitr",
    "lsmeans","pastecs","sjstats","car","ordinal",
    "Rcpp","corrplot", "ggpubr", "EnvStats",
    "easyStats", "cowplot","see","datawizard", 
    "ggcorrplot", "lavaan", "qualtRics", "effsize",
    "stringr")

# install.packages(Mypackages) #you must remove the # in this comment if you need to install the packages! 
lapply(Mypackages,
       require,
       character.only = TRUE)

options(knitr.kable.NA = '—')
set.seed(1)  

Load Data

Code
# read in data files
setwd("~/Desktop")
data_raw <-read_survey("/Users/mtrenfield17/Desktop/Research/Boston College Research/Morality Lab Research/Study 14/Moral Pilot N20/Study14_MoralN20.csv")

Functions

Code
plot_fn <- function(data, iv, dv, coln = NULL, rown = NULL, facet_var = NULL, facet_var2 = NULL, 
                    x_label = "", y_label = "", title = "", 
                    x_text_size = 13, y_text_size = 13, x_title_size = 13, y_title_size = 13, 
                    plot_title_size = 16, facet_text_size = 12, 
                    x_levels = NULL, x_labels = NULL) {
  # Reorder the x variable if x_levels is provided
  if (!is.null(x_levels)) {
    data[[deparse(substitute(iv))]] <- factor(data[[deparse(substitute(iv))]], levels = x_levels)
  }
  # Rename the x variable if x_labels is provided
  if (!is.null(x_labels)) {
    data[[deparse(substitute(iv))]] <- factor(data[[deparse(substitute(iv))]], labels = x_labels)
  }

  # Create the base plot
  part1 <- ggplot(data, aes(x = {{iv}}, y = {{dv}}, fill = {{iv}})) +
    geom_violin(alpha = 0.3, scale = "count") + 
    stat_summary(fun = "mean", geom = "point", size = 3, color = "black") +
    stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2,
                 size = 1.5, color = "black") +
    theme_classic() +
    xlab(x_label) +
    ylab(y_label) +
    ggtitle(title) +
    theme(
      panel.background = element_rect(fill = "transparent"), 
      legend.position = "right", 
      plot.title = element_text(face = "bold", hjust = 0.5, size = plot_title_size), 
      plot.subtitle = element_text(hjust = 0.5),
      panel.grid.major.y = element_line(color='grey75'), 
      axis.text.x = element_text(face = "plain", size = x_text_size, color = "black"),
      axis.text.y = element_text(face = "plain", size = y_text_size, color = "black"),
      axis.title.x = element_text(face = "plain", size = x_title_size, color = "black"),
      axis.title.y = element_text(face = "plain", size = y_title_size, color = "black", 
                                  margin = margin(t = 0, r = 10, b = 0, l = 0)),
      panel.border = element_rect(color = "black", fill = NA, size = 1),
      strip.text = element_text(size = facet_text_size)  # Adjust the facet text size
    )
  
  # Check if a facet_var (row) and facet_var2 (column) are provided
  if (!is.null(facet_var) & !is.null(facet_var2)) {
    # If both row and column variables are provided, use facet_grid
    part1 <- part1 + facet_grid(as.formula(paste(facet_var, "~", facet_var2)))
  } else if (!is.null(facet_var)) {
    # If only one facet variable is provided, facet by rows
    part1 <- part1 + facet_wrap(as.formula(paste("~", facet_var)), 
                                ncol = if (!is.null(coln)) coln else NULL, 
                                nrow = if (!is.null(rown)) rown else NULL, 
                                scales = "free", as.table = TRUE)
  }
  
  # Final plot adjustments
  ggpar(part1, legend = "none")
}

Reshaping data

Demographics

Code
# making a column for white vs non-white
data_raw$White <- ifelse(grepl("White", data_raw$Race_Ethnicity_TEXT), "White", "Non-White")

# making a column for URM vs non-URM
urm_groups <- c("Black", "Hispanic or Latino/a/x", "American Indian and Native Alaskan", "Pacific Islander or Native Hawaiian", "Middle Eastern and North African")

data_raw$URM <- ifelse(grepl(paste(urm_groups, collapse="|"), data_raw$Race_Ethnicity_TEXT), "URM", "Non-URM")

## Making a Gender column with just Man & Woman
data_raw <- data_raw %>%
  mutate(genderMF = ifelse(Gender_TEXT %in% c("Man", "Woman"), Gender_TEXT, NA))

# changing numeric demos to numeric
data_raw <- data_raw %>% mutate_at(vars(Age, attn_self), as.numeric)

# reordering demos
data_raw <- data_raw %>%
  mutate(
    Gender_TEXT = factor(Gender_TEXT, levels = c("Man", "Woman", "I identify as:")),
    # pol_TEXT = factor(pol_TEXT, levels = c("Very Liberal", "Liberal", "Somewhat Liberal", "Moderate",
    # "Somewhat Conservative", "Conservative", "Very Conservative")),
    # edu_TEXT = factor(edu_TEXT, levels = c("Some schooling, but no high school diploma or degree", 
    # "High school diploma or GED", "Some college, Technical degree, or Associates degree", 
    # "Bachelor's degree", "Graduate degree (Masters, PhD, etc)")),
    # inc_TEXT = factor(inc_TEXT, levels = c("less than $25,000", "$25,000 - $49,999", "$50,000 - $74,999", 
    # "$75,000 - $99,999", "$100,000 - $149,999", "$150,000 - $199,999","more than $200,000")),
    # political_group = factor(political_group, levels = c("Liberal", "Moderate", "Conservative")),
    White = factor(White, levels = c("White", "Non-White")),
    URM = factor(URM, levels = c("Non-URM", "URM")),
    genderMF = factor(genderMF, levels = c("Man", "Woman"))
  )

Study Details

Code
# filtering people who failed the attn check
data_raw$attn_self <- as.numeric(data_raw$attn_self)
data <- data_raw %>% filter(attn_self > 2)

# making numeric DVs numeric
start_col <- "1_moral_public"
end_col <- "40_private_feedback"

cols_in_range <- colnames(data)[which(colnames(data) == start_col):which(colnames(data) == end_col)]

columns_to_convert <- grep(".*(?<!_feedback)$", cols_in_range, value = TRUE, perl = TRUE)

for (col in columns_to_convert) {
  data[[col]] <- ifelse(data[[col]] != "feedback", as.numeric(data[[col]]), data[[col]])
}

# Renaming

## renaming motives from # to text
motive_code <- c("1" = "Rep", "2" = "Norm", "3" = "Injuct", "4" = "Principle")

### Identify the columns that match the pattern "_motive_public|_motive_private"
cols_to_rename <- grep("motive_(public|private)_[1-4]", colnames(data), value = TRUE)

for (col in cols_to_rename) {
  number <- str_extract(col, "[1-4]$") # Extract the motive number at the end (1-4)
  type <- ifelse(str_detect(col, "public"), "public", "private")   # Extract whether it's public or private
  motive_group <- motive_code[[number]]   # Get the corresponding motive group from motive_code
  new_name <- str_replace(col, paste0("motive_", type, "_", number),  
                          paste0("motive", motive_group, "_", type)) # Create the new column name
  colnames(data)[colnames(data) == col] <- new_name   # Rename the column in the dataset
}

## Rename feedback columns from condition_feedback to feedback_condition 
colnames(data) <- gsub("_(public|private)_feedback$", "_feedback_\\1", colnames(data))

# make dataset long
data_long <- data %>% gather(stim, resp, "1_moral_public":"40_feedback_private")

## Split variable names into respective DVs
data_long <- data_long %>%
  separate(stim, into = c("scenario", "DV", "condition"), sep = "_")

# shift dataset back to wide format
data_long_spread <- data_long %>%
  pivot_wider(names_from = DV, values_from = resp)


# removing dud rows 
# Define the range of columns to check for NA
columns_to_check <- c("moral", "virtue", "motiveRep", "motiveNorm", "motiveInjuct", "motivePrinciple")  # Adjust the column names as needed

# Filter rows where not all values in the specified columns are NA
data_long_spread <- data_long_spread %>%
  filter(rowSums(is.na(dplyr::select(., all_of(columns_to_check)))) != length(columns_to_check))


# Convert outcomes to numeric
data_long_spread[columns_to_check] <- lapply(data_long_spread[columns_to_check], as.numeric)

Making column for virtue

Code
data_long_spread <- data_long_spread %>% mutate(scenarioVirtue = case_when(
  scenario == "1" ~ 'care',
  scenario == "2" ~ 'care',
  scenario == "3" ~ 'charity',
  scenario == "4" ~ 'charity',
  scenario == "5" ~ 'citizenship',
  scenario == "6" ~ 'citizenship',
  scenario == "7" ~ 'civility',
  scenario == "8" ~ 'civility',
  scenario == "9" ~ 'compassion',
  scenario == "10" ~ 'compassion',
  scenario == "11" ~ 'courage',
  scenario == "12" ~ 'courage',
  scenario == "13" ~ 'curiosity',
  scenario == "14" ~ 'curiosity',
  scenario == "15" ~ 'empathy',
  scenario == "16" ~ 'empathy',
  scenario == "17" ~ 'fairness',
  scenario == "18" ~ 'fairness',
  scenario == "19" ~ 'forgiveness',
  scenario == "20" ~ 'forgiveness',
  scenario == "21" ~ 'generosity',
  scenario == "22" ~ 'generosity',
  scenario == "23" ~ 'gratitude',
  scenario == "24" ~ 'gratitude',
  scenario == "25" ~ 'honesty',
  scenario == "26" ~ 'honesty',
  scenario == "27" ~ 'humility',
  scenario == "28" ~ 'humility',
  scenario == "29" ~ 'integrity',
  scenario == "30" ~ 'integrity',
  scenario == "31" ~ 'kindness',
  scenario == "32" ~ 'kindness',
  scenario == "33" ~ 'love',
  scenario == "34" ~ 'love',
  scenario == "35" ~ 'loyalty',
  scenario == "36" ~ 'loyalty',
  scenario == "37" ~ 'patience',
  scenario == "38" ~ 'patience',
  scenario == "39" ~ 'perseverance',
  scenario == "40" ~ 'perseverance',
  TRUE ~ scenario
))

Renaming scenarios

Code
# renaming scenarios to text
data_long_spread <- data_long_spread %>% mutate(scenarioText = case_when(
  scenario == "1" ~ 'birthday gift',
  scenario == "2" ~ 'sick colleague soup',
  scenario == "3" ~ 'donations local charity',
  scenario == "4" ~ 'GoFundMe Donation',
  scenario == "5" ~ 'inform the police',
  scenario == "6" ~ 'climate change petition signature',
  scenario == "7" ~ 'defuse fight',
  scenario == "8" ~ 'complimenting colleague',
  scenario == "9" ~ 'helping elderly person cross street',
  scenario == "10" ~ 'rescue stray dog',
  scenario == "11" ~ 'town hall meeting policy',
  scenario == "12" ~ 'defending kid against bullies',
  scenario == "13" ~ 'reading',
  scenario == "14" ~ 'asking coworker about hobbies',
  scenario == "15" ~ 'emphathizing with injured friends pain',
  scenario == "16" ~ 'crying for friend - deceased family member',
  scenario == "17" ~ 'referee makes fair calls',
  scenario == "18" ~ 'lunch server gives food equally',
  scenario == "19" ~ 'Forgiving bag theft',
  scenario == "20" ~ 'late to birthday dinner',
  scenario == "21" ~ 'giving money to homeless individual',
  scenario == "22" ~ 'leaving 40% tip',
  scenario == "23" ~ 'writing thank you card',
  scenario == "24" ~ 'customer thanking cashier',
  scenario == "25" ~ 'testifying in court',
  scenario == "26" ~ 'coworker disapproves project',
  scenario == "27" ~ 'coworker promotion',
  scenario == "28" ~ 'perfect score',
  scenario == "29" ~ 'football game bet',
  scenario == "30" ~ 'bribed testimony',
  scenario == "31" ~ 'boss compliment',
  scenario == "32" ~ 'helping elderly carrying groceries',
  scenario == "33" ~ 'hug romantic partner',
  scenario == "34" ~ 'kiss romantic partner',
  scenario == "35" ~ 'rejecting romantic advance while in a relationship',
  scenario == "36" ~ 'staying at the company ',
  scenario == "37" ~ '3 hour wait for concert',
  scenario == "38" ~ '1 hour wait for food',
  scenario == "39" ~ 'four mile run',
  scenario == "40" ~ 'new personal lifting record',
  TRUE ~ scenario
))

Data Quality Checks

Attention Check

  • 0 people rejected the consent
  • 0 people failed the attention check
Code
data_raw <- data_raw %>% filter(consent == 1)

data_raw %>%
  group_by(attn_self) %>%
  dplyr::summarise(n = n()) %>%
  mutate(freq = n / sum(n))

Demographics

Code
# Subset your data frame to include only the demographic columns
demo_data <- data[, c("Gender_TEXT", "Race_Ethnicity_TEXT", "White", "URM")]

# Age
mean(data$Age, na.rm=TRUE)
[1] 36.4
Code
sd(data$Age, na.rm=TRUE)
[1] 13.84122
Code
# Loop through each demographic column and calculate frequency counts
freq_tables <- list()

for (col in names(demo_data)) {
  {
    freq_table <- as.data.frame(table(demo_data[[col]]))
    freq_table$Percent <- round(freq_table$Freq / sum(freq_table$Freq) * 100, 2)
    freq_tables[[col]] <- freq_table
  }
}

# Print the frequency tables
for (i in seq_along(freq_tables)) {
  if (!is.null(freq_tables[[i]])) {
    cat("\nTable of frequencies for", names(freq_tables)[i], ":\n")
    print(freq_tables[[i]])
  }
}

Table of frequencies for Gender_TEXT :
            Var1 Freq Percent
1            Man   20      50
2          Woman   20      50
3 I identify as:    0       0

Table of frequencies for Race_Ethnicity_TEXT :
                              Var1 Freq Percent
1        Black or African American    4    10.0
2                       East Asian    1     2.5
3 East Asian,Southeast Asian,White    1     2.5
4         Hispanic or Latina/o/x/e    3     7.5
5   Hispanic or Latina/o/x/e,White    2     5.0
6                            White   29    72.5

Table of frequencies for White :
       Var1 Freq Percent
1     White   32      80
2 Non-White    8      20

Table of frequencies for URM :
     Var1 Freq Percent
1 Non-URM   36      90
2     URM    4      10

Demographic Plot

Code
# List of demographic columns to plot
demographic_columns <- c("Gender_TEXT", "Race_Ethnicity_TEXT", "White", "URM")  # Use column names as strings

# Function to create percent plot
create_percent_plot <- function(data, column) {
  # Calculate the frequency and percentage for each category
  freq_table <- data %>%
    group_by(across(all_of(column))) %>%
    dplyr::summarise(Freq = n()) %>%
    mutate(Percent = Freq / sum(Freq) * 100)
  
  # Create the plot
  p <- ggplot(freq_table, aes_string(x = column, y = "Percent", fill = column)) +
    geom_bar(stat = "identity", position = "dodge") +
    scale_y_continuous(labels = scales::percent_format(scale = 1)) +
    labs(x = column, y = "Percentage", title = paste("Distribution of", column)) +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
  
  return(p)
}

# Loop through demographic columns and plot
lapply(demographic_columns, function(col) create_percent_plot(demo_data, col))
[[1]]


[[2]]


[[3]]


[[4]]

Code
ggplot(data, aes(x = Age)) +
  geom_histogram(binwidth = 5, color = "black", alpha = 0.7) +
  labs(title = "Age", x = " ", y = "Distribution") +
  theme_minimal()

Correlations

Code
# Convert categorical variables to numeric and create new columns
data_long_spread <- data_long_spread %>%
  mutate(
    condition_numeric = as.numeric(factor(condition, levels = c("public", "private"))),
    genderMF_numeric = as.numeric(factor(genderMF, levels = c("Man", "Woman"))),
    White_numeric = as.numeric(factor(White, levels = c("Non-White", "White"))),
    URM_numeric = as.numeric(factor(URM, levels = c("Non-URM", "URM")))
  )

# Select correlation
DVs <- data_long_spread[c("condition_numeric", "moral", "virtue", "motiveRep", "motiveNorm", "motiveInjuct", "motivePrinciple", "Age", "genderMF_numeric", "White_numeric", "URM_numeric")]

# Compute pairwise correlations
corr_DVs <- cor(DVs, use = "complete.obs")

# Plot the correlation matrix
corrplot(corr_DVs, is.corr = TRUE, type = "lower", lower = "circle", tl.cex = 0.7, insig = "label_sig", diag = TRUE)

moral plot

Code
data_long_spread$condition <- factor(data_long_spread$condition, levels = c("private", "public"))

plot_fn(data_long_spread, condition, moral, y_label = "How Moral is the Behavior?", title = "Morality judgments across action anonymity", y_title_size = 20, x_text_size = 20, y_text_size = 20, plot_title_size = 25, facet_text_size = 18)

moral plot by virtue

Code
data_long_spread$condition <- factor(data_long_spread$condition, levels = c("public", "private"))

ggplot(data_long_spread, aes(x = moral, y = fct_rev(fct_inorder(scenarioVirtue)), fill = condition)) +
  stat_summary(fun = mean, geom = "bar", position = position_dodge()) + 
  stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2, position = position_dodge(0.9)) +
  labs(x = "", y = "Scenario", title = "How moral is this behavior?") + 
  coord_cartesian(xlim = c(1, 7)) + 
  scale_x_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) + 
  scale_fill_manual(values = c("public" = "#00BFC4", "private" = "#F8766D")) +  # Colors swapped
  theme(
    plot.title = element_text(hjust = 0.5, size = 24),
    axis.text.x = element_text(face = "plain", size = 20, color = "black"),
    axis.text.y = element_text(face = "plain", size = 17, color = "black"),
    axis.title.y = element_text(face = "plain", size = 20, color = "black"),
    axis.title.x = element_text(face = "plain", size = 20, color = "black"),
    legend.position = "bottom",  # Move legend to the bottom
    legend.text = element_text(size = 15),  # Increase legend text size
    legend.title = element_text(size = 18)  # Increase legend title size
  )

moral plot by scenario

Code
# Ensure scenarioVirtue is a factor ordered alphabetically
data_long_spread <- data_long_spread %>%
  mutate(scenarioVirtue = fct_inorder(scenarioVirtue))

# Reorder scenarioText based on the levels of scenarioVirtue
data_long_spread <- data_long_spread %>%
  mutate(scenarioText = fct_reorder(scenarioText, as.numeric(scenarioVirtue)))


ggplot(data_long_spread, aes(x = moral, y = fct_rev(scenarioText), fill = condition)) +
  stat_summary(fun = mean, geom = "bar", position = position_dodge()) + 
  stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2, position = position_dodge(0.9)) +
  labs(x = "", y = "Scenario", title = "How moral is this behavior?") + 
  coord_cartesian(xlim = c(1, 7)) + 
  scale_x_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) + 
  scale_fill_manual(values = c("public" = "#00BFC4", "private" = "#F8766D")) +  # Colors swapped
  theme(
    plot.title = element_text(hjust = 0.5, size = 24),
    axis.text.x = element_text(face = "plain", size = 20, color = "black"),
    axis.text.y = element_text(face = "plain", size = 17, color = "black"),
    axis.title.y = element_text(face = "plain", size = 20, color = "black"),
    axis.title.x = element_text(face = "plain", size = 20, color = "black"),
    legend.position = "bottom",  # Move legend to the bottom
    legend.text = element_text(size = 15),  # Increase legend text size
    legend.title = element_text(size = 18)  # Increase legend title size
  ) 

virtuous plot

Code
data_long_spread$condition <- factor(data_long_spread$condition, levels = c("private", "public"))

plot_fn(data_long_spread, condition, virtue, y_label = "How Virtuous is the Behavior?", title = "Virtue judgments across action anonymity", y_title_size = 20, x_text_size = 20, y_text_size = 20, plot_title_size = 25, facet_text_size = 18)

virtue plot by virtue

Code
data_long_spread$condition <- factor(data_long_spread$condition, levels = c("public", "private"))

ggplot(data_long_spread, aes(x = virtue, y = fct_rev(fct_inorder(scenarioVirtue)), fill = condition)) +
  stat_summary(fun = mean, geom = "bar", position = position_dodge()) + 
  stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2, position = position_dodge(0.9)) +
  labs(x = "", y = "Scenario", title = "How virtuous is this behavior?") + 
  coord_cartesian(xlim = c(1, 7)) + 
  scale_x_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) + 
  scale_fill_manual(values = c("public" = "#00BFC4", "private" = "#F8766D")) +  # Colors swapped
  theme(
    plot.title = element_text(hjust = 0.5, size = 24),
    axis.text.x = element_text(face = "plain", size = 20, color = "black"),
    axis.text.y = element_text(face = "plain", size = 17, color = "black"),
    axis.title.y = element_text(face = "plain", size = 20, color = "black"),
    axis.title.x = element_text(face = "plain", size = 20, color = "black"),
    legend.position = "bottom",  # Move legend to the bottom
    legend.text = element_text(size = 15),  # Increase legend text size
    legend.title = element_text(size = 18)  # Increase legend title size
  )

virtue plot by scenario

Code
data_long_spread$condition <- factor(data_long_spread$condition, levels = c("public", "private"))

ggplot(data_long_spread, aes(x = virtue, y = fct_rev(scenarioText), fill = condition)) +
  stat_summary(fun = mean, geom = "bar", position = position_dodge()) + 
  stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2, position = position_dodge(0.9)) +
  labs(x = "", y = "Scenario", title = "How virtuous is this behavior?") + 
  coord_cartesian(xlim = c(1, 7)) + 
  scale_x_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) + 
  scale_fill_manual(values = c("public" = "#00BFC4", "private" = "#F8766D")) +  # Colors swapped
  theme(
    plot.title = element_text(hjust = 0.5, size = 24),
    axis.text.x = element_text(face = "plain", size = 20, color = "black"),
    axis.text.y = element_text(face = "plain", size = 17, color = "black"),
    axis.title.y = element_text(face = "plain", size = 20, color = "black"),
    axis.title.x = element_text(face = "plain", size = 20, color = "black"),
    legend.position = "bottom",  # Move legend to the bottom
    legend.text = element_text(size = 15),  # Increase legend text size
    legend.title = element_text(size = 18)  # Increase legend title size
  )

motive plot

Code
data_long_motive <- data_long_spread %>% gather(motive, resp, "motiveRep":"motivePrinciple") %>% 
  mutate(motive = str_replace(motive, "motive", ""))  # Remove the word "motive"

data_long_motive$condition <- factor(data_long_motive$condition, levels = c("private", "public"))

ggplot(data_long_motive, aes(x = motive, y = resp, fill = condition)) +
    geom_violin(alpha = 0.3, scale = "count", position = position_dodge(width = 0.8)) + 
    stat_summary(fun = "mean", geom = "point", size = 3, color = "black", 
                 position = position_dodge(width = 0.8)) +
    stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2,
                 size = 1.5, color = "black", position = position_dodge(width = 0.8)) +
    theme_classic() +
    xlab("Motive") +
    ylab("") +
    ggtitle("Perceptions of actor's motive across anonymity") +
  theme(
    panel.background = element_rect(fill = "transparent"), 
    plot.title = element_text(hjust = 0.5, size = 24),
    axis.text.x = element_text(face = "plain", size = 20, color = "black"),
    axis.text.y = element_text(face = "plain", size = 17, color = "black"),
    axis.title.y = element_text(face = "plain", size = 20, color = "black"),
    axis.title.x = element_text(face = "plain", size = 20, color = "black"),
    legend.position = "bottom",  # Move legend to the bottom
    legend.text = element_text(size = 15),  # Increase legend text size
    legend.title = element_text(size = 18)  # Increase legend title size
  )

motive plot by virtue

Code
ggplot(data_long_motive, aes(x = motive, y = resp, fill = condition)) +
    geom_violin(alpha = 0.3, scale = "count", position = position_dodge(width = 0.8)) + 
    stat_summary(fun = "mean", geom = "point", size = 3, color = "black", 
                 position = position_dodge(width = 0.8)) +
    stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2,
                 size = 1.5, color = "black", position = position_dodge(width = 0.8)) +
    theme_classic() +
    xlab("Motive") +
    ylab("") +
    ggtitle("Perceptions of actor's motive across anonymity") +
  facet_wrap("scenarioVirtue") +
  theme(
    panel.background = element_rect(fill = "transparent"), 
    plot.title = element_text(hjust = 0.5, size = 24),
    axis.text.x = element_text(face = "plain", size = 12, color = "black"),
    axis.text.y = element_text(face = "plain", size = 17, color = "black"),
    axis.title.y = element_text(face = "plain", size = 20, color = "black"),
    axis.title.x = element_text(face = "plain", size = 20, color = "black"),
    legend.position = "bottom",  # Move legend to the bottom
    legend.text = element_text(size = 15),  # Increase legend text size
    legend.title = element_text(size = 18)  # Increase legend title size
  )

motive plot by scenario

Code
ggplot(data_long_motive, aes(x = motive, y = resp, fill = condition)) +
    geom_violin(alpha = 0.3, scale = "count", position = position_dodge(width = 0.8)) + 
    stat_summary(fun = "mean", geom = "point", size = 3, color = "black", 
                 position = position_dodge(width = 0.8)) +
    stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = 0.2,
                 size = 1.5, color = "black", position = position_dodge(width = 0.8)) +
    theme_classic() +
    xlab("Motive") +
    ylab("") +
    ggtitle("Perceptions of actor's motive across anonymity") +
  facet_wrap("scenarioText") +
  theme(
    panel.background = element_rect(fill = "transparent"), 
    plot.title = element_text(hjust = 0.5, size = 24),
    axis.text.x = element_text(face = "plain", size = 11, color = "black"),
    axis.text.y = element_text(face = "plain", size = 17, color = "black"),
    axis.title.y = element_text(face = "plain", size = 20, color = "black"),
    axis.title.x = element_text(face = "plain", size = 20, color = "black"),
    legend.position = "bottom",  # Move legend to the bottom
    legend.text = element_text(size = 15),  # Increase legend text size
    legend.title = element_text(size = 18)  # Increase legend title size
  )