OSCR

Same Sentences, Different Grammars, Different Brain Responses?: An MEG Study on Case and Agreement Encoding in Hindi and Nepali Split-Ergative Structures.

Code ↔ Paper

5 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 5 matches
  1. [1] § METHODS › Data Preprocessing ↔ scripts.zip/scripts/1_SgTrialRegressions_withMean_looseOrientation.py, lines 239–298 · score 0.87 · loose orientation, covariance matrices, inverse solution, warped, source space, vertices
  2. [2] § METHODS › Data Analysis ↔ scripts.zip/scripts/1_SgTrialRegressions_withMean_looseOrientation.py, lines 492–549 · score 0.57 · linear regression, verb stem cloze, epochs
  3. [3] § METHODS › Data Analysis ↔ scripts.zip/scripts/1_SgTrialRegressions_withMean_looseOrientation.py, lines 39–61 · score 0.54 · language network, sensor space, permutation, threshold, temporal, brain
  4. [4] § RESULTS › MEG Results › Subject epoch analyses ↔ scripts.zip/scripts/1_SgTrialRegressions_withMean_looseOrientation.py, lines 39–61 · score 0.51 · temporal lobe, Sensor space, post, thresholded, onset, brain
  5. [5] § METHODS › Data Analysis ↔ scripts.zip/scripts/1_SgTrialRegressions_withMean_looseOrientation.py, lines 111–176 · score 0.50 · 0–1000 ms, onset, space, language, Nepali, Hindi

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 · 1,336 lines · 47 KB · no license · 5 matches

  1. ### RUN THIS IN EELBRAIN
  2. import csv
  3. import mne
  4. import numpy as np
  5. import pandas as pd
  6. import os
  7. import csv
  8. from mne.stats import fdr_correction, linear_regression, spatio_temporal_cluster_1samp_test
  9. import pickle
  10. import scipy
  11. from matplotlib import rc
  12. import matplotlib.pyplot as plt
  13. from mpl_toolkits.axes_grid1 import make_axes_locatable
  14. import seaborn as sn
  15. from helpers import *
  16. rc('font',**{'family':'sans-serif','sans-serif':['Helvetica Neue']})
  17. rc('font',**{'family':'serif','serif':['Helvetica Neue']})
  18. rc('text', usetex=False)
  19. rePlot = False
  20. decim = 4 # 1000 Hz -> 250 Hz
  21. makeSTCs = False # load in fwd/cov/bem for each participant to make inv?
  22. SNR = 2.0 # for regressions; 3.0 for ANOVAs
  23. fixed = False # False for orientation free (=unsigned), True for fixed orientation (=signed)
  24. # Parameters for spatio-temporal cluster-based permutation test
  25. n_permutations = 10000
  26. pThresh = 0.05
  27. plotThresh = 0.10
  28. tail = 0
  29. searchTMin = int(000) # 200ms post-onset; units are MILISECONDS; 0 = minimal time
  30. searchTMax = int(1000) # 800ms post-onset; units are MILISECONDS; 1000 = maximal time
  31. title = date + ''
  32. formulas = [
  33. 'Case_SubjNP',
  34. #'Cloze_V_Morph',
  35. #'Case_ObjNP+VGen',
  36. ]
  37. spaces = [
  38. # 'sensor', # sensor-space analysis
  39. 'source_wholebrain', # whole-brain analysis
  40. #'source_bilat', # bilateral language network analyses
  41. #'source_temp' # bilateral temporal lobe analyses
  42. ]
  43. #'Cloze_V' <- just cloze manipulation
  44. #formulas = ['Cloze_V_Morph']
  45. # Construct the left and right hemisphere 'language network' search spaces for
  46. # exploratory analyses for case manipulations, and left/right temporal lobe
  47. # for more targeted N400 analyses of the cloze manipulation
  48. labels = mne.read_labels_from_annot('fsaverage', 'aparc')
  49. regions = ['bankssts', 'inferiorparietal', 'insula', 'lateralorbitofrontal', #'medialorbitofrontal',
  50. 'middletemporal', 'parsopercularis', 'parsorbitalis', 'parstriangularis', 'superiortemporal', 'supramarginal', 'temporalpole', 'transversetemporal']# 'inferiortemporal', 'fusiform', 'rostralmiddlefrontal', 'caudalmiddlefrontal']
  51. tempRegions = ['bankssts', 'superiortemporal', 'middletemporal', 'transversetemporal', 'inferiorparietal', 'supramarginal', 'temporalpole', 'parsopercularis', 'parsorbitalis', 'parstriangularis', 'insula']
  52. leftLngNet = mne.Label(hemi='lh')
  53. leftTemp = mne.Label(hemi='lh')
  54. leftLngNet.subject = 'fsaverage'
  55. leftTemp.subject = 'fsaverage'
  56. rightLngNet = mne.Label(hemi='rh')
  57. rightLngNet.subject = 'fsaverage'
  58. rightTemp = mne.Label(hemi='rh')
  59. rightTemp.subject = 'fsaverage'
  60. for region in regions:
  61. for label in labels:
  62. if region+'-lh' == label.name:
  63. leftLngNet += label
  64. elif region+'-rh' == label.name:
  65. rightLngNet += label
  66. for region in tempRegions:
  67. for label in labels:
  68. if region+'-lh' == label.name:
  69. leftTemp += label
  70. elif region+'-rh' == label.name:
  71. rightTemp += label
  72. bilatLngNet = mne.BiHemiLabel(lh=leftLngNet, rh=rightLngNet)
  73. bilatTemp = mne.BiHemiLabel(lh=leftTemp, rh=rightTemp)
  74. # These are the variables that are categorical (e.g. 1/0s) or
  75. # that are factorial as part of the initial experiment design
  76. catVars = ['UpcomingVGenF', 'GenF', 'ClozeF']
  77. factVars = ['ObjCF', 'SubjCF', 'InteractionF']
  78. # Each analysis is done on a 1000ms epoch
  79. #times = np.arange(0,1001,1) # 0-1000ms in 1ms increments; time scale for all plots
  80. times = np.arange(0,1000,4) # 0-1000ms in 1ms increments at 250Hz (4ms increments)
  81. print('Reading in fsaverage source space...')
  82. srcFName = os.path.join(subjects_dir, 'fsaverage', 'bem', 'fsaverage-ico-4-src.fif')
  83. fsave_src = mne.read_source_spaces(fname=srcFName)
  84. for language in ['Hindi', 'Nepali']:
  85. if language == 'Hindi':
  86. subjects = h_subjects
  87. regressorFname = 'Hindi_regressors_withCorpusFreq_z.csv'
  88. subExpName = 'HObjAgr'
  89. else:
  90. subjects = n_subjects
  91. regressorFname = 'Nepali_regressors_withCorpusFreq.csv'
  92. subExpName = 'NObjAgr'
  93. # Get the regressors;
  94. # these have been z-scored.
  95. # Cloze values are drawn from the norming study
  96. with open(regressorFname, 'r') as f:
  97. regressors = [i for i in csv.DictReader(f)]
  98. for formula in formulas:
  99. if formula == 'Case_SubjNP':
  100. model = ['Intercept', 'SubjCF', 'TrialNum', 'W1Length']#, 'W1Freq']
  101. tmin = -1.0 # Units are in SECONDS; 0 = object onset
  102. tmax = 0.0 # Units are in SECONDS; 0 = object onset
  103. elif formula == 'Case_ObjNP':
  104. model = ['Intercept', 'SubjCF', 'ObjCF', 'InteractionF', 'TrialNum', 'W2Length']#, 'W2Freq']
  105. tmin = 0.0 # Units are in SECONDS; 0 = object onset
  106. tmax = 1.0 # Units are in SECONDS; 0 = object onset
  107. elif formula == 'Case_ObjNP+VGen':
  108. if language == 'Hindi':
  109. model = ['Intercept', 'SubjCF', 'ObjCF', 'InteractionF', 'TrialNum', 'W2Length', 'UpcomingVGenF']#, 'W2Freq']
  110. else:
  111. model = ['Intercept', 'SubjCF', 'ObjCF', 'InteractionF', 'TrialNum', 'W2Length', 'GenF']#, 'W2Freq']
  112. tmin = 0.0 # Units are in SECONDS; 0 = object onset
  113. tmax = 1.0 # Units are in SECONDS; 0 = object onset
  114. elif formula == 'VGen_ObjNP':
  115. if language == 'Hindi':
  116. model = ['Intercept', 'UpcomingVGenF', 'TrialNum', 'W2Length']#, 'W2Freq']
  117. else:
  118. model = ['Intercept', 'GenF', 'TrialNum', 'W2Length']#, 'W2Freq']
  119. tmin = 0.0 # Units are in SECONDS; 0 = object onset
  120. tmax = 1.0 # Units are in SECONDS; 0 = object onset
  121. elif formula == 'Cloze_V':
  122. model = ['Intercept', 'ClozeF', 'TrialNum', 'W3Length']#, 'W3Freq']
  123. tmin = 1.0 # Units are in SECONDS; 0 = object onset
  124. tmax = 2.0 # Units are in SECONDS; 0 = object onset
  125. elif formula == 'Cloze_V_Morph':
  126. if language == 'Hindi':
  127. model = ['Intercept', 'ClozeF', 'TrialNum', 'W3Length', 'UpcomingVGenF', 'SubjCF']#, 'W3Freq']
  128. elif language == 'Nepali':
  129. model = ['Intercept', 'ClozeF', 'TrialNum', 'W3Length', 'GenF', 'SubjCF']#, 'W3Freq']
  130. tmin = 1.0 # Units are in SECONDS; 0 = object onset
  131. tmax = 2.0 # Units are in SECONDS; 0 = object onset
  132. # Check to see if we've already computed this participant's
  133. # betas or not; if we have, then we load those
  134. betas = dict()
  135. betas_src = dict()
  136. raw_conds = dict()
  137. raw_conds_src = dict()
  138. expConds = [
  139. # SubjC:
  140. 'ErgPerf', 'NomImpf',
  141. # ObjC:
  142. 'Acc', 'Bare',
  143. # SubjC x ObjC:
  144. 'ErgAcc', 'ErgBare', 'NomAcc', 'NomBare',
  145. # Cloze:
  146. 'LowCloze', 'HighCloze',
  147. # VGen:
  148. 'MVerb_Hindi', 'FVerb_Hindi',
  149. # VGen
  150. 'MVerb_Nepali', 'FVerb_Nepali']
  151. expCondFactors = {
  152. 'ErgPerf':['MHiErgBare', 'MHiErgAcc', 'MLoErgBare', 'MLoErgAcc', 'FHiErgBare', 'FHiErgAcc', 'FLoErgBare', 'FLoErgAcc'],
  153. 'NomImpf': ['MHiBareBare', 'MHiBareAcc', 'MLoBareBare', 'MLoBareAcc', 'FHiBareBare', 'FHiBareAcc', 'FLoBareBare', 'FLoBareAcc'],
  154. 'Acc' : ['MHiErgAcc', 'MHiBareAcc', 'MLoBareAcc', 'MLoErgAcc', 'FHiBareAcc', 'FHiErgAcc', 'FLoBareAcc', 'FLoErgAcc'],
  155. 'Bare': ['MHiErgBare', 'MHiBareBare', 'MLoBareBare', 'MLoErgBare', 'FHiBareBare', 'FHiErgBare', 'FLoBareBare', 'FLoErgBare'],
  156. 'NomAcc': ['MHiBareAcc', 'MLoBareAcc', 'FHiBareAcc', 'FLoBareAcc'],
  157. 'NomBare': ['MHiBareBare', 'MLoBareBare','FHiBareBare','FLoBareBare'],
  158. 'ErgAcc': ['MHiErgAcc', 'MLoErgAcc', 'FHiErgAcc', 'FLoErgAcc'],
  159. 'ErgBare': ['MHiErgBare', 'MLoErgBare', 'FHiErgBare', 'FLoErgBare'],
  160. 'LowCloze': ['MLoBareAcc', 'FLoBareAcc', 'MLoBareBare', 'FLoBareBare', 'MLoErgAcc', 'FLoErgAcc', 'MLoErgBare', 'FLoErgBare'],
  161. 'HighCloze': ['MHiBareAcc', 'FHiBareAcc', 'MHiBareBare', 'FHiBareBare', 'MHiErgAcc', 'FHiErgAcc', 'MHiErgBare', 'FHiErgBare'],
  162. 'MVerb_Nepali': ['MLoBareAcc', 'MLoBareBare', 'MLoErgAcc', 'MLoErgBare'],
  163. 'FVerb_Nepali': ['FLoBareAcc', 'FLoBareBare', 'FLoErgAcc', 'FLoErgBare'],
  164. 'MVerb_Hindi': ['MLoBareAcc', 'MLoBareBare', 'MLoErgAcc', 'FLoErgAcc', 'FLoErgBare'],
  165. 'FVerb_Hindi': ['FLoBareAcc', 'FLoBareBare', 'MLoErgBare']
  166. }
  167. for cond in expConds:
  168. raw_conds[cond] = []
  169. raw_conds_src[cond] = []
  170. for cond in model:
  171. betas[cond] = []
  172. betas_src[cond] = []
  173. for subject in subjects:
  174. indPlotFolder = os.path.join('/Volumes', 'falconlab', 'experiments', expName, 'plots', 'individuals', language, subject)
  175. if not os.path.exists(indPlotFolder):
  176. os.makedirs(indPlotFolder)
  177. MEGDir = os.path.join('/Volumes', 'falconlab', 'data', 'meg', subject, subExpName)
  178. for fName in os.listdir(MEGDir):
  179. if fName.endswith('retainedEpochs.csv'):
  180. retEposFname = fName
  181. STCDir = os.path.join('/Volumes', 'falconlab', 'data', 'stc', subject, expName)
  182. if not os.path.exists(STCDir):
  183. os.makedirs(STCDir)
  184. subjBetas = dict()
  185. subjBetas_src = dict()
  186. print('Reading in raw data for subject %s' %subject)
  187. epos = mne.read_epochs(os.path.join(MEGDir, '%s-ica-good-epo.fif' %(subject)))
  188. epos = epos.resample(sfreq=250)
  189. if makeSTCs:
  190. print('Calculating covariance matrix for subject %s' %subject)
  191. cov = mne.compute_covariance(epos,tmin=baseline[0],tmax=baseline[1], method=['shrunk', 'diagonal_fixed', 'empirical'])
  192. print('Reading in forward solution, trans file, source space, and morphing to fsaverage...')
  193. fwd_fname = os.path.join(MEGDir, subject+'-fwd.fif')
  194. trans = os.path.join(MEGDir, subject+'-trans.fif')
  195. # cov_fname = os.path.join(language, 'MEG', subject, subject+'-GrAvgFixed-cov.fif')
  196. print('Reading in source space...')
  197. src = mne.read_source_spaces(os.path.join(subjects_dir, subject, 'bem', subject+'-ico-4-src.fif'))
  198. print('Warping to fsaverage vertices...')
  199. morph = mne.compute_source_morph(src, subject_from=subject, subject_to='fsaverage', spacing=4, subjects_dir=subjects_dir)
  200. try:
  201. print('Reading in BEM solution...')
  202. # bemFName = os.path.join(subjects_dir, subject, 'bem', subject+'-5120-5120-5120-bem-sol.fif')
  203. bemFName = os.path.join(subjects_dir, subject, 'bem', subject+'-inner_skull-bem-sol.fif')
  204. bem = mne.read_bem_solution(bemFName)
  205. except:
  206. print("Can't read in BEM solution for subject %s, calculating it again..." %subject)
  207. conductivity = (0.3, #0.006, 0.3
  208. ) # for three layers layer
  209. bemModel = mne.make_bem_model(subject=subject, ico=4, conductivity=conductivity, subjects_dir=subjects_dir)
  210. bem = mne.make_bem_solution(bemModel)
  211. mne.write_bem_solution(bemFName, bem, overwrite=True)
  212. print('Reading in forward solution...')
  213. # if os.path.exists(fwd_fname):
  214. # fwd = mne.read_forward_solution(fwd_fname)
  215. # else:
  216. fwd = mne.make_forward_solution(info=epos.info,
  217. trans=trans, src=src, bem=bem,
  218. eeg=False,
  219. meg=True,
  220. # mag=True,
  221. ignore_ref=True)
  222. print('Calculating inverse solution...')
  223. inv = mne.minimum_norm.make_inverse_operator(epos.info, fwd, cov, depth=None, fixed=fixed, loose=0.20)
  224. print('Checking on STCs...')
  225. if rePlot:
  226. retEposFile = open(os.path.join(MEGDir, retEposFname),'r')
  227. retEpos = pd.DataFrame([i for i in csv.DictReader(retEposFile)])
  228. retEposFile.seek(0)
  229. retEposFile.close()
  230. totalObs = len(retEpos)
  231. totalAcc = len(retEpos.query("`Hit?` == 'Hit'")) / len(retEpos.query("`Hit?` != 'NaN'"))
  232. with open(os.path.join(indPlotFolder, 'behavioral.txt'), 'w') as f:
  233. f.write('Total epochs retained: ' + str(totalObs) + '\n')
  234. f.write('Total correct: ' + str(totalAcc))
  235. print('Replotting by-subject data for subject %s....' %subject)
  236. print('Plotting butterfly plot')
  237. fig = epos.average().plot(gfp=True, show=False)
  238. fig = tidyFig(fig)
  239. fig.savefig(os.path.join(indPlotFolder, 'butterflyPlot_REST.png'))
  240. evokedPlot = epos.average()
  241. whitePlot = mne.viz.plot_evoked_white(evokedPlot, cov, show=False)
  242. whitePlot.savefig(os.path.join(indPlotFolder, 'whitenedCov.png'))
  243. try:
  244. srcAlign = mne.viz.plot_alignment(epos.info, subject=subject, src=src, trans=trans,
  245. #trans = 'fsaverage',
  246. surfaces=dict(white=0.4, outer_skull=0.2, head=0.5),
  247. # ['head-dense', 'outer_skull', 'white'],
  248. fwd = fwd,
  249. bem = bem,
  250. # eeg = 'projected')
  251. # eeg=dict(original=0.0, projected = 0.5))
  252. )
  253. screenshot = srcAlign.plotter.screenshot()
  254. fig, ax = plt.subplots(figsize=(12,12))
  255. ax.imshow(screenshot, origin='upper')
  256. ax.set_axis_off() # Disable axis labels and ticks
  257. fig.tight_layout()
  258. fig.savefig(os.path.join(indPlotFolder, 'srcAlign.png'), dpi=300)
  259. except:
  260. srcAlign = mne.viz.plot_alignment(epos.info, subject=subject, src=src, trans=trans,
  261. #trans = 'fsaverage',
  262. surfaces=dict(white=0.4, head=0.5),
  263. # ['head-dense', 'outer_skull', 'white'],
  264. fwd = fwd,
  265. bem = bem,
  266. # eeg = 'projected')
  267. # eeg=dict(original=0.0, projected = 0.5))
  268. )
  269. screenshot = srcAlign.plotter.screenshot()
  270. fig, ax = plt.subplots(figsize=(12,12))
  271. ax.imshow(screenshot, origin='upper')
  272. ax.set_axis_off() # Disable axis labels and ticks
  273. fig.tight_layout()
  274. fig.savefig(os.path.join(indPlotFolder, 'srcAlign.png'), dpi=300)
  275. cov = mne.compute_covariance(epos,tmin=baseline[0],tmax=baseline[1], method=['shrunk', 'diagonal_fixed', 'empirical'])
  276. epos.crop(tmin=tmin, tmax=tmax)
  277. epos.pick(picks=['meg'])
  278. for cond in expConds:
  279. if language == 'Hindi' and 'Nepali' in cond:
  280. pass
  281. elif language == 'Nepali' and 'Hindi' in cond:
  282. pass
  283. else:
  284. stcFName = os.path.join(STCDir, language + '_' + cond + '_' + str(int(tmin)) + '-' + str(int(tmax)) + '_src_raw_loose_250Hz')
  285. if os.path.exists(stcFName+'-lh.stc'):
  286. print('STC for %s trials for subject %s in time window %s - %s already exists!' %(cond, subject, str(int(tmin)), str(int(tmax))))
  287. stc = mne.read_source_estimate(stcFName)
  288. elif makeSTCs:
  289. print('Calculating STC for %s trials for subject %s in time window %s - %s' %(cond, subject, str(int(tmin)), str(int(tmax))))
  290. stc = morph.apply(mne.minimum_norm.apply_inverse(epos[expCondFactors[cond]].average(), inv, lambda2 = 1.0 / 3.0 ** 2.0, method='dSPM'))
  291. stc.save(stcFName)
  292. raw_conds_src[cond].append(stc)
  293. print('Averaging raw sensor + src space data by key comparison in experiment design')
  294. for cond in expConds:
  295. if language == 'Hindi' and 'Nepali' in cond:
  296. pass
  297. elif language == 'Nepali' and 'Hindi' in cond:
  298. pass
  299. else:
  300. raw_conds[cond].append(epos[expCondFactors[cond]].average())
  301. if os.path.exists(os.path.join(STCDir, language+'_'+formula+'_sns_250Hz_signed.pickle')) and os.path.exists(os.path.join(STCDir, language+'_'+formula+'_src_250Hz_loose.pickle')):
  302. print('**********Loading betas for subject %s' %subject)
  303. with open(os.path.join(STCDir, language+'_'+formula+'_sns_250Hz_signed.pickle'),'rb') as f:
  304. subjBetas = pickle.load(f)
  305. with open(os.path.join(STCDir, language+'_'+formula+'_src_250Hz_loose.pickle'),'rb') as f:
  306. subjBetas_src = pickle.load(f)
  307. else:
  308. MEGDir = os.path.join('/Volumes', 'falconlab', 'data', 'meg', subject, subExpName)
  309. retEposFile = open(os.path.join(MEGDir, retEposFname),'r')
  310. retEpos = pd.DataFrame([i for i in csv.DictReader(retEposFile)])
  311. retEposFile.seek(0)
  312. retEposFile.close()
  313. totalObs = len(retEpos)
  314. totalAcc = len(retEpos.query("`Hit?` == 'Hit'")) / len(retEpos.query("`Hit?` != 'NaN'"))
  315. with open(os.path.join(indPlotFolder, 'behavioral.txt'), 'w') as f:
  316. f.write('Total epochs retained: ' + str(totalObs) + '\n')
  317. f.write('Total correct: ' + str(totalAcc))
  318. evokedPlot = epos.average()
  319. whitePlot = mne.viz.plot_evoked_white(evokedPlot, cov, show=False)
  320. whitePlot.savefig(os.path.join(indPlotFolder, 'whitenedCov.png'))
  321. print('Plotting butterfly plot')
  322. fig = epos.average().plot(gfp=True, show=False)
  323. fig = tidyFig(fig)
  324. fig.savefig(os.path.join(indPlotFolder, 'butterflyPlot_REST.png'))
  325. # Initialize the parameters that we're including as regressors
  326. epos.metadata['W1Freq'] = 0
  327. epos.metadata['W2Freq'] = 0
  328. epos.metadata['W3Freq'] = 0
  329. epos.metadata['W1Length'] = 0
  330. epos.metadata['W2Length'] = 0
  331. epos.metadata['W3Length'] = 0
  332. epos.metadata['VStemCloze'] = 0
  333. epos.metadata['GenF'] = 0 # 0 is for MF
  334. epos.metadata['SubjCF'] = 0 # Nom; 1 = Erg
  335. epos.metadata['ObjCF'] = 0 # Acc; 1 = Bare
  336. epos.metadata['ClozeF'] = 0 # Hi; 1 = Lo
  337. epos.metadata['InteractionF'] = 0 # For all conditions except for Erg:Bare; 1 = Erg:Bare
  338. epos.metadata['UpcomingVGenF'] = 0 # For masculine
  339. # Assign an intercept
  340. epos.metadata['Intercept'] = 1
  341. for i, row in epos.metadata.iterrows():
  342. # I didn't include the itemset number in
  343. # the log files, so we need to reconstruct this
  344. # by looking at the individual words
  345. w1 = row['W1']
  346. w2 = row['W2']
  347. w3 = row['W3']
  348. epos.metadata.at[i, 'W1Length'] = len(w1)
  349. epos.metadata.at[i, 'W2Length'] = len(w2)
  350. epos.metadata.at[i, 'W3Length'] = len(w3)
  351. if row['Gen'] == 'FM' or row['Gen'] == 'F':
  352. epos.metadata.at[i, 'GenF'] = 1
  353. if row['SubjC'] == 'Erg':
  354. epos.metadata.at[i, 'SubjCF'] = 1
  355. if row['ObjC'] == 'Bare':
  356. epos.metadata.at[i, 'ObjCF'] = 1
  357. if row['SubjC'] == 'Erg' and row['ObjC'] == 'Bare':
  358. epos.metadata.at[i, 'InteractionF'] = 1
  359. if row['SubjC'] == 'Bare' and row['Gen'] == 'FM':
  360. epos.metadata.at[i, 'UpcomingVGenF'] = 1
  361. elif row['SubjC'] == 'Erg' and row['ObjC'] == 'Bare' and row['Gen'] == 'MF':
  362. epos.metadata.at[i, 'UpcomingVGenF'] = 1
  363. if row['Cloze'] == 'Lo':
  364. epos.metadata.at[i, 'ClozeF'] = 1
  365. # The regressors file stores the low vs. high cloze
  366. # verb stems in different columns, so this helps
  367. # pick the right value
  368. clozeCond = row['Cloze']
  369. foundRegressors = False
  370. for x in regressors:
  371. if not foundRegressors:
  372. if w1 == x['W1'] and w2 == x['W2'] and w3 == x['W3'] and clozeCond == x['Cloze']:
  373. epos.metadata.at[i, 'W1Freq'] = float(x['W1Freq'])
  374. epos.metadata.at[i, 'W2Freq'] = float(x['W2Freq'])
  375. epos.metadata.at[i, 'W3Freq'] = float(x['W3Freq'])
  376. if clozeCond == 'Lo':
  377. epos.metadata.at[i, 'VStemCloze'] = float(x['LoP'])
  378. elif clozeCond == 'Hi':
  379. epos.metadata.at[i, 'VStemCloze'] = float(x['HiP'])
  380. else:
  381. print("Couldn't find regressor")
  382. epos.metadata['TrialNum'] = epos.metadata['TrialNum'].astype(float)
  383. try:
  384. srcAlign = mne.viz.plot_alignment(epos.info, subject=subject, src=src, trans=trans,
  385. #trans = 'fsaverage',
  386. surfaces=dict(white=0.4, outer_skull=0.2, head=0.5),
  387. # ['head-dense', 'outer_skull', 'white'],
  388. fwd = fwd,
  389. bem = bem,
  390. # eeg = 'projected')
  391. # eeg=dict(original=0.0, projected = 0.5))
  392. )
  393. screenshot = srcAlign.plotter.screenshot()
  394. fig, ax = plt.subplots(figsize=(12,12))
  395. ax.imshow(screenshot, origin='upper')
  396. ax.set_axis_off() # Disable axis labels and ticks
  397. fig.tight_layout()
  398. fig.savefig(os.path.join(indPlotFolder, 'srcAlign.png'), dpi=300)
  399. except:
  400. print("Can't plot source alignment")
  401. res = linear_regression(epos.apply_function(abs), epos.metadata[model], names=model)
  402. for cond in model:
  403. if cond not in subjBetas:
  404. subjBetas[cond] = list()
  405. subjBetas_src[cond] = list()
  406. subjBetas[cond].append(res[cond].beta)
  407. print('Applying inverse solution...')
  408. if makeSTCs:
  409. stcs = mne.minimum_norm.apply_inverse_epochs(epos, inv, lambda2 = 1.0 / SNR ** 2.0, method = 'dSPM', return_generator=True)
  410. res = linear_regression(stcs, epos.metadata[model], names=model)
  411. for cond in model:
  412. newStc = morph.apply(res[cond].beta)
  413. subjBetas_src[cond].append(newStc)
  414. print('Saving betas...')
  415. with open(os.path.join(STCDir, language+'_'+formula+'_sns_250Hz_signed.pickle'), 'wb') as f:
  416. pickle.dump(subjBetas, f)
  417. if makeSTCs:
  418. with open(os.path.join(STCDir, language+'_'+formula+'_src_250Hz_loose.pickle'), 'wb') as f:
  419. pickle.dump(subjBetas_src, f)
  420. print('Assigning subject betas to group-level betas...')
  421. for cond in model:
  422. betas[cond] += subjBetas[cond]
  423. betas_src[cond] += subjBetas_src[cond]
  424. print('Setting up for spatio-temporal analyses...')
  425. try:
  426. src_adjacency = mne.spatial_src_adjacency(src[:1])
  427. src_adjacency_whole = mne.spatial_src_adjacency(src)
  428. except:
  429. srcFName = os.path.join(subjects_dir, 'fsaverage', 'bem', 'fsaverage-ico-4-src.fif')
  430. src = mne.read_source_spaces(fname=srcFName)
  431. src_adjacency = mne.spatial_src_adjacency(fsave_src[:1])
  432. src_adjacency_whole = mne.spatial_src_adjacency(fsave_src)
  433. sns_adjacency, ch_names = mne.channels.find_ch_adjacency(betas['Intercept'][0].info, 'mag')
  434. print('Plotting data...')
  435. for space in spaces:
  436. note = '-' + str(searchTMin) + '-' + str(searchTMax) + 'ms' + '_' + space + '_p' + str(pThresh)
  437. groupPlotFolder = os.path.join('/Volumes', 'falconlab', 'experiments', expName, 'stats', language, formula + '_N=' + str(len(subjects)) + note)
  438. # groupPlotFolder = os.path.join(title, language, formula)
  439. if not os.path.exists(groupPlotFolder):
  440. os.makedirs(groupPlotFolder)
  441. for cond in model:
  442. if cond != 'Intercept' and cond != 'TrialNum' and 'Length' not in cond:
  443. # plot key ROIs...
  444. if space == 'sensor':
  445. adjacency = sns_adjacency
  446. searchSpace = 'allSensors'
  447. searchSpace_Ixs = np.arange(0,208,1)
  448. combined = mne.combine_evoked(betas[cond], weights='equal')
  449. tmp = combined.plot(show=False)
  450. tmp.axes[0].set_title(cond)
  451. tmp.figure.set_size_inches(6,3)
  452. tmp.savefig(os.path.join(groupPlotFolder, '%s_%s_Plot_%s' %(language, formula, cond)), dpi=300)
  453. elif space == 'source_lh' or space == 'source_rh' or space == 'source_wholebrain':
  454. # searchSpace_Ixs = [np.arange(0,2562,1), np.arange(0,2562,1)]
  455. searchSpace_Ixs = np.arange(0,2562,1)
  456. cortex = [(0.8, 0.8, 0.8), (0.55, 0.55, 0.55)]
  457. adjacency = src_adjacency
  458. searchSpace = 'wholebrain'
  459. elif space == 'source_ltemp':
  460. searchSpace = leftTemp
  461. searchSpace_Ixs = leftTemp.get_vertices_used(np.arange(0,2562,1))
  462. cortex = [(0.3, 0.3, 0.3), (0.0, 0.0, 0.0)]
  463. elif space == 'source_rtemp':
  464. searchSpace = rightTemp
  465. searchSpace_Ixs = rightTemp.get_vertices_used(np.arange(0,2562,1))
  466. cortex = [(0.3, 0.3, 0.3), (0.0, 0.0, 0.0)]
  467. elif space == 'source_lhlgnet':
  468. searchSpace = leftLngNet
  469. searchSpace_Ixs = leftLngNet.get_vertices_used(np.arange(0,2562,1))
  470. cortex = [(0.3, 0.3, 0.3), (0.0, 0.0, 0.0)]
  471. elif space == 'source_rhlgnet':
  472. searchSpace = rightLngNet
  473. searchSpace_Ixs = rightLngNet.get_vertices_used(np.arange(0,2562,1))
  474. cortex = [(0.3, 0.3, 0.3), (0.0, 0.0, 0.0)]
  475. elif space == 'source_bilat':
  476. searchSpace = bilatLngNet
  477. searchSpace_Ixs_Lh = bilatLngNet.lh.get_vertices_used(np.arange(0,2562,1))
  478. searchSpace_Ixs_Rh = bilatLngNet.rh.get_vertices_used(np.arange(0,2562,1))
  479. cortex = [(0.3, 0.3, 0.3), (0.0, 0.0, 0.0)]
  480. searchSpace_Ixs = np.concatenate([searchSpace_Ixs_Lh, searchSpace_Ixs_Rh+2562])
  481. elif space == 'source_temp':
  482. searchSpace = bilatTemp
  483. searchSpace_Ixs_Lh = bilatTemp.lh.get_vertices_used(np.arange(0,2562,1))
  484. searchSpace_Ixs_Rh = bilatTemp.rh.get_vertices_used(np.arange(0,2562,1))
  485. cortex = [(0.3, 0.3, 0.3), (0.0, 0.0, 0.0)]
  486. searchSpace_Ixs = np.concatenate([searchSpace_Ixs_Lh, searchSpace_Ixs_Rh+2562])
  487. if space == 'source_lh' or space == 'source_lhlgnet' or space == 'source_ltemp':
  488. hemi = 'lh'
  489. searchSpace_Ixs_List = [searchSpace_Ixs, []]
  490. elif space == 'source_rh' or space == 'source_rhlgnet' or space == 'source_rtemp':
  491. hemi = 'rh'
  492. searchSpace_Ixs_List = [[], searchSpace_Ixs]
  493. elif space == 'source_wholebrain':
  494. searchSpace_Ixs_Lh = np.arange(0,2562,1)
  495. searchSpace_Ixs_Rh = np.arange(0,2562,1)
  496. elif space == 'source_bilat' or space == 'source_wholebrain' or space == 'source_temp':
  497. searchSpace_Ixs_List = [searchSpace_Ixs_Lh, searchSpace_Ixs_Rh]
  498. if space == 'source_bilat' or space == 'source_temp':
  499. tmp_adj = src_adjacency_whole.toarray()
  500. allIxs = np.unique(np.concatenate([searchSpace_Ixs_Lh, 2562+searchSpace_Ixs_Rh]))
  501. tmp_adj = tmp_adj[allIxs, :]
  502. tmp_adj = tmp_adj[:, allIxs]
  503. adjacency = scipy.sparse.coo_matrix(tmp_adj)
  504. elif space != 'sensor':
  505. tmp_adj = src_adjacency.toarray()
  506. tmp_adj = tmp_adj[searchSpace_Ixs, :]
  507. tmp_adj = tmp_adj[:, searchSpace_Ixs]
  508. adjacency = scipy.sparse.coo_matrix(tmp_adj)
  509. elif space == 'source_wholebrain':
  510. allIxs = np.arange(0, 5124, 1)
  511. seaerchSpace_Ixs_List = [np.arange(0,2562,1), np.arange(0,2562,1)]
  512. adjacency = src_adjacency_whole
  513. searchSpace_Ixs = allIxs
  514. # Time indices that we are searching through
  515. timeWindow_Ixs = times[int(searchTMin/decim):int(searchTMax/decim)]
  516. print('Sensor space permutation tests...')
  517. if space == 'sensor':
  518. rawData = np.array([x.get_data() for x in betas[cond]])
  519. elif space == 'source_lh' or space == 'source_lhlgnet' or space == 'source_ltemp':
  520. rawData = np.array([x.lh_data for x in betas_src[cond]])
  521. elif space == 'source_rh' or space == 'source_rhlgnet' or space == 'source_rtemp':
  522. rawData = np.array([x.rh_data for x in betas_src[cond]])
  523. elif space == 'source_bilat' or space == 'source_wholebrain' or space == 'source_temp':
  524. rawData = np.array([x.data for x in betas_src[cond]])
  525. # Original epoching including 0 and 1000ms,
  526. # so decimation creates epochs of different sizes;
  527. # we're reducing these all to 250 time points
  528. if rawData.shape[2] > int(1000/decim):
  529. rawData = rawData[:,:,:int(1000/decim)]
  530. # subset to search times
  531. data = rawData[:, :, int(searchTMin/decim):int(searchTMax/decim)]
  532. # subset to search space
  533. data = data[:, searchSpace_Ixs, :]
  534. # rearrange data to subjects x times x spaces
  535. data = np.transpose(data, [0, 2, 1])
  536. # degrees of freedom and calculating t-threshold
  537. df = len(subjects) - 1
  538. tThresh = scipy.stats.distributions.t.ppf(1 - pThresh / 2, df = df)
  539. print('One sample t-test over beta values for %s in %s' %(cond, space))
  540. T0, clusters, clusterPs, H0 = clu = spatio_temporal_cluster_1samp_test(
  541. data,
  542. threshold = tThresh,
  543. n_permutations = n_permutations,
  544. tail = tail,
  545. adjacency = adjacency,
  546. verbose = True,
  547. n_jobs = 30)
  548. good_cluster_inds = np.where(clusterPs < plotThresh)[0]
  549. for i_clu, clu_idx in enumerate(good_cluster_inds):
  550. clusterInfo = ''
  551. clusterTimes, clusterSpaces = clusters[clu_idx]
  552. clusterPVal = round(clusterPs[clu_idx], 2)
  553. clusterSize = round(sum(T0[clusters[clu_idx][0], clusters[clu_idx][1]]), 2)
  554. clusterTime_Ixs = np.unique(clusterTimes)
  555. clusterSpace_Ixs = np.unique(clusterSpaces)
  556. # Averaging over time dimension for T values for spatial plot
  557. #t_map = np.ma.masked_invalid(T0[clusterTime_Ixs, ...]).mean(0)
  558. t_map = T0[clusterTime_Ixs, ...].mean(0)
  559. if True:
  560. # threshhold the t-values to t > 1.6
  561. subTMap = np.where(abs(t_map) > 1.2)[0] # indices of
  562. clusterSpace_Ixs = clusterSpace_Ixs[np.in1d(clusterSpace_Ixs, subTMap)]
  563. # Subset to just the spatial points that are significant
  564. sig_t_map = t_map[clusterSpace_Ixs]
  565. # Test to see if you have positive and negative values and exclude
  566. # those that aren't relevant
  567. negPts = np.where(sig_t_map < 0)
  568. posPts = np.where(sig_t_map > 0)
  569. if negPts[0].shape[0] != 0 and posPts[0].shape[0] != 0:
  570. print('Cluster has positive and negative t-values; removing the smallest')
  571. if negPts[0].shape[0] > posPts[0].shape[0]:
  572. sig_t_map = sig_t_map[negPts]
  573. clusterSpace_Ixs = clusterSpace_Ixs[negPts]
  574. else:
  575. sig_t_map = sig_t_map[posPts]
  576. clusterSpace_Ixs = clusterSpace_Ixs[posPts]
  577. tValmin = np.percentile(sig_t_map, 2.5)
  578. tValmean = np.percentile(sig_t_map, 50)
  579. tValmax = np.percentile(sig_t_map, 97.5)
  580. # For plotting purposes -- what's the minimum, mean, and maximum t-values that we'll plot?
  581. if tValmin > 0 and tValmax > 0:
  582. cmap = 'Reds'
  583. clim = dict(kind="value", lims=[tValmin, tValmean, tValmax])
  584. elif tValmin < 0 and tValmax < 0:
  585. cmap = 'Blues'
  586. sig_t_map = -1 * sig_t_map
  587. clim = dict(kind="value", lims=[abs(tValmax), abs(tValmean), abs(tValmin)])
  588. # elif 'sensor' in space:
  589. # cmap = 'coolwarm'
  590. # clim = dict(kind="value", lims=[-1*abs(tValmax), 0, abs(tValmax)])
  591. else:
  592. cmap = 'coolwarm'
  593. clim = dict(kind="value", lims=[-1*max(abs(tValmin), abs(tValmax)), abs(tValmean), max(abs(tValmin), abs(tValmax))])
  594. print(i_clu, clu_idx, cmap)
  595. # Now we'll fetch the significant times:
  596. sig_Times = timeWindow_Ixs[clusterTime_Ixs]
  597. # For reporting, we'll want to know the MEG channel names
  598. # / ico-4 src points
  599. sig_Spaces = searchSpace_Ixs[clusterSpace_Ixs]
  600. if space == 'sensor':
  601. views = ['sensor']
  602. elif 'wholebrain' in space:
  603. views = ['lateral', 'ventral', 'medial']
  604. else:
  605. views = ['lateral']
  606. for view in views:
  607. print(view)
  608. # fig = plt.subplots(figsize=(14, 3))
  609. fig = plt.figure(figsize=(20, 3))
  610. gs = plt.GridSpec(nrows=2, ncols=2, width_ratios=[1,3])
  611. ax_topo = fig.add_subplot(gs[0,0])
  612. ax_signals = fig.add_subplot(gs[0,1])
  613. ax_bar = fig.add_subplot(gs[1,0])
  614. ax_rawsigs = fig.add_subplot(gs[1,1])
  615. plt.tight_layout()
  616. if space == 'sensor':
  617. mask = np.zeros((t_map.shape[0], 1), dtype=bool)
  618. mask[sig_Spaces, :] = True
  619. if cmap == 'Blues':
  620. t_evoked = mne.EvokedArray(-1*t_map[:, np.newaxis], betas['Intercept'][0].info, tmin=0)
  621. else:
  622. t_evoked = mne.EvokedArray(t_map[:, np.newaxis], betas['Intercept'][0].info, tmin=0)
  623. t_evoked.plot_topomap(times=0,
  624. mask=mask,
  625. axes=ax_topo,
  626. cmap=cmap,
  627. ch_type = 'mag',
  628. scalings=dict(mag=1),
  629. vlim=(clim['lims'][0], clim['lims'][2]),
  630. #show=False,
  631. #sensors=False,
  632. extrapolate='local',
  633. colorbar=False, mask_params=dict(markersize=9, markerfacecolor='#CCFFFF', markeredgecolor='#00CCCC'))
  634. ax_topo.set_title("")
  635. else:
  636. if min(sig_Spaces) > 2562 and ('bilat' in space or '_temp' in space or 'wholebrain' in space):
  637. hemis = ['rh']
  638. sig_Spaces = sig_Spaces - 2562
  639. if type(searchSpace) is mne.label.BiHemiLabel:
  640. searchSpace = searchSpace.rh
  641. elif min(sig_Spaces) > 2562:
  642. print('hi!!')
  643. hemis = ['rh']
  644. else:
  645. hemis = ['lh']
  646. if type(searchSpace) is mne.label.BiHemiLabel:
  647. searchSpace = searchSpace.lh
  648. for hemi in hemis:
  649. if hemi == 'lh':
  650. t_stc = mne.SourceEstimate(sig_t_map, [sig_Spaces, []], 0, 0.001)
  651. else:
  652. t_stc = mne.SourceEstimate(sig_t_map, [[], sig_Spaces], 0, 0.001)
  653. try:
  654. # save label to file for cross-language comparisons
  655. newLabel = mne.Label(vertices=sig_Spaces, hemi=hemi)
  656. newLabel.fill(src=fsave_src)
  657. #fName = '%s-%s-%s-%s-clNo%s-p=%s-%s_%s-label' %(language, formula, space, cond, str(i_clu), str(clusterPVal), cond, hemi)
  658. fName = '%s-%s-%s-%s-clNo%s-p=%s-%s' %(language, formula, space, cond, str(i_clu), str(clusterPVal), cond)
  659. newLabel.save(os.path.join(groupPlotFolder, fName))
  660. brain = Brain(subject = 'fsaverage',
  661. subjects_dir = subjects_dir,
  662. hemi=hemi,
  663. size = 1000,
  664. surf = 'inflated',
  665. views = view,
  666. cortex = cortex,
  667. background = 'white',
  668. )
  669. if 'wholebrain' not in space:
  670. brain.add_label(searchSpace, color='white', alpha=0.8)
  671. if hemi == 'lh':
  672. tData = t_stc.lh_data
  673. vertNo = t_stc.lh_vertno
  674. else:
  675. tData = t_stc.rh_data
  676. vertNo = t_stc.rh_vertno
  677. # if cmap != 'coolwarm':
  678. brain.add_data(tData,
  679. hemi=hemi,
  680. vertices=vertNo,
  681. colorbar = False,
  682. clim = clim,
  683. colormap = cmap,
  684. smoothing_steps=8,
  685. # fmin = clim['lims'][0],
  686. # fmid = clim['lims'][1],
  687. # fmax = clim['lims'][2],
  688. transparent = True,
  689. alpha=1.0
  690. # # vertices = vertices[0]
  691. )
  692. # else:
  693. # tData_neg = tData[np.where(tData < 0)[0]]
  694. # tData_pos = tData[np.where(tData > 0)[0]]
  695. # tValmin_pos = np.percentile(tData_pos, 2.5)
  696. # tValmean_pos = np.percentile(tData_pos, 50)
  697. # tValmax_pos = np.percentile(tData_pos, 97.5)
  698. # tValmin_neg = np.percentile(tData_pos, 2.5)
  699. # tValmean_neg = np.percentile(tData_pos, 50)
  700. # tValmax_neg = np.percentile(tData_pos, 97.5)
  701. # clim_pos = dict(kind="value", lims=[tValmin_pos, tValmean_pos, tValmax_pos])
  702. # clim_neg = dict(kind="value", lims=[abs(tValmax_neg), abs(tValmean_neg), abs(tValmin_neg)])
  703. # tData_neg = -1 * tData_neg
  704. # brain.add_data(tData_pos,
  705. # hemi=hemi,
  706. # vertices=vertNo[np.where(tData > 0)[0]],
  707. # colorbar = False,
  708. # clim = clim_pos,
  709. # colormap = 'Reds',
  710. # smoothing_steps=8,
  711. # # fmin = clim['lims'][0],
  712. # # fmid = clim['lims'][1],
  713. # # fmax = clim['lims'][2],
  714. # transparent = True,
  715. # alpha=1.0
  716. # # # vertices = vertices[0]
  717. # )
  718. # brain.add_data(tData_neg,
  719. # hemi=hemi,
  720. # vertices=vertNo[np.where(tData < 0)[0]],
  721. # colorbar = False,
  722. # clim = clim_neg,
  723. # colormap = 'Blues',
  724. # smoothing_steps=8,
  725. # # fmin = clim['lims'][0],
  726. # # fmid = clim['lims'][1],
  727. # # fmax = clim['lims'][2],
  728. # transparent = True,
  729. # alpha=1.0
  730. # # # vertices = vertices[0]
  731. # )
  732. img = brain.screenshot()
  733. nonwhite_pix = (img != 255).any(-1)
  734. nonwhite_row = nonwhite_pix.any(1)
  735. nonwhite_col = nonwhite_pix.any(0)
  736. cropped_screenshot = img[nonwhite_row][:, nonwhite_col]
  737. # ax_topo.imshow(cropped_screenshot
  738. # ax_topo.axis('off')
  739. ax_topo.imshow(cropped_screenshot)
  740. ax_topo.set_yticklabels([])
  741. ax_topo.set_xticklabels([])
  742. ax_topo.set_yticks([])
  743. ax_topo.set_xticks([])
  744. ax_topo.spines[:].set_visible(False)
  745. except:
  746. print("Can't plot data in this hemisphere")
  747. divider = make_axes_locatable(ax_topo)
  748. ax_colorbar = divider.append_axes('left', size='3%', pad=0.2)
  749. mne.viz.plot_brain_colorbar(ax_colorbar, clim=clim,
  750. transparent=True, colormap=cmap,
  751. label='', bgcolor='white')
  752. if cmap == 'Blues':
  753. ylabs = ax_colorbar.get_yticklabels()
  754. for x in ylabs:
  755. x.set_text('-' + x.get_text())
  756. ax_colorbar.set_yticklabels(ylabs)
  757. ax_topo.set_xlabel('Averaged t-map %s - %s ms' %(str(round(sig_Times[0])), str(round(sig_Times[-1]))))
  758. #ax_signals = divider.append_axes('right', size='300%', pad=1.2)
  759. title = 'v = {0}, p = {1}, {2} source'.format(clusterSize, clusterPVal, len(sig_Spaces))
  760. if len(sig_Spaces) > 1:
  761. title += "s (mean)"
  762. # Take the raw data and subset to significant spatial indices
  763. # And average over those spatial coodinates
  764. # Then average over the participants and calculate SE
  765. t_Timecourse = rawData[:, sig_Spaces, :]
  766. t_Timecourse = np.average(t_Timecourse,axis=1)
  767. t_tc_means = np.average(t_Timecourse,axis=0)
  768. t_tc_upper = t_tc_means + 1.96*scipy.stats.sem(t_Timecourse, axis=0)
  769. t_tc_lower = t_tc_means - 1.96*scipy.stats.sem(t_Timecourse, axis=0)
  770. # Do this fore the baseline/intercept
  771. #baseline_Timecourse = baselineData[:, sig_Spaces, :]
  772. #baseline_Timecourse = np.average(baseline_Timecourse, axis=1)
  773. #baseline_tc_means = np.average(baseline_Timecourse, axis=0)
  774. #baseline_tc_upper = baseline_tc_means + 1.96*scipy.stats.sem(baseline_Timecourse, axis=0)
  775. #baseline_tc_lower = baseline_tc_means - 1.96*scipy.stats.sem(baseline_Timecourse, axis=0)
  776. #baseline_Avgs = baseline_Timecourse[:, clusterTime_Ixs]
  777. #baseline_Avgs = baseline_Avgs.flatten()
  778. #baseline_Avgs = np.average(baseline_Avgs, axis = 0)
  779. # And other factorial variables
  780. # Baseline/Intercept is 'NomAcc'
  781. color='blue'
  782. label=cond
  783. ax_signals.plot(times, t_tc_means, color, label = label)
  784. ax_signals.fill_between(times, y1=t_tc_upper, y2=t_tc_lower, color=color, alpha=0.1, interpolate=True)
  785. ymin, ymax = ax_signals.get_ylim()
  786. if space == 'sensor':
  787. fig = tidyFig(fig, ax = 1, xmin=times[0], xmax=times[-1], size=(15,5))
  788. else:
  789. fig = tidyFig(fig, ax = 1, xmin=times[0], xmax=times[-1], ylim=ymin, size=(15,5))
  790. ax_signals.fill_betweenx((-1*max(abs(ymin), abs(ymax)), max(abs(ymin), abs(ymax))), sig_Times[0], sig_Times[-1], color='lightblue', alpha=0.2, zorder=500)
  791. ax_signals.fill_betweenx((-1*max(abs(ymin), abs(ymax)), max(abs(ymin), abs(ymax))), times[0], timeWindow_Ixs[0], color='grey', alpha=0.3)
  792. ax_signals.fill_betweenx((-1*max(abs(ymin), abs(ymax)), max(abs(ymin), abs(ymax))), timeWindow_Ixs[-1], times[-1], color='grey', alpha=0.3)
  793. ax_signals.hlines(y=0, color='black', xmin=times[0], xmax=times[-1])
  794. ax_signals.set_xlabel('Times (ms)')
  795. if space == 'sensor':
  796. ax_signals.set_ylabel('Activity (β, fT)')
  797. else:
  798. ax_signals.set_ylabel('Activity (β, dSPM)')
  799. ax_signals.set_title(title)
  800. # fig.subplots_adjust(bottom=.05)
  801. if cond == 'ObjCF':
  802. comparisons = ['Acc', 'Bare']
  803. colors = ['#128BFC', '#9C7535']
  804. elif cond == 'SubjCF':
  805. comparisons = ['NomImpf', 'ErgPerf']
  806. colors = ['#128BFC', '#9C7535']
  807. elif cond == 'InteractionF':
  808. comparisons = ['ErgAcc', 'ErgBare', 'NomAcc', 'NomBare',]
  809. colors = ['#128BFC', '#D89014', '#3B488A', '#9C7535']
  810. elif cond == 'UpcomingVGenF':
  811. comparisons = ['MVerb_Hindi', 'FVerb_Hindi']
  812. colors = ['#D83F23', '#428669']
  813. elif cond == 'GenF':
  814. comparisons = ['MVerb_Nepali', 'FVerb_Nepali']
  815. colors = ['#D83F23', '#428669']
  816. elif cond == 'ClozeF':
  817. comparisons = ['HighCloze', 'LowCloze']
  818. colors = ['#4A5C3D', '#CF23D8']
  819. # Palette
  820. palette = dict()
  821. # For ptcpt x space x time data; constrained to cluster spatial coordinates
  822. data_3d = dict()
  823. # For ptcpt x time data; averaged over space
  824. data_tc = dict()
  825. # SEs for plotting
  826. data_tc_se = dict()
  827. # For ptpct data; averaged over space and time
  828. data_1d = dict()
  829. for i in range(0,len(comparisons)):
  830. color = colors[i]
  831. comparison = comparisons[i]
  832. palette[comparison] = color
  833. for comparison in comparisons:
  834. if space == 'sensor':
  835. # participants x space x time
  836. data_3d[comparison] = np.array([x.get_data() for x in raw_conds[comparison]])
  837. # Subset to significant spaces in the cluster
  838. data_3d[comparison] = data_3d[comparison][:, sig_Spaces, :]
  839. else:
  840. data_3d[comparison] = np.array([x.extract_label_time_course(newLabel, src=fsave_src, mode='mean') for x in raw_conds_src[comparison]])
  841. # Clip off the extra ms if needed
  842. if data_3d[comparison].shape[2] > int(1000/decim):
  843. data_3d[comparison] = data_3d[comparison][:,:,:int(1000/decim)]
  844. # Average over space and participants for the tiemecourse data
  845. data_tc[comparison] = np.average(data_3d[comparison],axis=1)
  846. # Calculate the SE before averaging over participants
  847. data_tc_se[comparison] = scipy.stats.sem(data_tc[comparison], axis=0)
  848. # Average across participants for the mean timecourse
  849. data_tc[comparison] = np.average(data_tc[comparison], axis=0)
  850. upper_tc = data_tc[comparison] + 1.96*data_tc_se[comparison]
  851. lower_tc = data_tc[comparison] - 1.96*data_tc_se[comparison]
  852. # bare_tc_lower = bareTC_means - 1.96*scipy.stats.sem(bareTC, axis=0)
  853. ax_rawsigs.plot(times, data_tc[comparison], color = palette[comparison], label=comparison)
  854. ax_rawsigs.fill_between(times, y1=upper_tc, y2=lower_tc, color=palette[comparison], alpha=0.1, interpolate=True)
  855. ymin, ymax = ax_rawsigs.get_ylim()
  856. ax_rawsigs.fill_betweenx((-1*max(abs(ymin), abs(ymax)), max(abs(ymin), abs(ymax))), sig_Times[0], sig_Times[-1], color='lightblue', alpha=0.2, zorder=500)
  857. ax_rawsigs.fill_betweenx((-1*max(abs(ymin), abs(ymax)), max(abs(ymin), abs(ymax))), times[0], timeWindow_Ixs[0], color='grey', alpha=0.3)
  858. ax_rawsigs.fill_betweenx((-1*max(abs(ymin), abs(ymax)), max(abs(ymin), abs(ymax))), timeWindow_Ixs[-1], times[-1], color='grey', alpha=0.3)
  859. if space == 'sensor':
  860. fig = tidyFig(fig, ax = 3, xmin=times[0], xmax=times[-1], size=(15,5))
  861. else:
  862. fig = tidyFig(fig, ax = 3, xmin=times[0], xmax=times[-1], ylim=ymin, size=(15,5))
  863. ax_rawsigs.hlines(y=0, color='black', xmin=times[0], xmax=times[-1])
  864. ax_rawsigs.set_xlabel('Times (ms)')
  865. if space == 'sensor':
  866. ax_rawsigs.set_ylabel('Activity (fT)')
  867. else:
  868. ax_rawsigs.set_ylabel('Activity (dSPM)')
  869. ax_rawsigs.legend(loc='lower right')
  870. if space == 'sensor' and len(comparisons) == 2:
  871. std = comparisons[0]
  872. contrast = comparisons[1]
  873. stdRaw = mne.combine_evoked(raw_conds[std], weights=[1]*len(subjects))
  874. contrastRaw = mne.combine_evoked(raw_conds[contrast], weights=[1]*len(subjects))
  875. stdRaw = stdRaw.crop(tmin=tmin+sig_Times[0]/1000, tmax=tmin+sig_Times[-1]/1000)
  876. contrastRaw = contrastRaw.crop(tmin=tmin+sig_Times[0]/1000, tmax=tmin+sig_Times[-1]/1000)
  877. midPt = (sig_Times[0]/1000 + sig_Times[-1]/1000)/2
  878. maxMidDiff = (sig_Times[-1]/1000) - midPt
  879. diff = mne.combine_evoked([stdRaw, contrastRaw], weights=[1, -1])
  880. diffData = diff.get_data().mean(axis=1)
  881. diffEvoked = mne.EvokedArray(diffData[:, np.newaxis], betas['Intercept'][0].info, tmin=0)
  882. diffMin = min(diffData)
  883. diffMax = max(diffData)
  884. diffMean = diffData.mean()
  885. extremeVal = max(abs(diffMin), abs(diffMax))
  886. clim = dict(kind="value", lims=[-extremeVal, diffMean, extremeVal])
  887. diffEvoked.plot_topomap(times=0,
  888. mask=mask,
  889. axes=ax_bar,
  890. cmap='coolwarm',
  891. ch_type = 'mag',
  892. scalings=dict(mag=1),
  893. vlim=(-1*extremeVal, 1*extremeVal),
  894. #show=False,
  895. #sensors=False,
  896. extrapolate='local',
  897. colorbar=False, mask_params=dict(markersize=9, markerfacecolor='#CCFFFF', markeredgecolor='#00CCCC'))
  898. ax_bar.set_title("")
  899. ax_bar.set_xlabel("Average signal during cluster: %s - %s" %(std, contrast))
  900. divider = make_axes_locatable(ax_bar)
  901. ax_colorbar2 = divider.append_axes('left', size='3%', pad=0.2)
  902. mne.viz.plot_brain_colorbar(ax_colorbar2, clim=clim,
  903. transparent=True, colormap='coolwarm',
  904. label='', bgcolor='white')
  905. # We'll plot the difference in the MEG topography
  906. else:
  907. # We'll plot the bar graph in the cluster
  908. # The dataframe for plotting in seaborn
  909. avgMeans = dict()
  910. for comparison in comparisons:
  911. # Data has already been constrainted to
  912. # spatial coordinates, so now we will constrain to
  913. # time indices
  914. tmp = data_3d[comparison][:, :, clusterTime_Ixs]
  915. # Then we will average over space...
  916. tmp = np.average(tmp, axis=1)
  917. # ... and time
  918. tmp = np.average(tmp, axis=1)
  919. data_1d[comparison] = tmp
  920. avgMeans[comparison] = data_1d[comparison]
  921. sn.barplot(avgMeans, ax=ax_bar, palette=palette, width=0.8)
  922. # ax_bar.set_
  923. # sn.barplot(avgMeans, x='variable', y='value', ax=ax_bar, palette=colors, hue='variable', width=1, errorbar='se')
  924. avgs = []
  925. for x in list(avgMeans.keys()):
  926. avgs.append(avgMeans[x].mean())
  927. biggestVal = max(avgs)
  928. scale = (max(0,abs(ymin))+min(abs(ymax), biggestVal))/100
  929. yIncrement = 1.1
  930. for i_fact in range(0,len(list(avgMeans.keys()))):
  931. for j_fact in range(0,len(list(avgMeans.keys()))):
  932. if i_fact < j_fact:
  933. ilab = list(avgMeans.keys())[i_fact]
  934. jlab = list(avgMeans.keys())[j_fact]
  935. tval, pval = scipy.stats.ttest_ind(a = avgMeans[ilab], b = avgMeans[jlab])
  936. tTestRes = '%s vs. %s: t = %s, p = %s\n' %(ilab, jlab, str(tval), str(pval))
  937. clusterInfo += tTestRes
  938. iavg = avgMeans[ilab].mean()
  939. javg = avgMeans[jlab].mean()
  940. biggestVal = max(abs(biggestVal), abs(iavg), abs(javg))
  941. if iavg <= 0 and javg <= 0:
  942. yval = min(iavg, javg, -1*biggestVal) - 1.0*((yIncrement-1)*scale)
  943. else:
  944. yval = max(iavg, javg, biggestVal) + 1.0*((yIncrement-1)*scale)
  945. if pval < 0.05:
  946. yIncrement += 1.1
  947. ax_bar.plot([i_fact, i_fact, j_fact, j_fact], [yval, yval+0.1*yval, yval+0.1*yval, yval], lw=1.5, c='black')
  948. ax_bar.text((i_fact+j_fact) * 0.5, yval, '*', ha='center', va='center', color='black', fontsize=20)
  949. avgs.append(yval)
  950. avgs.append(biggestVal)
  951. ax_bar.spines['top'].set_visible(False)
  952. ax_bar.spines['right'].set_visible(False)
  953. ax_bar.spines['bottom'].set_visible(False)
  954. ax_bar.title.set_visible(False)
  955. # ax_bar.set_xticks([])
  956. # ax_bar.xaxis.set_major_formatter(plt.NullFormatter())
  957. ax_bar.set_xticklabels(comparisons)
  958. # newMin, newMax = ax_bar.get_ylim()
  959. # if sum(1 for number in avgs if number < 0) == 0:
  960. # ax_bar.set_ylim(ymin = max(0, ymin, newMin), ymax = min(max(avgs), newMax)+scale)
  961. # else:
  962. # ax_bar.set_ylim(ymin = max(ymin, newMin), ymax = min(max(avgs), newMax)+scale)
  963. fig.subplots_adjust(bottom=.05)
  964. # label_params = ax_signals.get_legend_handles_labels()
  965. # figl, axl = plt.subplots()
  966. # axl.axis(False)
  967. # axl.legend(*label_params, ncols = 2, loc="center", bbox_to_anchor=(0.5, 0.5))
  968. # fName = '%s-%s-%s-%s-clNo%s-p=%s-%s_LABEL_ONLY.png' %(language, formula, space, cond, str(i_clu), str(clusterPVal), cond)
  969. # figl.savefig(os.path.join(groupPlotFolder, fName + "_LABEL_ONLY.png"))
  970. fName = '%s-%s-%s-%s-clNo%s-p=%s-%s_%s.png' %(language, formula, space, cond, str(i_clu), str(clusterPVal), cond, view)
  971. fig.savefig(os.path.join(groupPlotFolder, fName))
  972. clusterInfo += 'formula: ' + formula + '\n'
  973. clusterInfo += 'effect: ' + cond + '\n'
  974. clusterInfo += 'n_permutations: ' + str(n_permutations) + '\n'
  975. clusterInfo += 'pThresh: ' + str(pThresh) + '\n'
  976. clusterInfo += 'plotThresh: ' + str(plotThresh) + '\n'
  977. clusterInfo += 'timeWindow_Ixs: ' + str(timeWindow_Ixs) + '\n'
  978. clusterInfo += 'space: ' + str(space) + '\n'
  979. clusterInfo += 'searchSpace_Ixs: ' + str(searchSpace_Ixs) + '\n'
  980. clusterInfo += 'i_clu: ' + str(i_clu) + '\n'
  981. clusterInfo += 'clusterPVal: ' + str(clusterPVal) + '\n'
  982. clusterInfo += 'sig_Times: ' + str(sig_Times) + '\n'
  983. clusterInfo += 'sig_Spaces: ' + str(sig_Spaces) + '\n'
  984. fName = '%s-%s-%s-%s-clNo%s-p=%s-%s_info.txt' %(language, formula, space, cond, str(i_clu), str(clusterPVal), cond)
  985. with open(os.path.join(groupPlotFolder, fName), 'w') as f:
  986. f.write(clusterInfo)

1_SgTrialRegressions_withMean_looseOrientation.py, no license · at the source

Overview

Authors: Dustin A. Chacón1,2, Subhekshya Shrestha1,2, Brian W. Dillon3, Rajesh Bhatt3, Diogo Almeida2, Alec Marantz2,4
  1. University of California, Santa Cruz, Santa Cruz, CA, USA
  2. New York University Abu Dhabi, Abu Dhabi, United Arab Emirates
  3. University of Massachusetts Amherst, Amherst, MA, USA
  4. New York University, New York, NY, USA
Institutions: New York University Abu Dhabi (United Arab Emirates); University of California, Santa Cruz (United States); University of Massachusetts Amherst (United States); New York University (United States)
Journal: Neurobiology of language (Cambridge, Mass.), volume 7, article NOL.a.247
Dates: received 12 February 2024; accepted 19 February 2026; published online 4 June 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1162/nol.a.247 · PMID 42272623 · PMCID PMC13249467 · OpenAlex W7133501173
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: MEG (modality)
Methods: Connectivity, Statistics, Smoothing, state filtering, decompositions, Preprocessing, fMRI & imaging, Physiology & signal measures
Keywords: agreement, case, electrophysiology, ergativity, Hindi, magnetoencephalography (MEG), Nepali, neurolinguistics, psycholinguistics, sentence processing
Topic: Neurobiology of Language and Bilingualism (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Citations: not cited yet (Europe PMC); 125 references in the paper

Abstract

At first glance, the brain’s language network appears to be universal, but languages clearly differ. Does the brain adapt to the specific details of individual grammatical systems? Here, we present a magnetoencephalography (MEG) study on case and agreement in Hindi and Nepali. Both languages use split-ergative case systems. However, these systems interact with verb agreement differently—in Hindi, case features conspire to determine which noun phrase (NP) the verb agrees with (subject, object, or neither), but in Nepali the verb always agrees with the subject NP. We found that NPs with different case values elicit different MEG signals around 200–500 and 600–900 ms. In subsequent exploratory analyses, we failed to find a reliable difference in this brain activity between the two languages corresponding to the different relations between case and agreement. However, we identified a portion of the left temporoparietal junction as exhibiting a statistically nonsignificant effect that may warrant further investigation.

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 5 matches between paragraphs and lines of code.

OSF qrzpm

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 13 files
Software Heritage: not checked
Found in: “DATA AND CODE AVAILABILITY STATEMENTS”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: MNE-Python (3 files), NumPy (3 files), pandas (3 files), Matplotlib (2 files), SciPy (2 files), seaborn (2 files), 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)
4 files
At the source: osf.io/qrzpm

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;
  • 4 scripts, each with its path and the digest of its content;
  • 5 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 statements

Preprocessed MEG data and processing code have been made publicly available on Open Science Framework at https://osf.io/qrzpm.

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, 6 authors, 10 keywords, 1 funder, 116 references.

Cite

This paper

Chacón, D. A., Shrestha, S., Dillon, B. W., Bhatt, R., Almeida, D., & Marantz, A. (2026). Same Sentences, Different Grammars, Different Brain Responses?: An MEG Study on Case and Agreement Encoding in Hindi and Nepali Split-Ergative Structures. Neurobiology of language (Cambridge, Mass.), 7, NOL.a.247. https://doi.org/10.1162/nol.a.247

BibTeX

@article{chacon2026same,
author = {Chacón, Dustin A. and Shrestha, Subhekshya and Dillon, Brian W. and Bhatt, Rajesh and Almeida, Diogo and Marantz, Alec},
title = {{Same Sentences, Different Grammars, Different Brain Responses?: An MEG Study on Case and Agreement Encoding in Hindi and Nepali Split-Ergative Structures}},
journal = {Neurobiology of language (Cambridge, Mass.)},
year = {2026},
month = jun,
volume = {7},
pages = {NOL.a.247},
publisher = {MIT Press},
issn = {2641-4368},
doi = {10.1162/nol.a.247},
url = {https://doi.org/10.1162/nol.a.247},
pmid = {42272623},
pmcid = {PMC13249467}
}

RIS

TY - JOUR
AU - Chacón, Dustin A.
AU - Shrestha, Subhekshya
AU - Dillon, Brian W.
AU - Bhatt, Rajesh
AU - Almeida, Diogo
AU - Marantz, Alec
TI - Same Sentences, Different Grammars, Different Brain Responses?: An MEG Study on Case and Agreement Encoding in Hindi and Nepali Split-Ergative Structures
T2 - Neurobiology of language (Cambridge, Mass.)
J2 - Neurobiol Lang (Camb)
PY - 2026
DA - 2026/06/04
VL - 7
SP - NOL.a.247
SN - 2641-4368
PB - MIT Press
DO - 10.1162/nol.a.247
UR - https://doi.org/10.1162/nol.a.247
LA - en
ER -

CSL-JSON

{
"id": "10.1162/nol.a.247",
"type": "article-journal",
"title": "Same Sentences, Different Grammars, Different Brain Responses?: An MEG Study on Case and Agreement Encoding in Hindi and Nepali Split-Ergative Structures",
"container-title": "Neurobiology of language (Cambridge, Mass.)",
"author": [
{
"family": "Chacón",
"given": "Dustin A."
},
{
"family": "Shrestha",
"given": "Subhekshya"
},
{
"family": "Dillon",
"given": "Brian W."
},
{
"family": "Bhatt",
"given": "Rajesh"
},
{
"family": "Almeida",
"given": "Diogo"
},
{
"family": "Marantz",
"given": "Alec"
}
],
"container-title-short": "Neurobiol Lang (Camb)",
"volume": "7",
"page": "NOL.a.247",
"DOI": "10.1162/nol.a.247",
"PMID": "42272623",
"PMCID": "PMC13249467",
"ISSN": "2641-4368",
"publisher": "MIT Press",
"URL": "https://doi.org/10.1162/nol.a.247",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
4
]
]
}
}

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/nol.a.254
A Unified Neural Time Course for Words, Phrases, and Sentences: MEG Evidence from Parallel Presentation.
Journal: Neurobiology of language (Cambridge, Mass.)
In common: MEG, 10 references
[2] doi:10.1093/cercor/bhag075 [code]
Cortical dynamics of icon perception: effects of concreteness and attractiveness.
Journal: Cerebral cortex (New York, N.Y. : 1991)
In common: MNE-Python, statsmodels, pandas, 3 other tools, MEG, 6 references
[3] doi:10.1162/nol.a.264 [code]
The Temporal Dynamics of the Labeling Algorithm During Natural Language Comprehension: Neural Evidence for Phrase Grammatical Type Generation.
Journal: Neurobiology of language (Cambridge, Mass.)
In common: 9 references
[4] doi:10.1523/eneuro.0254-25.2026 [code]
Spatiotemporal Dynamics in Prespeech Semantic Category Decoding: An Intracranial EEG Study.
Journal: eNeuro
In common: statsmodels, seaborn, pandas, 3 other tools, 4 references
[5] doi:10.1093/nc/niag029 [code]
A data-driven approach to identifying and evaluating connectivity-based neural correlates of conscious visual perception.
Journal: Neuroscience of consciousness
In common: MNE-Python, statsmodels, seaborn, 4 other tools, MEG, 2 references
[6] doi:10.1162/imag.a.1246 [code]
A 3.5-minute-long reading-based fMRI localizer for the language network.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: statsmodels, seaborn, pandas, 3 other tools, 3 references
[7] doi:10.1038/s41467-026-75240-0 [code]
Dynamic acoustic-to-categorical representations of phonemes and prosody along ventral and dorsal speech streams.
Journal: Nature communications
In common: MNE-Python, seaborn, pandas, 3 other tools, MEG, 2 references
[8] doi:10.1016/j.neuroimage.2026.122051 [code]
Determining hemispheric language dominance from MEG beta-power modulations: Concordance with fMRI.
Journal: NeuroImage
In common: MNE-Python, pandas, SciPy, 2 other tools, MEG, 3 references
[9] doi:10.1371/journal.pbio.3003755 [code]
Action information is integrated into entorhinal representations of conceptual space and is reflected in eye movements.
Journal: PLoS biology
In common: MNE-Python, statsmodels, seaborn, 4 other tools, 2 references
[10] doi:10.1523/eneuro.0041-26.2026 [code]
Ocular Speech Tracking Persists in Blindness, but Its Dynamics and Oculo-Cerebral Connectivity Depend on Visual Status.
Journal: eNeuro
In common: MNE-Python, statsmodels, seaborn, 4 other tools, MEG, 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.