SIM VS EMPERICAL VS PPC
Code
library(reticulate)
# use_python("/opt/anaconda3/envs/hssm_venv/bin/python")
use_condaenv("hssm_venv", required = TRUE)Code
import pandas as pd
import numpy as npsource scripts with functions
Code
source("Functions/packages.R")load packages
Code
loadpackages()
conflicts_prefer(rstatix::filter)
conflicts_prefer(dplyr::rename)
conflicts_prefer(dplyr::summarise)
conflicts_prefer(dplyr::summarize)
conflicts_prefer(dplyr::mutate)
conflicts_prefer(dplyr::arrange)
conflicts_prefer(dplyr::count)
conflicts_prefer(dplyr::filter)GLOBAL SETTINGS (font, colors, paths)
Code
poster_font <- "Book Antiqua"
if (requireNamespace("systemfonts", quietly = TRUE)) {
if (!poster_font %in% systemfonts::system_fonts()$family) {
poster_font <- "Palatino"
message("Book Antiqua not found on this system -- falling back to Palatino")
}
}
theme_poster_font <- theme(text = element_text(family = poster_font))
save_device <- if (requireNamespace("ragg", quietly = TRUE)) ragg::agg_png else "png"
# one color per SOURCE (orange = observed/empirical and blue = model-predicted,
# to stay consistent with the script-4 PPC figures)
colors_source <- c(
"Simulated (power analysis)" = "#7570B3",
"Empirical (pre-trip)" = "#FF7F0E",
"Predicted (GLMM)" = "#1B9E77",
"PPC (DDM)" = "#1F77B4"
)
# ---- EDIT PATHS HERE IF NEEDED (relative to project root) --------------------
sim_csv <- "simed_data_02.18.26_mood_congruency.csv"
empirical_csv <- "pre-processed_full-ema_data/full_ema_pre-trip.csv"
ppc_csv <- "post-model_outputs_cpc2026/ppc_sim_data_full_cpc2026.csv"
# ppc_csv is exported by pasting this at the bottom of script 4 and running once:
# sim_data_full.to_csv(f"{OUTPUT_PATH}/ppc_sim_data_full_cpc2026.csv", index=False)
#-------------------------------------------------------------------------------
has_ppc <- file.exists(here::here(ppc_csv))
if (!has_ppc) message("PPC csv not found -- PPC rows/plots will be skipped. ",
"Export it from script 4 (see comment above).")
if (!dir.exists(here::here("figures"))) dir.create(here::here("figures"))==============================================================================
LOAD + PREP EACH SOURCE
==============================================================================
1. SIMULATED (power analysis congruency experiment)
Code
sim_raw <- read_csv(here::here(sim_csv))
# the sim df was re-indexed to a full 30 EMAs x 24 trials grid, so subjects
# with <30 EMAs have all-NA rows -> completed trials are rows with non-NA rt
sim_trials <- sim_raw %>% filter(!is.na(rt))
# one row per subject x EMA for session-level (affect) descriptives
sim_sessions <- sim_trials %>%
distinct(subject_id, ema_num, affect_z_emalevel)
# PosNeg trials + chose_positive (same coding as poweranalyses script)
sim_posneg <- sim_trials %>%
filter(delta_valence != 0) %>%
mutate(
chose_pos = as.integer(
(delta_valence == -1 & response_0_1 == 0) |
(delta_valence == 1 & response_0_1 == 1)
),
rt_s = rt, # sim rt is already in SECONDS
affect_z = affect_z_emalevel,
source = "Simulated (power analysis)"
)
tibble(
step = "simulated congruency experiment",
n_subjects = n_distinct(sim_trials$subject_id),
n_sessions = nrow(sim_sessions),
n_trials = nrow(sim_trials),
n_posneg_trials = nrow(sim_posneg)
) %>% gt() %>% tab_options(table.width = pct(80))| step | n_subjects | n_sessions | n_trials | n_posneg_trials |
|---|---|---|---|---|
| simulated congruency experiment | 70 | 2010 | 48240 | 32160 |
2. EMPIRICAL (pre-trip)
Code
emas_pre <- read_csv(here::here(empirical_csv))
emas_pre$rt <- as.numeric(emas_pre$rt)
emas_pre$response <- as.numeric(emas_pre$response)
emas_pre$ema_number <- emas_pre$session_number
emas_pre$delta_valence <- ifelse(emas_pre$valence %in% c("PosPos", "NegNeg"), 0,
ifelse(emas_pre$right_valence == "positive", 1, -1))
emas_pre$chose_pos <- ifelse(emas_pre$delta_valence == 0, NA,
ifelse(emas_pre$delta_valence == 1,
ifelse(emas_pre$response == 1, 1, 0),
ifelse(emas_pre$response == 0, 1, 0)))
emas_pre$delta_posneg_sb <- ifelse(emas_pre$delta_valence == 0, NA,
ifelse(emas_pre$delta_valence == 1,
emas_pre$right_base_rating - emas_pre$left_base_rating,
emas_pre$left_base_rating - emas_pre$right_base_rating))
emas_pre$is_noresponse <- emas_pre$valid_response == FALSE | is.na(emas_pre$valid_response)
emas_pre$under_200ms <- emas_pre$rt < 200
emas_pre$somehow_over_5000ms <- emas_pre$rt > 5000
emas_pre$rt_quality <- case_when(
emas_pre$is_noresponse ~ "no_response",
emas_pre$under_200ms ~ "under_200ms",
emas_pre$somehow_over_5000ms ~ "somehow_over_5000ms",
TRUE ~ "valid"
)
emas_pre <- emas_pre %>% filter(rt_quality == "valid", !is.na(response), !is.na(rt))
emas_pre <- emas_pre %>%
filter(!is.na(affect_z),
!is.na(rt),
rt >= 200,
rt <= 5000)
emas_pre <- emas_pre %>% mutate(rt_s = rt / 1000) # empirical rt is in MS -> convert to s
# one row per subject x EMA for session-level (affect) descriptives
emp_sessions <- emas_pre %>%
distinct(PUNS_ID, ema_number, affect, affect_z)
posneg_pre <- emas_pre %>%
filter(!is.na(chose_pos)) %>%
mutate(source = "Empirical (pre-trip)")
tibble(
step = "empirical pre-trip (post-QC)",
n_subjects = n_distinct(emas_pre$PUNS_ID),
n_sessions = nrow(emp_sessions),
n_trials = nrow(emas_pre),
n_posneg_trials = nrow(posneg_pre)
) %>% gt() %>% tab_options(table.width = pct(80))| step | n_subjects | n_sessions | n_trials | n_posneg_trials |
|---|---|---|---|---|
| empirical pre-trip (post-QC) | 26 | 519 | 12172 | 6149 |
3. PREDICTED (GLMM) – refit script 3 models, keep fitted values
Code
# models copied unchanged from script 3
choice_model_pretrip <- glmer(chose_pos ~ affect_z + delta_posneg_sb + ema_number +
(1 + affect_z + delta_posneg_sb + ema_number || PUNS_ID),
data = posneg_pre, family = binomial)
posneg_pre <- posneg_pre %>%
group_by(PUNS_ID) %>%
mutate(rt_z = as.numeric(scale(rt_s))) %>%
ungroup()
poly_matrix_rt <- poly(posneg_pre$delta_posneg_sb, degree = 2)
posneg_pre$delta_posneg_sb_lin <- poly_matrix_rt[, 1]
posneg_pre$delta_posneg_sb_quad <- poly_matrix_rt[, 2]
rt_model_pretrip <- lmerTest::lmer(
rt_z ~ affect_z * delta_posneg_sb_lin + delta_posneg_sb_quad + ema_number +
(1 + affect_z + delta_posneg_sb_lin + delta_posneg_sb_quad || PUNS_ID),
data = posneg_pre)
# fitted values back on the response scales
# (rt_z -> seconds using the same grand mean/sd back-transform as script 3)
pt_rt_stats <- posneg_pre %>%
group_by(PUNS_ID) %>%
summarise(pt_mean = mean(rt_s, na.rm = TRUE),
pt_sd = sd(rt_s, na.rm = TRUE), .groups = "drop")
grand_mean <- mean(pt_rt_stats$pt_mean)
grand_sd <- mean(pt_rt_stats$pt_sd)
posneg_pre$pred_p_chose_pos_glmm <- fitted(choice_model_pretrip)
posneg_pre$pred_rt_z_glmm <- fitted(rt_model_pretrip)
posneg_pre$pred_rt_s_glmm <- posneg_pre$pred_rt_z_glmm * grand_sd + grand_mean
glmm_pred <- posneg_pre %>%
transmute(
subject = as.character(PUNS_ID),
affect_z,
rt_s = pred_rt_s_glmm,
p_chose_pos = pred_p_chose_pos_glmm,
source = "Predicted (GLMM)"
)4. PPC (DDM posterior predictive sims from script 4)
Code
if (has_ppc) {
ppc_raw <- read_csv(here::here(ppc_csv))
# pred_rt is in SECONDS; pred_choice = 1 means chose right
# recover chose_pos on PosNeg trials using delta_valence
ppc_posneg <- ppc_raw %>%
filter(delta_valence != 0) %>%
mutate(
chose_pos = as.integer(
(delta_valence == -1 & pred_choice == 0) |
(delta_valence == 1 & pred_choice == 1)
),
rt_s = abs(pred_rt), # hssm sims can code rt sign by response boundary
source = "PPC (DDM)"
)
tibble(
step = "ppc sims (cpc_model, 100 sims per trial)",
n_subjects = n_distinct(ppc_posneg$subject),
n_sims = n_distinct(ppc_posneg$sim_num),
n_posneg_rows = nrow(ppc_posneg)
) %>% gt() %>% tab_options(table.width = pct(80))
}| step | n_subjects | n_sims | n_posneg_rows |
|---|---|---|---|
| ppc sims (cpc_model, 100 sims per trial) | 25 | 100 | 601000 |
==============================================================================
COMPLIANCE
==============================================================================
Empirical pre-trip compliance
Code
pt_compliance <- emas_pre %>%
group_by(PUNS_ID) %>%
summarise(
n_emas = n_distinct(ema_number),
compliance = n_emas / 30
) %>%
ungroup() %>%
arrange(desc(compliance))
pt_compliance %>%
gt() %>%
fmt_percent(columns = compliance, decimals = 1) %>%
tab_header(title = "Pre-trip EMA compliance by participant") %>%
tab_options(table.width = pct(80))| Pre-trip EMA compliance by participant | ||
| PUNS_ID | n_emas | compliance |
|---|---|---|
| 1033 | 29 | 96.7% |
| 1044 | 29 | 96.7% |
| 1015 | 28 | 93.3% |
| 1086 | 28 | 93.3% |
| 1032 | 25 | 83.3% |
| 1107 | 25 | 83.3% |
| 1021 | 24 | 80.0% |
| 1050 | 24 | 80.0% |
| 1083 | 24 | 80.0% |
| 1068 | 21 | 70.0% |
| 1088 | 21 | 70.0% |
| 1017 | 20 | 66.7% |
| 1060 | 19 | 63.3% |
| 1071 | 19 | 63.3% |
| 1027 | 18 | 60.0% |
| 1067 | 18 | 60.0% |
| 1070 | 18 | 60.0% |
| 1065 | 17 | 56.7% |
| 1024 | 16 | 53.3% |
| 1096 | 16 | 53.3% |
| 1016 | 14 | 46.7% |
| 1035 | 14 | 46.7% |
| 1094 | 14 | 46.7% |
| 1026 | 13 | 43.3% |
| 1080 | 13 | 43.3% |
| 1045 | 12 | 40.0% |
Simulated compliance
Code
sim_compliance <- sim_trials %>%
group_by(subject_id) %>%
summarise(n_emas = n_distinct(ema_num),
compliance = n_emas / 30,
.groups = "drop")
# by design: 40 pts x 30 EMAs, 20 pts x 28, 10 pts x 25
sim_compliance %>%
count(n_emas, name = "n_pts") %>%
mutate(compliance = n_emas / 30) %>%
gt() %>%
fmt_percent(columns = compliance, decimals = 1) %>%
tab_header(title = "Simulated compliance structure (power analysis design)") %>%
tab_options(table.width = pct(80))| Simulated compliance structure (power analysis design) | ||
| n_emas | n_pts | compliance |
|---|---|---|
| 25 | 10 | 83.3% |
| 28 | 20 | 93.3% |
| 30 | 40 | 100.0% |
Compliance: simulated vs. empirical summary
Code
compliance_compare <- bind_rows(
sim_compliance %>%
summarise(source = "Simulated (power analysis)",
n_pts = n(),
mean_compliance = mean(compliance),
median_compliance = median(compliance),
min_compliance = min(compliance),
max_compliance = max(compliance)),
pt_compliance %>%
summarise(source = "Empirical (pre-trip)",
n_pts = n(),
mean_compliance = mean(compliance),
median_compliance = median(compliance),
min_compliance = min(compliance),
max_compliance = max(compliance))
)
compliance_compare %>%
gt() %>%
fmt_percent(columns = mean_compliance:max_compliance, decimals = 1) %>%
tab_header(title = "Compliance: simulated (power analysis) vs. empirical") %>%
tab_options(table.width = pct(80))| Compliance: simulated (power analysis) vs. empirical | |||||
| source | n_pts | mean_compliance | median_compliance | min_compliance | max_compliance |
|---|---|---|---|---|---|
| Simulated (power analysis) | 70 | 95.7% | 100.0% | 83.3% | 100.0% |
| Empirical (pre-trip) | 26 | 66.5% | 63.3% | 40.0% | 96.7% |
==============================================================================
AFFECT + AFFECT_Z (session level)
==============================================================================
Raw affect (empirical) vs. generating values used in the sims
Code
# empirical: person means / between-person SD of means / mean within-person SD
emp_affect_structure <- emp_sessions %>%
group_by(PUNS_ID) %>%
summarise(pt_mean_affect = mean(affect, na.rm = TRUE),
pt_sd_affect = sd(affect, na.rm = TRUE),
.groups = "drop") %>%
summarise(
mean_of_pt_means = mean(pt_mean_affect, na.rm = TRUE),
between_pt_sd = sd(pt_mean_affect, na.rm = TRUE),
mean_within_pt_sd = mean(pt_sd_affect, na.rm = TRUE)
)
affect_structure_compare <- bind_rows(
tibble(source = "Simulated (generating values, from TREAD)",
mean_of_pt_means = 10.675,
between_pt_sd = 1.442,
mean_within_pt_sd = 1.473),
emp_affect_structure %>% mutate(source = "Empirical (pre-trip)")
) %>%
select(source, everything())
affect_structure_compare %>%
gt() %>%
fmt_number(columns = -source, decimals = 3) %>%
tab_header(title = "Raw affect structure: sim generating values vs. empirical") %>%
tab_options(table.width = pct(80))| Raw affect structure: sim generating values vs. empirical | |||
| source | mean_of_pt_means | between_pt_sd | mean_within_pt_sd |
|---|---|---|---|
| Simulated (generating values, from TREAD) | 10.675 | 1.442 | 1.473 |
| Empirical (pre-trip) | 6.871 | 1.047 | 1.544 |
affect_z descriptives (simulated vs. empirical)
Code
affect_z_compare <- bind_rows(
sim_sessions %>%
transmute(source = "Simulated (power analysis)", affect_z = affect_z_emalevel),
emp_sessions %>%
transmute(source = "Empirical (pre-trip)", affect_z)
)
affect_z_compare %>%
group_by(source) %>%
summarise(
n_sessions = n(),
mean = mean(affect_z, na.rm = TRUE),
sd = sd(affect_z, na.rm = TRUE),
median = median(affect_z, na.rm = TRUE),
min = min(affect_z, na.rm = TRUE),
max = max(affect_z, na.rm = TRUE),
.groups = "drop"
) %>%
gt() %>%
fmt_number(columns = mean:max, decimals = 3) %>%
tab_header(title = "affect_z (session level): simulated vs. empirical") %>%
tab_options(table.width = pct(80))| affect_z (session level): simulated vs. empirical | ||||||
| source | n_sessions | mean | sd | median | min | max |
|---|---|---|---|---|---|---|
| Empirical (pre-trip) | 519 | 0.000 | 1.000 | 0.107 | −3.806 | 2.542 |
| Simulated (power analysis) | 2010 | 0.000 | 1.000 | −0.003 | −3.150 | 3.417 |
affect_z distributions
Code
p_affect_z <- ggplot(affect_z_compare, aes(x = affect_z, fill = source, color = source)) +
geom_density(alpha = 0.3, linewidth = 1) +
scale_fill_manual(values = colors_source, name = "Source") +
scale_color_manual(values = colors_source, name = "Source") +
labs(x = "affect_z (within-person z-scored affect)", y = "Density",
title = "Session-level affect_z: simulated vs. empirical") +
theme_minimal(base_size = 16) +
theme(panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, linewidth = 1),
plot.title = element_text(hjust = 0.5)) +
theme_poster_font
p_affect_zCode
ggsave(here::here("figures", "compare_affect_z_sim_vs_empirical.png"),
plot = p_affect_z, width = 9, height = 6, dpi = 300, bg = "white",
device = save_device)Raw affect distribution + trajectory over sessions (empirical)
Code
p_affect_raw <- ggplot(emp_sessions, aes(x = affect)) +
geom_histogram(bins = 25, fill = colors_source[["Empirical (pre-trip)"]],
color = "white", alpha = 0.85) +
labs(x = "Affect (raw, session level)", y = "Count",
title = "Empirical raw affect (pre-trip sessions)") +
theme_minimal(base_size = 16) +
theme(panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, linewidth = 1),
plot.title = element_text(hjust = 0.5)) +
theme_poster_font
affect_by_session <- emp_sessions %>%
group_by(ema_number) %>%
summarise(mean_affect = mean(affect, na.rm = TRUE),
se = sd(affect, na.rm = TRUE) / sqrt(n()),
.groups = "drop")
p_affect_time <- ggplot(affect_by_session, aes(x = ema_number, y = mean_affect)) +
geom_ribbon(aes(ymin = mean_affect - se, ymax = mean_affect + se),
alpha = 0.25, fill = colors_source[["Empirical (pre-trip)"]]) +
geom_line(color = colors_source[["Empirical (pre-trip)"]], linewidth = 1) +
geom_point(color = colors_source[["Empirical (pre-trip)"]], size = 2) +
labs(x = "EMA number (pre-trip)", y = "Mean affect (raw)",
title = "Empirical raw affect across pre-trip sessions") +
theme_minimal(base_size = 16) +
theme(panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, linewidth = 1),
plot.title = element_text(hjust = 0.5)) +
theme_poster_font
p_affect_raw + p_affect_time==============================================================================
RT (PosNeg trials, seconds, all four sources)
==============================================================================
Code
rt_all_sources <- bind_rows(
sim_posneg %>% select(source, rt_s, affect_z, chose_pos),
posneg_pre %>% select(source, rt_s, affect_z, chose_pos),
glmm_pred %>% transmute(source, rt_s, affect_z, chose_pos = NA_integer_),
if (has_ppc) ppc_posneg %>% select(source, rt_s, affect_z, chose_pos) else NULL
) %>%
mutate(source = factor(source, levels = names(colors_source)))
rt_all_sources %>%
group_by(source) %>%
summarise(
n_rows = n(),
mean_rt_s = mean(rt_s, na.rm = TRUE),
sd_rt_s = sd(rt_s, na.rm = TRUE),
median_rt_s = median(rt_s, na.rm = TRUE),
min_rt_s = min(rt_s, na.rm = TRUE),
max_rt_s = max(rt_s, na.rm = TRUE),
.groups = "drop"
) %>%
gt() %>%
fmt_number(columns = mean_rt_s:max_rt_s, decimals = 3) %>%
tab_header(title = "RT (s), PosNeg trials: simulated vs. empirical vs. GLMM-predicted vs. PPC",
subtitle = "GLMM row = fitted values (conditional means), so its SD is expectedly smaller than raw/simulated RTs; PPC rows pool 100 simulations per trial") %>%
tab_options(table.width = pct(90))| RT (s), PosNeg trials: simulated vs. empirical vs. GLMM-predicted vs. PPC | ||||||
| GLMM row = fitted values (conditional means), so its SD is expectedly smaller than raw/simulated RTs; PPC rows pool 100 simulations per trial | ||||||
| source | n_rows | mean_rt_s | sd_rt_s | median_rt_s | min_rt_s | max_rt_s |
|---|---|---|---|---|---|---|
| Simulated (power analysis) | 32160 | 1.500 | 0.819 | 1.247 | 0.591 | 11.440 |
| Empirical (pre-trip) | 6149 | 1.724 | 0.819 | 1.510 | 0.221 | 4.982 |
| Predicted (GLMM) | 6149 | 1.725 | 0.167 | 1.728 | 1.117 | 2.296 |
| PPC (DDM) | 601000 | 1.744 | 1.081 | 1.398 | 0.585 | 16.892 |
RT distributions
Code
p_rt_dens <- ggplot(rt_all_sources, aes(x = rt_s, fill = source, color = source)) +
geom_density(alpha = 0.25, linewidth = 1) +
coord_cartesian(xlim = c(0, 5)) +
scale_fill_manual(values = colors_source, name = "Source") +
scale_color_manual(values = colors_source, name = "Source") +
labs(x = "Reaction Time (s)", y = "Density",
title = "RT distributions by source (PosNeg trials)") +
theme_minimal(base_size = 16) +
theme(panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, linewidth = 1),
plot.title = element_text(hjust = 0.5)) +
theme_poster_font
p_rt_densCode
ggsave(here::here("figures", "compare_rt_distributions_by_source.png"),
plot = p_rt_dens, width = 9, height = 6, dpi = 300, bg = "white",
device = save_device)Code
p_rt_box <- ggplot(rt_all_sources, aes(x = source, y = rt_s, fill = source)) +
geom_boxplot(outlier.alpha = 0.1, width = 0.6) +
coord_cartesian(ylim = c(0, 5)) +
scale_fill_manual(values = colors_source, name = "Source") +
labs(x = NULL, y = "Reaction Time (s)",
title = "RT by source (PosNeg trials)") +
theme_minimal(base_size = 16) +
theme(panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, linewidth = 1),
plot.title = element_text(hjust = 0.5),
legend.position = "none",
axis.text.x = element_text(angle = 20, hjust = 1)) +
theme_poster_font
p_rt_box==============================================================================
CHOICE: p(chose positive)
==============================================================================
Overall p(chose pos) by source
Code
p_chose_pos_by_source <- bind_rows(
sim_posneg %>%
summarise(source = "Simulated (power analysis)",
p_chose_pos = mean(chose_pos, na.rm = TRUE),
n = n()),
posneg_pre %>%
summarise(source = "Empirical (pre-trip)",
p_chose_pos = mean(chose_pos, na.rm = TRUE),
n = n()),
glmm_pred %>%
summarise(source = "Predicted (GLMM)",
p_chose_pos = mean(p_chose_pos, na.rm = TRUE),
n = n()),
if (has_ppc) ppc_posneg %>%
summarise(source = "PPC (DDM)",
p_chose_pos = mean(chose_pos, na.rm = TRUE),
n = n()) else NULL
)
p_chose_pos_by_source %>%
gt() %>%
fmt_number(columns = p_chose_pos, decimals = 3) %>%
tab_header(title = "Overall p(chose positive) on PosNeg trials, by source") %>%
tab_options(table.width = pct(80))| Overall p(chose positive) on PosNeg trials, by source | ||
| source | p_chose_pos | n |
|---|---|---|
| Simulated (power analysis) | 0.574 | 32160 |
| Empirical (pre-trip) | 0.804 | 6149 |
| Predicted (GLMM) | 0.804 | 6149 |
| PPC (DDM) | 0.801 | 601000 |
p(chose pos) by affect bin, by source
Code
# same +/- 0.6 SD binning as script 3
bin_affect <- function(df) {
df %>%
mutate(affect_z_bin = case_when(
affect_z < -0.6 ~ "unpleasant",
affect_z > 0.6 ~ "pleasant",
TRUE ~ "neutral"
),
affect_z_bin = factor(affect_z_bin, levels = c("unpleasant", "neutral", "pleasant")))
}
choice_by_affect_source <- bind_rows(
sim_posneg %>% bin_affect() %>%
group_by(affect_z_bin) %>%
summarise(p = mean(chose_pos, na.rm = TRUE),
se = sd(chose_pos, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
mutate(source = "Simulated (power analysis)"),
posneg_pre %>% bin_affect() %>%
group_by(affect_z_bin) %>%
summarise(p = mean(chose_pos, na.rm = TRUE),
se = sd(chose_pos, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
mutate(source = "Empirical (pre-trip)"),
glmm_pred %>% bin_affect() %>%
group_by(affect_z_bin) %>%
summarise(p = mean(p_chose_pos, na.rm = TRUE),
se = sd(p_chose_pos, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
mutate(source = "Predicted (GLMM)"),
if (has_ppc) ppc_posneg %>% bin_affect() %>%
group_by(affect_z_bin) %>%
summarise(p = mean(chose_pos, na.rm = TRUE),
se = sd(chose_pos, na.rm = TRUE) / sqrt(n()), .groups = "drop") %>%
mutate(source = "PPC (DDM)") else NULL
) %>%
mutate(source = factor(source, levels = names(colors_source)))
p_choice_affect <- ggplot(choice_by_affect_source,
aes(x = affect_z_bin, y = p, color = source, group = source)) +
geom_point(size = 3, position = position_dodge(width = 0.3)) +
geom_line(linewidth = 1, position = position_dodge(width = 0.3)) +
geom_errorbar(aes(ymin = p - se, ymax = p + se), width = 0.15,
position = position_dodge(width = 0.3)) +
geom_hline(yintercept = 0.5, linetype = "dashed", color = "gray40") +
scale_color_manual(values = colors_source, name = "Source") +
scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, by = 0.2)) +
labs(x = "Affect bin (±0.6 SD)", y = "P(Choose Positive)",
title = "p(chose positive) by affect, by source (PosNeg trials)") +
theme_minimal(base_size = 16) +
theme(panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill = NA, linewidth = 1),
plot.title = element_text(hjust = 0.5)) +
theme_poster_font
p_choice_affectCode
ggsave(here::here("figures", "compare_choice_by_affect_by_source.png"),
plot = p_choice_affect, width = 9, height = 6, dpi = 300, bg = "white",
device = save_device)==============================================================================
GRAND SUMMARY TABLE (one row per source)
==============================================================================
Code
grand_summary <- bind_rows(
sim_posneg %>%
summarise(source = "Simulated (power analysis)",
n_pts = n_distinct(subject_id),
n_sessions = nrow(sim_sessions),
mean_compliance = mean(sim_compliance$compliance),
mean_affect_z = mean(affect_z, na.rm = TRUE),
sd_affect_z = sd(affect_z, na.rm = TRUE),
mean_rt_s = mean(rt_s, na.rm = TRUE),
sd_rt_s = sd(rt_s, na.rm = TRUE),
p_chose_pos = mean(chose_pos, na.rm = TRUE)),
posneg_pre %>%
summarise(source = "Empirical (pre-trip)",
n_pts = n_distinct(PUNS_ID),
n_sessions = nrow(emp_sessions),
mean_compliance = mean(pt_compliance$compliance),
mean_affect_z = mean(affect_z, na.rm = TRUE),
sd_affect_z = sd(affect_z, na.rm = TRUE),
mean_rt_s = mean(rt_s, na.rm = TRUE),
sd_rt_s = sd(rt_s, na.rm = TRUE),
p_chose_pos = mean(chose_pos, na.rm = TRUE)),
glmm_pred %>%
summarise(source = "Predicted (GLMM)",
n_pts = n_distinct(subject),
n_sessions = NA_integer_,
mean_compliance = NA_real_,
mean_affect_z = NA_real_,
sd_affect_z = NA_real_,
mean_rt_s = mean(rt_s, na.rm = TRUE),
sd_rt_s = sd(rt_s, na.rm = TRUE),
p_chose_pos = mean(p_chose_pos, na.rm = TRUE)),
if (has_ppc) ppc_posneg %>%
summarise(source = "PPC (DDM)",
n_pts = n_distinct(subject),
n_sessions = NA_integer_,
mean_compliance = NA_real_,
mean_affect_z = NA_real_,
sd_affect_z = NA_real_,
mean_rt_s = mean(rt_s, na.rm = TRUE),
sd_rt_s = sd(rt_s, na.rm = TRUE),
p_chose_pos = mean(chose_pos, na.rm = TRUE)) else NULL
)
grand_summary %>%
gt() %>%
fmt_percent(columns = mean_compliance, decimals = 1) %>%
fmt_number(columns = c(mean_affect_z, sd_affect_z, mean_rt_s, sd_rt_s, p_chose_pos),
decimals = 3) %>%
sub_missing(missing_text = "--") %>%
tab_header(title = "Grand summary by source (PosNeg trials)",
subtitle = "affect_z / compliance blank for GLMM + PPC rows since affect enters those as a predictor") %>%
tab_options(table.width = pct(95))| Grand summary by source (PosNeg trials) | ||||||||
| affect_z / compliance blank for GLMM + PPC rows since affect enters those as a predictor | ||||||||
| source | n_pts | n_sessions | mean_compliance | mean_affect_z | sd_affect_z | mean_rt_s | sd_rt_s | p_chose_pos |
|---|---|---|---|---|---|---|---|---|
| Simulated (power analysis) | 70 | 2010 | 95.7% | 0.000 | 1.000 | 1.500 | 0.819 | 0.574 |
| Empirical (pre-trip) | 26 | 519 | 66.5% | 0.000 | 0.998 | 1.724 | 0.819 | 0.804 |
| Predicted (GLMM) | 26 | – | – | – | – | 1.725 | 0.167 | 0.804 |
| PPC (DDM) | 25 | – | – | – | – | 1.744 | 1.081 | 0.801 |