Final Project

Author

Hannah Park

Final Project

Install Packages and Load Library

# Call up the library
install.packages('tidyverse')    
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('dplyr')
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('BSDA')
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('RODBC')    
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('coin')
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('BHH2')
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('ggplot2')    
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('ggmosaic')
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('corrplot')
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('psych')    
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
install.packages('ggfortify')
Installing package into '/cloud/lib/x86_64-pc-linux-gnu-library/4.3'
(as 'lib' is unspecified)
# Call up the library
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.0
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(BSDA)
Loading required package: lattice

Attaching package: 'BSDA'

The following object is masked from 'package:datasets':

    Orange
library(RODBC)
library(coin)
Loading required package: survival
library(BHH2)
library(ggplot2)
library(ggmosaic) # mosaic plot
library(corrplot) # correlation matrix
corrplot 0.92 loaded
library(psych) # KMO test

Attaching package: 'psych'

The following objects are masked from 'package:ggplot2':

    %+%, alpha
library(ggfortify) # PCA visualization

Open & Set Up Files

# read in the file
raw_results <- read.csv("num_data.csv") #numeric value

dem_results <- read.csv("results.csv") #text value 

final_cleaned <- read.csv("final_cleaned.csv")

Clean Data

Delete Arbitrary Columns

col_deleted_results <- raw_results[, !(names(raw_results) %in% c('RecordedDate','Prolific.ID','Debrief...P.Input','StartDate', 'EndDate', 'Status', 'IPAddress', 'Progress', 'Duration..in.seconds.','Finished','ResponseId','RecipientLastName','RecipientFirstName','RecipientEmail','ExternalReference','LocationLatitude','LocationLongitude','DistributionChannel','UserLanguage','Survey.Access','Survey.Access_4_TEXT','Debrief.P.Comments','raffle...email.info'))]

# delete headers 
row_deleted_results <- col_deleted_results[-(1:2), ]

Attention Check

Delete data from participants who did not pass attention check questions

# Remove rows where any of the specified columns does not contain "3"
attent_check <- consent_col_deleted[(consent_col_deleted$V1.N6...Attention.Q == "3" | consent_col_deleted$V1.N6...Attention.Q == "" | is.na(consent_col_deleted$V1.N6...Attention.Q)) &
               (consent_col_deleted$V1.N6...Attention.Q.1 == "3" | consent_col_deleted$V1.N6...Attention.Q.1 == "" | is.na(consent_col_deleted$V1.N6...Attention.Q.1)) &
               (consent_col_deleted$V2.N8...Attention.Q == "3" | consent_col_deleted$V2.N8...Attention.Q == "" | is.na(consent_col_deleted$V2.N8...Attention.Q)) &
               (consent_col_deleted$V2.N8...Attention.Q.1 == "3" | consent_col_deleted$V2.N8...Attention.Q.1 == "" | is.na(consent_col_deleted$V2.N8...Attention.Q.1)), ]

# Remove attention check columns
cleaned_data <- attent_check[, !(names(attent_check) %in% c('V1.N6...Attention.Q','V1.N6...Attention.Q.1','V2.N8...Attention.Q','V2.N8...Attention.Q.1'))]

Convert Data

# Convert to integer 
emotional_total <- as.integer(cleaned_data$`V1.N6...Emotional.Q`)
logical_total <- as.integer(cleaned_data$`V1.N6...Logical.Q`)

# Replace NA values in 'emotional_total' with values from 'V2.N8...Emotional.Q'
na_indices_emotional <- is.na(emotional_total)
emotional_total[na_indices_emotional] <- as.integer(cleaned_data$`V2.N8...Emotional.Q`[na_indices_emotional])

# Replace NA values in 'logical_total' with values from 'V2.N8...Logical.Q'
na_indices_logical <- is.na(logical_total)
logical_total[na_indices_logical] <- as.integer(cleaned_data$`V2.N8...Logical.Q`[na_indices_logical])

# Combine into a new data frame
cleaned <- data.frame(emotional_total, logical_total)

Demographic Information

Clean Demographic Data

dem_results <- dem_results[c("Age", "Race", "Gender","Social.Media.Apps")]
dem_results <- dem_results[-(1:3), ]
dem_results[dem_results == ""] <- NA
dem_results <- na.omit(dem_results)

Age

dem_results$Age <- factor(dem_results$Age)

Bar Chart Visualization

ggplot(dem_results, aes(x = Age)) +
  geom_bar(fill = "blue", color = "black") +
  labs(title = "Distribution of Age Groups", x = "Age Group", y = "Count") +
  theme_minimal()

Table

age_table <- table(dem_results$Age)
print(age_table)

            18-20             21-25             25-30             35-40 
               28                39                21                11 
              40+ Prefer not to say 
               13                 4 

Pie Chart Visualization

# Calculate percentages for Age Group
age_counts <- table(dem_results$Age)

# Directly create age_df with correct column names and data
age_df <- data.frame(Category = names(age_counts), Count = as.integer(age_counts))

# Calculate the Percentage column
age_df$Percentage <- (age_df$Count / sum(age_df$Count)) * 100

# ggplot
ggplot(age_df, aes(x = "", y = Percentage, fill = Category)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  labs(title = "Pie Chart of Age Group Distribution", x = NULL, y = NULL) +
  theme_void() +
  theme(legend.title = element_blank())

Race

# Convert to character to ensure manipulation works
dem_results$Race <- as.character(dem_results$Race)  
dem_results$Race <- ifelse(grepl(",", dem_results$Race),
                           "Two or More Races",
                           dem_results$Race)

Bar Chart Visualization

ggplot(dem_results, aes(x = Race)) +
  geom_bar(fill = "red", color = "black") +
  labs(title = "Distribution of Race Groups", x = "Race Group", y = "Count") +
  theme_minimal()

Table

race_table <- table(dem_results$Race)
print(race_table)

                 Asian Black/African American     Hispanic or Latino 
                    18                     17                     13 
     Prefer not to say      Two or More Races        White/Caucasian 
                     4                      9                     55 

Pie Chart Visualization

# Calculate percentages for Race
race_counts <- table(dem_results$Race)
race_df <- data.frame(Category = names(race_counts), Count = as.integer(race_counts))
race_df$Percentage <- (race_df$Count / sum(race_df$Count)) * 100

# Pie Chart for Race
ggplot(race_df, aes(x = "", y = Percentage, fill = Category)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  labs(title = "Pie Chart of Race Distribution", x = NULL, y = NULL) +
  theme_void() +
  theme(legend.title = element_blank())

Gender

# Reclassify specified gender categories
dem_results$Gender <- as.character(dem_results$Gender)
dem_results$Gender <- ifelse(grepl(",", dem_results$Gender) | 
                               dem_results$Gender == "Non-binary / third gender",
                             "Non-binary/Other",
                             dem_results$Gender)
dem_results$Gender <- factor(dem_results$Gender)

Bar Chart Visualization

ggplot(dem_results, aes(x = Gender)) +
  geom_bar(fill = "blue", color = "black") +
  labs(title = "Distribution of Gender Groups", x = "Gender Group", y = "Count") +
  theme_minimal()

Table

gender_table <- table(dem_results$Gender)
print(gender_table)

           Female              Male  Non-binary/Other Prefer not to say 
               59                47                 9                 1 

Pie Chart Visualization

# Convert the gender table to a data frame for plotting
gender_df <- as.data.frame(gender_table)
names(gender_df) <- c("Gender", "Count")

# Calculate percentages
gender_df$Percentage <- (gender_df$Count / sum(gender_df$Count)) * 100

ggplot(gender_df, aes(x = "", y = Percentage, fill = Gender)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar(theta = "y") +
  labs(title = "Pie Chart of Gender Distribution", x = NULL, y = NULL) +
  theme_void() +
  theme(legend.title = element_blank())

Descriptive Statistics

Emotional

# Calculate descriptive statistics for emotional total
descriptive_stats_emotional <- final_cleaned %>%
  summarise(
    Mean = mean(emotional_total, na.rm = TRUE),
    Median = median(emotional_total, na.rm = TRUE),
    Standard_Deviation = sd(emotional_total, na.rm = TRUE),
    Variance = var(emotional_total, na.rm = TRUE),
    IQR = IQR(emotional_total, na.rm = TRUE),
    Minimum = min(emotional_total, na.rm = TRUE),
    Maximum = max(emotional_total, na.rm = TRUE)
  )

# Display the statistics for emotional total
print(descriptive_stats_emotional)
      Mean Median Standard_Deviation Variance IQR Minimum Maximum
1 2.474138      2           1.281719 1.642804   3       1       5

Logical

# Calculate descriptive statistics for logical total
descriptive_stats_logical <- final_cleaned %>%
  summarise(
    Mean = mean(logical_total, na.rm = TRUE),
    Median = median(logical_total, na.rm = TRUE),
    Standard_Deviation = sd(logical_total, na.rm = TRUE),
    Variance = var(logical_total, na.rm = TRUE),
    IQR = IQR(logical_total, na.rm = TRUE),
    Minimum = min(logical_total, na.rm = TRUE),
    Maximum = max(logical_total, na.rm = TRUE)
  )

# Display the statistics for logical total
print(descriptive_stats_logical)
     Mean Median Standard_Deviation Variance  IQR Minimum Maximum
1 2.37931      2           1.227618 1.507046 2.25       1       5

Main Plots

All Vignette & Conditions

# Creating frequency tables and converting them to data frames while renaming columns
V1N6_emo_freq <- as.data.frame(table(na.omit(cleaned_data[["V1.N6...Emotional.Q"]])))
V1N6_emo_freq <- V1N6_emo_freq[-1, ]  # Assuming the first row is to be omitted
V1N6_emo_freq$Type = "V1N6 Emotional"
names(V1N6_emo_freq)[1] = "Answer"

V1N6_log_freq <- as.data.frame(table(na.omit(cleaned_data[["V1.N6...Logical.Q"]])))
V1N6_log_freq <- V1N6_log_freq[-1, ]
V1N6_log_freq$Type = "V1N6 Logical"
names(V1N6_log_freq)[1] = "Answer"

V2N8_emo_freq <- as.data.frame(table(na.omit(cleaned_data[["V2.N8...Emotional.Q"]])))
V2N8_emo_freq <- V2N8_emo_freq[-1, ]
V2N8_emo_freq$Type = "V2N8 Emotional"
names(V2N8_emo_freq)[1] = "Answer"

V2N8_log_freq <- as.data.frame(table(na.omit(cleaned_data[["V2.N8...Logical.Q"]])))
V2N8_log_freq <- V2N8_log_freq[-1, ]
V2N8_log_freq$Type = "V2N8 Logical"
names(V2N8_log_freq)[1] = "Answer"

# Combine frequency tables
freq_tables <- rbind(V1N6_emo_freq, V1N6_log_freq, V2N8_emo_freq, V2N8_log_freq)

ggplot(data = freq_tables, aes(x = Answer, y = Freq, fill = Type)) +
  geom_bar(position = "dodge", stat = "identity") +
  labs(title = "Responses Distribution", x = "Response Type", y = "Frequency") +
  scale_x_discrete(labels = c("Completely unacceptable", "Somewhat unacceptable", "Neutral", "Somewhat acceptable", "Completely acceptable")) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))  # Adjust x-axis labels for better readability

Emotional vs Logical

final_cleaned_wide <- final_cleaned
final_cleaned_wide$person <- 1:nrow(final_cleaned_wide)
colnames(final_cleaned_wide) <- c("total_emo", "total_log", "person")

final_cleaned_wide <- pivot_longer(final_cleaned_wide, 
                                   cols = c(total_emo, total_log), 
                                   names_to = "condition", 
                                   values_to = "measurement")

freq_emo <- table(final_cleaned$emotional_total)
freq_log <- table(final_cleaned$logical_total)

# Convert tables to data frames
freq_emo_df <- as.data.frame(freq_emo)
freq_log_df <- as.data.frame(freq_log)

# Naming columns
names(freq_emo_df) <- c("Measurement", "Freq")
names(freq_log_df) <- c("Measurement", "Freq")

freq_emo_df$Type <- "Emotional"
freq_log_df$Type <- "Logical"

freq_table <- rbind(freq_emo_df, freq_log_df)

ggplot(data = freq_table, aes(fill = Type, x = Measurement, y = Freq)) +
  geom_bar(position = "dodge", stat = "identity") +
  scale_x_discrete(labels = c("1" = "Completely unacceptable", 
                              "2" = "Somewhat unacceptable", 
                              "3" = "Neutral", 
                              "4" = "Somewhat acceptable", 
                              "5" = "Completely acceptable")) +
  labs(title = "Frequency Distribution by Type", x = "Measurement", y = "Frequency") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.title = element_blank())

By Vignette

# Extract and process responses for both vignettes
process_vignette <- function(emotional_col, logical_col) {
  temp1 <- as.numeric(unlist(cleaned_data[[emotional_col]]))
  temp2 <- as.numeric(unlist(cleaned_data[[logical_col]]))
  na.omit(c(temp1, temp2))
}

vignette1 <- process_vignette("V1.N6...Emotional.Q", "V1.N6...Logical.Q")
vignette2 <- process_vignette("V2.N8...Emotional.Q", "V2.N8...Logical.Q")

create_freq_df <- function(vignette, type_label) {
  table_vig <- table(vignette)
  df_vig <- as.data.frame(table_vig)
  names(df_vig) <- c("Measurement", "Freq")
  df_vig$Type <- type_label
  df_vig
}

tableV1 <- create_freq_df(vignette1, "Vignette 1")
tableV2 <- create_freq_df(vignette2, "Vignette 2")

vignette_table <- bind_rows(tableV1, tableV2)

ggplot(data = vignette_table, aes(fill = Type, x = Measurement, y = Freq)) + 
  geom_bar(position = "dodge", stat = "identity") + 
  scale_x_discrete(labels = c("1" = "Completely unacceptable", 
                              "2" = "Somewhat unacceptable", 
                              "3" = "Neutral", 
                              "4" = "Somewhat acceptable", 
                              "5" = "Completely acceptable")) + 
  labs(title = "Frequency of Responses by Vignette", x = "Response Type", y = "Frequency") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Assumption Checks for Paired T-Test

emo_vector <- as.numeric(unlist(final_cleaned[["emotional_total"]]))
log_vector <- as.numeric(unlist(final_cleaned[["logical_total"]]))

Check #1: Do we have a binary categorical IV?

Yes, we have a binary categorical IV – our two factors are emotional and logical.

Check #2: Do we have a continuous DV?

Yes, we have a continuous DV that ranges from 1 to 5.

Check #3: Are the observations dependent on each

other?

No, this can be eliminated due to randomness on whether the emotional or the logical comment set is given first

Check #4: Are the morality rating difference in the

two conditions normally distributed?

## Usage: Shapiro-Wilk normality test 
## Note: sample size is very large (n > 30), so if our IV were continuous we can ignore data distribution and use parametric tests (b/c CLT assumes that sampling distribution is normalized) -- but we should still do distribution testing due to the high discretization of our DV

shapiro_emo <- shapiro.test(emo_vector)
shapiro_log <- shapiro.test(log_vector)

# Calculate differences
difference_vector <- emo_vector - log_vector
shapiro_diff <- shapiro.test(difference_vector)
difference_df = data.frame(difference_vector)

# Print results
print(shapiro_emo)

    Shapiro-Wilk normality test

data:  emo_vector
W = 0.85686, p-value = 3.288e-09
print(shapiro_log)

    Shapiro-Wilk normality test

data:  log_vector
W = 0.86053, p-value = 4.643e-09
print(shapiro_diff)

    Shapiro-Wilk normality test

data:  difference_vector
W = 0.94234, p-value = 8.224e-05
In both cases, p-value << 0.05, so we CANNOT assume normality. Fails normality test.

Because the individual sample Shapiro-Wilk test failed, we can try and just assess the difference between the two and see if it is normally distributed. But, still failed normality test.

Histogram Visualization

ggplot(data = difference_df, aes(x = difference_vector)) + geom_histogram(bins = 7, color = "black", fill = "white")

Paired t-test (check for effect between

emotional vs. logical comments)

cleaned_emo_vector = as.vector(unlist(final_cleaned["emotional_total"]))
cleaned_log_vector = as.vector(unlist(final_cleaned["logical_total"]))
type_t_result = t.test(x = as.vector(cleaned_emo_vector), 
 y = as.vector(cleaned_log_vector), 
 paired = TRUE, 
 alternative = "two.sided")
type_t_result

    Paired t-test

data:  as.vector(cleaned_emo_vector) and as.vector(cleaned_log_vector)
t = 0.71301, df = 115, p-value = 0.4773
alternative hypothesis: true mean difference is not equal to 0
95 percent confidence interval:
 -0.1686118  0.3582670
sample estimates:
mean difference 
     0.09482759 

Summary Bar Graphs for Main Effects

# Data Preparation and Statistics Calculation
stats_summary <- final_cleaned %>%
  summarise(
    Mean_Emotional = mean(emotional_total, na.rm = TRUE),
    SD_Emotional = sd(emotional_total, na.rm = TRUE),
    Mean_Logical = mean(logical_total, na.rm = TRUE),
    SD_Logical = sd(logical_total, na.rm = TRUE)
  )

# Transform data for plotting
stats_plot <- data.frame(
  Condition = c("Emotional", "Logical"),
  Mean = c(stats_summary$Mean_Emotional, stats_summary$Mean_Logical),
  SD = c(stats_summary$SD_Emotional, stats_summary$SD_Logical)
)
# Plotting the bar graph with error bars
ggplot(stats_plot, aes(x = Condition, y = Mean, fill = Condition)) +
  geom_bar(stat = "identity", width = 0.5) +
  geom_errorbar(aes(ymin = Mean - SD, ymax = Mean + SD), width = 0.2, color = "black") +
  labs(title = "Mean Scores of Emotional vs. Logical Comments",
       x = "Condition",
       y = "Rating of Morality") +
  theme_minimal() +
  scale_fill_brewer(palette = "Pastel1")  # Adding a color palette for visual distinction