## [1] "Data Loaded. Checking Survey Labels:"
##
## Baseline Post-Training Early Learning Late Learning
## 48 48 48 48
# ==============================================================================
# DEMOGRAPHICS: Footedness Check
# ==============================================================================
# Load demographics
library(readxl)
demo_df <- read_excel("Demographics.xlsx")
View(demo_df)
# Filter for the 3 main groups
demo_df <- demo_df %>%
filter(Group %in% c("CON", "FAM", "OMM")) %>%
mutate(Group = factor(Group, levels = c("CON", "FAM", "OMM")))
# Create a Contingency Table
foot_table <- table(demo_df$Group, demo_df$Footedness)
print("--- Footedness Counts by Group ---")
## [1] "--- Footedness Counts by Group ---"
print(foot_table)
##
## Comfortable with both feet Left-foot Right-foot
## CON 1 3 12
## FAM 2 1 13
## OMM 0 3 13
# Run Chi-Square Test
print("--- Chi-Square Test for Footedness ---")
## [1] "--- Chi-Square Test for Footedness ---"
foot_chi <- chisq.test(foot_table)
## Warning in chisq.test(foot_table): Chi-squared approximation may be incorrect
print(foot_chi)
##
## Pearson's Chi-squared test
##
## data: foot_table
## X-squared = 3.1955, df = 4, p-value = 0.5257
# 1. Gaming Hours Per Week (ANOVA)
print("--- ANOVA: Gaming Hours Per Week ---")
## [1] "--- ANOVA: Gaming Hours Per Week ---"
m_gaming_hours <- lm(GamingHoursPerWeek ~ Group, data = demo_df)
print(anova(m_gaming_hours))
## Analysis of Variance Table
##
## Response: GamingHoursPerWeek
## Df Sum Sq Mean Sq F value Pr(>F)
## Group 2 100.5 50.250 0.9325 0.401
## Residuals 45 2424.8 53.885
# 2. Gaming Level Self-Rating (Chi-Square)
print("--- Chi-Square Test: Gaming Level Self-Rating ---")
## [1] "--- Chi-Square Test: Gaming Level Self-Rating ---"
gaming_table <- table(demo_df$Group, demo_df$GamingLevelSelfRating)
print(gaming_table)
##
## Advanced Beginner Complete beginner/I do not game Expert Intermediate
## CON 4 1 4 0 7
## FAM 3 4 6 1 2
## OMM 5 2 6 0 3
gaming_chi <- chisq.test(gaming_table)
## Warning in chisq.test(gaming_table): Chi-squared approximation may be incorrect
print(gaming_chi)
##
## Pearson's Chi-squared test
##
## data: gaming_table
## X-squared = 8.5, df = 8, p-value = 0.3862
# Optional: Kruskal-Wallis for Hours (Robust to Skew/Outliers)
kruskal.test(GamingHoursPerWeek ~ Group, data = demo_df)
##
## Kruskal-Wallis rank sum test
##
## data: GamingHoursPerWeek by Group
## Kruskal-Wallis chi-squared = 1.0407, df = 2, p-value = 0.5943
# ==============================================================================
# 2. ACCURACY ANALYSIS (Linear "Lukas" Method - Consistent)
# ==============================================================================
# --- A. Learning Phase (Blocks 1-6) ---
ACC_Learn <- trial_df %>% filter(Session <= 6)
ACC_Learn$Subject <- factor(ACC_Learn$Subject)
ACC_Learn$Group <- factor(ACC_Learn$Group, levels = c("CON", "FAM", "OMM"))
ACC_Learn$Session <- factor(ACC_Learn$Session, levels=c('1', '2', '3', '4', '5', '6'))
ACC_Learn$feedback.ACC.trial <- as.numeric(ACC_Learn$feedback.ACC.trial)
print("--- MODEL: ACCURACY LEARNING (Linear LMER) ---")
## [1] "--- MODEL: ACCURACY LEARNING (Linear LMER) ---"
accuracymodel <- lmer(feedback.ACC.trial ~ Session * Group + (1|Subject),
data=ACC_Learn, REML = FALSE)
print(Anova(accuracymodel, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: feedback.ACC.trial
## Chisq Df Pr(>Chisq)
## Session 551.3683 5 < 2.2e-16 ***
## Group 0.4717 2 0.789895
## Session:Group 24.7158 10 0.005911 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# --- B. Test Phase (Retention & Transfer) ---
ACC_Test <- trial_df %>% filter(Session > 6)
ACC_Test$Subject <- factor(ACC_Test$Subject)
ACC_Test$Group <- factor(ACC_Test$Group, levels = c("CON", "FAM", "OMM"))
ACC_Test$Condition <- factor(ACC_Test$Condition, levels=c("Retention", "Transfer"))
ACC_Test$feedback.ACC.trial <- as.numeric(ACC_Test$feedback.ACC.trial)
print("--- MODEL: ACCURACY TEST (Linear LMER) ---")
## [1] "--- MODEL: ACCURACY TEST (Linear LMER) ---"
accuracyTestModel <- lmer(feedback.ACC.trial ~ Condition * Group + (1|Subject),
data=ACC_Test, REML = FALSE)
print(Anova(accuracyTestModel, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: feedback.ACC.trial
## Chisq Df Pr(>Chisq)
## Condition 71.5140 1 <2e-16 ***
## Group 0.5147 2 0.7731
## Condition:Group 0.8815 2 0.6435
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# --- C. Pairwise T-Tests (Learning Blocks) ---
run_pairwise_ttests_acc <- function(data_subset, value_col) {
groups <- c("OMM", "FAM", "CON")
pairs <- combn(groups, 2, simplify = FALSE)
results <- list()
for(pair in pairs) {
g1 <- pair[1]; g2 <- pair[2]
d1 <- data_subset %>% filter(Group == g1)
d2 <- data_subset %>% filter(Group == g2)
if(nrow(d1) > 1 & nrow(d2) > 1) {
t_res <- t.test(d1[[value_col]], d2[[value_col]])
results[[paste(g1, "vs", g2)]] <- data.frame(
Comparison = paste(g1, "vs", g2),
p_value = t_res$p.value,
Mean_G1 = mean(d1[[value_col]]),
Mean_G2 = mean(d2[[value_col]]),
Diff_Raw = mean(d1[[value_col]]) - mean(d2[[value_col]])
)
}
}
bind_rows(results)
}
print("--- T-TESTS: Accuracy Learning (Block 1-6) ---")
## [1] "--- T-TESTS: Accuracy Learning (Block 1-6) ---"
for(b in 1:6) {
block_res <- run_pairwise_ttests_acc(ACC_Learn %>% filter(Session == b), "feedback.ACC.trial")
print(paste(">> BLOCK", b))
print(block_res %>% mutate(p_value = round(p_value, 4), Diff_Raw = round(Diff_Raw, 3)))
}
## [1] ">> BLOCK 1"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0910 0.6562500 0.6966146 -0.040
## 2 OMM vs CON 0.0049 0.6562500 0.7226562 -0.066
## 3 FAM vs CON 0.2612 0.6966146 0.7226562 -0.026
## [1] ">> BLOCK 2"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.1718 0.8906250 0.9114583 -0.021
## 2 OMM vs CON 0.1030 0.8906250 0.8632812 0.027
## 3 FAM vs CON 0.0028 0.9114583 0.8632812 0.048
## [1] ">> BLOCK 3"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.7500 0.8867188 0.8815104 0.005
## 2 OMM vs CON 0.3107 0.8867188 0.8697917 0.017
## 3 FAM vs CON 0.4868 0.8815104 0.8697917 0.012
## [1] ">> BLOCK 4"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.8785 0.8736979 0.8710938 0.003
## 2 OMM vs CON 0.2670 0.8736979 0.8919271 -0.018
## 3 FAM vs CON 0.2068 0.8710938 0.8919271 -0.021
## [1] ">> BLOCK 5"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0554 0.8567708 0.8893229 -0.033
## 2 OMM vs CON 0.5543 0.8567708 0.8671875 -0.010
## 3 FAM vs CON 0.1849 0.8893229 0.8671875 0.022
## [1] ">> BLOCK 6"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0323 0.8424479 0.8802083 -0.038
## 2 OMM vs CON 0.5224 0.8424479 0.8541667 -0.012
## 3 FAM vs CON 0.1328 0.8802083 0.8541667 0.026
print("--- T-TESTS: Accuracy Test Phase (Retention & Transfer) ---")
## [1] "--- T-TESTS: Accuracy Test Phase (Retention & Transfer) ---"
for(cond in c("Retention", "Transfer")) {
test_res <- run_pairwise_ttests_acc(ACC_Test %>% filter(Condition == cond), "feedback.ACC.trial")
print(paste(">> CONDITION:", cond))
print(test_res %>% mutate(p_value = round(p_value, 4), Diff_Raw = round(Diff_Raw, 3)))
}
## [1] ">> CONDITION: Retention"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.9431 0.8489583 0.8502604 -0.001
## 2 OMM vs CON 0.1609 0.8489583 0.8736979 -0.025
## 3 FAM vs CON 0.1832 0.8502604 0.8736979 -0.023
## [1] ">> CONDITION: Transfer"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.2849 0.7682292 0.7447917 0.023
## 2 OMM vs CON 0.4630 0.7682292 0.7838542 -0.016
## 3 FAM vs CON 0.0714 0.7447917 0.7838542 -0.039
# --- D. Step-Level Accuracy (Checking for Speed-Accuracy Trade-offs) ---
print("--- MODEL: STEP-LEVEL ACCURACY (Aggregated LMM) ---")
## [1] "--- MODEL: STEP-LEVEL ACCURACY (Aggregated LMM) ---"
# 1. Calculate % Accuracy per Subject per Step (collapsing across blocks)
acc_step_summary <- foot_df %>%
filter(Session <= 6) %>%
group_by(Subject, Group, foot.position) %>%
summarise(Step_Accuracy = mean(feedback.ACC, na.rm = TRUE), .groups = "drop")
# 2. Run a standard Linear Mixed Model (lmer)
m_acc_step_linear <- lmer(Step_Accuracy ~ Group * as.factor(foot.position) + (1|Subject),
data = acc_step_summary)
print(Anova(m_acc_step_linear, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: Step_Accuracy
## Chisq Df Pr(>Chisq)
## Group 0.2330 2 0.8900
## as.factor(foot.position) 288.4034 5 <2e-16 ***
## Group:as.factor(foot.position) 6.1003 10 0.8068
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# ==============================================================================
# 3. FILTERING LOGIC (For RT Models)
# ==============================================================================
# --- A. ACCURACY ONLY (For Stats - LogRT) ---
apply_filtering_acc_only <- function(df, target_col) {
valid_df <- df %>% filter(feedback.ACC.trial == 1, .data[[target_col]] > 0)
valid_df$Log_RT <- log(valid_df[[target_col]])
return(valid_df)
}
df_learn <- apply_filtering_acc_only(trial_df %>% filter(Condition == "Learning"), "feedback.RT.mean")
df_test <- apply_filtering_acc_only(trial_df %>% filter(Condition %in% c("Retention", "Transfer")), "feedback.RT.mean")
df_foot_clean <- apply_filtering_acc_only(foot_df, "feedback.RT")
# --- B. 2.5 SD CLEANING (For Plots & % Improvement) ---
apply_filtering_plot <- function(df, target_col) {
valid_df <- df %>% filter(feedback.ACC.trial == 1, .data[[target_col]] > 0)
valid_df$Log_RT <- log(valid_df[[target_col]])
mean_log <- mean(valid_df$Log_RT)
sd_log <- sd(valid_df$Log_RT)
final_df <- valid_df %>%
filter(Log_RT >= (mean_log - 2.5 * sd_log) & Log_RT <= (mean_log + 2.5 * sd_log))
return(final_df)
}
df_learn_plot <- apply_filtering_plot(trial_df %>% filter(Condition == "Learning"), "feedback.RT.mean")
df_test_plot <- apply_filtering_plot(trial_df %>% filter(Condition %in% c("Retention", "Transfer")), "feedback.RT.mean")
df_foot_plot <- apply_filtering_plot(foot_df, "feedback.RT")
# ==============================================================================
# 4. STATISTICAL MODELS (RT ONLY)
# ==============================================================================
# --- A. RT Learning (LMM) ---
print("--- MODEL: RT LEARNING (Slope) ---")
## [1] "--- MODEL: RT LEARNING (Slope) ---"
m_rt_learn <- lmer(Log_RT ~ Group * as.numeric(Session) + (1|Subject), data = df_learn)
print(Anova(m_rt_learn, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: Log_RT
## Chisq Df Pr(>Chisq)
## Group 0.2357 2 0.8888
## as.numeric(Session) 3785.6317 1 < 2.2e-16 ***
## Group:as.numeric(Session) 32.2258 2 1.005e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# --- B. RT Test (LMM) ---
print("--- MODEL: RT TEST (Retention vs Transfer) ---")
## [1] "--- MODEL: RT TEST (Retention vs Transfer) ---"
m_rt_test <- lmer(Log_RT ~ Group * Condition + (1|Subject), data = df_test)
print(Anova(m_rt_test, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: Log_RT
## Chisq Df Pr(>Chisq)
## Group 0.2312 2 0.8908276
## Condition 746.9451 1 < 2.2e-16 ***
## Group:Condition 17.7158 2 0.0001423 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# --- C. Concatenation Learning (LMM - Step Level) ---
print("--- MODEL: CONCATENATION LEARNING (Group x Block x Step) ---")
## [1] "--- MODEL: CONCATENATION LEARNING (Group x Block x Step) ---"
m_foot_learn <- lmer(Log_RT ~ Group * as.factor(Session) * as.factor(foot.position) + (1|Subject),
data = df_foot_clean %>% filter(Session <= 6))
print(Anova(m_foot_learn, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: Log_RT
## Chisq Df Pr(>Chisq)
## Group 0.4362 2 0.8041
## as.factor(Session) 13721.1997 5 <2e-16 ***
## as.factor(foot.position) 17995.4985 5 <2e-16 ***
## Group:as.factor(Session) 194.3912 10 <2e-16 ***
## Group:as.factor(foot.position) 302.4145 10 <2e-16 ***
## as.factor(Session):as.factor(foot.position) 192.4877 25 <2e-16 ***
## Group:as.factor(Session):as.factor(foot.position) 228.4567 50 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# --- D. Concatenation Test (LMM - Step Level) ---
print("--- MODEL: CONCATENATION TEST (Group x Condition x Step) ---")
## [1] "--- MODEL: CONCATENATION TEST (Group x Condition x Step) ---"
m_foot_test <- lmer(Log_RT ~ Group * Panel_Label * as.factor(foot.position) + (1|Subject),
data = df_foot_clean %>% filter(Session > 6))
print(Anova(m_foot_test, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: Log_RT
## Chisq Df Pr(>Chisq)
## Group 0.2763 2 0.8709
## Panel_Label 1672.2449 1 < 2.2e-16 ***
## as.factor(foot.position) 6979.9902 5 < 2.2e-16 ***
## Group:Panel_Label 39.3200 2 2.896e-09 ***
## Group:as.factor(foot.position) 97.5077 10 < 2.2e-16 ***
## Panel_Label:as.factor(foot.position) 78.6621 5 1.598e-15 ***
## Group:Panel_Label:as.factor(foot.position) 44.1915 10 3.041e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# ==============================================================================
# 5. EXPLORATORY T-TESTS (RT)
# ==============================================================================
run_pairwise_ttests <- function(data_subset, value_col, log_col) {
groups <- c("OMM", "FAM", "CON")
pairs <- combn(groups, 2, simplify = FALSE)
results <- list()
for(pair in pairs) {
g1 <- pair[1]; g2 <- pair[2]
d1 <- data_subset %>% filter(Group == g1)
d2 <- data_subset %>% filter(Group == g2)
if(nrow(d1) > 1 & nrow(d2) > 1) {
t_res <- t.test(d1[[log_col]], d2[[log_col]])
results[[paste(g1, "vs", g2)]] <- data.frame(
Comparison = paste(g1, "vs", g2),
p_value = t_res$p.value,
Mean_G1 = mean(d1[[value_col]]), Mean_G2 = mean(d2[[value_col]]),
Diff_Raw = mean(d1[[value_col]]) - mean(d2[[value_col]])
)
}
}
bind_rows(results)
}
# --- A1. Learning RT ---
print("--- T-TESTS: RT Learning (Block 1-6) ---")
## [1] "--- T-TESTS: RT Learning (Block 1-6) ---"
for(b in 1:6) {
block_res <- run_pairwise_ttests(df_learn %>% filter(Session == b), "feedback.RT.mean", "Log_RT")
print(paste(">> BLOCK", b))
print(block_res %>% mutate(p_value = round(p_value, 4), Diff_Raw = round(Diff_Raw)))
}
## [1] ">> BLOCK 1"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0031 687.8442 646.7439 41
## 2 OMM vs CON 0.0000 687.8442 637.8715 50
## 3 FAM vs CON 0.1789 646.7439 637.8715 9
## [1] ">> BLOCK 2"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0000 482.5470 427.8462 55
## 2 OMM vs CON 0.0000 482.5470 409.9186 73
## 3 FAM vs CON 0.4202 427.8462 409.9186 18
## [1] ">> BLOCK 3"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0002 411.6951 375.8831 36
## 2 OMM vs CON 0.0003 411.6951 381.0337 31
## 3 FAM vs CON 0.9739 375.8831 381.0337 -5
## [1] ">> BLOCK 4"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0008 389.6975 354.3286 35
## 2 OMM vs CON 0.0071 389.6975 358.7377 31
## 3 FAM vs CON 0.4312 354.3286 358.7377 -4
## [1] ">> BLOCK 5"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0116 377.8642 357.0178 21
## 2 OMM vs CON 0.0007 377.8642 342.5105 35
## 3 FAM vs CON 0.4705 357.0178 342.5105 15
## [1] ">> BLOCK 6"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.7336 355.2022 353.8190 1
## 2 OMM vs CON 0.1474 355.2022 334.7414 20
## 3 FAM vs CON 0.0627 353.8190 334.7414 19
# --- A2. Test Phase RT ---
print("--- T-TESTS: Test Phase RT ---")
## [1] "--- T-TESTS: Test Phase RT ---"
for(cond in c("Retention", "Transfer")) {
test_res <- run_pairwise_ttests(df_test %>% filter(Condition == cond), "feedback.RT.mean", "Log_RT")
print(paste(">> CONDITION:", cond))
print(test_res %>% mutate(p_value = round(p_value, 4), Diff_Raw = round(Diff_Raw)))
}
## [1] ">> CONDITION: Retention"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.3561 364.0999 349.1460 15
## 2 OMM vs CON 0.0082 364.0999 324.2983 40
## 3 FAM vs CON 0.0801 349.1460 324.2983 25
## [1] ">> CONDITION: Transfer"
## Comparison p_value Mean_G1 Mean_G2 Diff_Raw
## 1 OMM vs FAM 0.0008 468.8932 411.0204 58
## 2 OMM vs CON 0.0001 468.8932 401.4023 67
## 3 FAM vs CON 0.7250 411.0204 401.4023 10
# --- B. Concatenation RT ---
concat_results <- list()
for(b in 1:6) { # Learning
for(p in 1:6) {
res <- run_pairwise_ttests(df_foot_clean %>% filter(Session == b, foot.position == p), "feedback.RT", "Log_RT")
if(nrow(res) > 0) { res$Phase <- paste("Block", b); res$Step <- p; concat_results[[paste("B", b, p)]] <- res }
}
}
for(cond in c("Retention", "Transfer")) { # Test
for(p in 1:6) {
res <- run_pairwise_ttests(df_foot_clean %>% filter(Panel_Label == cond, foot.position == p), "feedback.RT", "Log_RT")
if(nrow(res) > 0) { res$Phase <- cond; res$Step <- p; concat_results[[paste(cond, p)]] <- res }
}
}
print("--- T-TESTS: Concatenation (Significant Only) ---")
## [1] "--- T-TESTS: Concatenation (Significant Only) ---"
final_concat <- bind_rows(concat_results) %>% filter(p_value < 0.05) %>% select(Phase, Step, Comparison, p_value, Diff_Raw)
print(head(final_concat, 100))
## Phase Step Comparison p_value Diff_Raw
## 1 Block 1 1 FAM vs CON 2.926226e-02 -209.7592321
## 2 Block 1 2 OMM vs FAM 2.620306e-05 68.6304703
## 3 Block 1 2 OMM vs CON 4.670809e-11 107.0418704
## 4 Block 1 2 FAM vs CON 2.978048e-02 38.4114002
## 5 Block 1 3 OMM vs FAM 5.146015e-03 45.0401276
## 6 Block 1 3 OMM vs CON 2.880810e-06 118.0499785
## 7 Block 1 4 OMM vs FAM 1.102689e-02 18.5755526
## 8 Block 1 4 OMM vs CON 4.294532e-08 106.7172887
## 9 Block 1 4 FAM vs CON 8.955553e-03 88.1417361
## 10 Block 1 5 OMM vs FAM 6.230306e-08 103.3303108
## 11 Block 1 5 OMM vs CON 1.652877e-11 155.3145324
## 12 Block 1 6 OMM vs FAM 1.156872e-05 70.9329625
## 13 Block 1 6 OMM vs CON 1.780815e-06 82.3797083
## 14 Block 2 1 OMM vs FAM 7.780598e-03 -5.2963409
## 15 Block 2 1 OMM vs CON 1.494462e-06 80.2488886
## 16 Block 2 2 OMM vs FAM 7.274168e-05 45.0920217
## 17 Block 2 2 OMM vs CON 5.705961e-11 68.0049284
## 18 Block 2 2 FAM vs CON 1.602813e-03 22.9129067
## 19 Block 2 3 OMM vs FAM 3.355918e-08 67.3701838
## 20 Block 2 3 OMM vs CON 9.433374e-12 86.9931465
## 21 Block 2 4 OMM vs FAM 1.437731e-09 72.9670927
## 22 Block 2 4 OMM vs CON 1.546658e-13 89.8249186
## 23 Block 2 5 OMM vs FAM 1.704973e-10 81.9947285
## 24 Block 2 5 OMM vs CON 5.350715e-11 87.4419769
## 25 Block 2 6 OMM vs FAM 1.067780e-09 66.0773350
## 26 Block 2 6 OMM vs CON 3.182774e-03 23.2569924
## 27 Block 2 6 FAM vs CON 3.131489e-03 -42.8203426
## 28 Block 3 1 OMM vs FAM 2.520562e-06 33.8301893
## 29 Block 3 1 OMM vs CON 1.684813e-03 20.3063235
## 30 Block 3 1 FAM vs CON 4.855950e-02 -13.5238659
## 31 Block 3 2 OMM vs CON 7.666155e-05 17.1280171
## 32 Block 3 2 FAM vs CON 6.346276e-08 27.3582112
## 33 Block 3 3 OMM vs FAM 5.226842e-03 29.6381570
## 34 Block 3 3 OMM vs CON 4.021453e-08 49.8556961
## 35 Block 3 3 FAM vs CON 2.646057e-03 20.2175391
## 36 Block 3 4 OMM vs FAM 1.009982e-03 27.5799860
## 37 Block 3 4 OMM vs CON 8.244858e-07 28.9661140
## 38 Block 3 5 OMM vs FAM 4.168149e-07 74.3059841
## 39 Block 3 5 OMM vs CON 6.465741e-08 67.5205712
## 40 Block 3 6 OMM vs FAM 2.163221e-05 59.7478402
## 41 Block 3 6 FAM vs CON 4.476545e-05 -59.5563202
## 42 Block 4 1 OMM vs FAM 3.503340e-03 54.8205365
## 43 Block 4 1 OMM vs CON 2.444126e-03 61.7071938
## 44 Block 4 2 FAM vs CON 2.795740e-02 4.1968119
## 45 Block 4 3 OMM vs FAM 1.796622e-03 29.5190789
## 46 Block 4 3 OMM vs CON 1.289094e-06 47.1184723
## 47 Block 4 4 OMM vs FAM 2.435674e-03 29.0621387
## 48 Block 4 4 OMM vs CON 2.611729e-03 26.1094956
## 49 Block 4 5 OMM vs FAM 1.828391e-07 73.2048902
## 50 Block 4 5 OMM vs CON 1.504925e-05 61.9291960
## 51 Block 4 6 OMM vs FAM 1.103089e-02 35.4796580
## 52 Block 4 6 FAM vs CON 5.220804e-05 -40.9092032
## 53 Block 5 1 OMM vs FAM 6.197775e-04 49.7937781
## 54 Block 5 1 OMM vs CON 6.235256e-13 128.4866188
## 55 Block 5 1 FAM vs CON 2.688475e-04 78.6928407
## 56 Block 5 3 OMM vs FAM 3.365879e-02 12.1547704
## 57 Block 5 3 OMM vs CON 1.012254e-04 36.0594394
## 58 Block 5 4 OMM vs CON 1.406548e-02 18.4980604
## 59 Block 5 5 OMM vs FAM 9.837092e-07 50.9223722
## 60 Block 5 5 OMM vs CON 2.939599e-07 54.6118824
## 61 Block 5 6 FAM vs CON 1.696904e-02 -24.2855755
## 62 Block 6 1 OMM vs FAM 1.072453e-02 38.9089654
## 63 Block 6 1 OMM vs CON 2.706324e-08 86.0333340
## 64 Block 6 1 FAM vs CON 2.263673e-03 47.1243686
## 65 Block 6 2 OMM vs FAM 6.099048e-04 -31.6602915
## 66 Block 6 2 FAM vs CON 6.927375e-03 -0.1463324
## 67 Block 6 3 OMM vs CON 7.631619e-04 30.6019009
## 68 Block 6 3 FAM vs CON 3.721458e-04 38.5996717
## 69 Block 6 4 FAM vs CON 1.840610e-02 17.2818949
## 70 Block 6 5 OMM vs FAM 1.674826e-02 33.2912212
## 71 Block 6 5 OMM vs CON 2.347018e-05 46.6081681
## 72 Block 6 6 OMM vs CON 2.320048e-02 -26.1138486
## 73 Retention 1 OMM vs FAM 4.508165e-02 37.2758176
## 74 Retention 1 OMM vs CON 1.271498e-12 138.8451103
## 75 Retention 1 FAM vs CON 2.648589e-07 101.5692927
## 76 Retention 3 OMM vs CON 1.807403e-03 43.8259031
## 77 Retention 3 FAM vs CON 4.554302e-03 39.6534052
## 78 Retention 5 OMM vs FAM 2.197188e-03 47.6577523
## 79 Retention 5 OMM vs CON 2.506808e-04 56.8386485
## 80 Transfer 1 OMM vs FAM 4.971358e-04 128.8279246
## 81 Transfer 1 OMM vs CON 1.096730e-09 197.6400079
## 82 Transfer 1 FAM vs CON 7.235065e-03 68.8120833
## 83 Transfer 2 OMM vs CON 2.998438e-02 22.2744411
## 84 Transfer 3 OMM vs FAM 2.920290e-02 38.7690885
## 85 Transfer 4 OMM vs CON 3.120026e-02 44.4083169
## 86 Transfer 5 OMM vs FAM 1.818928e-02 59.9735688
## 87 Transfer 5 OMM vs CON 1.471898e-03 55.4082156
## 88 Transfer 6 OMM vs FAM 1.892055e-05 53.9776046
## 89 Transfer 6 OMM vs CON 1.262649e-06 61.8591475
# ==============================================================================
# 6. RT IMPROVEMENT ANALYSIS (% Change Block 1 -> 6)
# ==============================================================================
# Filter Correct Trials & Remove Outliers (2.5 SD) from raw data first
rt_clean_imp <- trial_df %>%
filter(Session %in% c(1, 6), feedback.ACC.trial == 1, feedback.RT.mean > 0) %>%
group_by(Subject, Session) %>%
mutate(mn=mean(feedback.RT.mean), sd=sd(feedback.RT.mean),
is_outlier = feedback.RT.mean > (mn+2.5*sd) | feedback.RT.mean < (mn-2.5*sd)) %>%
filter(!is_outlier) %>% ungroup()
# Calculate Means & % Difference
rt_summary <- rt_clean_imp %>%
group_by(Subject, Group, Session) %>%
summarise(Mean_RT = mean(feedback.RT.mean), .groups = "drop") %>%
pivot_wider(names_from = Session, values_from = Mean_RT, names_prefix = "Block") %>%
mutate(percentage_diff = (Block1 - Block6) / Block1 * 100) %>%
filter(!is.na(percentage_diff))
# ANOVA (Type II)
print("--- ANOVA: RT Improvement % (Block 1 to 6) ---")
## [1] "--- ANOVA: RT Improvement % (Block 1 to 6) ---"
m_improv <- lm(percentage_diff ~ Group, data = rt_summary)
print(Anova(m_improv, type="II"))
## Anova Table (Type II tests)
##
## Response: percentage_diff
## Sum Sq Df F value Pr(>F)
## Group 423.3 2 0.5197 0.5982
## Residuals 18328.0 45
# Plot (Lukas Style)
# Using model effects to get proper error bars
m_improv_eff <- allEffects(m_improv)
m_improv_df <- as.data.frame(m_improv_eff[[1]])
p_improv <- ggplot(m_improv_df, aes(x=Group, y=fit, fill=Group)) +
geom_bar(stat="identity", color="black", show.legend = FALSE, width=0.7) +
geom_errorbar(aes(ymin=lower, ymax=upper), width=.2) +
scale_fill_manual(values = custom_colors) +
labs(title="RT Improvement (Block 1 to 6)", y="% Improvement", x=NULL) +
theme_classic() + theme(legend.position="none")
print(p_improv)

# ==============================================================================
# 7. SELF-REPORT ANALYSIS (LMM + Individual Data Points)
# ==============================================================================
# 1. Prepare Data (Effort Only)
df_survey_eff <- survey_df %>%
filter(!is.na(Measure_Label)) %>%
select(Participant, Group, Measure_Label, Effort) %>%
mutate(Effort = as.numeric(Effort))
# 2. Linear Mixed Model (Group x Phase)
print("--- MODEL: SUBJECTIVE EFFORT (Group x Phase) ---")
## [1] "--- MODEL: SUBJECTIVE EFFORT (Group x Phase) ---"
m_effort <- lmer(Effort ~ Group * Measure_Label + (1|Participant),
data = df_survey_eff, REML = FALSE)
print(Anova(m_effort, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: Effort
## Chisq Df Pr(>Chisq)
## Group 2.8076 2 0.24566
## Measure_Label 103.1879 3 < 2e-16 ***
## Group:Measure_Label 12.9126 6 0.04445 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# 3. Exploratory Pairwise Comparisons
print("--- PAIRWISE CONTRASTS (Effort by Phase) ---")
## [1] "--- PAIRWISE CONTRASTS (Effort by Phase) ---"
emm_effort <- emmeans(m_effort, ~ Group | Measure_Label)
print(pairs(emm_effort, adjust="none"))
## Measure_Label = Baseline:
## contrast estimate SE df t.ratio p.value
## CON - FAM 0.688 1.68 180 0.410 0.6826
## CON - OMM 0.500 1.68 180 0.298 0.7661
## FAM - OMM -0.188 1.68 180 -0.112 0.9112
##
## Measure_Label = Post-Training:
## contrast estimate SE df t.ratio p.value
## CON - FAM -4.812 1.68 180 -2.867 0.0046
## CON - OMM -4.562 1.68 180 -2.718 0.0072
## FAM - OMM 0.250 1.68 180 0.149 0.8818
##
## Measure_Label = Early Learning:
## contrast estimate SE df t.ratio p.value
## CON - FAM -1.438 1.68 180 -0.856 0.3929
## CON - OMM 0.375 1.68 180 0.223 0.8235
## FAM - OMM 1.812 1.68 180 1.080 0.2817
##
## Measure_Label = Late Learning:
## contrast estimate SE df t.ratio p.value
## CON - FAM -0.562 1.68 180 -0.335 0.7379
## CON - OMM -2.250 1.68 180 -1.340 0.1818
## FAM - OMM -1.688 1.68 180 -1.005 0.3161
##
## Degrees-of-freedom method: kenward-roger
# 4. Plot (Bar Chart + Individual Points)
p_effort <- ggplot(df_survey_eff, aes(x=Measure_Label, y=Effort, fill=Group)) +
# A. The Bars
stat_summary(fun = mean, geom = "bar", position = position_dodge(0.8),
width = 0.7, color="black", alpha=0.6) +
# B. The Individual Points
geom_point(position = position_jitterdodge(jitter.width = 0.15, dodge.width = 0.8),
size = 1.5, alpha = 0.4, show.legend = FALSE) +
# C. The Error Bars (SE)
stat_summary(fun.data = mean_se, geom = "errorbar",
width = 0.2, position = position_dodge(0.8), color="black") +
# ---------------------------------------------------------
# NEW: Significance Brackets for "Post-Training" (x = 2)
# ---------------------------------------------------------
# Bracket 1: CON (1.73) vs FAM (2.0)
annotate("segment", x = 1.73, xend = 2.0, y = 22, yend = 22) +
annotate("segment", x = 1.73, xend = 1.73, y = 21, yend = 22) +
annotate("segment", x = 2.0, xend = 2.0, y = 21, yend = 22) +
annotate("text", x = 1.865, y = 22.5, label = "**", color = "red", size = 6) +
# Bracket 2: CON (1.73) vs OMM (2.27)
# (Drawn slightly higher at y=24 to stack nicely above Bracket 1)
annotate("segment", x = 1.73, xend = 2.27, y = 24, yend = 24) +
annotate("segment", x = 1.73, xend = 1.73, y = 23, yend = 24) +
annotate("segment", x = 2.27, xend = 2.27, y = 23, yend = 24) +
annotate("text", x = 2.0, y = 24.5, label = "**", color = "red", size = 6) +
# Ensure the Y-axis is tall enough to fit the brackets
scale_y_continuous(limits = c(0, 26)) +
scale_fill_manual(values = custom_colors) +
labs(title="5A) Subjective Effort across Phases",
subtitle="Bars = Mean + SE; Dots = Individual Participants",
y="NASA-TLX Effort Score", x=NULL) +
theme_classic() +
theme(legend.position="top", axis.text.x = element_text(angle=15, hjust=1))
print(p_effort)

# ==============================================================================
# 8. FINAL COMPOSITE PLOTS
# ==============================================================================
mean_ci95 <- function(x) {
m <- mean(x, na.rm = TRUE)
se <- sd(x, na.rm = TRUE) / sqrt(sum(!is.na(x)))
return(data.frame(y = m, ymin = m - 1.96 * se, ymax = m + 1.96 * se))
}
# --- 1. Prepare Participant-Level Aggregated Accuracy ---
# Calculate % accuracy per participant per block for Learning
acc_subj_learn <- trial_df %>%
filter(Session <= 6) %>%
group_by(Subject, Group, Session) %>%
summarise(Acc_Rate = mean(as.numeric(feedback.ACC.trial), na.rm = TRUE), .groups = "drop")
# Calculate % accuracy per participant per condition for Test
acc_subj_test <- trial_df %>%
filter(Session > 6) %>%
group_by(Subject, Group, Condition) %>%
summarise(Acc_Rate = mean(as.numeric(feedback.ACC.trial), na.rm = TRUE), .groups = "drop")
# --- 2. FIGURE 2A: Learning Accuracy (Boxplot + Jitter) ---
p1a <- ggplot(acc_subj_learn, aes(x = factor(Session), y = Acc_Rate, fill = Group, color = Group)) +
# Add raw participant data points
geom_point(position = position_jitterdodge(jitter.width = 0.15, dodge.width = 0.75),
alpha = 0.3, size = 1, show.legend = FALSE) +
# Add boxplots
geom_boxplot(position = position_dodge(width = 0.75),
alpha = 0.7, outlier.shape = NA, color = "black") +
# Add Significance Bracket: Block 1 CON (x=0.75) vs OMM (x=1.25)
annotate("segment", x = 0.75, xend = 1.25, y = 1.02, yend = 1.02) +
annotate("segment", x = 0.75, xend = 0.75, y = 1.00, yend = 1.02) +
annotate("segment", x = 1.25, xend = 1.25, y = 1.00, yend = 1.02) +
annotate("text", x = 1, y = 1.04, label = "**", color = "red", size = 6) +
# Set Y-axis to accommodate the bracket above 100%
scale_y_continuous(labels = scales::percent, limits = c(0, 1.1)) +
scale_fill_manual(values = custom_colors) +
scale_color_manual(values = custom_colors) +
labs(title = "2A) Accuracy (Learning)", y = "Accuracy", x = "Block") +
theme_classic() +
theme(legend.position = "top", axis.text.x = element_blank())
print(p1a)

# --- ALTERNATIVE FIGURE 2B: RT Learning (Boxplot + Jitter) ---
p1b <- ggplot(df_learn_plot, aes(x = factor(Session), y = feedback.RT.mean, fill = Group, color = Group)) +
# Add raw data points
geom_point(position = position_jitterdodge(jitter.width = 0.2, dodge.width = 0.75),
alpha = 0.3, size = 1, show.legend = FALSE) +
# Add boxplots
geom_boxplot(position = position_dodge(width = 0.75),
alpha = 0.7, outlier.shape = NA, color = "black") +
# Add Significance Bracket: Block 1 FAM (x=1) vs OMM (x=1.25)
annotate("segment", x = 1, xend = 1.25, y = 1350, yend = 1350) +
annotate("segment", x = 1, xend = 1, y = 1300, yend = 1350) +
annotate("segment", x = 1.25, xend = 1.25, y = 1300, yend = 1350) +
annotate("text", x = 1.125, y = 1400, label = "*", color = "red", size = 6) +
# Add Significance Bracket: Block 2 FAM (x=2) vs OMM (x=2.25)
annotate("segment", x = 2, xend = 2.25, y = 1150, yend = 1150) +
annotate("segment", x = 2, xend = 2, y = 1100, yend = 1150) +
annotate("segment", x = 2.25, xend = 2.25, y = 1100, yend = 1150) +
annotate("text", x = 2.125, y = 1200, label = "*", color = "red", size = 6) +
# Extend the Y-axis so the brackets don't get cut off
coord_cartesian(ylim = c(0, 1500)) +
scale_fill_manual(values = custom_colors) +
scale_color_manual(values = custom_colors) +
labs(title = "2B) Response Time (Learning)", y = "RT (ms)", x = "Block") +
theme_classic() +
theme(legend.position = "none")
print(p1b)

# --- 2A: Accuracy Test (Error Bars for Categorical) ---
# Using raw means with CI
acc_test_summ <- trial_df %>%
filter(Session > 6) %>%
group_by(Group, Condition) %>%
summarise(
M = mean(feedback.ACC.trial),
SE = sd(feedback.ACC.trial)/sqrt(n()),
CI = 1.96 * SE,
.groups = "drop"
)
p2a <- ggplot(acc_subj_test, aes(x = Condition, y = Acc_Rate, fill = Group, color = Group)) +
geom_point(position = position_jitterdodge(jitter.width = 0.15, dodge.width = 0.75),
alpha = 0.3, size = 1, show.legend = FALSE) +
geom_boxplot(position = position_dodge(width = 0.75),
alpha = 0.7, outlier.shape = NA, color = "black") +
# FIX: Use coord_cartesian to "zoom" without deleting data below 40% or above 100%
coord_cartesian(ylim = c(0.3, 1.05)) +
scale_y_continuous(labels = scales::percent) +
scale_fill_manual(values = custom_colors) +
scale_color_manual(values = custom_colors) +
labs(title = "3A) Accuracy (Test)", y = "Accuracy", x = "Condition") +
theme_classic() +
theme(legend.position = "top")
print(p2a)

# --- 2B: RT Test ---
p2b <- ggplot(df_test_plot, aes(x = Condition, y = feedback.RT.mean, fill = Group, color = Group)) +
geom_point(position = position_jitterdodge(jitter.width = 0.2, dodge.width = 0.75),
alpha = 0.3, size = 1, show.legend = FALSE) +
geom_boxplot(position = position_dodge(width = 0.75),
alpha = 0.7, outlier.shape = NA, color = "black") +
# Add Significance Bracket: Transfer FAM (x=2) vs OMM (x=2.25)
annotate("segment", x = 2, xend = 2.25, y = 1350, yend = 1350) +
annotate("segment", x = 2, xend = 2, y = 1300, yend = 1350) +
annotate("segment", x = 2.25, xend = 2.25, y = 1300, yend = 1350) +
annotate("text", x = 2.125, y = 1380, label = "*", color = "red", size = 6) +
# FIX: Extend coord_cartesian to 1500 to give the asterisk plenty of breathing room
coord_cartesian(ylim = c(0, 1500)) +
scale_fill_manual(values = custom_colors) +
scale_color_manual(values = custom_colors) +
labs(title = "3B) Response Time (Test)", y = "RT (ms)", x = "Condition") +
theme_classic() +
theme(legend.position = "none")
print(p2b)

# --- 3: Concatenation (Ribbon) ---
pd <- position_dodge(0.2)
p3a <- ggplot(df_foot_plot, aes(x=foot.position, y=feedback.RT, color=Group, fill=Group, group=Group)) +
stat_summary(fun.data = mean_ci95, geom = "ribbon", alpha = 0.2, color = NA) +
stat_summary(fun = mean, geom = "line", size = 1, position = pd) +
stat_summary(fun = mean, geom = "point", size = 1.5, position = pd) +
facet_wrap(~Panel_Label, nrow=2) +
scale_color_manual(values=custom_colors) + scale_fill_manual(values=custom_colors) +
labs(title="3. Concatenation Dynamics", subtitle="Mean RT +/- 95% CI", x="Step", y="Step RT (ms)") +
coord_cartesian(ylim=c(0, 1000)) + theme_classic() + theme(strip.background=element_blank(), legend.position="top")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# Alternative 3b
pd <- position_dodge(0.2)
p3b <- ggplot(df_foot_plot, aes(x = foot.position, y = feedback.RT, color = Group, fill = Group, group = Group)) +
# 1. Add a subtle shaded background explicitly highlighting Steps 4 and 5
annotate("rect", xmin = 3.8, xmax = 5.2, ymin = -Inf, ymax = Inf,
alpha = 0.1, fill = "black", color = NA) +
# 2. Drop the ribbon (since CIs are tiny) and just use clean lines and slightly larger points
stat_summary(fun = mean, geom = "line", size = 1.2, position = pd) +
stat_summary(fun = mean, geom = "point", size = 2.5, position = pd) +
facet_wrap(~Panel_Label, nrow = 2) +
scale_color_manual(values = custom_colors) +
scale_fill_manual(values = custom_colors) +
labs(title = "3. Concatenation Dynamics",
subtitle = "Shaded region highlights theoretical chunk boundaries (Steps 4 & 5)",
x = "Sequence Step", y = "Step RT (ms)") +
# Adjusted the y-limit slightly so the bump fills the space better
coord_cartesian(ylim = c(200, 1000)) +
theme_classic() +
theme(strip.background = element_blank(),
legend.position = "top",
text = element_text(size = 14))
print(p3b)

# Alternative3c.
# Create a tiny dataframe telling ggplot exactly where to put the stars
stars_df <- data.frame(
Panel_Label = c("Block 1", "Block 1", "Block 2", "Block 2", "Block 3", "Block 3", "Block 4", "Block 4", "Block 5", "Block 5", "Block 6", "Retention", "Transfer"),
foot.position = c(4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5),
feedback.RT = c(850, 850, 600, 600, 500, 500, 500, 500, 500, 500, 500, 500, 500), # Adjust Y heights if they overlap the lines
label = c("*", "***", "*", "***", "*", "***", "*", "***", "*", "***", "***", "***", "***")
)
# 2. Build the plot
p3c <- ggplot(df_foot_plot, aes(x = foot.position, y = feedback.RT, color = Group, group = Group)) +
stat_summary(fun = mean, geom = "line", size = 1, position = pd) +
stat_summary(fun = mean, geom = "point", size = 2, position = pd) +
# FIX: inherit.aes = FALSE prevents the 'Group not found' error
geom_text(data = stars_df, aes(x = foot.position, y = feedback.RT, label = label),
color = "red", size = 6, inherit.aes = FALSE) +
facet_wrap(~Panel_Label, nrow = 4) +
scale_color_manual(values = custom_colors) +
labs(title = "4. Concatenation Dynamics", x = "Step", y = "Step RT (ms)") +
coord_cartesian(ylim = c(0, 1000)) +
theme_classic() +
theme(strip.background = element_blank(), legend.position = "top")
print(p3c)

# Compile and Output All
grid1 <- plot_grid(p1a, p1b, ncol = 1, align = "v", rel_heights = c(0.8, 1))
grid2 <- plot_grid(p2a, p2b, ncol = 1, align = "v", rel_heights = c(0.8, 1))
print(grid1)

print(grid2)

print(p3a)

print(p3b)

print(p3c)

# ==============================================================================
# 9. CORRELATION: Does Effort Predict Learning?
# ==============================================================================
# 1. Calculate Learning Magnitude (Standard Mean Method)
# Using 2.5SD cleaning to ensure the "Change Score" isn't driven by one bad trial
rt_clean_corr <- trial_df %>%
filter(Session %in% c(1, 6), feedback.ACC.trial == 1, feedback.RT.mean > 0) %>%
group_by(Subject, Session) %>%
mutate(
mn = mean(feedback.RT.mean),
sd = sd(feedback.RT.mean),
is_outlier = feedback.RT.mean > (mn + 2.5*sd) | feedback.RT.mean < (mn - 2.5*sd)
) %>%
filter(!is_outlier) %>%
ungroup()
# Calculate % Improvement
rt_summary <- rt_clean_corr %>%
group_by(Subject, Group, Session) %>%
summarise(Mean_RT = mean(feedback.RT.mean), .groups = "drop") %>%
pivot_wider(names_from = Session, values_from = Mean_RT, names_prefix = "Block") %>%
mutate(percentage_diff = (Block1 - Block6) / Block1 * 100) %>%
filter(!is.na(percentage_diff))
# 2. Prepare Effort Data (Post-Training)
effort_m2 <- survey_df %>%
filter(Measure == 2) %>%
select(Participant, Effort) %>%
mutate(Effort = as.numeric(Effort)) %>%
rename(Subject = Participant, PostTraining_Effort = Effort)
# 3. Merge
rt_summary$Subject <- as.character(rt_summary$Subject)
effort_m2$Subject <- as.character(effort_m2$Subject)
corr_df <- inner_join(rt_summary, effort_m2, by="Subject")
# 4. Statistical Test (Spearman, One-Tailed)
# Hypothesis: Higher Effort -> Greater Improvement (Positive Correlation)
print("--- CORRELATION: RT Improvement % vs Effort ---")
## [1] "--- CORRELATION: RT Improvement % vs Effort ---"
cor_res <- cor.test(corr_df$percentage_diff, corr_df$PostTraining_Effort,
method = "spearman", alternative = "greater")
## Warning in cor.test.default(corr_df$percentage_diff,
## corr_df$PostTraining_Effort, : Cannot compute exact p-value with ties
print(cor_res)
##
## Spearman's rank correlation rho
##
## data: corr_df$percentage_diff and corr_df$PostTraining_Effort
## S = 13506, p-value = 0.03331
## alternative hypothesis: true rho is greater than 0
## sample estimates:
## rho
## 0.2669547
# 5. Visualization (Scatterplot with Regression)
p_corr <- ggplot(corr_df, aes(x=PostTraining_Effort, y=percentage_diff)) +
geom_point(aes(color=Group), size=3, alpha=0.7) +
# Add regression line to show the trend
geom_smooth(method="lm", color="black", se=TRUE, fill="grey80", alpha=0.5) +
scale_color_manual(values = custom_colors) +
labs(title="5B) Effort Predicts Motor Learning",
subtitle = paste("Spearman rho =", round(cor_res$estimate, 2), ", p =", round(cor_res$p.value, 3), "(1-tailed)"),
x="Subjective Effort (Post-Training)",
y="RT Improvement (Block 1 to 6 %)") +
theme_classic() +
theme(legend.position="top")
print(p_corr)
## `geom_smooth()` using formula = 'y ~ x'

# ==============================================================================
# 10. MECHANISM ANALYSIS: Does Group Moderate the Effort-Learning Link?
# ==============================================================================
# --- 1. Prepare Data ---
effort_m2 <- survey_df %>% filter(Measure == 2) %>%
select(Participant, Effort) %>% mutate(Effort=as.numeric(Effort)) %>%
rename(Subject = Participant, PostTraining_Effort = Effort)
rt_summary$Subject <- as.character(rt_summary$Subject)
effort_m2$Subject <- as.character(effort_m2$Subject)
mech_df <- inner_join(rt_summary, effort_m2, by="Subject")
# --- 2. The Interaction Model (Moderation) ---
# Does the relationship between Effort and Learning differ by Group?
print("--- MODEL: Learning ~ Effort * Group ---")
## [1] "--- MODEL: Learning ~ Effort * Group ---"
m_mech <- lm(percentage_diff ~ PostTraining_Effort * Group, data = mech_df)
print(Anova(m_mech, type="II"))
## Anova Table (Type II tests)
##
## Response: percentage_diff
## Sum Sq Df F value Pr(>F)
## PostTraining_Effort 568.4 1 1.4170 0.2406
## Group 209.4 2 0.2611 0.7715
## PostTraining_Effort:Group 913.4 2 1.1386 0.3300
## Residuals 16846.3 42
# --- 3. Group-Specific Correlations ---
# Let's see the 'r' value for each group individually
print("--- CORRELATIONS BY GROUP ---")
## [1] "--- CORRELATIONS BY GROUP ---"
group_cors <- mech_df %>%
group_by(Group) %>%
summarise(
r = cor(percentage_diff, PostTraining_Effort, method = "spearman"),
p = cor.test(percentage_diff, PostTraining_Effort, method = "spearman")$p.value,
n = n()
)
## Warning: There were 3 warnings in `summarise()`.
## The first warning was:
## ℹ In argument: `p = cor.test(percentage_diff, PostTraining_Effort, method =
## "spearman")$p.value`.
## ℹ In group 1: `Group = CON`.
## Caused by warning in `cor.test.default()`:
## ! Cannot compute exact p-value with ties
## ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 2 remaining warnings.
print(group_cors)
## # A tibble: 3 × 4
## Group r p n
## <fct> <dbl> <dbl> <int>
## 1 CON 0.381 0.145 16
## 2 FAM 0.416 0.109 16
## 3 OMM -0.0488 0.858 16
# --- 4. Alternative 3-Way Interaction Model (Moderation of Learning Slope) ---
print("--- MODEL: LMM EFFORT MODERATION ON LEARNING RATE ---")
## [1] "--- MODEL: LMM EFFORT MODERATION ON LEARNING RATE ---"
# Join the effort scores to your trial-level RT learning dataframe
df_learn_effort <- df_learn %>%
inner_join(effort_m2, by="Subject")
# Run the LMM testing how Effort moderates the RT slope over Sessions
m_rt_effort <- lmer(Log_RT ~ Group * as.numeric(Session) * PostTraining_Effort + (1|Subject),
data = df_learn_effort)
print(Anova(m_rt_effort, type="II"))
## Analysis of Deviance Table (Type II Wald chisquare tests)
##
## Response: Log_RT
## Chisq Df Pr(>Chisq)
## Group 0.6724 2 0.7145
## as.numeric(Session) 3849.7017 1 < 2.2e-16 ***
## PostTraining_Effort 0.9948 1 0.3186
## Group:as.numeric(Session) 25.2245 2 3.331e-06 ***
## Group:PostTraining_Effort 1.0389 2 0.5949
## as.numeric(Session):PostTraining_Effort 37.1745 1 1.080e-09 ***
## Group:as.numeric(Session):PostTraining_Effort 162.6103 2 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# ==============================================================================
# 11. VISUALIZING THE DISSOCIATION
# ==============================================================================
p_mech_final <- ggplot(mech_df, aes(x=PostTraining_Effort, y=percentage_diff, color=Group)) +
geom_point(size=3, alpha=0.6) +
# Separate regression lines
geom_smooth(method="lm", se=FALSE, size=1.5) +
scale_color_manual(values = custom_colors) +
labs(title="5C) Distinct Learning Regimes",
subtitle="FAM/CON rely on Effort; OMM improves via Strategy (Dissociated)",
x="Subjective Effort (Post-Training)", y="RT Improvement (%)") +
theme_classic() + theme(legend.position="top")
print(p_mech_final)
## `geom_smooth()` using formula = 'y ~ x'

# ==============================================================================
# 12. FIGURE 4: SUBJECTIVE MECHANISMS (Composite Plot)
# ==============================================================================
# --- Ensure Plots are Ready (from previous sections) ---
# p_effort (from Section 7: Bar + Raincloud)
# p_corr (from Section 8: Global Correlation)
# p_mech_final (from Section 9: Group Regimes)
# --- 1. Clean up Titles for the Composite Figure ---
# We remove the standard titles so we can use the A/B/C labels clearly
p4a <- p_effort +
#labs(title = "5A) Subjective Effort Trajectory", subtitle = NULL) +
theme(plot.margin = margin(10, 10, 10, 10))
p4b <- p_corr +
#labs(title = "5B) Global Mechanism: Effort Predicts Learning", subtitle = NULL) +
theme(plot.margin = margin(10, 10, 10, 10))
p4c <- p_mech_final +
#labs(title = "5C) Distinct Regimes: Strategy vs. Effort", subtitle = NULL) +
theme(plot.margin = margin(10, 10, 10, 10))
# --- 2. Combine Vertically using cowplot ---
# Rel_heights: Give the Bar chart (A) slightly less space than the Scatters (B/C)
figure4_grid <- plot_grid(
p4a,
p4b,
p4c,
ncol = 1,
#labels = c(),
label_size = 14,
align = "v",
rel_heights = c(0.8, 1, 1)
)
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
# --- 3. Display and Save ---
print(figure4_grid)

# Optional: Save for Manuscript (High Res)
# ggsave("Figure4_Mechanisms.png", figure4_grid, width = 6, height = 12, dpi = 300)
# ggsave("Figure4_Mechanisms.pdf", figure4_grid, width = 6, height = 12)
# Figure 2: Learning Dynamics (Tall - 2 panels)
ggsave("Figure2_Learning.png", plot = grid1, width = 6.5, height = 8, units = "in", dpi = 300)
# Figure 3: Test Phase (Tall - 2 panels)
ggsave("Figure3_Test.png", plot = grid2, width = 6.5, height = 8, units = "in", dpi = 300)
# Figure 4: Concatenation Dynamics (Wide - Faceted)
ggsave("Figure4_Concatenation.png", plot = p3c, width = 7.5, height = 9, units = "in", dpi = 300)
# Figure 5: Subjective Mechanisms (Very Tall - 3 panels)
# Note: You already built this as 'figure4_grid' in your script, but it is Figure 5 in the text
ggsave("Figure5_Mechanisms.png", plot = figure4_grid, width = 6.5, height = 9, units = "in", dpi = 300)