OSCR

Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content.

Code ↔ Paper

23 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 23 matches
  1. [1] § Methods › Annotation of task events › Sensory and motor features ↔ shinobi_fmri/glm/utils.py, lines 565–621 · score 0.87 · canonical HRF, Optical flow, Audio envelope, task regressors, Button press, downsampled
  2. [2] § Methods › Statistical analyses › First-level (session) general linear model ↔ shinobi_fmri/glm/compute_session_level.py, lines 1–59 · score 0.85 · minimum cluster extent, noise model, fMRIPrep, AR, design matrix, FWHM
  3. [3] § Methods › Statistical analyses › Brain activation maps derived from HCP test-retest dataset ↔ shinobi_fmri/visualization/hcp_tasks.py, lines 35–44 · score 0.82 · left_foot, left_hand, right_foot, right_hand, cues, tongue
  4. [4] § Methods › Statistical analyses › Brain activation maps derived from HCP test-retest dataset ↔ notebooks/NB5_beta_correlations.ipynb, lines 117–128 · score 0.81 · left_foot, left_hand, right_foot, right_hand, cues, tongue
  5. [5] § Methods › Statistical analyses › First-level (session) general linear model ↔ shinobi_fmri/glm/utils.py, lines 721–793 · score 0.78 · global signal, fMRIPrep, design matrix, convolved, CSF, SPM
  6. [6] § Methods › Neuroimaging acquisitions › The shinobi dataset › At-home training ↔ shinobi_fmri/visualization/viz_training_comparison.py, lines 1–72 · score 0.73 · shinobi_training, home training, scanner sessions, weeks, metrics, behavioral
  7. [7] § Methods › Annotation of task events › Memory (RAM) mining ↔ notebooks/NB2_replay_visualizations.ipynb, lines 172–214 · score 0.66 · Replay bk2, gym retro, game variables
  8. [8] § Results › Within- and between-participant variability of brain maps compared to classical functional localizer tasks ↔ shinobi_fmri/visualization/viz_beta_correlations.py, lines 349–414 · score 0.64 · Optical flow, Audio envelope, Button press, HealthLoss, SD, Inter
  9. [9] § Results › Within- and between-participant variability of brain maps compared to classical functional localizer tasks ↔ shinobi_fmri/visualization/viz_beta_correlations.py, lines 349–414 · score 0.64 · Optical flow, Audio envelope, Button press, HealthLoss, SD, inter
  10. [10] § Methods › Statistical analyses › Second-level (participant) general linear model ↔ shinobi_fmri/glm/compute_session_level.py, lines 1–59 · score 0.62 · minimum cluster extent, nilearn, smoothing, uncorrected, FDR, voxel
  11. [11] § Results › GLM regressors had low to moderate correlations for most game events ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 465–517 · score 0.60 · optical flow, Audio envelope, button press, HealthLoss, luminance, regressors
  12. [12] § Results › Within- and between-participant variability of brain maps compared to classical functional localizer tasks ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 465–517 · score 0.60 · Optical flow, Audio envelope, Button press, HealthLoss, Luminance, regressors
  13. [13] § Methods › Statistical analyses › Second-level (participant) general linear model ↔ shinobi_fmri/glm/compute_subject_level.py, lines 1–66 · score 0.60 · minimum cluster extent, intercept, smoothing, uncorrected, FDR, voxel
  14. [14] § Methods › Annotation of task events › Data structure for events ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 93–235 · score 0.60 · annotated_events.tsv, button press, HealthLoss, onset, desc, Rows
  15. [15] § Results › Game events activation maps are discriminable ↔ tasks.py, lines 23–40 · score 0.60 · optical flow, audio envelope, button press, HealthLoss, luminance, errors
  16. [16] § Methods › Neuroimaging acquisitions › MRI preprocessing ↔ shinobi_fmri/glm/utils.py, lines 721–793 · score 0.56 · wm_csf, high_pass, motion, preprocessing, MRI
  17. [17] § Methods › Neuroimaging acquisitions › MRI preprocessing ↔ shinobi_fmri/visualization/viz_regressor_correlations.py, lines 93–235 · score 0.55 · wm_csf, high_pass, motion, preprocessing
  18. [18] § Methods › Neuroimaging acquisitions › The shinobi dataset › At-home training ↔ tasks.py, lines 1240–1275 · score 0.55 · home training, scanner sessions, metrics, behavioral, shinobi
  19. [19] § Methods › Annotation of task events › Data structure for events ↔ shinobi_fmri/descriptive/dataset_summary.py, lines 42–186 · score 0.55 · annotated_events.tsv, HealthLoss, fMRI, bk2, desc, Rows
  20. [20] § Methods › Behavioral acquisitions › Videogame interface ↔ shinobi_fmri/annotations/generate_train_json.py, lines 125–135 · score 0.55 · Ninja Master, Shinobi III, replays, variables, player, game
  21. [21] § Methods › Statistical analyses › MVPA classification ↔ shinobi_fmri/mvpa/compute_mvpa.py, lines 131–229 · score 0.54 · dummy classifier, confusion matrix, MVPA, Model
  22. [22] § Methods › Statistical analyses › MVPA classification ↔ shinobi_fmri/mvpa/compute_mvpa.py, lines 398–454 · score 0.52 · dummy classifier, confusion matrix, MVPA
  23. [23] § Methods › Statistical analyses › Beta maps correlations ↔ notebooks/NB5_beta_correlations.ipynb, lines 11–87 · score 0.52 · inter annotation, beta maps, ICC, correlations

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

Python · 854 lines · 32 KB · MIT · 4 matches

  1. """
  2. Generate design matrices and correlation visualization for GLM regressors.
  3. This script computes and visualizes design matrices and correlations between
  4. key regressors in the GLM. It produces:
  5. 1. Design matrices (one per run) showing all regressors
  6. 2. 2x2 correlation grid showing subject-averaged correlations
  7. The 2x2 grid focuses on Shinobi task conditions and low-level confounds
  8. (psychophysics: luminance, optical_flow, audio_envelope; and button press:
  9. button_presses_count) to identify multicollinearity between task events and
  10. low-level visual/audio features and motor activity.
  11. """
  12. import os
  13. import os.path as op
  14. import argparse
  15. import pickle
  16. import logging
  17. import re
  18. import warnings
  19. from typing import Any, Dict, List, Optional
  20. import numpy as np
  21. import pandas as pd
  22. import matplotlib.pyplot as plt
  23. import seaborn as sns
  24. from tqdm import tqdm
  25. from nilearn.glm.first_level import make_first_level_design_matrix
  26. from nilearn.signal import clean
  27. import nilearn.interfaces.fmriprep
  28. import shinobi_fmri.config as config
  29. from shinobi_fmri.utils.logger import AnalysisLogger
  30. from shinobi_fmri.glm.utils import add_psychophysics_confounds, add_button_press_confounds
  31. from shinobi_fmri.visualization.hcp_tasks import SHINOBI_COLOR
  32. # Suppress specific warnings
  33. warnings.filterwarnings('ignore', category=UserWarning, module='nilearn')
  34. warnings.filterwarnings('ignore', category=UserWarning, module='sklearn')
  35. warnings.filterwarnings('ignore', category=DeprecationWarning)
  36. def setup_argparse():
  37. """Set up command-line argument parser."""
  38. parser = argparse.ArgumentParser(
  39. description="Generate correlation matrices for design matrix regressors"
  40. )
  41. parser.add_argument(
  42. "-s", "--subject",
  43. default=None,
  44. type=str,
  45. help="Specific subject to process (default: all subjects)",
  46. )
  47. parser.add_argument(
  48. "--data-path",
  49. default=config.path_to_data,
  50. type=str,
  51. help=f"Path to data directory (default: {config.path_to_data})",
  52. )
  53. parser.add_argument(
  54. "--figures-path",
  55. default=config.figures_path,
  56. type=str,
  57. help=f"Path to figures output directory (default: {config.figures_path})",
  58. )
  59. parser.add_argument(
  60. "--skip-generation",
  61. action="store_true",
  62. help="Skip design matrix generation, only plot from existing pickle file",
  63. )
  64. parser.add_argument(
  65. "--exclude-low-level",
  66. action="store_true",
  67. default=False,
  68. help="Exclude low-level confounds (psychophysics and button presses) from design matrix (default: False, confounds are included)",
  69. )
  70. parser.add_argument(
  71. "-v", "--verbose",
  72. action="count",
  73. default=0,
  74. help="Increase verbosity level (e.g. -v for INFO, -vv for DEBUG)",
  75. )
  76. parser.add_argument(
  77. "--log-dir",
  78. default=None,
  79. help="Directory for log files",
  80. )
  81. return parser
  82. def process_single_run(sub, ses, run, path_to_data, figures_path, exclude_low_level=False):
  83. """Helper function to process a single run."""
  84. t_r = 1.49
  85. hrf_model = 'spm'
  86. try:
  87. # Zero-pad run number for events filename (run-01 format)
  88. run_padded = run.zfill(2)
  89. # Build file paths
  90. # Build file paths
  91. events_base_path = op.join(path_to_data, "shinobi", sub, ses, "func")
  92. annotated_events = op.join(
  93. events_base_path,
  94. f"{sub}_{ses}_task-shinobi_run-{run_padded}_desc-annotated_events.tsv"
  95. )
  96. standard_events = op.join(
  97. events_base_path,
  98. f"{sub}_{ses}_task-shinobi_run-{run_padded}_events.tsv"
  99. )
  100. if op.exists(annotated_events):
  101. events_fname = annotated_events
  102. elif op.exists(standard_events):
  103. events_fname = standard_events
  104. else:
  105. events_fname = annotated_events # Default for error reporting
  106. fmri_fname = op.join(
  107. path_to_data, "shinobi.fmriprep",
  108. sub, ses, "func",
  109. f"{sub}_{ses}_task-shinobi_run-{run}_space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz",
  110. )
  111. # Check if events file exists
  112. if not op.exists(events_fname):
  113. return {
  114. 'success': False,
  115. 'subject': sub,
  116. 'session': ses,
  117. 'run': run,
  118. 'error': f"Events file not found: {events_fname}"
  119. }
  120. # Load events
  121. run_events = pd.read_csv(events_fname, sep='\t', low_memory=False)
  122. # Load confounds using nilearn interface (returns tuple)
  123. try:
  124. confounds = nilearn.interfaces.fmriprep.load_confounds(
  125. fmri_fname,
  126. strategy=("motion", "high_pass", "wm_csf"),
  127. motion="full",
  128. wm_csf="basic",
  129. global_signal="full",
  130. )
  131. except Exception as e:
  132. return {
  133. 'success': False,
  134. 'subject': sub,
  135. 'session': ses,
  136. 'run': run,
  137. 'error': f"Could not load confounds (broken link or missing file): {str(e)}"
  138. }
  139. # Add low-level confounds unless excluded
  140. if not exclude_low_level:
  141. confounds = add_psychophysics_confounds(confounds, run_events, path_to_data, t_r=t_r)
  142. confounds = add_button_press_confounds(confounds, run_events, t_r=t_r)
  143. # Extract DataFrame from tuple
  144. confounds = confounds[0]
  145. # Generate design matrix
  146. n_slices = confounds.shape[0]
  147. frame_times = np.arange(n_slices) * t_r
  148. # Filter events to keep only conditions from config (excludes gym-retro_game, etc.)
  149. events_cols = ['onset', 'duration', 'trial_type']
  150. if 'modulation' in run_events.columns:
  151. events_cols.append('modulation')
  152. events_df_clean = run_events[events_cols].copy()
  153. # Keep only conditions defined in config.CONDITIONS
  154. events_df_clean = events_df_clean[events_df_clean['trial_type'].isin(config.CONDITIONS)]
  155. # Drop rows with NaN onset (can't model events without onset time)
  156. events_df_clean = events_df_clean.dropna(subset=['onset'])
  157. # Suppress nilearn warning about zero-duration events (Kill, HealthLoss are impulse events)
  158. with warnings.catch_warnings():
  159. warnings.filterwarnings('ignore', message='.*null duration.*')
  160. design_matrix_raw = make_first_level_design_matrix(
  161. frame_times,
  162. events=events_df_clean,
  163. drift_model=None,
  164. hrf_model=hrf_model,
  165. add_regs=confounds,
  166. add_reg_names=None
  167. )
  168. # Clean design matrix
  169. regressors_clean = clean(
  170. design_matrix_raw.to_numpy(),
  171. detrend=True,
  172. standardize='zscore_sample',
  173. high_pass=None,
  174. t_r=t_r,
  175. ensure_finite=True,
  176. confounds=None,
  177. )
  178. design_matrix_clean = pd.DataFrame(
  179. regressors_clean,
  180. columns=design_matrix_raw.columns.to_list()
  181. )
  182. # Save design matrix plot
  183. from nilearn import plotting as nlplot
  184. design_matrix_clean_fname = op.join(
  185. figures_path, "design_matrices",
  186. f"design_matrix_clean_{sub}_{ses}_run-{run_padded}.png",
  187. )
  188. os.makedirs(op.dirname(design_matrix_clean_fname), exist_ok=True)
  189. nlplot.plot_design_matrix(
  190. design_matrix_clean,
  191. output_file=design_matrix_clean_fname
  192. )
  193. return {
  194. 'success': True,
  195. 'regressors': design_matrix_clean,
  196. 'subject': sub,
  197. 'session': ses,
  198. 'run': run
  199. }
  200. except Exception as e:
  201. return {
  202. 'success': False,
  203. 'subject': sub,
  204. 'session': ses,
  205. 'run': run,
  206. 'error': str(e)
  207. }
  208. def build_design_matrices(subjects, path_to_data, figures_path, exclude_low_level=False, logger=None):
  209. """
  210. Build design matrices for all runs across specified subjects.
  211. Args:
  212. subjects: List of subject IDs to process
  213. path_to_data: Path to data directory
  214. figures_path: Path to figures directory
  215. exclude_low_level: Exclude low-level confounds (psychophysics and button presses)
  216. logger: AnalysisLogger instance
  217. Returns:
  218. dict: Dictionary containing design matrices, subjects, sessions, and runs
  219. """
  220. regressors_dict = {
  221. 'regressors': [],
  222. 'subject': [],
  223. 'session': [],
  224. 'run': []
  225. }
  226. # Collect all tasks to run
  227. tasks = []
  228. # Pre-scan for tasks
  229. for sub in subjects:
  230. fmriprep_sub_path = op.join(path_to_data, "shinobi.fmriprep", sub)
  231. if not op.exists(fmriprep_sub_path):
  232. if logger:
  233. logger.warning(f"Subject directory not found: {fmriprep_sub_path}")
  234. continue
  235. sessions = [d for d in os.listdir(fmriprep_sub_path)
  236. if d.startswith('ses-') and op.isdir(op.join(fmriprep_sub_path, d))]
  237. for ses in sorted(sessions):
  238. ses_fpath = op.join(path_to_data, "shinobi.fmriprep", sub, ses, "func")
  239. if not op.exists(ses_fpath):
  240. if logger:
  241. logger.warning(f"Func directory not found: {ses_fpath}")
  242. continue
  243. ses_files = os.listdir(ses_fpath)
  244. run_files = [
  245. x for x in ses_files
  246. if "space-MNI152NLin2009cAsym_desc-preproc_bold.nii.gz" in x
  247. ]
  248. # Extract run numbers
  249. run_list = []
  250. for fname in run_files:
  251. match = re.search(r'run-(\d+)', fname)
  252. if match:
  253. run_list.append(match.group(1))
  254. for run in sorted(run_list):
  255. tasks.append((sub, ses, run))
  256. # Process sequentially
  257. if logger:
  258. logger.info(f"Processing {len(tasks)} runs...")
  259. for sub, ses, run in tqdm(tasks, desc="Building design matrices"):
  260. res = process_single_run(
  261. sub, ses, run, path_to_data, figures_path, exclude_low_level
  262. )
  263. if res['success']:
  264. regressors_dict['regressors'].append(res['regressors'])
  265. regressors_dict['subject'].append(res['subject'])
  266. regressors_dict['session'].append(res['session'])
  267. regressors_dict['run'].append(res['run'])
  268. if logger:
  269. logger.summary.add_computed(f"Design matrix: {res['subject']} {res['session']} run {res['run']}")
  270. else:
  271. if logger:
  272. logger.error(f"Error processing {res['subject']} {res['session']} run {res['run']}: {res['error']}")
  273. logger.summary.add_error(f"{res['subject']} {res['session']} run {res['run']}", res['error'])
  274. else:
  275. print(f"Error processing {res['subject']} {res['session']} run {res['run']}: {res['error']}")
  276. return regressors_dict
  277. def plot_run_correlations(regressors_dict, figures_path, logger=None):
  278. """
  279. Plot correlation matrices for each run.
  280. Args:
  281. regressors_dict: Dictionary containing design matrices
  282. figures_path: Path to save figures
  283. logger: AnalysisLogger instance
  284. """
  285. output_dir = op.join(figures_path, 'design_matrices')
  286. os.makedirs(output_dir, exist_ok=True)
  287. regressors_dict['corr_mat'] = []
  288. pbar = tqdm(enumerate(regressors_dict['regressors']),
  289. total=len(regressors_dict['regressors']),
  290. desc="Plotting run correlations")
  291. for run_idx, run in pbar:
  292. sub = regressors_dict['subject'][run_idx]
  293. ses = regressors_dict['session'][run_idx]
  294. run_num = regressors_dict['run'][run_idx]
  295. if logger:
  296. logger.info(f"Plotting correlations for {sub} {ses} run {run_num}")
  297. # Compute correlation matrix (excluding constant)
  298. corr = regressors_dict['regressors'][run_idx].corr()
  299. if 'constant' in corr.index:
  300. corr = corr.drop(index='constant').drop(columns='constant')
  301. regressors_dict['corr_mat'].append(corr)
  302. mask = np.triu(np.ones_like(corr, dtype=bool))
  303. # Heatmap
  304. f, ax = plt.subplots(figsize=(30, 25))
  305. sns.heatmap(
  306. corr, mask=mask, center=0,
  307. square=True, linewidths=.5, annot=False,
  308. cbar_kws={"shrink": .5}
  309. ).set_title(f'{sub} {ses} run {run_num}')
  310. fig_fname = op.join(
  311. output_dir,
  312. f'regressor_correlations_{sub}_{ses}_run-{run_num}.png'
  313. )
  314. plt.savefig(fig_fname, dpi=100, bbox_inches='tight')
  315. plt.close()
  316. # Cluster map (only if no NaN/inf values)
  317. if np.all(np.isfinite(corr.values)):
  318. try:
  319. f = sns.clustermap(
  320. corr, mask=mask, figsize=(30, 25),
  321. cbar_kws={"shrink": .5}
  322. )
  323. fig_fname = op.join(
  324. output_dir,
  325. f'regressor_correlations_{sub}_{ses}_run-{run_num}_cluster.png'
  326. )
  327. f.savefig(fig_fname, dpi=100, bbox_inches='tight')
  328. plt.close()
  329. except ValueError as e:
  330. if logger:
  331. logger.warning(f"Could not create clustermap for {sub} {ses} run {run_num}: {str(e)}")
  332. if logger:
  333. logger.summary.add_computed(f"Run correlations: {sub} {ses} run {run_num}")
  334. pbar.set_postfix({"Subject": sub, "Session": ses, "Run": run_num})
  335. def plot_subject_averaged_correlations(regressors_dict, subjects, figures_path, exclude_low_level=False, logger=None):
  336. """
  337. Plot subject-averaged correlation matrices.
  338. Args:
  339. regressors_dict: Dictionary containing correlation matrices
  340. subjects: List of subjects to process
  341. figures_path: Path to save figures
  342. exclude_low_level: Exclude low-level confounds from design matrix
  343. logger: AnalysisLogger instance
  344. """
  345. output_dir = op.join(figures_path, 'design_matrices')
  346. os.makedirs(output_dir, exist_ok=True)
  347. # Add suffix to filename if low-level confounds are included
  348. suffix = "" if exclude_low_level else "_low-level"
  349. for sub in tqdm(subjects, desc="Plotting subject-averaged correlations"):
  350. if logger:
  351. logger.info(f"Plotting averaged correlations for {sub}")
  352. # Collect all correlation matrices for this subject
  353. subj_corrs = []
  354. for idx, corr_mat in enumerate(regressors_dict['corr_mat']):
  355. if regressors_dict['subject'][idx] == sub:
  356. subj_corrs.append(corr_mat)
  357. if not subj_corrs:
  358. if logger:
  359. logger.warning(f"No correlation matrices found for {sub}")
  360. continue
  361. # Average across runs
  362. averaged_corr_mat = pd.concat(subj_corrs, axis=0).groupby(level=0).mean()
  363. mask = np.triu(np.ones_like(averaged_corr_mat, dtype=bool))
  364. # Heatmap with annotations
  365. f, ax = plt.subplots(figsize=(30, 25))
  366. title = f'{sub} - Averaged across runs'
  367. if not exclude_low_level:
  368. title += ' (with low-level confounds)'
  369. sns.heatmap(
  370. averaged_corr_mat, mask=mask, center=0,
  371. square=True, linewidths=.5, annot=True,
  372. cbar_kws={"shrink": .5}, fmt='.2f'
  373. ).set_title(title)
  374. fig_fname = op.join(output_dir, f'regressor_correlations_{sub}{suffix}.png')
  375. plt.savefig(fig_fname, dpi=100, bbox_inches='tight')
  376. plt.close()
  377. # Cluster map (only if no NaN/inf values)
  378. if np.all(np.isfinite(averaged_corr_mat.values)):
  379. try:
  380. f = sns.clustermap(
  381. averaged_corr_mat, mask=mask, figsize=(30, 25),
  382. center=0, square=True, linewidths=.5, annot=True,
  383. cbar_kws={"shrink": .5}, fmt='.2f'
  384. )
  385. fig_fname = op.join(output_dir, f'regressor_correlations_{sub}_cluster{suffix}.png')
  386. f.savefig(fig_fname, dpi=100, bbox_inches='tight')
  387. plt.close()
  388. except ValueError as e:
  389. if logger:
  390. logger.warning(f"Could not create clustermap for {sub}: {str(e)}")
  391. else:
  392. if logger:
  393. logger.warning(f"Skipping clustermap for {sub} due to non-finite correlation values")
  394. if logger:
  395. logger.summary.add_computed(f"Subject-averaged correlations: {sub}")
  396. def plot_2x2_subject_correlations(regressors_dict, subjects, figures_path, exclude_low_level=False, logger=None):
  397. """
  398. Plot a 2x2 grid of subject-averaged correlation matrices.
  399. Creates a compact visualization showing the lower triangle of each subject's
  400. averaged correlation matrix in a 2x2 layout with a shared red-white-blue colorbar.
  401. Only includes 8 Shinobi task conditions (DOWN, HIT, HealthGain, HealthLoss,
  402. JUMP, Kill, LEFT, RIGHT), 3 psychophysics confounds (luminance,
  403. optical_flow, audio_envelope), and button press confounds (button_presses_count).
  404. Correlation values are displayed in each cell with 2 decimal places.
  405. Args:
  406. regressors_dict: Dictionary containing correlation matrices
  407. subjects: List of subjects to process (should be 4 subjects for 2x2 grid)
  408. figures_path: Path to save figures
  409. exclude_low_level: Exclude low-level confounds from design matrix
  410. logger: AnalysisLogger instance
  411. """
  412. output_dir = op.join(figures_path, 'design_matrices')
  413. os.makedirs(output_dir, exist_ok=True)
  414. # Add suffix to filename if low-level confounds are included
  415. suffix = "" if exclude_low_level else "_low-level"
  416. if logger:
  417. logger.info(f"Plotting 2x2 grid for {len(subjects)} subjects")
  418. # Define which regressors to include and their order
  419. # Match order from descriptive_annotations: RIGHT, LEFT, DOWN, HIT, JUMP, Kill, HealthLoss
  420. # Then add low-level features
  421. shinobi_conditions = ['RIGHT', 'LEFT', 'DOWN', 'HIT', 'JUMP', 'Kill', 'HealthLoss']
  422. # Psychophysics confounds (internal names)
  423. psychophysics_confounds = ['luminance', 'optical_flow', 'audio_envelope']
  424. # Button press confounds
  425. button_confounds = ['button_presses_count']
  426. # Combined list in desired order
  427. regressors_to_include = shinobi_conditions + psychophysics_confounds + button_confounds
  428. # Display names for cleaner labels (all should be displayed)
  429. display_names = {
  430. 'RIGHT': 'RIGHT',
  431. 'LEFT': 'LEFT',
  432. 'DOWN': 'DOWN',
  433. 'HIT': 'HIT',
  434. 'JUMP': 'JUMP',
  435. 'Kill': 'Kill',
  436. 'HealthLoss': 'HealthLoss',
  437. 'luminance': 'Luminance',
  438. 'optical_flow': 'Optical flow',
  439. 'audio_envelope': 'Audio envelope',
  440. 'button_presses_count': 'Button press'
  441. }
  442. # Prepare averaged correlation matrices for each subject
  443. subject_corr_mats = {}
  444. all_values = [] # To compute global vmin/vmax
  445. for sub in subjects:
  446. # Collect all correlation matrices for this subject
  447. subj_corrs = []
  448. for idx, corr_mat in enumerate(regressors_dict['corr_mat']):
  449. if regressors_dict['subject'][idx] == sub:
  450. subj_corrs.append(corr_mat)
  451. if not subj_corrs:
  452. if logger:
  453. logger.warning(f"No correlation matrices found for {sub}")
  454. continue
  455. # Average across runs
  456. averaged_corr_mat = pd.concat(subj_corrs, axis=0).groupby(level=0).mean()
  457. # Filter to keep only specified regressors (in the specified order)
  458. available_regressors = [r for r in regressors_to_include if r in averaged_corr_mat.index]
  459. if len(available_regressors) == 0:
  460. if logger:
  461. logger.warning(f"No target regressors found for {sub}")
  462. logger.warning(f"Available columns: {list(averaged_corr_mat.columns)}")
  463. continue
  464. # Reorder to match desired order
  465. averaged_corr_mat = averaged_corr_mat.loc[available_regressors, available_regressors]
  466. subject_corr_mats[sub] = averaged_corr_mat
  467. if logger:
  468. logger.info(f"{sub}: Found {len(available_regressors)} regressors: {available_regressors}")
  469. # Collect values from lower triangle for global color scale
  470. mask_lower = np.tril(np.ones_like(averaged_corr_mat, dtype=bool), k=-1)
  471. all_values.extend(averaged_corr_mat.values[mask_lower])
  472. if not subject_corr_mats:
  473. if logger:
  474. logger.error("No correlation matrices to plot")
  475. return
  476. # Compute and save pairwise correlation statistics table (all regressors including low-level)
  477. stats_df = compute_pairwise_correlation_stats(subject_corr_mats, regressors_to_include)
  478. save_correlation_stats_table(stats_df, config.TABLE_PATH, suffix, logger)
  479. # Print key pairs to console for quick manuscript reference
  480. key_pairs = [
  481. ('HIT', 'Kill'), ('DOWN', 'HIT'), ('DOWN', 'Kill'),
  482. ('JUMP', 'HIT'), ('RIGHT', 'LEFT'),
  483. ]
  484. print("\nFig 5 – Pairwise Pearson r between Shinobi conditions (mean across runs, per subject):")
  485. header = f"{'Pair':<25}" + "".join(f"{s:>12}" for s in subject_corr_mats.keys())
  486. header += f"{'mean_r':>10} {'range':>16}"
  487. print(header)
  488. for pair in stats_df.index:
  489. if pair in stats_df.index:
  490. row = stats_df.loc[pair]
  491. subj_vals = "".join(f"{row.get(s, float('nan')):>12.3f}" for s in subject_corr_mats.keys())
  492. label = f"{pair[0]}–{pair[1]}"
  493. print(f"{label:<25}{subj_vals}{row['mean_r']:>10.3f} [{row['min_r']:.3f}, {row['max_r']:.3f}]")
  494. # Compute global color scale
  495. vmin = np.min(all_values)
  496. vmax = np.max(all_values)
  497. abs_max = max(abs(vmin), abs(vmax))
  498. # Create 2x2 figure - compact layout
  499. # Top row x-labels will extend into empty upper triangle of bottom row
  500. fig, axes = plt.subplots(2, 2, figsize=(28, 26))
  501. fig.subplots_adjust(hspace=0.10, wspace=0.15)
  502. axes = axes.flatten()
  503. # Plot bottom row first (indices 2, 3), then top row (indices 0, 1)
  504. # This ensures top row x-labels are drawn on top of bottom row's empty space
  505. plot_order = [2, 3, 0, 1]
  506. # Plot each subject in a subplot
  507. for plot_idx in plot_order:
  508. sub_idx = plot_idx
  509. if sub_idx >= len(subjects[:4]):
  510. axes[plot_idx].set_visible(False)
  511. continue
  512. sub = subjects[sub_idx]
  513. ax = axes[plot_idx]
  514. # Set higher zorder for top row so their labels appear on top
  515. if plot_idx in [0, 1]:
  516. ax.set_zorder(10)
  517. if sub not in subject_corr_mats:
  518. ax.set_visible(False)
  519. continue
  520. corr_mat = subject_corr_mats[sub]
  521. # Create mask for upper triangle (including diagonal)
  522. mask = np.triu(np.ones_like(corr_mat, dtype=bool))
  523. # Plot heatmap with RdBu_r colormap (red-white-blue)
  524. sns.heatmap(
  525. corr_mat,
  526. mask=mask,
  527. center=0,
  528. vmin=-abs_max,
  529. vmax=abs_max,
  530. cmap='RdBu_r',
  531. square=True,
  532. linewidths=0.5,
  533. cbar=False, # We'll add a shared colorbar later
  534. ax=ax,
  535. xticklabels=True,
  536. yticklabels=True,
  537. annot=True, # Show correlation values in squares
  538. fmt='.2f', # Two decimal places
  539. annot_kws={'fontsize': 16} # Font size for annotations (doubled from 8)
  540. )
  541. # Make axes background transparent so top row x-labels show through
  542. # bottom row's masked (empty) upper triangle
  543. ax.set_facecolor('none')
  544. ax.patch.set_alpha(0)
  545. # Subtitle (subject name) - not bold, positioned near top of matrix
  546. ax.text(0.5, 0.87, f'{sub}', transform=ax.transAxes,
  547. fontsize=32, fontweight='normal', ha='center', va='top')
  548. # Get current tick labels and create display names list
  549. current_labels = [t.get_text() for t in ax.get_xticklabels()]
  550. display_labels = [display_names.get(lbl, lbl) for lbl in current_labels]
  551. # For lower triangle heatmap: hide first y-label and last x-label (diagonal cells)
  552. x_display = display_labels.copy()
  553. y_display = display_labels.copy()
  554. if len(x_display) > 0:
  555. x_display[-1] = '' # Last column has no visible data
  556. if len(y_display) > 0:
  557. y_display[0] = '' # First row has no visible data
  558. # Apply orange bold styling to all tick labels
  559. # ha='center' aligns rotated x-labels with their ticks
  560. ax.set_xticklabels(x_display, rotation=90, ha='center', va='top', fontsize=18,
  561. fontweight='bold', color=SHINOBI_COLOR)
  562. ax.set_yticklabels(y_display, rotation=0, ha='right', va='center', fontsize=18,
  563. fontweight='bold', color=SHINOBI_COLOR)
  564. # Ensure tick labels are not clipped by subplot boundaries
  565. ax.tick_params(axis='x', which='both', pad=2)
  566. ax.tick_params(axis='y', which='both', pad=2)
  567. for label in ax.get_xticklabels() + ax.get_yticklabels():
  568. label.set_clip_on(False)
  569. # Hide unused subplots if less than 4 subjects
  570. for idx in range(len(subjects[:4]), 4):
  571. axes[idx].set_visible(False)
  572. # Add shared colorbar on the right (size of one subplot, vertically centered)
  573. fig.subplots_adjust(right=0.92)
  574. # Calculate vertical center and height to match one subplot
  575. subplot_height = 0.35 # Approximate height of one subplot in figure coordinates
  576. vertical_center = 0.5 # Center of figure
  577. cbar_bottom = vertical_center - (subplot_height / 2)
  578. cbar_ax = fig.add_axes([0.94, cbar_bottom, 0.02, subplot_height])
  579. sm = plt.cm.ScalarMappable(
  580. cmap='RdBu_r',
  581. norm=plt.Normalize(vmin=-abs_max, vmax=abs_max)
  582. )
  583. sm.set_array([])
  584. cbar = fig.colorbar(sm, cax=cbar_ax)
  585. # Set colorbar tick label size (doubled from default)
  586. cbar.ax.tick_params(labelsize=20)
  587. # Add "Mean Pearson r" label on the right side of the colorbar (vertical)
  588. cbar.set_label('Mean Pearson r', fontsize=24, rotation=270, labelpad=30)
  589. # Save main panel directly to figures_path (not subdirectory)
  590. fig_fname = op.join(figures_path, f'regressor_correlations_panel{suffix}.png')
  591. plt.savefig(fig_fname, dpi=150, bbox_inches='tight')
  592. plt.close()
  593. if logger:
  594. logger.info(f"Saved 2x2 correlation plot to {fig_fname}")
  595. logger.summary.add_computed("2x2 subject correlation grid")
  596. def compute_pairwise_correlation_stats(
  597. subject_corr_mats: Dict[str, pd.DataFrame],
  598. conditions: List[str],
  599. ) -> pd.DataFrame:
  600. """
  601. Compute per-subject and summary statistics for all pairwise correlations
  602. between Shinobi conditions.
  603. For each unique condition pair (lower triangle), reports the per-subject
  604. mean Pearson r (already averaged across runs) and the range across subjects.
  605. Args:
  606. subject_corr_mats: Dict mapping subject ID -> run-averaged correlation DataFrame.
  607. conditions: Ordered list of condition names to include.
  608. Returns:
  609. DataFrame with rows = (condition_A, condition_B) pairs and
  610. columns = one column per subject + mean_r, min_r, max_r.
  611. """
  612. subjects = list(subject_corr_mats.keys())
  613. rows = []
  614. for i, cond_a in enumerate(conditions):
  615. for cond_b in conditions[i + 1:]:
  616. row: Dict[str, Any] = {'condition_A': cond_a, 'condition_B': cond_b}
  617. values = []
  618. for sub in subjects:
  619. mat = subject_corr_mats[sub]
  620. r = mat.loc[cond_a, cond_b] if (cond_a in mat.index and cond_b in mat.index) else np.nan
  621. row[sub] = round(float(r), 3) if not np.isnan(r) else np.nan
  622. if not np.isnan(r):
  623. values.append(r)
  624. row['mean_r'] = round(np.mean(values), 3) if values else np.nan
  625. row['min_r'] = round(np.min(values), 3) if values else np.nan
  626. row['max_r'] = round(np.max(values), 3) if values else np.nan
  627. rows.append(row)
  628. df = pd.DataFrame(rows).set_index(['condition_A', 'condition_B'])
  629. return df
  630. def save_correlation_stats_table(
  631. stats_df: pd.DataFrame,
  632. output_dir: str,
  633. suffix: str,
  634. logger: Optional[Any] = None,
  635. ) -> List[str]:
  636. """
  637. Save pairwise correlation statistics as CSV and LaTeX.
  638. Files written:
  639. - fig5_pairwise_correlation_stats{suffix}.csv
  640. - fig5_pairwise_correlation_stats{suffix}.tex
  641. Args:
  642. stats_df: Output of compute_pairwise_correlation_stats().
  643. output_dir: Directory to save tables.
  644. suffix: Filename suffix (e.g. '' or '_low-level').
  645. logger: AnalysisLogger instance.
  646. Returns:
  647. List of saved file paths.
  648. """
  649. os.makedirs(output_dir, exist_ok=True)
  650. stem = f"fig5_pairwise_correlation_stats{suffix}"
  651. csv_path = op.join(output_dir, f"{stem}.csv")
  652. tex_path = op.join(output_dir, f"{stem}.tex")
  653. stats_df.to_csv(csv_path)
  654. stats_df.to_latex(tex_path, float_format="%.3f", na_rep="—")
  655. if logger:
  656. logger.info(f"Pairwise correlation stats saved to {csv_path}")
  657. return [csv_path, tex_path]
  658. def main():
  659. """Main execution function."""
  660. parser = setup_argparse()
  661. args = parser.parse_args()
  662. # Setup logging
  663. verbosity_map = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG}
  664. verbosity_level = verbosity_map.get(args.verbose, logging.DEBUG)
  665. logger = AnalysisLogger(
  666. log_name="regressor_correlations",
  667. verbosity=verbosity_level,
  668. log_dir=args.log_dir
  669. )
  670. # Configuration
  671. figures_path = args.figures_path
  672. path_to_data = args.data_path
  673. subjects = [args.subject] if args.subject else config.subjects
  674. logger.info(f"Processing subjects: {subjects}")
  675. logger.info(f"Data path: {path_to_data}")
  676. logger.info(f"Figures path: {figures_path}")
  677. # Path for pickle file
  678. regressors_dict_fname = op.join(
  679. path_to_data, "processed", "regressors_dict.pkl"
  680. )
  681. # Build or load design matrices
  682. if args.skip_generation and op.exists(regressors_dict_fname):
  683. logger.info(f"Loading existing regressors from {regressors_dict_fname}")
  684. with open(regressors_dict_fname, "rb") as f:
  685. regressors_dict = pickle.load(f)
  686. logger.summary.add_skipped("Design matrix generation (loaded from pickle)")
  687. else:
  688. logger.info("Building design matrices...")
  689. if not args.exclude_low_level:
  690. logger.info("Including low-level confounds (psychophysics and button presses)")
  691. regressors_dict = build_design_matrices(
  692. subjects, path_to_data, figures_path,
  693. exclude_low_level=args.exclude_low_level,
  694. logger=logger
  695. )
  696. # Save to pickle
  697. os.makedirs(op.dirname(regressors_dict_fname), exist_ok=True)
  698. logger.info(f"Saving regressors to {regressors_dict_fname}")
  699. with open(regressors_dict_fname, "wb") as f:
  700. pickle.dump(regressors_dict, f)
  701. # Compute correlation matrices (needed for 2x2 plot)
  702. logger.info("Computing correlation matrices...")
  703. regressors_dict['corr_mat'] = []
  704. for run_idx, run in enumerate(regressors_dict['regressors']):
  705. # Compute correlation matrix (excluding constant)
  706. corr = run.corr()
  707. if 'constant' in corr.index:
  708. corr = corr.drop(index='constant').drop(columns='constant')
  709. regressors_dict['corr_mat'].append(corr)
  710. # Generate 2x2 subject correlation grid (Shinobi conditions + 3 psychophysics confounds only)
  711. logger.info("Plotting 2x2 subject correlation grid...")
  712. plot_2x2_subject_correlations(regressors_dict, subjects, figures_path, exclude_low_level=args.exclude_low_level, logger=logger)
  713. logger.info("Done!")
  714. logger.close()
  715. if __name__ == "__main__":
  716. main()

viz_regressor_correlations.py at commit 0f205fc, under MIT · at the source

Overview

  1. Department of Psychology, University of Montréal, Montréal, Canada
  2. Centre de Recherche de l’Institut Universitaire de Gériatrie de Montréal (CRIUGM), Montréal, Canada
  3. Swiss National Centre of Competence in Research, University of Geneva, Geneva, Switzerland
  4. IMT Atlantique, Brest, France
  5. Mila – Quebec AI Institute, Montréal, Canada
  6. UNIQUE – Union Neurosciences et Intelligence Artificielle – Québec, Québec, Canada
Journal: Imaging neuroscience (Cambridge, Mass.), volume 4, article IMAG.a.1256
Dates: received 11 June 2025; accepted 24 April 2026; published online 1 June 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1162/imag.a.1256 · PMID 42238756 · PMCID PMC13228873 · OpenAlex W4388002190
Open access: diamond, a free copy (OpenAlex)
Status: code verified
Categories: fMRI (modality), human (organism)
Methods: Connectivity, Statistics, Machine learning, Preprocessing, fMRI & imaging
Keywords: naturalistic neuroimaging, video games, automated annotation, fMRI, cognitive neuroscience
Topic: Educational Games and Gamification (Developmental and Educational Psychology, Psychology), according to OpenAlex
Citations: cited by 1 paper (Europe PMC); 101 references in the paper

Abstract

In recent years, videogames have gathered interest in cognitive neuroscience for their potential to study cognition in dynamical and naturalistic contexts. Yet, the complexity of game environments often challenges traditional modeling approaches, and current annotation methods—typically manual or based on modified games—remain labor-intensive and limited in scope. Here, we introduce a flexible and scalable framework using the gym-retro Python library to emulate a classic action-platformer, Shinobi III: Return of the Ninja Master, and automatically annotate gameplay events directly from the game’s memory states. This setup enables the identification of both player actions (e.g., jumping, hitting) and feedback events (e.g., killing an enemy, being hit), without modifying the game. Four individuals played the videogame for a combined total of 32 h (>7 h each) while undergoing functional magnetic resonance imaging (fMRI). Resulting activation maps revealed distributed engagement of visual, motor, executive, and limbic systems, consistent with the cognitive demands of gameplay. Within-participant reproducibility of brain responses across sessions was robust across event types (r ≈ .25–.55), with some consistency observed even for rarer events like HealthLoss. Between-participant correlations were notably lower, reflecting participant-specific neural signatures. Multivoxel pattern analysis showed that brain responses to different in-game events were highly discriminable, with classification accuracy typically around or above 90%, though occasionally dropping to ~40% for less frequent events. These findings demonstrate that automated emulator-based annotations enable robust, interpretable, and scalable mapping of naturalistic cognitive processes using commercial videogames.

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

Repositories

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

courtois-neuromod/shinobi_fmri

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 0f205fc994d0625c45e9d38c518fea009a589f5f, 15 April 2026
Languages: Python (48), Shell (11), Jupyter (8)
Size: 89 files, 67 scripts
Software Heritage: not archived
Found in: “Data and Code Availability”
Holds: README, license file, environment (requirements.txt, setup.py), tests, documentation, 8 notebooks
Not found: CITATION.cff, continuous integration
Tools: NumPy (34 files), Matplotlib (25 files), pandas (25 files), seaborn (18 files), NiBabel (15 files), Nilearn (15 files), SciPy (9 files), Pillow (4 files), scikit-learn (3 files), PyTorch (1 file), statannotations (1 file), statsmodels (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
69 files

Zenodo 15628556

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data and Code Availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Matplotlib (21 files), NumPy (20 files), pandas (17 files), Nilearn (14 files), NiBabel (13 files), seaborn (9 files), SciPy (4 files), Pillow (2 files), scikit-learn (2 files), DataLad (1 file), PyTorch (1 file), statannotations (1 file), statsmodels (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
55 files

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:

  • 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 120 scripts, each with its path and the digest of its content;
  • 23 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

Datasets cited

Data and Code Availability

All datasets used in this study are part of the CNeuroMod dataset (https://www.cneuromod.ca/). The code developed for this research is publicly available on GitHub (https://github.com/courtois-neuromod/shinobi_fmri) and archived on Zenodo for long-term preservation (https://doi.org/10.5281/zenodo.15628556). Processed datasets used to generate the tables and figures in this manuscript are provided as Supplementary Material (https://doi.org/10.1162/IMAG.a.1256#supplementary-data) accompanying the paper.

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, pages, dates, 9 authors, 5 keywords, 4 funders, 84 references.

Cite

This paper

Harel, Y., Pinsard, B., Boyle, J., Borghesani, V., Le Clei, M., Mignot, P.-H., St-Laurent, M., Jerbi, K., & Bellec, L. (2026). Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1256. https://doi.org/10.1162/imag.a.1256

BibTeX

@article{harel2026gamer,
author = {Harel, Yann and Pinsard, Basile and Boyle, Julie and Borghesani, Valentina and Le Clei, Maximilien and Mignot, Paul-Henri and St-Laurent, Marie and Jerbi, Karim and Bellec, Lune},
title = {{Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = jun,
volume = {4},
pages = {IMAG.a.1256},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/imag.a.1256},
url = {https://doi.org/10.1162/imag.a.1256},
pmid = {42238756},
pmcid = {PMC13228873}
}

RIS

TY - JOUR
AU - Harel, Yann
AU - Pinsard, Basile
AU - Boyle, Julie
AU - Borghesani, Valentina
AU - Le Clei, Maximilien
AU - Mignot, Paul-Henri
AU - St-Laurent, Marie
AU - Jerbi, Karim
AU - Bellec, Lune
TI - Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/06/01
VL - 4
SP - IMAG.a.1256
SN - 2837-6056
PB - MIT Press
DO - 10.1162/imag.a.1256
UR - https://doi.org/10.1162/imag.a.1256
LA - en
ER -

CSL-JSON

{
"id": "10.1162/imag.a.1256",
"type": "article-journal",
"title": "Gamer in the scanner: Event-related analysis of fMRI activity during retro videogame play guided by automated annotations of game content",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Harel",
"given": "Yann"
},
{
"family": "Pinsard",
"given": "Basile"
},
{
"family": "Boyle",
"given": "Julie"
},
{
"family": "Borghesani",
"given": "Valentina"
},
{
"family": "Le Clei",
"given": "Maximilien"
},
{
"family": "Mignot",
"given": "Paul-Henri"
},
{
"family": "St-Laurent",
"given": "Marie"
},
{
"family": "Jerbi",
"given": "Karim"
},
{
"family": "Bellec",
"given": "Lune"
}
],
"container-title-short": "Imaging Neurosci (Camb)",
"volume": "4",
"page": "IMAG.a.1256",
"DOI": "10.1162/imag.a.1256",
"PMID": "42238756",
"PMCID": "PMC13228873",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://doi.org/10.1162/imag.a.1256",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
1
]
]
}
}

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.1162/imag.a.1286 [code]
Behavioral imitation with artificial neural networks leads to personalized models of brain dynamics during videogame play.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Nilearn, Pillow, statsmodels, 8 other tools, fMRI, 14 references
[2] doi:10.21203/rs.3.rs-9326213/v1 [code]
Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brain
Journal: Research Square (preprint)
In common: statannotations, Nilearn, Pillow, 8 other tools, fMRI, 2 references
[3] doi:10.1038/s42003-026-10169-0 [code]
Shared representations in brains and models reveal a two-route cortical organization during scene perception.
Journal: Communications biology
In common: Pillow, statsmodels, NiBabel, 7 other tools, 4 references
[4] doi:10.1162/imag.a.1309 [code]
Probing the content of semantic representations in body-selective regions.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Pillow, statsmodels, NiBabel, 7 other tools, 4 references
[5] doi:10.1162/nol.a.271 [code]
Compositional Complexity in Text and Images.
Journal: Neurobiology of language (Cambridge, Mass.)
In common: Nilearn, Pillow, NiBabel, 7 other tools, fMRI, 4 references
[6] doi:10.7554/elife.107933 [code]
Modality-agnostic decoding of vision and language from fMRI.
Journal: eLife
In common: Nilearn, Pillow, statsmodels, 8 other tools, fMRI, 2 references
[7] doi:10.64898/2026.08.18.26360725 [code]
Temporal pole blurring in hippocampal sclerosis reflects seizure-disrupted myelination
Journal: medRxiv (preprint)
In common: statannotations, Nilearn, Pillow, 9 other tools
[8] doi:10.1007/s00234-026-04103-8 [code]
Enhanced detection of subtle cortical abnormalities in focal epilepsy using 7 T MRI surface-based models and graph neural networks.
Journal: Neuroradiology
In common: statannotations, Nilearn, Pillow, 9 other tools
[9] doi:10.1038/s41597-026-07248-6 [code]
A large-scale fMRI dataset for vision-language semantic association.
Journal: Scientific data
In common: Nilearn, Pillow, NiBabel, 6 other tools, fMRI, 3 references
[10] doi:10.1162/imag.a.105 [code]
Right posterior theta reflects human parahippocampal phase resetting by salient cues during goal-directed navigation
Journal: n/a
In common: statannotations, Nilearn, statsmodels, 6 other tools, fMRI, 2 references

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.