OSCR

Deciding to simulate: Cognitive mechanisms of predicting the decisions of others.

Code ↔ Paper

4 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 4 matches
  1. [1] § STAR★Methods › Quantification and statistical analysis › Computational modeling ↔ Modelling code + output/EUDM_heurtest.Rmd, lines 14–71 · score 0.58 · probability matching, drift rate, Diffusion, DDM, heur, threshold
  2. [2] § STAR★Methods › Quantification and statistical analysis › Computational modeling ↔ Modelling code + output/parameter_recovery.Rmd, lines 53–156 · score 0.56 · highest negative log, likelihood, simulated, fitting, posterior, RT
  3. [3] § Results › Modeling results ↔ Modelling code + output/EUDM_heurtest.Rmd, lines 14–71 · score 0.54 · probability matching, drift rate, DDM, accumulation, heur, simulated
  4. [4] § STAR★Methods › Quantification and statistical analysis › Computational modeling ↔ Modelling code + output/posterior_predictive_check.Rmd, lines 56–118 · score 0.50 · posterior predictive check, iteration, simulated, RT, risky, probability

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

R Markdown · 505 lines · 20 KB · no license · 2 matches

  1. ```{r}
  2. library(readxl)
  3. library(tidyverse)
  4. library(ggplot2)
  5. library(rstan)
  6. library(DescTools)
  7. library(bayestestR)
  8. library(ggdist)
  9. options(mc.cores = parallel::detectCores())
  10. rstan_options(auto_write = TRUE)
  11. ```
  12. ```{r}
  13. #set up a function to simulate data using a basic DDM (i.e., one that ignores trial-to-trial information)
  14. gen_trial_heurDM <- function(options, drift_rate, threshold, ndt, rel_sp, noise_constant=1, dt=0.001, max_rt=10) {
  15. n_trials <- length(options$magnitude)
  16. acc <- rep(NA, n_trials)
  17. rt <- rep(NA, n_trials)
  18. max_tsteps <- max_rt/dt
  19. if (length(drift_rate)==1){
  20. drift_rate = rep(drift_rate,n_trials)
  21. }
  22. # initialize the diffusion process
  23. tstep <- 0
  24. x <- rep(rel_sp*threshold, n_trials) # vector of accumulated evidence at t=tstep
  25. ongoing <- rep(TRUE, n_trials) # have the accumulators reached the bound?
  26. # start accumulating
  27. while (sum(ongoing) > 0 & tstep < max_tsteps) {
  28. x[ongoing] <- x[ongoing] + rnorm(mean=drift_rate[ongoing]*dt,
  29. sd=noise_constant*sqrt(dt),
  30. n=sum(ongoing))
  31. tstep <- tstep + 1
  32. # ended trials
  33. ended_correct <- (x >= threshold)
  34. ended_incorrect <- (x <= 0)
  35. # store results and filter out ended trials
  36. if(sum(ended_correct) > 0) {
  37. acc[ended_correct & ongoing] <- 1
  38. rt[ended_correct & ongoing] <- dt*tstep + ndt
  39. ongoing[ended_correct] <- FALSE
  40. }
  41. if(sum(ended_incorrect) > 0) {
  42. acc[ended_incorrect & ongoing] <- 0
  43. rt[ended_incorrect & ongoing] <- dt*tstep + ndt
  44. ongoing[ended_incorrect] <- FALSE
  45. }
  46. }
  47. return (data.frame(trial=seq(1, n_trials), accuracy=acc, rt=rt, RM=options$magnitude, RP=options$probability, SM=options$safe_magnitude,drift_rate=drift_rate,threshold=threshold,ndt=ndt,starting_point=rel_sp))
  48. }
  49. #simulate data for X participant, with a constant drift rate (corresponding to a probability matching-like response strategy)
  50. options <- read_excel("K:/options_MP.xlsx")
  51. drifts <- c(-0.6,-0.2,0.2,0.6)
  52. npart <- 200
  53. for(d in drifts){
  54. for (i in 1:npart){
  55. sim_part_data <- gen_trial_heurDM(options, drift_rate=d, threshold=2.25, ndt=0.65, rel_sp=0.5)
  56. sim_part_data$participant = i
  57. sim_part_data$minRT <- min(sim_part_data$rt)
  58. #print(c(mean(sim_part_data$accuracy),mean(sim_part_data$rt[sim_part_data$accuracy==1]),mean(sim_part_data$rt[sim_part_data$accuracy==0])))
  59. assign(paste('sim_part_data',i, sep=''),sim_part_data)
  60. }
  61. rm(sim_part_data)
  62. df_list <- mget(ls(pattern = "^sim_part_data"))
  63. assign(paste('df_list_drift',d,sep=''),df_list)
  64. rm(df_list)
  65. }
  66. df_list <- mget(ls(pattern = "^df_list"))
  67. ```
  68. ```{r}
  69. all_rhats_below_1.1 <- function(fit_object) {
  70. all(summary(fit_object)$summary[,"Rhat"] < 1.1, na.rm = TRUE)
  71. }
  72. fit_and_save_model <- function(stan_file, data_for_stan, fit_obj_name, save_path, chains=4, iter=2000, warmup=750) {
  73. fit <- stan(file=stan_file, data=data_for_stan, chains=chains, iter=iter, warmup=warmup)
  74. assign(fit_obj_name, fit, envir = .GlobalEnv)
  75. save(list=fit_obj_name, file=save_path)
  76. return(fit)
  77. }
  78. # ---------- Model Specifications ----------
  79. model_specs <- list(
  80. EU = list(suffix="_EU", stan_file="K:/Erik/noncentered_EUDM_decor.stan"),
  81. heur = list(suffix="_heur", stan_file="K:/Erik/noncentered_heurDM.stan")
  82. )
  83. for (i in 1:length(seq_along(df_list))) {
  84. sim_data <- bind_rows(df_list[[i]])
  85. sim_data = filter(sim_data,!is.na(accuracy)==TRUE & rt>0.2)
  86. sim_data$accuracy_recoded = sim_data$accuracy
  87. sim_data[sim_data$accuracy==0, "accuracy_recoded"] = -1
  88. sim_data <- sim_data %>%
  89. group_by(participant) %>%
  90. mutate(participant = cur_group_id())
  91. minRT <- summarize(sim_data,minRT=min(rt))
  92. sim_data_for_stan = list(
  93. N = dim(sim_data)[1],
  94. L = length(unique(sim_data$participant)),
  95. accuracy = sim_data$accuracy_recoded,
  96. rt = sim_data$rt,
  97. RM = sim_data$RM,
  98. SM = sim_data$SM,
  99. RP = sim_data$RP/100,
  100. participant = sim_data$participant,
  101. minRT = minRT$minRT
  102. )
  103. fit_base_name <- paste0("fit_",substr(names(df_list)[i], 9, nchar(names(df_list)[i])))
  104. for (modspec in model_specs) {
  105. suffix <- modspec$suffix
  106. stan_file <- modspec$stan_file
  107. fit_obj_name <- paste0(fit_base_name, suffix)
  108. save_path <- paste0("K:/Erik/heurtest/", fit_obj_name, ".RData")
  109. attempt <- 1
  110. repeat {
  111. if (file.exists(save_path)) {
  112. load(save_path) # loads fit_obj_name variable into environment
  113. fit_object <- get(fit_obj_name)
  114. if (all_rhats_below_1.1(fit_object)) {
  115. message(sprintf("Rhat < 1.1 for %s, skipping fit.", fit_obj_name))
  116. rm(list=fit_obj_name, envir=.GlobalEnv)
  117. gc()
  118. break
  119. } else {
  120. message(sprintf("Rhat >= 1.1 for %s, refitting (attempt %d)...", fit_obj_name, attempt))
  121. }
  122. } else {
  123. message(sprintf("No RData for %s, fitting...", fit_obj_name))
  124. }
  125. # Fit and save
  126. fit_object <- fit_and_save_model(stan_file, sim_data_for_stan, fit_obj_name, save_path)
  127. if (all_rhats_below_1.1(fit_object)) {
  128. rm(list=fit_obj_name, envir=.GlobalEnv)
  129. gc()
  130. break
  131. } else {
  132. rm(list=fit_obj_name, envir=.GlobalEnv)
  133. gc()
  134. attempt <- attempt + 1
  135. # Optional: set a limit to avoid infinite loops
  136. # if(attempt > 10) stop(sprintf("Failed to fit %s after 10 attempts.", fit_obj_name))
  137. }
  138. }
  139. }
  140. }
  141. pars = c("mu_threshold","mu_ndt","mu_starting_point","mu_drift_scaling","mu_gamma","mu_alpha","sd_threshold","sd_ndt","sd_starting_point","sd_drift_scaling","sd_gamma","sd_alpha","z_drift_scaling[1]","z_threshold[1]","z_ndt[1]","z_starting_point[1]","z_gamma[1]","z_alpha[1]")
  142. ```
  143. ```{r}
  144. #perform LOO comparison to see which model wins
  145. library(loo)
  146. path <- "K:/Erik/heurtest/"
  147. files <- list.files(path = path, pattern = "^fit_drift", full.names = TRUE)
  148. #Parse drift value and model name from filenames
  149. extract_info <- function(filename) {
  150. fname <- basename(filename)
  151. m <- regexec("fit_drift(-?)([0-9.]+)_([A-Za-z]+)\\.RData", fname)
  152. parts <- regmatches(fname, m)[[1]]
  153. drift_val <- as.numeric(paste0(parts[2], parts[3]))
  154. list(
  155. drift = drift_val,
  156. model = parts[4],
  157. file = filename,
  158. fitname = gsub("\\.RData$", "", fname)
  159. )
  160. }
  161. info_list <- lapply(files, extract_info)
  162. info_df <- do.call(rbind, lapply(info_list, as.data.frame))
  163. rownames(info_df) <- NULL
  164. unique_drifts <- unique(info_df$drift)
  165. for (dr in unique_drifts) {
  166. sub <- info_df[info_df$drift == dr, ]
  167. if (!all(c("heur", "EU") %in% sub$model)) next
  168. sub <- sub[match(c("heur", "EU"), sub$model), ]
  169. loo_results <- list()
  170. names_models <- as.character(sub$model)
  171. for (i in 1:2) {
  172. load(sub$file[i]) # Object(s) loaded into the workspace
  173. fit <- get(as.character(sub$fitname[i]))
  174. ll <- extract_log_lik(fit)
  175. loo_results[[names_models[i]]] <- loo(ll)
  176. rm(list = as.character(sub$fitname[i]))
  177. }
  178. loo_compare <- loo_compare(loo_results$heur, loo_results$EU)
  179. drift_str <- if (dr < 0) paste0("neg", abs(dr)) else as.character(dr)
  180. assign(paste0("result_loo_", drift_str), list(
  181. drift = dr,
  182. loo_results = loo_results,
  183. loo_compare = loo_compare
  184. ))
  185. }
  186. # Clean workspace except loo results and needed variables
  187. all_objects <- ls()
  188. keep <- c("files", grep("^result_loo_", all_objects, value = TRUE))
  189. rm(list = setdiff(all_objects, keep))
  190. gc()
  191. save.image("K:/Erik/heurtest/loo_comparison_heur.RData")
  192. ```
  193. ```{r}
  194. #print out LOO values
  195. load(paste0("K:/Erik/heurtest/loo_comparison_heur.RData"))
  196. loo_objects <- ls(pattern = "^result_loo_")
  197. # Iterate over each object name, retrieve the object, and print the entire content
  198. for (obj_name in loo_objects) {
  199. cat("\nObject Name:", obj_name, "\n")
  200. # Retrieve the object
  201. loo_object <- get(obj_name)
  202. # Check if the object has a suitable method to be coerced into a data frame
  203. if (is.list(loo_object)) {
  204. print(loo_object$loo_compare)
  205. } else {
  206. # Fallback plan to just print if nothing else
  207. print(loo_object)
  208. }
  209. }
  210. ```
  211. ```{r}
  212. #extract the posteriors from the EUDM for the different conditions
  213. path = "K:/Erik/heurtest/"
  214. pars = c("mu_threshold","mu_ndt","mu_starting_point","mu_drift_scaling","mu_alpha","sd_threshold","sd_ndt","sd_starting_point","sd_drift_scaling","sd_alpha","z_threshold","z_ndt","z_starting_point","z_drift_scaling","z_alpha","threshold_sbj","ndt_sbj","starting_point_sbj","drift_scaling_sbj","alpha_sbj","threshold_gl","ndt_gl","starting_point_gl","drift_scaling_gl","alpha_gl","log_lik")
  215. files <- list.files(path = path, pattern = "_EU", full.names = TRUE)
  216. for (ind in 1:length(files)){
  217. load(files[ind])
  218. name <- substr(files[ind], 18, nchar(files[ind])-6)
  219. post <- extract(get(name),pars=pars)
  220. assign(paste0("post_",substr(files[ind], 27, nchar(files[ind])-9)),post)
  221. all_objects <- ls()
  222. keep <- c("files","pars",grep("^post_", all_objects, value = TRUE))
  223. rm(list = setdiff(all_objects, keep))
  224. }
  225. save.image(paste0(path,"EUDM_posteriors_heur.RData"))
  226. ```
  227. ```{r paged.print=FALSE}
  228. load(paste0(path,"EUDM_posteriors_heur.RData"))
  229. #getting the descriptive stats for the posterior parameter values
  230. cond <- c("post_-0.2","post_-0.6","post_0.2","post_0.6")
  231. for (ind in 1:length(cond)){
  232. posteriors <- get(cond[ind])
  233. params <- data.frame(matrix(NA,nrow=6,ncol=4))
  234. for (i in 1:5){
  235. params[i,1] <- mean(posteriors[[i+20]])
  236. params[i,2] <- sd(posteriors[[i+20]])
  237. params[i,3] <- as.numeric(hdi(posteriors[[i+20]])[2])
  238. params[i,4] <- as.numeric(hdi(posteriors[[i+20]])[3])
  239. }
  240. colnames(params) <- c("mean","sd","HDIl","HDIu")
  241. assign(paste0("params_",substr(cond[ind],6,nchar(cond[ind]))),params)
  242. }
  243. #group-level
  244. objects <- ls(pattern = "^params_")
  245. # Iterate over each object name, retrieve the object, and print the entire content
  246. for (obj_name in objects) {
  247. cat("\nObject Name:", obj_name, "\n")
  248. # Retrieve the object
  249. param_object <- get(obj_name)
  250. row.names(param_object) <- c("threshold","ndt","starting point","drift scaling","alpha")
  251. # Fallback plan to just print if nothing else
  252. print(param_object)
  253. }
  254. ```
  255. ```{r}
  256. #visualizing the parameters from each condition
  257. params <- c("threshold_gl", "ndt_gl", "starting_point_gl",
  258. "drift_scaling_gl", "alpha_gl")
  259. post_names <- ls(pattern = "^post_")
  260. draw_list <- lapply(post_names, function(obj_name) {
  261. lst <- get(obj_name)
  262. drift <- sub("post_", "", obj_name)
  263. df <- as.data.frame(lst[params]) # dataframe with param columns
  264. df$draw <- 1:nrow(df) # keep draw index for melting if needed
  265. df$condition <- drift
  266. df
  267. })
  268. combined_df <- bind_rows(draw_list)
  269. # Reshape to long format: each row is one posterior draw for one parameter and one condition
  270. long_df <- combined_df %>%
  271. pivot_longer(cols = all_of(params), names_to = "parameter", values_to = "value") %>%
  272. select(-draw) # Can keep 'draw' if you want to track draw index
  273. long_df$parameter <- factor(long_df$parameter, levels = params)
  274. long_df$condition <- factor(long_df$condition,levels = c(-0.6,-0.2,0.2,0.6))
  275. long_df <- filter(long_df,condition!="names")
  276. summary_df <- long_df %>%
  277. group_by(parameter, condition) %>%
  278. mean_qi(value, .width = 0.95) %>%
  279. rename(
  280. Mean = value,
  281. HDI_lower = .lower,
  282. HDI_upper = .upper
  283. )
  284. colors <- c("brown", "orange", "blue", "darkblue") # or any palette you prefer
  285. plot_pars <- ggplot(long_df, aes(x = value, y = condition, color = condition, fill = condition)) +
  286. # Crossbars for mean & interval
  287. geom_crossbar(data = summary_df,
  288. aes(x = Mean, y = condition, xmin = HDI_lower, xmax = HDI_upper, fill = condition),
  289. fatten = 3, width = 0.5, color = "black", alpha = 0.5,
  290. position = position_dodge2(width = 0.6)) +
  291. # For axis scaling hack (optional, see below)
  292. geom_point(alpha = 0, show.legend = FALSE) +
  293. # A red vertical line at x = 0 for reference
  294. scale_color_manual(values = colors) +
  295. scale_fill_manual(values = colors) +
  296. facet_wrap(~parameter, scales = "free_x", nrow = length(unique(long_df$parameter)), strip.position = "left") +
  297. labs(
  298. x = "Posterior value",
  299. y = "Condition (drift rate)"
  300. ) +
  301. theme_bw() +
  302. theme(
  303. strip.text.y = element_text(angle = 0, size = 13),
  304. axis.text.y = element_text(size = 13),
  305. axis.text.x = element_text(size = 12),
  306. axis.title.x = element_text(size = 15, margin = margin(t = 10)),
  307. legend.text = element_text(size = 12),
  308. legend.title = element_text(size = 13),
  309. strip.background = element_blank()
  310. )
  311. ggsave("K:/Erik/heurtest/heur_parameters.png", device = "png", plot = plot_pars, dpi = 300, height = 10, width = 6.5)
  312. ```
  313. ```{r}
  314. #testing for between-stage group differences
  315. for (cond in 1:7){
  316. if (cond==1){
  317. cond1 <- "control1"
  318. cond2 <- "control0"
  319. } else if (cond==2){
  320. cond1 <- "controlneutral1"
  321. cond2 <- "controlneutral0"
  322. } else if (cond==3){
  323. cond1 <- "main1"
  324. cond2 <- "main0"
  325. } else if (cond==4){
  326. cond1 <- "main2"
  327. cond2 <- "main0"
  328. } else if (cond==5){
  329. cond1 <- "controlneutral1"
  330. cond2 <- "control1"
  331. } else if (cond==6){
  332. cond1 <- "main1"
  333. cond2 <- "control1"
  334. } else if (cond==7){
  335. cond1 <- "main2"
  336. cond2 <- "control1"
  337. }
  338. threshold_dif_LP <- get(paste0("post_",cond1,"LP"))$threshold_gl - get(paste0("post_",cond2,"LP"))$threshold_gl
  339. ndt_dif_LP <- get(paste0("post_",cond1,"LP"))$ndt_gl - get(paste0("post_",cond2,"LP"))$ndt_gl
  340. starting_point_dif_LP <- get(paste0("post_",cond1,"LP"))$starting_point_gl - get(paste0("post_",cond2,"LP"))$starting_point_gl
  341. drift_scaling_dif_LP <- get(paste0("post_",cond1,"LP"))$drift_scaling_gl - get(paste0("post_",cond2,"LP"))$drift_scaling_gl
  342. alpha_dif_LP <- get(paste0("post_",cond1,"LP"))$alpha_gl - get(paste0("post_",cond2,"LP"))$alpha_gl
  343. threshold_dif_HP <- get(paste0("post_",cond1,"HP"))$threshold_gl - get(paste0("post_",cond2,"HP"))$threshold_gl
  344. ndt_dif_HP <- get(paste0("post_",cond1,"HP"))$ndt_gl - get(paste0("post_",cond2,"HP"))$ndt_gl
  345. starting_point_dif_HP <- get(paste0("post_",cond1,"HP"))$starting_point_gl - get(paste0("post_",cond2,"HP"))$starting_point_gl
  346. drift_scaling_dif_HP <- get(paste0("post_",cond1,"HP"))$drift_scaling_gl - get(paste0("post_",cond2,"HP"))$drift_scaling_gl
  347. alpha_dif_HP <- get(paste0("post_",cond1,"HP"))$alpha_gl - get(paste0("post_",cond2,"HP"))$alpha_gl
  348. combined_df <- rbind(
  349. data.frame(Parameter = "threshold", Difference = threshold_dif_LP),
  350. data.frame(Parameter = "ndt", Difference = ndt_dif_LP),
  351. data.frame(Parameter = "starting point", Difference = starting_point_dif_LP),
  352. data.frame(Parameter = "drift scaling", Difference = drift_scaling_dif_LP),
  353. data.frame(Parameter = "alpha", Difference = alpha_dif_LP),
  354. data.frame(Parameter = "threshold", Difference = threshold_dif_HP),
  355. data.frame(Parameter = "ndt", Difference = ndt_dif_HP),
  356. data.frame(Parameter = "starting point", Difference = starting_point_dif_HP),
  357. data.frame(Parameter = "drift scaling", Difference = drift_scaling_dif_HP),
  358. data.frame(Parameter = "alpha", Difference = alpha_dif_HP)
  359. )
  360. combined_df$risk_group <- c(rep(0,length(combined_df$Parameter)/2),rep(2,length(combined_df$Parameter)/2))
  361. summary_df <- combined_df %>%
  362. group_by(risk_group,Parameter) %>%
  363. summarize(
  364. Mean = mean(Difference),
  365. HDI_lower = round(quantile(Difference, 0.025),3),
  366. HDI_upper = round(quantile(Difference, 0.975),3)
  367. )
  368. assign(paste0("summary_df_",cond1,"-",cond2),summary_df)
  369. #a "hacky" way to fix the x-axis scale for each parameter across panels, by inserting the endpoint values into the corresponding dataset
  370. risk_group <- c(rep(0,10),rep(2,10))
  371. Parameter <- rep(unique(combined_df$Parameter)[1:5],4)
  372. Difference <- rep(c(-0.2,-0.125,-0.15,-0.05,-1,0.75,0.01,0.2,0.01,3
  373. ),2)
  374. limits <- data.frame(Parameter,Difference,risk_group)
  375. combined_df <- rbind(combined_df,limits)
  376. if (cond1=="control0"){
  377. name1 <- "adaptation (self)"
  378. } else if (cond1=="control1"){
  379. name1 <- "post-adaptation (self)"
  380. } else if (cond1=="controlneutral0"){
  381. name1 <- "adaptation (self)"
  382. } else if (cond1=="controlneutral1"){
  383. name1 <- "risk-average other"
  384. } else if (cond1=="main0"){
  385. name1 <- "adaptation (self)"
  386. } else if (cond1=="main1"){
  387. name1 <- "risk-averse other"
  388. } else if (cond1=="main2"){
  389. name1 <- "risk-seeking other"
  390. }
  391. if (cond2=="control0"){
  392. name2 <- "adaptation (self)"
  393. } else if (cond2=="control1"){
  394. name2 <- "post-adaptation (self)"
  395. } else if (cond2=="controlneutral0"){
  396. name2 <- "adaptation (self)"
  397. } else if (cond2=="controlneutral1"){
  398. name2 <- "risk-average other"
  399. } else if (cond2=="main0"){
  400. name2 <- "adaptation (self)"
  401. } else if (cond2=="main1"){
  402. name2 <- "risk-averse other"
  403. } else if (cond2=="main2"){
  404. name2 <- "risk-seeking other"
  405. }
  406. colors <- c('#f1e6c1','#c8f1c8')
  407. # plot_pars <- ggplot(combined_df, aes(x = Difference, y = Parameter,color=as.factor(risk_group))) +
  408. # #geom_point(data = summary_df, aes(x = Mean), size = 3,alpha=2) +
  409. # geom_crossbar(data = summary_df, aes(x = Mean, y = Parameter, xmin = HDI_lower, xmax = HDI_upper,fill=as.factor(risk_group)), fatten = 3, width = 0.75,color="black",alpha=0.5,position=position_dodge2(width=0.5)) +
  410. # geom_vline(xintercept = 0, color = "black", linetype = "dashed") +
  411. # #geom_errorbar(data = summary_df, aes(x = Mean, y = Parameter, xmin = HDI_lower, xmax = HDI_upper), width = 0.3,) +
  412. # scale_color_manual(values = colors, labels = c('Low probability', 'High probability')) +
  413. # scale_fill_manual(values = colors, labels = c('Low probability', 'High probability')) +
  414. # facet_grid(Parameter ~ ., scales = "free_y", space = "free_y") +
  415. # labs(x = paste0("Difference ",name1," - \n",name2), y = NULL) +
  416. # theme_bw() +
  417. # xlim(c(-0.9,2.7)) +
  418. # labs(fill="Risk group") +
  419. # theme(strip.text = element_blank(),
  420. # axis.text.y=element_text(size=11),
  421. # axis.text.x=element_text(size=11),
  422. # axis.title.x=element_text(size=13,margin = margin(t = 10)),
  423. # legend.text=element_text(size=11),
  424. # legend.title=element_text(size=13)
  425. # )
  426. # ggsave(paste0("dif_",cond1,"-",cond2,".png"), device = "png", plot = plot_pars, dpi = 300, height = 5, width = 6)
  427. plot_pars <- ggplot(combined_df, aes(x = Difference, y = Parameter,color=as.factor(risk_group))) +
  428. #geom_point(data = summary_df, aes(x = Mean), size = 3,alpha=2) +
  429. geom_crossbar(data = summary_df, aes(x = Mean, y = Parameter, xmin = HDI_lower, xmax = HDI_upper,fill=as.factor(risk_group)), fatten = 3, width = 0.75,color="black",alpha=0.5,position=position_dodge2(width=0.5)) +
  430. geom_vline(xintercept = 0, color = "red", linewidth=1.5, linetype = "dashed") +
  431. geom_point(alpha=0,show.legend=FALSE) + #part of the "hack" solution to fix the x-axis scale across panels (because it takes into account values created in "limits" above)
  432. #geom_errorbar(data = summary_df, aes(x = Mean, y = Parameter, xmin = HDI_lower, xmax = HDI_upper), width = 0.3,) +
  433. scale_color_manual(values = colors, labels = c('Low probability', 'High probability')) +
  434. scale_fill_manual(values = colors, labels = c('Low probability', 'High probability')) +
  435. facet_wrap(~ Parameter, scales = "free", nrow = length(unique(combined_df$Parameter)),
  436. drop = TRUE) +
  437. labs(x = paste0("Difference ",name1," - \n",name2), y = NULL) +
  438. theme_bw() +
  439. labs(fill="Risk group") +
  440. theme(strip.text = element_blank(),
  441. axis.text.y=element_text(size=13),
  442. axis.text.x=element_text(size=12),
  443. axis.title.x=element_text(size=15,margin = margin(t = 10)),
  444. legend.text=element_text(size=12),
  445. legend.title=element_text(size=13)
  446. )
  447. ggsave(paste0("dif_",cond1,"-",cond2,".png"), device = "png", plot = plot_pars, dpi = 300, height = 7, width = 6.5)
  448. }
  449. ```

EUDM_heurtest.Rmd, no license · at the source

Overview

  1. Department of Psychology and Hamburg Center of Neural and Cognitive Systems, University of Hamburg, Von-Melle Park 11, 20146 Hamburg, Germany
  2. Paris Brain Institute - ICM, Hôpital de la Pitié-Salpêtrière, 47 Boulevard de l’Hôpital, 75013 Paris, France
Institutions: Universität Hamburg (Germany); Pitié-Salpêtrière Hospital (France); Institut du Cerveau (France)
Journal: iScience, volume 29, issue 7, article 116047
Dates: received 11 September 2025; accepted 5 May 2026; published online 24 June 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1016/j.isci.2026.116047 · PMID 42389597 · PMCID PMC13319925 · OpenAlex W4390884023
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), cognitive (subfield)
Methods: Statistics, Machine learning, Connectivity
Keywords: Neuroscience, Behavioral neuroscience, Cognitive neuroscience
Topic: Decision-Making and Behavioral Economics (General Decision Sciences, Decision Sciences), according to OpenAlex
Funding: European Research Council; ERC; European Union’s Horizon 2020 research and innovation program (948545)
Citations: not cited yet (Europe PMC); 77 references in the paper

Abstract

Existing behavioral and neural evidence suggests that people rely on mental simulation when predicting others’ decisions. If true, making predictions with a biased decision system should result in biased predictions. We tested this idea directly by biasing participants’ risk valuation through adaptation to either high- or low-probability contexts and then asking them to predict the choices of three distinct agents (risk-average, risk-averse, risk-seeking). The adaptation manipulation biased participants’ predictions for the risk-average and risk-averse agents, suggesting simulation with their own (biased) decision process when predicting these agents. In contrast, there was no adaptation effect for the risk-seeking agent. Similarly, participants’ own risk tendencies correlated with predictions for the risk-average and risk-averse, but not the risk-seeking agent. Drift diffusion modeling analyses showed that this adaptation bias was the result of changes in participants’ risk valuation parameter. Overall, these findings support simulation-based prediction while suggesting a modulating role of the other person’s characteristics.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 4 matches between paragraphs and lines of code.

OSF svyma

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Languages: R (6), Stan (4)
Size: 22 files, 10 scripts
Software Heritage: not checked
Found in: “Data and code availability”
Holds: 6 notebooks
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Stan (9 files), ggplot2 (6 files), tidyverse (6 files), easystats (4 files), afex (1 file), emmeans (1 file), lme4 (1 file), lmerTest (1 file), rstatix (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
10 files
At the source: osf.io/svyma/

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 10 scripts, each with its path and the digest of its content;
  • 4 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data and code availability

The behavioral data and code used for all analyses in this study are available at Open Science Framework: https://osf.io/svyma/. Any additional information required to reanalyze the data reported in this paper is available from the lead contact upon request.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 3 authors, 3 keywords, 3 funders, 74 references.

Cite

This paper

Stuchlý, E., Bavard, S., & Gluth, S. (2026). Deciding to simulate: Cognitive mechanisms of predicting the decisions of others. iScience, 29(7), 116047. https://doi.org/10.1016/j.isci.2026.116047

BibTeX

@article{stuchly2026deciding,
author = {Stuchlý, Erik and Bavard, Sophie and Gluth, Sebastian},
title = {{Deciding to simulate: Cognitive mechanisms of predicting the decisions of others}},
journal = {iScience},
year = {2026},
month = jun,
volume = {29},
number = {7},
pages = {116047},
publisher = {Elsevier},
issn = {2589-0042},
doi = {10.1016/j.isci.2026.116047},
url = {https://doi.org/10.1016/j.isci.2026.116047},
pmid = {42389597},
pmcid = {PMC13319925}
}

RIS

TY - JOUR
AU - Stuchlý, Erik
AU - Bavard, Sophie
AU - Gluth, Sebastian
TI - Deciding to simulate: Cognitive mechanisms of predicting the decisions of others
T2 - iScience
J2 - iScience
PY - 2026
DA - 2026/06/24
VL - 29
IS - 7
SP - 116047
SN - 2589-0042
PB - Elsevier
DO - 10.1016/j.isci.2026.116047
UR - https://doi.org/10.1016/j.isci.2026.116047
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.isci.2026.116047",
"type": "article-journal",
"title": "Deciding to simulate: Cognitive mechanisms of predicting the decisions of others",
"container-title": "iScience",
"author": [
{
"family": "Stuchlý",
"given": "Erik"
},
{
"family": "Bavard",
"given": "Sophie"
},
{
"family": "Gluth",
"given": "Sebastian"
}
],
"container-title-short": "iScience",
"volume": "29",
"issue": "7",
"page": "116047",
"DOI": "10.1016/j.isci.2026.116047",
"PMID": "42389597",
"PMCID": "PMC13319925",
"ISSN": "2589-0042",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.isci.2026.116047",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
24
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.7554/elife.103846 [code]
Overt visual attention modulates decision-related signals in the frontal cortex.
Journal: eLife
In common: lmerTest, lme4, ggplot2, 1 other tool, cognitive, 7 references
[2] doi:10.1126/sciadv.aec9291 [code]
Computational mechanisms of perception in autism revealed using games inspired by rodent operant tasks.
Journal: Science advances
In common: afex, Stan, rstatix, 5 other tools, cognitive
[3] doi:10.1073/pnas.2603114123 [code]
The human hippocampus can pattern separate memories by meaning.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: afex, rstatix, easystats, 5 other tools, cognitive
[4] doi:10.1192/bjp.2026.10664 [code]
Early effects of a novel 5-HT&lt;sub&gt;4&lt;/sub&gt;R agonist (PF-04995274) and the SSRI citalopram on emotional cognition in unmedicated depression: RESTAND study.
Journal: The British journal of psychiatry : the journal of mental science
In common: afex, rstatix, easystats, 4 other tools, cognitive
[5] doi:10.1371/journal.pbio.3003979 [code]
Impaired midfrontal‑motor theta phase synchronization characterizes maladaptive motivational behavior in people with obsessive‑compulsive disorder.
Journal: PLoS biology
In common: afex, Stan, emmeans, 4 other tools
[6] doi:10.1371/journal.pone.0355165 [code]
Pupillary dynamics during hands-off L2 driving and transitions of control under high cognitive load.
Journal: PloS one
In common: Stan, easystats, emmeans, 4 other tools, cognitive
[7] doi:10.1016/j.isci.2026.116747 [code]
Age and loneliness relate to reduced trust learning and alterations in amygdala function.
Journal: iScience
In common: Stan, easystats, emmeans, 4 other tools, cognitive
[8] doi:10.1162/imag.a.1313 [code]
Functional specialization of angular gyrus and precuneus subregions for perspective-guided autobiographical memory retrieval.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: afex, easystats, emmeans, 4 other tools, cognitive
[9] doi:10.1038/s41398-026-04141-z [code]
Effects on hippocampal activity following novel 5-HT4 receptor agonism in unmedicated patients with depression: the RESTAND study.
Journal: Translational psychiatry
In common: afex, rstatix, easystats, 4 other tools
[10] doi:10.1162/imag.a.1321 [code]
Phase similarity between similar objects indicates representational merging across retrieval training but not sleep.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: rstatix, easystats, emmeans, 3 other tools, cognitive, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.