Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

analyse_04_other_maximal_electrode.R 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. # this script is the same as analyse_03 but uses the data from the word vs noise maximal electrode instead of word vs consonants
  2. library(lme4)
  3. library(brms)
  4. library(ggdist)
  5. library(dplyr)
  6. library(purrr)
  7. library(readr)
  8. library(tidyr)
  9. library(ggplot2)
  10. library(scales)
  11. library(patchwork)
  12. library(ggnewscale)
  13. ggplot2::theme_set(ggplot2::theme_classic() + theme(strip.background = element_rect(fill = "white")))
  14. cong_cols <- c("#E69F00", "#009E73")
  15. cong_cols_light <- sapply(cong_cols, function(x) {
  16. x_rgb <- as.numeric(col2rgb(x))
  17. x_li_rgb <- round(rowMeans(cbind(x_rgb*1.25, c(255, 255, 255)*0.75)))
  18. x_li <- rgb(x_li_rgb[1], x_li_rgb[2], x_li_rgb[3], maxColorValue=255)
  19. x_li
  20. }, USE.NAMES=FALSE)
  21. # function to normalise between 0 and 1
  22. norm01 <- function(x, ...) (x-min(x, ...))/(max(x, ...)-min(x, ...))
  23. # get the stimuli's percentage of name agreement values
  24. stim <- read_csv("boss.csv", col_types = cols(perc_name_agree_denom_fq_inputs = col_number())) %>%
  25. select(filename, perc_name_agree_denom_fq_inputs) %>%
  26. rename(perc_name_agree = perc_name_agree_denom_fq_inputs)
  27. # import the max electrode data from the preprocessing, and set up the variables for the model
  28. d <- list.files("max_elec_data", pattern=".+\\.csv$", full.names=TRUE) %>%
  29. map_dfr(function(f) {
  30. read_csv(
  31. f,
  32. col_types=cols(
  33. date = col_date(format = "%d/%m/%Y"),
  34. trial_save_time = col_time(format = "%H:%M:%S"),
  35. subj_id = col_character(),
  36. stim_grp = col_integer(),
  37. resp_grp = col_integer(),
  38. sex = col_character(),
  39. trl_nr = col_integer(),
  40. item_nr = col_integer(),
  41. condition = col_character(),
  42. eeg_trigg = col_integer(),
  43. image = col_character(),
  44. string = col_character(),
  45. corr_ans = col_character(),
  46. resp = col_character(),
  47. acc = col_integer(),
  48. fix1_jitt_flip = col_integer(),
  49. fix2_jitt_flip = col_integer(),
  50. event = col_integer(),
  51. is_block_start = col_logical(),
  52. is_block_end = col_logical(),
  53. .default = col_double()
  54. )
  55. ) %>%
  56. select(
  57. subj_id, stim_grp, resp_grp, item_nr, condition,
  58. image, string, acc, rt, uV, uV_noise_elec
  59. )
  60. }) %>%
  61. left_join(stim, by=c("image" = "filename")) %>%
  62. mutate(
  63. cong_dev = as.numeric(scale(ifelse(condition=="A2", 0, 1), center=TRUE, scale=FALSE)),
  64. cong_dum_incong = as.numeric(condition=="A1"), # 0 is at incongruent
  65. cong_dum_cong = as.numeric(condition=="A2"), # 0 is at congruent
  66. prop_agree = perc_name_agree/100,
  67. pred_norm = norm01(prop_agree, na.rm=TRUE),
  68. pred_norm_max = pred_norm - 1
  69. )
  70. # fit the model ----
  71. m <- lmer(
  72. uV_noise_elec ~ cong_dev * pred_norm +
  73. (cong_dev * pred_norm | subj_id) +
  74. (cong_dev | image) +
  75. (1 | string),
  76. REML=FALSE,
  77. control = lmerControl(optimizer="bobyqa"),
  78. data=d
  79. )
  80. # the effect of the interaction
  81. m_no_interact <- update(m, ~. -cong_dev:pred_norm)
  82. eff_interact <- anova(m, m_no_interact)
  83. # decompose interaction by congruency ----
  84. # effect of predictability in incongruent trials
  85. m_incong <- lmer(
  86. uV_noise_elec ~ cong_dum_incong * pred_norm +
  87. (cong_dum_incong * pred_norm | subj_id) +
  88. (cong_dum_incong | image) +
  89. (1 | string),
  90. REML=FALSE,
  91. control = lmerControl(optimizer="bobyqa"),
  92. data=d
  93. )
  94. m_incong_no_pred <- update(m_incong, ~. -pred_norm)
  95. eff_pred_incong <- anova(m_incong, m_incong_no_pred)
  96. # effect of predictability in congruent trials
  97. m_cong <- lmer(
  98. uV_noise_elec ~ cong_dum_cong * pred_norm +
  99. (cong_dum_cong * pred_norm | subj_id) +
  100. (cong_dum_cong | image) +
  101. (1 | string),
  102. REML=FALSE,
  103. control = lmerControl(optimizer="bobyqa"),
  104. data=d
  105. )
  106. m_cong_no_pred <- update(m_cong, ~. -pred_norm)
  107. eff_pred_cong <- anova(m_cong, m_cong_no_pred)
  108. # decompose interaction by predictability ----
  109. # effect of congruency at minimum level of predictability is in main model
  110. # effect of congruency at maximum predictability
  111. m_maxpred <- lmer(
  112. uV_noise_elec ~ cong_dev * pred_norm_max +
  113. (cong_dev * pred_norm_max | subj_id) +
  114. (cong_dev | image) +
  115. (1 | string),
  116. REML=FALSE,
  117. control = lmerControl(optimizer="bobyqa"),
  118. data=d
  119. )
  120. m_maxpred_no_cong <- update(m_maxpred, ~. -cong_dev)
  121. eff_cong_maxpred <- anova(m_maxpred, m_maxpred_no_cong)
  122. # plot relationship ----
  123. d_cells_pred <- tibble(
  124. pred_norm = rep(range(d$pred_norm), 2),
  125. perc_name_agree = rep(range(d$perc_name_agree), 2),
  126. cong_dev = rep(unique(d$cong_dev), each=2),
  127. condition = ifelse(cong_dev==min(cong_dev), "A2", "A1")
  128. ) %>%
  129. mutate(pred_uV = predict(m, newdata=., re.form=~0))
  130. # get prediction intervals (no random slopes for feasibility)
  131. m_no_rand_s <- lmer(
  132. uV_noise_elec ~ cong_dev * pred_norm +
  133. (1 | subj_id) +
  134. (1 | image) +
  135. (1 | string),
  136. REML=FALSE,
  137. control = lmerControl(optimizer="bobyqa"),
  138. data=d
  139. )
  140. d_preds_boot <- expand_grid(
  141. cong_dev = unique(d$cong_dev),
  142. perc_name_agree = seq(min(d$perc_name_agree), max(d$perc_name_agree), 0.01)
  143. ) %>%
  144. mutate(
  145. prop_agree = perc_name_agree/100,
  146. pred_norm = norm01(prop_agree, na.rm=TRUE),
  147. condition = ifelse(cong_dev==min(cong_dev), "A2", "A1")
  148. )
  149. boots <- bootMer(m_no_rand_s, nsim=5000, FUN=function(m_i) {
  150. predict(m_i, newdata=d_preds_boot, re.form=~0)
  151. }, seed=3101, .progress="txt")
  152. ci_norm <- function(x, level=c(0.025, 0.975)) {
  153. qnorm(p=level, mean=mean(x), sd=sd(x))
  154. }
  155. d_preds_boot <- d_preds_boot %>%
  156. mutate(
  157. pred_int_lwr = apply(boots$t, 2, ci_norm, level=.025),
  158. pred_int_upr = apply(boots$t, 2, ci_norm, level=.975)
  159. )
  160. lmm_plot_scatter <- d %>%
  161. ggplot(aes(perc_name_agree, uV_noise_elec, colour = condition)) +
  162. geom_point(shape=1, show.legend=FALSE, alpha=0.25) +
  163. scale_colour_manual(name = NA, labels = NULL, values = cong_cols_light) +
  164. new_scale_colour() +
  165. geom_line(aes(y=pred_uV, colour = condition), data=d_cells_pred, linewidth=1.75) +
  166. scale_colour_manual(name = "Picture-Word Congruency", labels = c("Congruent", "Incongruent"), values = cong_cols) +
  167. scale_y_continuous(breaks=scales::extended_breaks(n=6)) +
  168. labs(x = "Predictability (%)", y = "N1 Amplitude (µV)", tag="a")
  169. lmm_plot_lines <- ggplot() +
  170. geom_ribbon(aes(x=perc_name_agree, ymin=pred_int_lwr, ymax=pred_int_upr, colour=condition), data=d_preds_boot, fill=NA, linetype="dashed", show.legend = FALSE) +
  171. geom_line(aes(x=perc_name_agree, y=pred_uV, colour=condition), data=d_cells_pred, linewidth=1.75) +
  172. scale_colour_manual(name = "Picture-Word Congruency", labels = c("Congruent", "Incongruent"), values = cong_cols) +
  173. scale_y_continuous(breaks=scales::extended_breaks(n=6)) +
  174. labs(x = "Predictability (%)", y = "N1 Amplitude (µV)", tag="b")
  175. lmm_plot <- (lmm_plot_scatter | lmm_plot_lines) +
  176. plot_layout(guides = "collect") &
  177. theme(legend.position = "bottom", plot.background = element_blank()) &
  178. scale_x_continuous(expand=c(0, 0))
  179. ggsave(file.path("figs", "04_lmm_summary_plot_noise_elec.png"), lmm_plot, device="png", type="cairo", width=6.5, height=3.75, dpi=600)
  180. ggsave(file.path("figs", "04_lmm_summary_plot_noise_elec.pdf"), lmm_plot, device="pdf", width=6.5, height=3.75)
  181. # evidences ratio for effect -----------------------------------------------
  182. p <- c(
  183. set_prior("normal(-5, 10)", class="Intercept"),
  184. set_prior("normal(0, 5)", class="b", coef="cong_dev"),
  185. set_prior("normal(0, 5)", class="b", coef="pred_norm"),
  186. set_prior("normal(0, 5)", class="b", coef="cong_dev:pred_norm"),
  187. set_prior("lkj_corr_cholesky(1)", class="L")
  188. )
  189. m_b <- brm(
  190. uV_noise_elec ~ cong_dev * pred_norm +
  191. (cong_dev * pred_norm | subj_id) +
  192. (cong_dev | image) +
  193. (1 | string),
  194. data = d,
  195. prior = p,
  196. chains = 5,
  197. cores = 5,
  198. iter = 5000,
  199. control = list(
  200. adapt_delta = 0.8,
  201. max_treedepth = 10
  202. ),
  203. refresh = 25,
  204. seed = 420
  205. )
  206. m_b_h <- hypothesis(m_b, "cong_dev:pred_norm>0")
  207. m_b_h_hdi <- as_draws_df(m_b, variable="b_cong_dev:pred_norm", regex=FALSE) %>%
  208. rename(int_est = `b_cong_dev:pred_norm`) %>%
  209. median_hdi(int_est, .width=0.89)
  210. m_b_pl <- as_draws_df(m_b, variable="b_cong_dev:pred_norm", regex=FALSE) %>%
  211. rename(int_est = `b_cong_dev:pred_norm`) %>%
  212. ggplot(aes(int_est, fill=after_stat(x>0))) +
  213. stat_slab(slab_colour="black", slab_size=0.4) +
  214. stat_pointinterval(
  215. .width=0.89, point_interval="median_hdi",
  216. point_size=1, size=1, show.legend = FALSE
  217. ) +
  218. geom_vline(xintercept=0, linetype="dashed") +
  219. geom_text(
  220. aes(x = x, y = y),
  221. data = tibble(x=-3.9, y=1),
  222. label = deparse(bquote(BF["01"]==.(round(1/m_b_h$hypothesis$Evid.Ratio, 2)))),
  223. parse = TRUE,
  224. hjust = 0,
  225. vjust = 1,
  226. size = 4.5
  227. ) +
  228. scale_y_continuous(expand = expansion(0, 0.04), limits = c(0, NA)) +
  229. coord_cartesian() +
  230. labs(
  231. x = "Congruency-Predictability Interaction (µV)",
  232. y = "Posterior Density"
  233. ) +
  234. theme(legend.position = "none") +
  235. scale_fill_manual(
  236. values = c("grey90", "#8B0000"),
  237. labels = c(bquote(H["0"]), bquote(H["1"]))
  238. )
  239. ggsave(file.path("figs", "04_post.png"), width=3.5, height=1.75, device="png", type="cairo")
  240. ggsave(file.path("figs", "04_post.pdf"), width=3.5, height=1.75, device="pdf")
  241. lmm_plot_post <- wrap_plots(
  242. lmm_plot,
  243. (
  244. m_b_pl +
  245. labs(
  246. fill = NULL,
  247. tag = "c",
  248. x = "Congruency-Predictability\nInteraction"
  249. ) +
  250. theme(
  251. plot.background = element_blank(),
  252. legend.position = "bottom"
  253. )
  254. )
  255. ) +
  256. plot_layout(widths = c(2, 1))
  257. ggsave(file.path("figs", "04_lmm_and_post.pdf"), lmm_plot_post, width=6.5, height=3, device="pdf")