OSCR

Long-term reliability and stability of parameterized resting state EEG: evidence from a five-year follow-up.

Code ↔ Paper

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

The 4 matches
  1. [1] § Method › Physiological recording and data reduction ↔ notebooks/IfAdo_preprocessing.ipynb, lines 86–149 · score 0.95 · Power Spectral Density, peak width limits, 3–40 Hz, peak height, peak threshold, aperiodic mode
  2. [2] § Method › Physiological recording and data reduction ↔ src/preprocessing.py, lines 195–312 · score 0.94 · Power Spectral Density, peak width limits, peak height, peak threshold, aperiodic mode, 1–8 Hz
  3. [3] § Method › Physiological recording and data reduction ↔ src/preprocessing.py, lines 195–312 · score 0.63 · MNE, segmented, rejected, overlapping, epochs, filtered
  4. [4] § Method › Physiological recording and data reduction ↔ notebooks/IfAdo_preprocessing.ipynb, lines 86–149 · score 0.57 · Bad channels, rejected, artifact, overlapping, epochs, filtered

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

Jupyter notebook · 1,986 lines · 168 KB · MIT · 2 matches

  1. # %% [markdown]
  2. # Long-term reliability and stability of parameterised resting state EEG
  3. # ======================================================================
  4. #
  5. # Analysis pipeline for 5-year test-retest reliability study.
  6. #
  7. # Data: Dortmund Vital Study (Getzmann et al., 2024)
  8. # https://doi.org/10.18112/openneuro.ds005385.v1.0.2
  9. #
  10. # Measures extracted:
  11. # - Aperiodic exponent
  12. # - Aperiodic offset
  13. # - Individual alpha peak frequency (IAPF)
  14. # - Alpha power (via FOOOF)
  15. # - Absolute Alpha power
  16. #
  17. # Repository: https://github.com/MindSpaceLab/Aperiodic_Test_Retest_5year
  18. # %%
  19. # Core Imports
  20. import os
  21. import numpy as np
  22. import pandas as pd
  23. import mne as mne
  24. import scipy as scipy
  25. import matplotlib.pyplot as plt
  26. import seaborn as sns
  27. import time
  28. from mne_icalabel import label_components
  29. from mne_bids import (
  30. BIDSPath,
  31. find_matching_paths,
  32. get_entity_vals,
  33. make_report,
  34. print_dir_tree,
  35. read_raw_bids,
  36. )
  37. from pyprep.find_noisy_channels import NoisyChannels
  38. import mne_bids as mne_bids
  39. import neurodsp as ndsp
  40. import fooof as fooof
  41. from pathlib import Path
  42. from sklearn.decomposition import PCA
  43. from sklearn.metrics import silhouette_score
  44. from sklearn.cluster import KMeans
  45. from scipy.spatial import ConvexHull
  46. from scipy.stats import pearsonr
  47. import stargazer
  48. #from statsmodels.stats.multitest import multipletests
  49. import warnings
  50. warnings.filterwarnings('ignore')
  51. # EEG processing
  52. mne.set_log_level('WARNING')
  53. # Add custom modules path
  54. import sys
  55. sys.path.insert(0, str(Path.cwd() / 'src'))
  56. print("✓ Core libraries loaded")
  57. %matplotlib qt
  58. # %%
  59. # Custom modules
  60. import sys
  61. import importlib
  62. sys.path.append("../src")
  63. module_name = ["preprocessing", 'utils','viz']
  64. for mod in module_name:
  65. if mod in sys.modules:
  66. del sys.modules[mod]
  67. try:
  68. import preprocessing as preproc
  69. import utils as utils
  70. import viz as viz
  71. print("✓ Custom modules loaded")
  72. except ImportError as e:
  73. print(f"⚠ Custom modules not found: {e}")
  74. print(" Create modules in src/ directory as needed")
  75. # %%
  76. # Configuration
  77. DATA_PATH = Path(os.path.realpath(
  78. os.path.join(os.path.abspath(''), '..', 'Data')))
  79. FIGURES_PATH = Path(os.path.realpath(
  80. os.path.join(os.path.abspath(''), '..', 'Figures')))
  81. RESULTS_PATH = Path(os.path.realpath(
  82. os.path.join(os.path.abspath(''), '..', 'Results')))
  83. # Create directories if they don't exist
  84. FIGURES_PATH.mkdir(parents=True, exist_ok=True)
  85. RESULTS_PATH.mkdir(parents=True, exist_ok=True)
  86. # Preprocessing parameters
  87. FILTER_LOW = 0.1
  88. FILTER_HIGH = 40
  89. EPOCH_DUR = 2.0 # seconds
  90. SAMPLE_RATE = 250
  91. REFERENCE = ['TP9', 'TP10']
  92. # Artifact rejection parameters
  93. THRESHOLD = 150e-6 # 100 µV
  94. # Parameters for PSD analysis
  95. BANDWIDTH = 2 # Multitaper bandwidth
  96. # FOOOF parameter settings
  97. SPEC_PARAM_SETTINGS = {
  98. 'freq_range': [3,40], # Frequency range to fit
  99. 'peak_width_limits': [1, 8], # Frequency resolution * 2 recommended
  100. 'min_peak_height': 0.1,
  101. #'max_n_peaks': 6,
  102. 'peak_threshold': 2,
  103. 'aperiodic_mode': 'fixed', # 'fixed' or 'knee'
  104. }
  105. N_JOBS = -1 # Number of jobs to run in parallel
  106. PSD_SETTINGS = {
  107. 'filter_low': 0.1,
  108. 'filter_high': 40,
  109. 'epoch_duration': 2.0,
  110. 'epoch_overlap': 0,
  111. 'reject_threshold': 200e-6, # 200 µV peak-to-peak, equivalent to ±100 µV
  112. 'fmin': 1,
  113. 'fmax': 40.0,
  114. 'n_fft': 1000,
  115. 'n_per_seg': 500,
  116. 'n_overlap': 250,
  117. 'window': 'hamming'
  118. }
  119. #Pre-ICC and HLM parameters
  120. FIT_THRESHOLD = 0.9 # Minimum fit quality for FOOOF model to be included
  121. BAD_CHAN_THRESHOLD = 0.5 # Maximum proportion of bad channels allowed per subject
  122. print(f"✓ Config set - Data: {DATA_PATH}, Filter: {FILTER_LOW}-{FILTER_HIGH} Hz, Reference: {REFERENCE}, Epoch: {EPOCH_DUR} s, "
  123. f"Sample rate: {SAMPLE_RATE} Hz, Artifact threshold: {THRESHOLD}, "
  124. f"PSD bandwidth: {BANDWIDTH}, "
  125. f"FOOOF settings: {SPEC_PARAM_SETTINGS},"
  126. f"Number of jobs: {N_JOBS},"
  127. f"PSD settings: {PSD_SETTINGS},"
  128. f"Pre-ICC and HLM settings: {{'fit_threshold': {FIT_THRESHOLD}, 'bad_chan_threshold': {BAD_CHAN_THRESHOLD}}}"
  129. )
  130. # %%
  131. #Although the data are stored in BIDs format, there are some issues with importing using MNE_BIDS to load data coming from EEGLAB - the formatting of the events causes some issues.
  132. #But, we'll still use it for managing the files.
  133. sessions = get_entity_vals(DATA_PATH, "session", ignore_sessions="off")
  134. datatype = "eeg" #Define the datatype as EEG
  135. extensions = [".edf"] # ignore .json files
  136. bids_paths = find_matching_paths(
  137. DATA_PATH, datatypes=datatype, sessions=sessions, extensions=extensions
  138. )
  139. subject_ids = get_entity_vals(DATA_PATH, "subject", ignore_subjects="on")
  140. #here we manually remove subject 485, as one of the ransac bad channels is a reference channel (Tp9). If you want to verify this, you can run the preprocessing on this subject alone.
  141. subject_ids.remove('485')
  142. tasks = ['EyesClosed', 'EyesOpen']
  143. sessions = ['1', '2']
  144. acquisitions = ['pre'] #,'post'] We only want pre-task data.
  145. task_combinations = list(enumerate([(task, ses, acq) for task in tasks for ses in sessions for acq in acquisitions]))
  146. print(f"✓ Found {len(subject_ids)} subjects with {len(bids_paths)} files in BIDS format and {len(task_combinations)} task combinations")
  147. for idx, (task, ses, acq) in task_combinations:
  148. print(f" {idx}: Task={task}, Session={ses}, Acquisition={acq}")
  149. # %% [markdown]
  150. # # 1. Perform initial preprocessing of the IfAdo dataset.
  151. # This includes resampling, re-referencing, RANSAC, ICA decomposition and removal of artifactual components.
  152. # %% [markdown]
  153. # ## 1.1 Preprocessing
  154. # %%
  155. #Comment out for now to avoid re-running full preprocessing on all subjects
  156. ica_removed_df, channels_removed_df, failed = preproc.process_data_bids(subject_ids, DATA_PATH, task_combinations, sample_rate=SAMPLE_RATE, reference=REFERENCE, overwrite=True, skip_existing=False)
  157. # %% [markdown]
  158. # ## 1.2 Epoching and FOOOOF
  159. # %%
  160. #Comment out so it doesn't re-run
  161. # Epoching and FOOOF processing - uncomment to run
  162. failed_files, epoch_nums = preproc.epoch_psd_fooof(subject_ids, DATA_PATH, task_combinations=task_combinations, psd_settings=PSD_SETTINGS, fooof_settings=SPEC_PARAM_SETTINGS, overwrite=True)
  163. # Save failed preprocessing subjects
  164. if failed_files:
  165. failed_df = pd.DataFrame(failed_files, columns=['subject'])
  166. failed_df.to_csv(DATA_PATH / 'failed_fooof.csv', index=False)
  167. print(f"✓ Saved failed fooof subjects to {DATA_PATH / 'failed_fooof.csv'}")
  168. # Save failed files as a csv
  169. failed_files_df = pd.DataFrame({'Failed Files': failed_files})
  170. failed_files_df.to_csv('failed_files.csv', index=False)
  171. # Save the epoch numbers as a csv
  172. epoch_nums.to_csv('epoch_numbers.csv', index=False)
  173. # Function to compare failed files with scanned files, just in case the failures were due to reasons other than missing files
  174. def compare_failed_files(failed_files_df, all_files,filepath):
  175. missing_files = []
  176. other_failures = []
  177. for failed_file in failed_files_df['Failed Files']:
  178. raw_file = failed_file.replace('_proc-preproc_eeg.fif', '.edf')
  179. raw_file_path = os.path.join(filepath, raw_file)
  180. if raw_file_path not in all_files:
  181. missing_files.append(failed_file)
  182. else:
  183. other_failures.append(failed_file)
  184. return missing_files, other_failures
  185. # Scan the data directory
  186. all_files = utils.scan_data_directory(DATA_PATH)
  187. #Load the failed files
  188. failed_files_df = pd.read_csv('failed_files.csv')
  189. # Compare the failed files to the scanned files
  190. missing_files, other_failures = compare_failed_files(failed_files_df, all_files,DATA_PATH)
  191. # %% [markdown]
  192. # # 2. Extract FOOOF parameters from each file.
  193. # %%
  194. import json
  195. from fooof.bands import Bands
  196. bands = Bands({'alpha': [8, 13]})
  197. from fooof.analysis import get_band_peak_fg
  198. from fooof.utils import trim_spectrum
  199. def extract_fooof_params(subject_ids, filepath):
  200. fooof_params_dict = {}
  201. for task in task_combinations:
  202. task_name = task[1][0]
  203. session = task[1][1]
  204. acquisition = task[1][2]
  205. # Initialize empty arrays for the outputs at the task level
  206. exponents = {}
  207. offsets = {}
  208. fits = {}
  209. errors = {}
  210. alpha_freq = {}
  211. alpha_amp = {}
  212. alpha_absolute = {}
  213. for subject in subject_ids:
  214. try:
  215. bids_path = BIDSPath(subject=subject, session=session, task=task_name, acquisition=acquisition, datatype=datatype, root=filepath, suffix='eeg', extension='.fif', processing='preproc')
  216. fooof_path = str(bids_path.fpath)
  217. fooof_path = fooof_path.replace('proc-preproc_eeg.fif', 'FOOOF.json')
  218. fg = fooof.FOOOFGroup()
  219. fg.load(fooof_path)
  220. subject = 'sub-' + subject
  221. exponents[subject] = fg.get_params('aperiodic_params', 'exponent')
  222. offsets[subject] = fg.get_params('aperiodic_params', 'offset')
  223. fits[subject] = fg.get_params('r_squared')
  224. errors[subject] = fg.get_params('error')
  225. alpha_fooof = get_band_peak_fg(fg, bands.alpha)
  226. alpha_freq[subject] = np.array(alpha_fooof)[:,0]
  227. alpha_amp[subject] = np.array(alpha_fooof)[:,1]
  228. af, asp = trim_spectrum(fg.freqs, fg.power_spectra, bands.alpha)
  229. alpha_absolute[subject] = np.log10(np.trapz(10**asp, af, axis=1))
  230. except Exception as e:
  231. print(f'Error processing {bids_path.basename}: {e}')
  232. fooof_params_dict[f'ses-{session}_task-{task_name}_acq-{acquisition}'] = {
  233. 'exponents': exponents,
  234. 'offsets': offsets,
  235. 'fits': fits,
  236. 'errors': errors,
  237. 'alpha_freq': alpha_freq,
  238. 'alpha_amp': alpha_amp,
  239. 'alpha_absolute': alpha_absolute
  240. }
  241. return fooof_params_dict
  242. # %%
  243. # Extracts FOOOF parameters for all subjects and tasks, saving to the data path.
  244. # Note that it will return an error if any subjects are missing data for any task, which most will be, since only ~200 have session 2 data
  245. fooof_params_dict_master = extract_fooof_params(subject_ids, DATA_PATH)
  246. #Splits the dictionary into separate dictionaries for each task
  247. for task in task_combinations:
  248. task_name = task[1][0]
  249. session = task[1][1]
  250. acquisition = task[1][2]
  251. fooof_params_dict = fooof_params_dict_master[f'ses-{session}_task-{task_name}_acq-{acquisition}']
  252. np.save(RESULTS_PATH / f'fooof_params_dict_{task_name}_{session}_{acquisition}.npy', fooof_params_dict)
  253. # %% [markdown]
  254. # ## 3. Load FOOOF parameters, demographics, and start cleaning data.
  255. # %%
  256. #Load the demographic data - participants.tsv
  257. participants = pd.read_csv(os.path.join(DATA_PATH,'participants.tsv'),delimiter='\t')
  258. #Because I had to do the main processing in stages, there are duplicate csvs recording the removed channels and ICA components. We need to merge these into a single dataframe.
  259. channels_files = utils.scan_data_directory(os.path.join(os.path.abspath('..'),'notebooks'))
  260. channels_files = [file for file in channels_files if 'channels_removed' in file]
  261. channels_removed = pd.concat([pd.read_csv(file) for file in channels_files])
  262. channels_removed['ID'] = channels_removed['ID'].str.split('_')
  263. channels_removed['Task'] = channels_removed['ID'].str[2]
  264. channels_removed['Session'] = channels_removed['ID'].str[1]
  265. channels_removed['Acquisition'] = channels_removed['ID'].str[3]
  266. channels_removed['ID'] = channels_removed['ID'].str[0]
  267. channels_removed = channels_removed.pivot_table(index='ID',columns=['Task','Session','Acquisition'],values='Channels Removed',aggfunc='mean')
  268. channels_removed.columns = ['_'.join(col).strip() for col in channels_removed.columns.values]
  269. channels_removed.columns = ['Channels_Removed_' + col for col in channels_removed.columns]
  270. ica_files = utils.scan_data_directory(os.path.join(os.path.abspath('..'),'notebooks'))
  271. ica_files = [file for file in ica_files if 'ica_removed' in file]
  272. ica_removed = pd.concat([pd.read_csv(file) for file in ica_files])
  273. ica_removed['ID'] = ica_removed['ID'].str.split('_')
  274. ica_removed['Task'] = ica_removed['ID'].str[2]
  275. ica_removed['Session'] = ica_removed['ID'].str[1]
  276. ica_removed['Acquisition'] = ica_removed['ID'].str[3]
  277. ica_removed['ID'] = ica_removed['ID'].str[0]
  278. ica_removed = ica_removed.pivot_table(index='ID',columns=['Task','Session','Acquisition'],values='ICAs Removed',aggfunc='mean')
  279. ica_removed.columns = ['_'.join(col).strip() for col in ica_removed.columns.values]
  280. ica_removed.columns = ['ICA_Removed_' + col for col in ica_removed.columns]
  281. demographic_data = participants.merge(channels_removed, left_on='participant_id', right_on='ID')
  282. demographic_data = demographic_data.merge(ica_removed, left_on='participant_id', right_on='ID')
  283. # Load epoch counts
  284. epoch_nums = pd.read_csv('epoch_numbers.csv')
  285. fname_col = epoch_nums.columns[0]
  286. epoch_nums_parsed = epoch_nums.copy()
  287. epoch_nums_parsed[['subject', 'session', 'task_acq', 'rest']] = (
  288. epoch_nums_parsed[fname_col].astype(str).str.split('_', n=3, expand=True)
  289. )
  290. epoch_nums_parsed['task'] = epoch_nums_parsed['task_acq'].str.replace('task-', '', regex=False)
  291. epoch_nums_parsed['session'] = epoch_nums_parsed['session'].str.replace('ses-', '', regex=False)
  292. epoch_nums_parsed['acquisition'] = epoch_nums_parsed['rest'].str.extract(r'acq-([A-Za-z0-9]+)')
  293. epoch_nums_parsed['task_session_acq'] = (
  294. epoch_nums_parsed['task'] + '_' + epoch_nums_parsed['session'] + '_' + epoch_nums_parsed['acquisition']
  295. )
  296. needed_cols = ['subject', 'task_session_acq', 'Original_N', 'Retained_N']
  297. m = epoch_nums_parsed.melt(
  298. id_vars=['subject', 'task_session_acq'],
  299. value_vars=['Original_N', 'Retained_N'],
  300. var_name='metric', value_name='N'
  301. )
  302. m['metric_clean'] = m['metric'].str.replace('_N', '', regex=False) # 'Original' or 'Retained'
  303. m['wide_col'] = 'Epochs' + m['metric_clean'] + '_' + m['task_session_acq']
  304. epoch_nums_wide = (
  305. m.pivot_table(index='subject', columns='wide_col', values='N', aggfunc='first')
  306. .reset_index()
  307. .rename(columns={'subject': 'participant_id'})
  308. )
  309. demographic_data = demographic_data.merge(epoch_nums_wide, on='participant_id', how='left')
  310. for task in task_combinations:
  311. task_name = task[1][0]
  312. session = task[1][1]
  313. acquisition = task[1][2]
  314. retained_col = f'EpochsRetained_{task_name}_{session}_{acquisition}'
  315. original_col = f'EpochsOriginal_{task_name}_{session}_{acquisition}'
  316. demographic_data[f'EpochsProportion_{task_name}_{session}_{acquisition}'] = demographic_data[retained_col] / demographic_data[original_col]
  317. #Here, we create some simple flags for later filtering.
  318. # Eyes Closed: >50% retention in BOTH session 1 and session 2 pre tasks
  319. demographic_data['Good_Retention_EyesClosed'] = (
  320. (demographic_data['session2'] == 'yes') &
  321. (demographic_data['EpochsProportion_EyesClosed_1_pre'] > 0.5) &
  322. (demographic_data['EpochsProportion_EyesClosed_2_pre'] > 0.5)
  323. )
  324. # Eyes Open: >50% retention in BOTH session 1 and session 2 pre tasks
  325. demographic_data['Good_Retention_EyesOpen'] = (
  326. (demographic_data['session2'] == 'yes') &
  327. (demographic_data['EpochsProportion_EyesOpen_1_pre'] > 0.5) &
  328. (demographic_data['EpochsProportion_EyesOpen_2_pre'] > 0.5)
  329. )
  330. # Print summary
  331. print("Summary of good retention (>50% epochs retained in both sessions):")
  332. print(f"Eyes Closed - Good retention: {demographic_data['Good_Retention_EyesClosed'].sum()} participants")
  333. print(f"Eyes Open - Good retention: {demographic_data['Good_Retention_EyesOpen'].sum()} participants")
  334. print(f"\nParticipants with session2='yes': {(demographic_data['session2'] == 'yes').sum()}")
  335. print(f"Eyes Closed good retention (session2=yes): {demographic_data['Good_Retention_EyesClosed'].sum()} participants")
  336. print(f"Eyes Open good retention (session2=yes): {demographic_data['Good_Retention_EyesOpen'].sum()} participants")
  337. print("Participants with >50% channels removed in any task/timepoint:")
  338. print(demographic_data[((demographic_data['Channels_Removed_task-EyesClosed_ses-1_acq-pre'] > 31) & (demographic_data['Channels_Removed_task-EyesClosed_ses-1_acq-pre'] < 64)) | ((demographic_data['Channels_Removed_task-EyesClosed_ses-2_acq-pre'] > 31) & (demographic_data['Channels_Removed_task-EyesClosed_ses-2_acq-pre'] < 64)) | ((demographic_data['Channels_Removed_task-EyesOpen_ses-1_acq-pre'] > 31) & (demographic_data['Channels_Removed_task-EyesOpen_ses-1_acq-pre'] < 64)) | ((demographic_data['Channels_Removed_task-EyesOpen_ses-2_acq-pre'] > 31) & (demographic_data['Channels_Removed_task-EyesOpen_ses-2_acq-pre'] < 64))]['participant_id'].tolist())
  339. demographic_data['Good_Ref_EyesClosed'] = (
  340. (demographic_data['Channels_Removed_task-EyesClosed_ses-1_acq-pre'] < 99) &
  341. (demographic_data['Channels_Removed_task-EyesClosed_ses-2_acq-pre'] < 99))
  342. demographic_data['Good_Ref_EyesOpen'] = (
  343. (demographic_data['Channels_Removed_task-EyesOpen_ses-1_acq-pre'] < 99) &
  344. (demographic_data['Channels_Removed_task-EyesOpen_ses-2_acq-pre'] < 99)
  345. )
  346. print("Summary of good reference (no bad reference channels at both time points):")
  347. print(f"Eyes Closed - Good reference: {demographic_data['Good_Ref_EyesClosed'].sum()} participants")
  348. print(f"Eyes Open - Good reference: {demographic_data['Good_Ref_EyesOpen'].sum()} participants")
  349. # %%
  350. #For each task, load the FOOOF results into its own dictionary
  351. fooof_params_dict_EyesClosed_1_pre = np.load(RESULTS_PATH / 'fooof_params_dict_EyesClosed_1_pre.npy', allow_pickle=True)[()]
  352. fooof_params_dict_EyesClosed_1_post = np.load(RESULTS_PATH / 'fooof_params_dict_EyesClosed_1_post.npy', allow_pickle=True)[()]
  353. fooof_params_dict_EyesClosed_2_pre = np.load(RESULTS_PATH / 'fooof_params_dict_EyesClosed_2_pre.npy', allow_pickle=True)[()]
  354. fooof_params_dict_EyesClosed_2_post = np.load(RESULTS_PATH / 'fooof_params_dict_EyesClosed_2_post.npy', allow_pickle=True)[()]
  355. fooof_params_dict_EyesOpen_1_pre = np.load(RESULTS_PATH / 'fooof_params_dict_EyesOpen_1_pre.npy', allow_pickle=True)[()]
  356. fooof_params_dict_EyesOpen_1_post = np.load(RESULTS_PATH / 'fooof_params_dict_EyesOpen_1_post.npy', allow_pickle=True)[()]
  357. fooof_params_dict_EyesOpen_2_pre = np.load(RESULTS_PATH / 'fooof_params_dict_EyesOpen_2_pre.npy', allow_pickle=True)[()]
  358. fooof_params_dict_EyesOpen_2_post = np.load(RESULTS_PATH / 'fooof_params_dict_EyesOpen_2_post.npy', allow_pickle=True)[()]
  359. # %%
  360. #Generate and save the good_mask for each task dictonary
  361. from utils import analyze_bad_fits, analyze_missing_alpha
  362. fooof_params_dict_EyesClosed_1_pre['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesClosed_1_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  363. fooof_params_dict_EyesClosed_1_post['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesClosed_1_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  364. fooof_params_dict_EyesClosed_2_pre['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesClosed_2_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  365. fooof_params_dict_EyesClosed_2_post['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesClosed_2_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  366. fooof_params_dict_EyesOpen_1_pre['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesOpen_1_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  367. fooof_params_dict_EyesOpen_1_post['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesOpen_1_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  368. fooof_params_dict_EyesOpen_2_pre['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesOpen_2_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  369. fooof_params_dict_EyesOpen_2_post['good_mask'] = analyze_bad_fits(fooof_params_dict_EyesOpen_2_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  370. #And generate and save the nans for each task dictonary
  371. fooof_params_dict_EyesClosed_1_pre['nans'] = analyze_bad_fits(fooof_params_dict_EyesClosed_1_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  372. fooof_params_dict_EyesClosed_1_post['nans'] = analyze_bad_fits(fooof_params_dict_EyesClosed_1_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  373. fooof_params_dict_EyesClosed_2_pre['nans'] = analyze_bad_fits(fooof_params_dict_EyesClosed_2_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  374. fooof_params_dict_EyesClosed_2_post['nans'] = analyze_bad_fits(fooof_params_dict_EyesClosed_2_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  375. fooof_params_dict_EyesOpen_1_pre['nans'] = analyze_bad_fits(fooof_params_dict_EyesOpen_1_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  376. fooof_params_dict_EyesOpen_1_post['nans'] = analyze_bad_fits(fooof_params_dict_EyesOpen_1_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  377. fooof_params_dict_EyesOpen_2_pre['nans'] = analyze_bad_fits(fooof_params_dict_EyesOpen_2_pre, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  378. fooof_params_dict_EyesOpen_2_post['nans'] = analyze_bad_fits(fooof_params_dict_EyesOpen_2_post, fit_threshold=FIT_THRESHOLD, bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  379. #Generate and save the good_mask for each task dictonary based on missing alpha peaks
  380. fooof_params_dict_EyesClosed_1_pre['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesClosed_1_pre, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  381. fooof_params_dict_EyesClosed_1_post['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesClosed_1_post, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  382. fooof_params_dict_EyesClosed_2_pre['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesClosed_2_pre, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  383. fooof_params_dict_EyesClosed_2_post['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesClosed_2_post, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  384. fooof_params_dict_EyesOpen_1_pre['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesOpen_1_pre, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  385. fooof_params_dict_EyesOpen_1_post['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesOpen_1_post, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  386. fooof_params_dict_EyesOpen_2_pre['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesOpen_2_pre, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  387. fooof_params_dict_EyesOpen_2_post['good_mask_alpha'] = analyze_missing_alpha(fooof_params_dict_EyesOpen_2_post, bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  388. # %%
  389. #Generated and save good_mask based on exponent values being >0. If they're <0, we replace them with NaN.
  390. from utils import analyze_bad_exponents
  391. fooof_params_dict_EyesClosed_1_pre['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_1_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  392. fooof_params_dict_EyesClosed_1_post['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_1_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  393. fooof_params_dict_EyesClosed_2_pre['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_2_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  394. fooof_params_dict_EyesClosed_2_post['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_2_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  395. fooof_params_dict_EyesOpen_1_pre['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_1_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  396. fooof_params_dict_EyesOpen_1_post['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_1_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  397. fooof_params_dict_EyesOpen_2_pre['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_2_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  398. fooof_params_dict_EyesOpen_2_post['good_mask_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_2_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['good_mask']
  399. #and generate nans for bad exponents - but not overwriting existing nans, but adding to them.
  400. fooof_params_dict_EyesClosed_1_pre['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_1_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  401. fooof_params_dict_EyesClosed_1_post['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_1_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  402. fooof_params_dict_EyesClosed_2_pre['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_2_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  403. fooof_params_dict_EyesClosed_2_post['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesClosed_2_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  404. fooof_params_dict_EyesOpen_1_pre['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_1_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  405. fooof_params_dict_EyesOpen_1_post['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_1_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  406. fooof_params_dict_EyesOpen_2_pre['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_2_pre,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  407. fooof_params_dict_EyesOpen_2_post['nans_exponent'] = analyze_bad_exponents(fooof_params_dict_EyesOpen_2_post,bad_channel_prop=BAD_CHAN_THRESHOLD)['nans']
  408. # %%
  409. #count how many subjects were removed for each task
  410. removed_counts = []
  411. for task_dict, task_name in [
  412. (fooof_params_dict_EyesClosed_1_pre, "EyesClosed_1_pre"),
  413. (fooof_params_dict_EyesClosed_1_post, "EyesClosed_1_post"),
  414. (fooof_params_dict_EyesClosed_2_pre, "EyesClosed_2_pre"),
  415. (fooof_params_dict_EyesClosed_2_post, "EyesClosed_2_post"),
  416. (fooof_params_dict_EyesOpen_1_pre, "EyesOpen_1_pre"),
  417. (fooof_params_dict_EyesOpen_1_post, "EyesOpen_1_post"),
  418. (fooof_params_dict_EyesOpen_2_pre, "EyesOpen_2_pre"),
  419. (fooof_params_dict_EyesOpen_2_post, "EyesOpen_2_post")
  420. ]:
  421. removed = sum(not v for v in task_dict['good_mask'].values())
  422. removed_counts.append({'Task': task_name, 'Type': 'FOOOF_Fits', 'Subjects_Removed': removed})
  423. print(f"{task_name}: {removed} subjects removed")
  424. print()
  425. #And of those, howmany removed due to exponents <0
  426. for task_dict, task_name in [
  427. (fooof_params_dict_EyesClosed_1_pre, "EyesClosed_1_pre"),
  428. (fooof_params_dict_EyesClosed_1_post, "EyesClosed_1_post"),
  429. (fooof_params_dict_EyesClosed_2_pre, "EyesClosed_2_pre"),
  430. (fooof_params_dict_EyesClosed_2_post, "EyesClosed_2_post"),
  431. (fooof_params_dict_EyesOpen_1_pre, "EyesOpen_1_pre"),
  432. (fooof_params_dict_EyesOpen_1_post, "EyesOpen_1_post"),
  433. (fooof_params_dict_EyesOpen_2_pre, "EyesOpen_2_pre"),
  434. (fooof_params_dict_EyesOpen_2_post, "EyesOpen_2_post")
  435. ]:
  436. removed = sum(not v for v in task_dict['good_mask_exponent'].values())
  437. removed_counts.append({'Task': task_name, 'Type': 'Bad_Exponents', 'Subjects_Removed': removed})
  438. print(f"{task_name} (exponent): {removed} subjects removed")
  439. print()
  440. #Count how many subjects were removed for each task based on missing alpha peaks
  441. for task_dict, task_name in [
  442. (fooof_params_dict_EyesClosed_1_pre, "EyesClosed_1_pre"),
  443. (fooof_params_dict_EyesClosed_1_post, "EyesClosed_1_post"),
  444. (fooof_params_dict_EyesClosed_2_pre, "EyesClosed_2_pre"),
  445. (fooof_params_dict_EyesClosed_2_post, "EyesClosed_2_post"),
  446. (fooof_params_dict_EyesOpen_1_pre, "EyesOpen_1_pre"),
  447. (fooof_params_dict_EyesOpen_1_post, "EyesOpen_1_post"),
  448. (fooof_params_dict_EyesOpen_2_pre, "EyesOpen_2_pre"),
  449. (fooof_params_dict_EyesOpen_2_post, "EyesOpen_2_post")
  450. ]:
  451. removed = sum(not v for v in task_dict['good_mask_alpha'].values())
  452. removed_counts.append({'Task': task_name, 'Type': 'Alpha_Peaks', 'Subjects_Removed': removed})
  453. print(f"{task_name} (alpha): {removed} subjects removed")
  454. # Convert to DataFrame and save
  455. removed_counts_df = pd.DataFrame(removed_counts)
  456. removed_counts_df.to_csv(RESULTS_PATH / 'subjects_removed_summary.csv', index=False)
  457. print(f"\nSummary saved to {RESULTS_PATH / 'subjects_removed_summary.csv'}")
  458. # %%
  459. #append the good_mask information to the demographic data frame.
  460. for key, value in fooof_params_dict_EyesClosed_1_pre['good_mask'].items():
  461. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesClosed_1_pre'] = value
  462. for key, value in fooof_params_dict_EyesClosed_1_post['good_mask'].items():
  463. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesClosed_1_post'] = value
  464. for key, value in fooof_params_dict_EyesClosed_2_pre['good_mask'].items():
  465. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesClosed_2_pre'] = value
  466. for key, value in fooof_params_dict_EyesClosed_2_post['good_mask'].items():
  467. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesClosed_2_post'] = value
  468. for key, value in fooof_params_dict_EyesOpen_1_pre['good_mask'].items():
  469. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesOpen_1_pre'] = value
  470. for key, value in fooof_params_dict_EyesOpen_1_post['good_mask'].items():
  471. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesOpen_1_post'] = value
  472. for key, value in fooof_params_dict_EyesOpen_2_pre['good_mask'].items():
  473. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesOpen_2_pre'] = value
  474. for key, value in fooof_params_dict_EyesOpen_2_post['good_mask'].items():
  475. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Fits_EyesOpen_2_post'] = value
  476. for key, value in fooof_params_dict_EyesClosed_1_pre['good_mask_alpha'].items():
  477. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesClosed_1_pre'] = value
  478. for key, value in fooof_params_dict_EyesClosed_1_post['good_mask_alpha'].items():
  479. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesClosed_1_post'] = value
  480. for key, value in fooof_params_dict_EyesClosed_2_pre['good_mask_alpha'].items():
  481. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesClosed_2_pre'] = value
  482. for key, value in fooof_params_dict_EyesClosed_2_post['good_mask_alpha'].items():
  483. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesClosed_2_post'] = value
  484. for key, value in fooof_params_dict_EyesOpen_1_pre['good_mask_alpha'].items():
  485. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesOpen_1_pre'] = value
  486. for key, value in fooof_params_dict_EyesOpen_1_post['good_mask_alpha'].items():
  487. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesOpen_1_post'] = value
  488. for key, value in fooof_params_dict_EyesOpen_2_pre['good_mask_alpha'].items():
  489. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesOpen_2_pre'] = value
  490. for key, value in fooof_params_dict_EyesOpen_2_post['good_mask_alpha'].items():
  491. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Alpha_EyesOpen_2_post'] = value
  492. for key, value in fooof_params_dict_EyesClosed_1_pre['good_mask_exponent'].items():
  493. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesClosed_1_pre'] = value
  494. for key, value in fooof_params_dict_EyesClosed_1_post['good_mask_exponent'].items():
  495. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesClosed_1_post'] = value
  496. for key, value in fooof_params_dict_EyesClosed_2_pre['good_mask_exponent'].items():
  497. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesClosed_2_pre'] = value
  498. for key, value in fooof_params_dict_EyesClosed_2_post['good_mask_exponent'].items():
  499. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesClosed_2_post'] = value
  500. for key, value in fooof_params_dict_EyesOpen_1_pre['good_mask_exponent'].items():
  501. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesOpen_1_pre'] = value
  502. for key, value in fooof_params_dict_EyesOpen_1_post['good_mask_exponent'].items():
  503. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesOpen_1_post'] = value
  504. for key, value in fooof_params_dict_EyesOpen_2_pre['good_mask_exponent'].items():
  505. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesOpen_2_pre'] = value
  506. for key, value in fooof_params_dict_EyesOpen_2_post['good_mask_exponent'].items():
  507. demographic_data.loc[demographic_data['participant_id'] == key, 'Good_Exponent_EyesOpen_2_post'] = value
  508. #calculate sum of good fits across all tasks for each subject and each task type
  509. demographic_data['Total_Good_Fits'] = demographic_data[['Good_Fits_EyesClosed_1_pre', 'Good_Fits_EyesClosed_1_post', 'Good_Fits_EyesClosed_2_pre', 'Good_Fits_EyesClosed_2_post', 'Good_Fits_EyesOpen_1_pre', 'Good_Fits_EyesOpen_1_post', 'Good_Fits_EyesOpen_2_pre', 'Good_Fits_EyesOpen_2_post']].sum(axis=1)
  510. demographic_data['Total_Good_Fits_Pre'] = demographic_data[['Good_Fits_EyesClosed_1_pre', 'Good_Fits_EyesClosed_2_pre', 'Good_Fits_EyesOpen_1_pre', 'Good_Fits_EyesOpen_2_pre']].sum(axis=1)
  511. demographic_data['Total_Good_Fits_Post'] = demographic_data[['Good_Fits_EyesClosed_1_post', 'Good_Fits_EyesClosed_2_post', 'Good_Fits_EyesOpen_1_post', 'Good_Fits_EyesOpen_2_post']].sum(axis=1)
  512. #and for alpha metrics
  513. demographic_data['Total_Good_Alpha'] = demographic_data[['Good_Alpha_EyesClosed_1_pre', 'Good_Alpha_EyesClosed_1_post', 'Good_Alpha_EyesClosed_2_pre', 'Good_Alpha_EyesClosed_2_post', 'Good_Alpha_EyesOpen_1_pre', 'Good_Alpha_EyesOpen_1_post',
  514. 'Good_Alpha_EyesOpen_2_pre', 'Good_Alpha_EyesOpen_2_post']].sum(axis=1)
  515. demographic_data['Total_Good_Alpha_Pre'] = demographic_data[['Good_Alpha_EyesClosed_1_pre', 'Good_Alpha_EyesClosed_2_pre', 'Good_Alpha_EyesOpen_1_pre', 'Good_Alpha_EyesOpen_2_pre']].sum(axis=1)
  516. demographic_data['Total_Good_Alpha_Post'] = demographic_data[['Good_Alpha_EyesClosed_1_post', 'Good_Alpha_EyesClosed_2_post', 'Good_Alpha_EyesOpen_1_post', 'Good_Alpha_EyesOpen_2_post']].sum(axis=1)
  517. #and for exponent metrics
  518. demographic_data['Total_Good_Exponent'] = demographic_data[['Good_Exponent_EyesClosed_1_pre', 'Good_Exponent_EyesClosed_1_post', 'Good_Exponent_EyesClosed_2_pre', 'Good_Exponent_EyesClosed_2_post', 'Good_Exponent_EyesOpen_1_pre', 'Good_Exponent_EyesOpen_1_post',
  519. 'Good_Exponent_EyesOpen_2_pre', 'Good_Exponent_EyesOpen_2_post']].sum(axis=1)
  520. demographic_data['Total_Good_Exponent_Pre'] = demographic_data[['Good_Exponent_EyesClosed_1_pre', 'Good_Exponent_EyesClosed_2_pre', 'Good_Exponent_EyesOpen_1_pre', 'Good_Exponent_EyesOpen_2_pre']].sum(axis=1)
  521. demographic_data['Total_Good_Exponent_Post'] = demographic_data[['Good_Exponent_EyesClosed_1_post', 'Good_Exponent_EyesClosed_2_post', 'Good_Exponent_EyesOpen_1_post', 'Good_Exponent_EyesOpen_2_post']].sum(axis=1)
  522. # %%
  523. #Here, we create interpolated mean values for each FOOOF parameter in each task dictionary
  524. from utils import replace_nans_with_mean_alpha_new, replace_nans_with_mean_new, replace_nans_with_mean_exponent
  525. fooof_params_dict_EyesClosed_1_pre['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_pre, 'exponents')
  526. fooof_params_dict_EyesClosed_1_post['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_post, 'exponents')
  527. fooof_params_dict_EyesClosed_2_pre['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_pre, 'exponents')
  528. fooof_params_dict_EyesClosed_2_post['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_post, 'exponents')
  529. fooof_params_dict_EyesOpen_1_pre['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_pre, 'exponents')
  530. fooof_params_dict_EyesOpen_1_post['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_post, 'exponents')
  531. fooof_params_dict_EyesOpen_2_pre['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_pre, 'exponents')
  532. fooof_params_dict_EyesOpen_2_post['exponent_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_post, 'exponents')
  533. fooof_params_dict_EyesClosed_1_pre['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_pre, 'offsets')
  534. fooof_params_dict_EyesClosed_1_post['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_post, 'offsets')
  535. fooof_params_dict_EyesClosed_2_pre['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_pre, 'offsets')
  536. fooof_params_dict_EyesClosed_2_post['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_post, 'offsets')
  537. fooof_params_dict_EyesOpen_1_pre['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_pre, 'offsets')
  538. fooof_params_dict_EyesOpen_1_post['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_post, 'offsets')
  539. fooof_params_dict_EyesOpen_2_pre['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_pre, 'offsets')
  540. fooof_params_dict_EyesOpen_2_post['offset_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_post, 'offsets')
  541. fooof_params_dict_EyesClosed_1_pre['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_pre, 'fits')
  542. fooof_params_dict_EyesClosed_1_post['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_post, 'fits')
  543. fooof_params_dict_EyesClosed_2_pre['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_pre, 'fits')
  544. fooof_params_dict_EyesClosed_2_post['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_post, 'fits')
  545. fooof_params_dict_EyesOpen_1_pre['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_pre, 'fits')
  546. fooof_params_dict_EyesOpen_1_post['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_post, 'fits')
  547. fooof_params_dict_EyesOpen_2_pre['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_pre, 'fits')
  548. fooof_params_dict_EyesOpen_2_post['fits_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_post, 'fits')
  549. fooof_params_dict_EyesClosed_1_pre['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_pre, 'errors')
  550. fooof_params_dict_EyesClosed_1_post['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_1_post, 'errors')
  551. fooof_params_dict_EyesClosed_2_pre['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_pre, 'errors')
  552. fooof_params_dict_EyesClosed_2_post['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesClosed_2_post, 'errors')
  553. fooof_params_dict_EyesOpen_1_pre['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_pre, 'errors')
  554. fooof_params_dict_EyesOpen_1_post['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_1_post, 'errors')
  555. fooof_params_dict_EyesOpen_2_pre['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_pre, 'errors')
  556. fooof_params_dict_EyesOpen_2_post['error_mean_interp'] = replace_nans_with_mean_new(fooof_params_dict_EyesOpen_2_post, 'errors')
  557. fooof_params_dict_EyesClosed_1_pre['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_1_pre, 'alpha_freq')
  558. fooof_params_dict_EyesClosed_1_post['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_1_post, 'alpha_freq')
  559. fooof_params_dict_EyesClosed_2_pre['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_2_pre, 'alpha_freq')
  560. fooof_params_dict_EyesClosed_2_post['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_2_post, 'alpha_freq')
  561. fooof_params_dict_EyesOpen_1_pre['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_1_pre, 'alpha_freq')
  562. fooof_params_dict_EyesOpen_1_post['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_1_post, 'alpha_freq')
  563. fooof_params_dict_EyesOpen_2_pre['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_2_pre, 'alpha_freq')
  564. fooof_params_dict_EyesOpen_2_post['alpha_freq_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_2_post, 'alpha_freq')
  565. fooof_params_dict_EyesClosed_1_pre['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_1_pre, 'alpha_amp')
  566. fooof_params_dict_EyesClosed_1_post['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_1_post, 'alpha_amp')
  567. fooof_params_dict_EyesClosed_2_pre['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_2_pre, 'alpha_amp')
  568. fooof_params_dict_EyesClosed_2_post['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesClosed_2_post, 'alpha_amp')
  569. fooof_params_dict_EyesOpen_1_pre['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_1_pre, 'alpha_amp')
  570. fooof_params_dict_EyesOpen_1_post['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_1_post, 'alpha_amp')
  571. fooof_params_dict_EyesOpen_2_pre['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_2_pre, 'alpha_amp')
  572. fooof_params_dict_EyesOpen_2_post['alpha_amp_mean_interp'] = replace_nans_with_mean_alpha_new(fooof_params_dict_EyesOpen_2_post, 'alpha_amp')
  573. #and for the exponent, replace chans with bad exponents with the mean value across that subject.
  574. fooof_params_dict_EyesClosed_1_pre['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesClosed_1_pre, 'exponents')
  575. fooof_params_dict_EyesClosed_1_post['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesClosed_1_post, 'exponents')
  576. fooof_params_dict_EyesClosed_2_pre['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesClosed_2_pre, 'exponents')
  577. fooof_params_dict_EyesClosed_2_post['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesClosed_2_post, 'exponents')
  578. fooof_params_dict_EyesOpen_1_pre['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesOpen_1_pre, 'exponents')
  579. fooof_params_dict_EyesOpen_1_post['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesOpen_1_post, 'exponents')
  580. fooof_params_dict_EyesOpen_2_pre['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesOpen_2_pre, 'exponents')
  581. fooof_params_dict_EyesOpen_2_post['exponent_mean_interp'] = replace_nans_with_mean_exponent(fooof_params_dict_EyesOpen_2_post, 'exponents')
  582. # %%
  583. # Calculate mean fit and error for each subject for each task and session, adding to demographic_data. This time, we're using the interpolated values
  584. for task in tasks:
  585. for session in ['1', '2']:
  586. for acq in ['pre']:
  587. task_key = f'{task}_{session}_{acq}'
  588. fooof_dict_name = f'fooof_params_dict_{task}_{session}_{acq}'
  589. if fooof_dict_name in globals():
  590. fooof_dict = globals()[fooof_dict_name]
  591. col_name = f'Mean_Fits_interp_{task_key}'
  592. demographic_data[col_name] = demographic_data['participant_id'].map(fooof_dict['fits_mean_interp']).apply(
  593. lambda x: np.nanmean([v for v in x if not np.isnan(v)]) if isinstance(x, (list, np.ndarray)) else np.nan
  594. )
  595. col_name_err = f'Mean_Error_interp_{task_key}'
  596. demographic_data[col_name_err] = demographic_data['participant_id'].map(fooof_dict['error_mean_interp']).apply(
  597. lambda x: np.nanmean([v for v in x if not np.isnan(v)]) if isinstance(x, (list, np.ndarray)) else np.nan
  598. )
  599. #and again, but for non-interpolated values
  600. for task in tasks:
  601. for session in ['1', '2']:
  602. for acq in ['pre']:
  603. task_key = f'{task}_{session}_{acq}'
  604. fooof_dict_name = f'fooof_params_dict_{task}_{session}_{acq}'
  605. if fooof_dict_name in globals():
  606. fooof_dict = globals()[fooof_dict_name]
  607. col_name = f'Mean_Fits_{task_key}'
  608. demographic_data[col_name] = demographic_data['participant_id'].map(fooof_dict['fits']).apply(
  609. lambda x: np.nanmean([v for v in x if not np.isnan(v)]) if isinstance(x, (list, np.ndarray)) else np.nan
  610. )
  611. col_name_err = f'Mean_Error_{task_key}'
  612. demographic_data[col_name_err] = demographic_data['participant_id'].map(fooof_dict['errors']).apply(
  613. lambda x: np.nanmean([v for v in x if not np.isnan(v)]) if isinstance(x, (list, np.ndarray)) else np.nan
  614. )
  615. # %% [markdown]
  616. # ## 4. PCA and KMeans clustering for each task and measure.
  617. # For these, we only include subjects with good fits in all 4 tasks that we care about (EyesClosed_1_pre, EyesClosed_2_pre, EyesOpen_1_pre, EyesOpen_2_pre).
  618. #
  619. # Due to multiple time points, we will only do pca and clustering on time 1 data, but separetly for each measure and eyes open vs closed.
  620. #
  621. # To do, comment code, build functions for the reorganizing of the data for pca and kmeans, and build functions for those.
  622. # %%
  623. #Load subject 101 epoched data to get channel names
  624. chan_info = mne.io.read_info(os.path.join(os.path.abspath('..'),'Data','sub-101','ses-1','eeg','sub-101_ses-1_task-EyesClosed_acq-pre_proc-epo_eeg.fif'))
  625. # %%
  626. #Create dataframes for each measure, which are a subset of subjects with good fits/peaksfor the tasks of interest. We won't use these for the actual ICCs/HLMs, but to identify clusters.
  627. from turtle import left
  628. exponent_df_EyesClosed_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_1_pre['exponent_mean_interp'], orient='index', columns=[f'Exponent_EyesClosed_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  629. exponent_df_EyesClosed_1_pre = exponent_df_EyesClosed_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  630. exponent_df_EyesClosed_1_pre = exponent_df_EyesClosed_1_pre[(exponent_df_EyesClosed_1_pre['Total_Good_Fits_Pre'] == 4) & (exponent_df_EyesClosed_1_pre['Good_Retention_EyesClosed'] == 1) & (exponent_df_EyesClosed_1_pre['Good_Ref_EyesClosed'] == 1)]
  631. exponent_df_EyesClosed_1_pre = exponent_df_EyesClosed_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  632. exponent_df_EyesClosed_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_2_pre['exponent_mean_interp'], orient='index', columns=[f'Exponent_EyesClosed_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  633. exponent_df_EyesClosed_2_pre = exponent_df_EyesClosed_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  634. exponent_df_EyesClosed_2_pre = exponent_df_EyesClosed_2_pre[(exponent_df_EyesClosed_2_pre['Total_Good_Fits_Pre'] == 4) & (exponent_df_EyesClosed_2_pre['Good_Retention_EyesClosed'] == 1) & (exponent_df_EyesClosed_2_pre['Good_Ref_EyesClosed'] == 1)]
  635. exponent_df_EyesClosed_2_pre = exponent_df_EyesClosed_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  636. exponent_df_EyesOpen_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_1_pre['exponent_mean_interp'], orient='index', columns=[f'Exponent_EyesOpen_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  637. exponent_df_EyesOpen_1_pre = exponent_df_EyesOpen_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  638. exponent_df_EyesOpen_1_pre = exponent_df_EyesOpen_1_pre[(exponent_df_EyesOpen_1_pre['Total_Good_Fits_Pre'] == 4) & (exponent_df_EyesOpen_1_pre['Good_Retention_EyesOpen'] == 1) & (exponent_df_EyesOpen_1_pre['Good_Ref_EyesOpen'] == 1)]
  639. exponent_df_EyesOpen_1_pre = exponent_df_EyesOpen_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  640. exponent_df_EyesOpen_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_2_pre['exponent_mean_interp'], orient='index', columns=[f'Exponent_EyesOpen_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  641. exponent_df_EyesOpen_2_pre = exponent_df_EyesOpen_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  642. exponent_df_EyesOpen_2_pre = exponent_df_EyesOpen_2_pre[(exponent_df_EyesOpen_2_pre['Total_Good_Fits_Pre'] == 4) & (exponent_df_EyesOpen_2_pre['Good_Retention_EyesOpen'] == 1) & (exponent_df_EyesOpen_2_pre['Good_Ref_EyesOpen'] == 1)]
  643. exponent_df_EyesOpen_2_pre = exponent_df_EyesOpen_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  644. offset_df_EyesClosed_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_1_pre['offset_mean_interp'], orient='index', columns=[f'Offset_EyesClosed_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  645. offset_df_EyesClosed_1_pre = offset_df_EyesClosed_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  646. offset_df_EyesClosed_1_pre = offset_df_EyesClosed_1_pre[(offset_df_EyesClosed_1_pre['Total_Good_Fits_Pre'] == 4) & (offset_df_EyesClosed_1_pre['Good_Retention_EyesClosed'] == 1) & (offset_df_EyesClosed_1_pre['Good_Ref_EyesClosed'] == 1)]
  647. offset_df_EyesClosed_1_pre = offset_df_EyesClosed_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  648. offset_df_EyesClosed_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_2_pre['offset_mean_interp'], orient='index', columns=[f'Offset_EyesClosed_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  649. offset_df_EyesClosed_2_pre = offset_df_EyesClosed_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  650. offset_df_EyesClosed_2_pre = offset_df_EyesClosed_2_pre[(offset_df_EyesClosed_2_pre['Total_Good_Fits_Pre'] == 4) & (offset_df_EyesClosed_2_pre['Good_Retention_EyesClosed'] == 1) & (offset_df_EyesClosed_2_pre['Good_Ref_EyesClosed'] == 1)]
  651. offset_df_EyesClosed_2_pre = offset_df_EyesClosed_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  652. offset_df_EyesOpen_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_1_pre['offset_mean_interp'], orient='index', columns=[f'Offset_EyesOpen_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  653. offset_df_EyesOpen_1_pre = offset_df_EyesOpen_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  654. offset_df_EyesOpen_1_pre = offset_df_EyesOpen_1_pre[(offset_df_EyesOpen_1_pre['Total_Good_Fits_Pre'] == 4) & (offset_df_EyesOpen_1_pre['Good_Retention_EyesOpen'] == 1) & (offset_df_EyesOpen_1_pre['Good_Ref_EyesOpen'] == 1)]
  655. offset_df_EyesOpen_1_pre = offset_df_EyesOpen_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  656. offset_df_EyesOpen_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_2_pre['offset_mean_interp'], orient='index', columns=[f'Offset_EyesOpen_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  657. offset_df_EyesOpen_2_pre = offset_df_EyesOpen_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  658. offset_df_EyesOpen_2_pre = offset_df_EyesOpen_2_pre[(offset_df_EyesOpen_2_pre['Total_Good_Fits_Pre'] == 4) & (offset_df_EyesOpen_2_pre['Good_Retention_EyesOpen'] == 1) & (offset_df_EyesOpen_2_pre['Good_Ref_EyesOpen'] == 1)]
  659. offset_df_EyesOpen_2_pre = offset_df_EyesOpen_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  660. alpha_freq_df_EyesClosed_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_1_pre['alpha_freq_mean_interp'], orient='index', columns=[f'AlphaFreq_EyesClosed_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  661. alpha_freq_df_EyesClosed_1_pre = alpha_freq_df_EyesClosed_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  662. alpha_freq_df_EyesClosed_1_pre = alpha_freq_df_EyesClosed_1_pre[(alpha_freq_df_EyesClosed_1_pre['Total_Good_Fits_Pre'] == 4) & (alpha_freq_df_EyesClosed_1_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_freq_df_EyesClosed_1_pre['Good_Retention_EyesClosed'] == 1) & (alpha_freq_df_EyesClosed_1_pre['Good_Ref_EyesClosed'] == 1)]
  663. alpha_freq_df_EyesClosed_1_pre = alpha_freq_df_EyesClosed_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  664. alpha_freq_df_EyesClosed_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_2_pre['alpha_freq_mean_interp'], orient='index', columns=[f'AlphaFreq_EyesClosed_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  665. alpha_freq_df_EyesClosed_2_pre = alpha_freq_df_EyesClosed_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  666. alpha_freq_df_EyesClosed_2_pre = alpha_freq_df_EyesClosed_2_pre[(alpha_freq_df_EyesClosed_2_pre['Total_Good_Fits_Pre'] == 4) & (alpha_freq_df_EyesClosed_2_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_freq_df_EyesClosed_2_pre['Good_Retention_EyesClosed'] == 1) & (alpha_freq_df_EyesClosed_2_pre['Good_Ref_EyesClosed'] == 1)]
  667. alpha_freq_df_EyesClosed_2_pre = alpha_freq_df_EyesClosed_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  668. alpha_freq_df_EyesOpen_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_1_pre['alpha_freq_mean_interp'], orient='index', columns=[f'AlphaFreq_EyesOpen_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  669. alpha_freq_df_EyesOpen_1_pre = alpha_freq_df_EyesOpen_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  670. alpha_freq_df_EyesOpen_1_pre = alpha_freq_df_EyesOpen_1_pre[(alpha_freq_df_EyesOpen_1_pre['Total_Good_Fits_Pre'] == 4) & (alpha_freq_df_EyesOpen_1_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_freq_df_EyesOpen_1_pre['Good_Retention_EyesOpen'] == 1) & (alpha_freq_df_EyesOpen_1_pre['Good_Ref_EyesOpen'] == 1)]
  671. alpha_freq_df_EyesOpen_1_pre = alpha_freq_df_EyesOpen_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  672. alpha_freq_df_EyesOpen_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_2_pre['alpha_freq_mean_interp'], orient='index', columns=[f'AlphaFreq_EyesOpen_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  673. alpha_freq_df_EyesOpen_2_pre = alpha_freq_df_EyesOpen_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  674. alpha_freq_df_EyesOpen_2_pre = alpha_freq_df_EyesOpen_2_pre[(alpha_freq_df_EyesOpen_2_pre['Total_Good_Fits_Pre'] == 4) & (alpha_freq_df_EyesOpen_2_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_freq_df_EyesOpen_2_pre['Good_Retention_EyesOpen'] == 1) & (alpha_freq_df_EyesOpen_2_pre['Good_Ref_EyesOpen'] == 1)]
  675. alpha_freq_df_EyesOpen_2_pre = alpha_freq_df_EyesOpen_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  676. alpha_amp_df_EyesClosed_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_1_pre['alpha_amp_mean_interp'], orient='index', columns=[f'AlphaAmp_EyesClosed_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  677. alpha_amp_df_EyesClosed_1_pre = alpha_amp_df_EyesClosed_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  678. alpha_amp_df_EyesClosed_1_pre = alpha_amp_df_EyesClosed_1_pre[(alpha_amp_df_EyesClosed_1_pre['Total_Good_Fits_Pre'] == 4) & (alpha_amp_df_EyesClosed_1_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_amp_df_EyesClosed_1_pre['Good_Retention_EyesClosed'] == 1) & (alpha_amp_df_EyesClosed_1_pre['Good_Ref_EyesClosed'] == 1)]
  679. alpha_amp_df_EyesClosed_1_pre = alpha_amp_df_EyesClosed_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  680. alpha_amp_df_EyesClosed_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_2_pre['alpha_amp_mean_interp'], orient='index', columns=[f'AlphaAmp_EyesClosed_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  681. alpha_amp_df_EyesClosed_2_pre = alpha_amp_df_EyesClosed_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed' ]], left_index=True, right_on='participant_id')
  682. alpha_amp_df_EyesClosed_2_pre = alpha_amp_df_EyesClosed_2_pre[(alpha_amp_df_EyesClosed_2_pre['Total_Good_Fits_Pre'] == 4) & (alpha_amp_df_EyesClosed_2_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_amp_df_EyesClosed_2_pre['Good_Retention_EyesClosed'] == 1) & (alpha_amp_df_EyesClosed_2_pre['Good_Ref_EyesClosed'] == 1)]
  683. alpha_amp_df_EyesClosed_2_pre = alpha_amp_df_EyesClosed_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  684. alpha_amp_df_EyesOpen_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_1_pre['alpha_amp_mean_interp'], orient='index', columns=[f'AlphaAmp_EyesOpen_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  685. alpha_amp_df_EyesOpen_1_pre = alpha_amp_df_EyesOpen_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  686. alpha_amp_df_EyesOpen_1_pre = alpha_amp_df_EyesOpen_1_pre[(alpha_amp_df_EyesOpen_1_pre['Total_Good_Fits_Pre'] == 4) & (alpha_amp_df_EyesOpen_1_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_amp_df_EyesOpen_1_pre['Good_Retention_EyesOpen'] == 1) & (alpha_amp_df_EyesOpen_1_pre['Good_Ref_EyesOpen'] == 1)]
  687. alpha_amp_df_EyesOpen_1_pre = alpha_amp_df_EyesOpen_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  688. alpha_amp_df_EyesOpen_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_2_pre['alpha_amp_mean_interp'], orient='index', columns=[f'AlphaAmp_EyesOpen_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  689. alpha_amp_df_EyesOpen_2_pre = alpha_amp_df_EyesOpen_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  690. alpha_amp_df_EyesOpen_2_pre = alpha_amp_df_EyesOpen_2_pre[(alpha_amp_df_EyesOpen_2_pre['Total_Good_Fits_Pre'] == 4) & (alpha_amp_df_EyesOpen_2_pre['Total_Good_Alpha_Pre'] == 4) & (alpha_amp_df_EyesOpen_2_pre['Good_Retention_EyesOpen'] == 1) & (alpha_amp_df_EyesOpen_2_pre['Good_Ref_EyesOpen'] == 1)]
  691. alpha_amp_df_EyesOpen_2_pre = alpha_amp_df_EyesOpen_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  692. #Absolute alpha power *could* use the entire sample of participants, as FOOOF paramaters don't really factor in. However, doing so would make it less comparable to the other alpha metrics.
  693. #Therefore, the same subset of participants with good fits/peaks for the alpha metrics will be used for absolute alpha power, to keep it consistent across measures.
  694. abs_alpha_df_EyesClosed_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_1_pre['alpha_absolute'], orient='index', columns=[f'AbsAlpha_EyesClosed_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  695. abs_alpha_df_EyesClosed_1_pre = abs_alpha_df_EyesClosed_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  696. abs_alpha_df_EyesClosed_1_pre = abs_alpha_df_EyesClosed_1_pre[(abs_alpha_df_EyesClosed_1_pre['Total_Good_Fits_Pre'] == 4) & (abs_alpha_df_EyesClosed_1_pre['Total_Good_Alpha_Pre'] == 4) & (abs_alpha_df_EyesClosed_1_pre['Good_Retention_EyesClosed'] == 1) & (abs_alpha_df_EyesClosed_1_pre['Good_Ref_EyesClosed'] == 1)]
  697. abs_alpha_df_EyesClosed_1_pre = abs_alpha_df_EyesClosed_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  698. abs_alpha_df_EyesClosed_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesClosed_2_pre['alpha_absolute'], orient='index', columns=[f'AbsAlpha_EyesClosed_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  699. abs_alpha_df_EyesClosed_2_pre = abs_alpha_df_EyesClosed_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesClosed','Good_Ref_EyesClosed']], left_index=True, right_on='participant_id')
  700. abs_alpha_df_EyesClosed_2_pre = abs_alpha_df_EyesClosed_2_pre[(abs_alpha_df_EyesClosed_2_pre['Total_Good_Fits_Pre'] == 4) & (abs_alpha_df_EyesClosed_2_pre['Total_Good_Alpha_Pre'] == 4) & (abs_alpha_df_EyesClosed_2_pre['Good_Retention_EyesClosed'] == 1) & (abs_alpha_df_EyesClosed_2_pre['Good_Ref_EyesClosed'] == 1)]
  701. abs_alpha_df_EyesClosed_2_pre = abs_alpha_df_EyesClosed_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesClosed','Good_Ref_EyesClosed'])
  702. abs_alpha_df_EyesOpen_1_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_1_pre['alpha_absolute'], orient='index', columns=[f'AbsAlpha_EyesOpen_1_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  703. abs_alpha_df_EyesOpen_1_pre = abs_alpha_df_EyesOpen_1_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  704. abs_alpha_df_EyesOpen_1_pre = abs_alpha_df_EyesOpen_1_pre[(abs_alpha_df_EyesOpen_1_pre['Total_Good_Fits_Pre'] == 4) & (abs_alpha_df_EyesOpen_1_pre['Total_Good_Alpha_Pre'] == 4) & (abs_alpha_df_EyesOpen_1_pre['Good_Retention_EyesOpen'] == 1) & (abs_alpha_df_EyesOpen_1_pre['Good_Ref_EyesOpen'] == 1)]
  705. abs_alpha_df_EyesOpen_1_pre = abs_alpha_df_EyesOpen_1_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  706. abs_alpha_df_EyesOpen_2_pre = pd.DataFrame.from_dict(fooof_params_dict_EyesOpen_2_pre['alpha_absolute'], orient='index', columns=[f'AbsAlpha_EyesOpen_2_pre_Ch{ch+1}' for ch in range(len(chan_info['ch_names']))])
  707. abs_alpha_df_EyesOpen_2_pre = abs_alpha_df_EyesOpen_2_pre.merge(demographic_data[['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre','Good_Retention_EyesOpen','Good_Ref_EyesOpen']], left_index=True, right_on='participant_id')
  708. abs_alpha_df_EyesOpen_2_pre = abs_alpha_df_EyesOpen_2_pre[(abs_alpha_df_EyesOpen_2_pre['Total_Good_Fits_Pre'] == 4) & (abs_alpha_df_EyesOpen_2_pre['Total_Good_Alpha_Pre'] == 4) & (abs_alpha_df_EyesOpen_2_pre['Good_Retention_EyesOpen'] == 1) & (abs_alpha_df_EyesOpen_2_pre['Good_Ref_EyesOpen'] == 1)]
  709. abs_alpha_df_EyesOpen_2_pre = abs_alpha_df_EyesOpen_2_pre.drop(columns=['participant_id', 'Total_Good_Fits_Pre', 'Total_Good_Alpha_Pre', 'Good_Retention_EyesOpen','Good_Ref_EyesOpen'])
  710. # %%
  711. #Run PCA on each dataframe and general plots.
  712. #Looking at the scree plots, it looks like 2 components is a good choice for all measures and both tasks.
  713. from utils import run_pca_and_plot
  714. plot_pca = False
  715. pca_exp_EyesClosed_1_pre = run_pca_and_plot(exponent_df_EyesClosed_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Exponent EC1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  716. pca_exp_EyesClosed_2_pre = run_pca_and_plot(exponent_df_EyesClosed_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Exponent EC2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  717. pca_exp_EyesOpen_1_pre = run_pca_and_plot(exponent_df_EyesOpen_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Exponent EO1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  718. pca_exp_EyesOpen_2_pre = run_pca_and_plot(exponent_df_EyesOpen_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Exponent EO2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  719. pca_off_EyesClosed_1_pre = run_pca_and_plot(offset_df_EyesClosed_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Offset EC1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  720. pca_off_EyesClosed_2_pre = run_pca_and_plot(offset_df_EyesClosed_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Offset EC2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  721. pca_off_EyesOpen_1_pre = run_pca_and_plot(offset_df_EyesOpen_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Offset EO1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  722. pca_off_EyesOpen_2_pre = run_pca_and_plot(offset_df_EyesOpen_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='Offset EO2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  723. pca_af_EyesClosed_1_pre = run_pca_and_plot(alpha_freq_df_EyesClosed_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaFreq EC1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  724. pca_af_EyesClosed_2_pre = run_pca_and_plot(alpha_freq_df_EyesClosed_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaFreq EC2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  725. pca_af_EyesOpen_1_pre = run_pca_and_plot(alpha_freq_df_EyesOpen_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaFreq EO1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  726. pca_af_EyesOpen_2_pre = run_pca_and_plot(alpha_freq_df_EyesOpen_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaFreq EO2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  727. pca_aa_EyesClosed_1_pre = run_pca_and_plot(alpha_amp_df_EyesClosed_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaAmp EC1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  728. pca_aa_EyesClosed_2_pre = run_pca_and_plot(alpha_amp_df_EyesClosed_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaAmp EC2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  729. pca_aa_EyesOpen_1_pre = run_pca_and_plot(alpha_amp_df_EyesOpen_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaAmp EO1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  730. pca_aa_EyesOpen_2_pre = run_pca_and_plot(alpha_amp_df_EyesOpen_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AlphaAmp EO2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  731. pca_aaf_EyesClosed_1_pre = run_pca_and_plot(abs_alpha_df_EyesClosed_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AbsAlpha EC1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  732. pca_aaf_EyesClosed_2_pre = run_pca_and_plot(abs_alpha_df_EyesClosed_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AbsAlpha EC2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  733. pca_aaf_EyesOpen_1_pre = run_pca_and_plot(abs_alpha_df_EyesOpen_1_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AbsAlpha EO1 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  734. pca_aaf_EyesOpen_2_pre = run_pca_and_plot(abs_alpha_df_EyesOpen_2_pre, chan_names=chan_info['ch_names'], n_components=10, title_prefix='AbsAlpha EO2 Pre',plot=plot_pca,save_path=str(FIGURES_PATH))
  735. # %%
  736. #Get kmeans diagnostics to help choose k for clustering channels
  737. from utils import kmeans_diagnostics
  738. k_range = range(1,11)
  739. res_diag_exponent_EyesClosed_1_pre = kmeans_diagnostics(exponent_df_EyesClosed_1_pre, k_values=k_range, transpose=True, save_prefix='Exponent_EyesClosed_1_pre', show=False,save_path=str(FIGURES_PATH))
  740. res_diag_exponent_EyesClosed_2_pre = kmeans_diagnostics(exponent_df_EyesClosed_2_pre, k_values=k_range, transpose=True, save_prefix='Exponent_EyesClosed_2_pre', show=False,save_path=str(FIGURES_PATH))
  741. res_diag_exponent_EyesOpen_1_pre = kmeans_diagnostics(exponent_df_EyesOpen_1_pre, k_values=k_range, transpose=True, save_prefix='Exponent_EyesOpen_1_pre', show=False,save_path=str(FIGURES_PATH))
  742. res_diag_exponent_EyesOpen_2_pre = kmeans_diagnostics(exponent_df_EyesOpen_2_pre, k_values=k_range, transpose=True, save_prefix='Exponent_EyesOpen_2_pre', show=False,save_path=str(FIGURES_PATH))
  743. res_diag_offset_EyesClosed_1_pre = kmeans_diagnostics(offset_df_EyesClosed_1_pre, k_values=k_range, transpose=True, save_prefix='Offset_EyesClosed_1_pre', show=False,save_path=str(FIGURES_PATH))
  744. res_diag_offset_EyesClosed_2_pre = kmeans_diagnostics(offset_df_EyesClosed_2_pre, k_values=k_range, transpose=True, save_prefix='Offset_EyesClosed_2_pre', show=False,save_path=str(FIGURES_PATH))
  745. res_diag_offset_EyesOpen_1_pre = kmeans_diagnostics(offset_df_EyesOpen_1_pre, k_values=k_range, transpose=True, save_prefix='Offset_EyesOpen_1_pre', show=False,save_path=str(FIGURES_PATH))
  746. res_diag_offset_EyesOpen_2_pre = kmeans_diagnostics(offset_df_EyesOpen_2_pre, k_values=k_range, transpose=True, save_prefix='Offset_EyesOpen_2_pre', show=False,save_path=str(FIGURES_PATH))
  747. res_diag_alpha_freq_EyesClosed_1_pre = kmeans_diagnostics(alpha_freq_df_EyesClosed_1_pre, k_values=k_range, transpose=True, save_prefix='AlphaFreq_EyesClosed_1_pre', show=False,save_path=str(FIGURES_PATH))
  748. res_diag_alpha_freq_EyesClosed_2_pre = kmeans_diagnostics(alpha_freq_df_EyesClosed_2_pre, k_values=k_range, transpose=True, save_prefix='AlphaFreq_EyesClosed_2_pre', show=False,save_path=str(FIGURES_PATH))
  749. res_diag_alpha_freq_EyesOpen_1_pre = kmeans_diagnostics(alpha_freq_df_EyesOpen_1_pre, k_values=k_range, transpose=True, save_prefix='AlphaFreq_EyesOpen_1_pre', show=False,save_path=str(FIGURES_PATH))
  750. res_diag_alpha_freq_EyesOpen_2_pre = kmeans_diagnostics(alpha_freq_df_EyesOpen_2_pre, k_values=k_range, transpose=True, save_prefix='AlphaFreq_EyesOpen_2_pre', show=False,save_path=str(FIGURES_PATH))
  751. res_diag_alpha_amp_EyesClosed_1_pre = kmeans_diagnostics(alpha_amp_df_EyesClosed_1_pre, k_values=k_range, transpose=True, save_prefix='AlphaAmp_EyesClosed_1_pre', show=False,save_path=str(FIGURES_PATH))
  752. res_diag_alpha_amp_EyesClosed_2_pre = kmeans_diagnostics(alpha_amp_df_EyesClosed_2_pre, k_values=k_range, transpose=True, save_prefix='AlphaAmp_EyesClosed_2_pre', show=False,save_path=str(FIGURES_PATH))
  753. res_diag_alpha_amp_EyesOpen_1_pre = kmeans_diagnostics(alpha_amp_df_EyesOpen_1_pre, k_values=k_range, transpose=True, save_prefix='AlphaAmp_EyesOpen_1_pre', show=False,save_path=str(FIGURES_PATH))
  754. res_diag_alpha_amp_EyesOpen_2_pre = kmeans_diagnostics(alpha_amp_df_EyesOpen_2_pre, k_values=k_range, transpose=True, save_prefix='AlphaAmp_EyesOpen_2_pre', show=False,save_path=str(FIGURES_PATH))
  755. res_diag_abs_alpha_EyesClosed_1_pre = kmeans_diagnostics(abs_alpha_df_EyesClosed_1_pre, k_values=k_range, transpose=True, save_prefix='AbsAlpha_EyesClosed_1_pre', show=False,save_path=str(FIGURES_PATH))
  756. res_diag_abs_alpha_EyesClosed_2_pre = kmeans_diagnostics(abs_alpha_df_EyesClosed_2_pre, k_values=k_range, transpose=True, save_prefix='AbsAlpha_EyesClosed_2_pre', show=False,save_path=str(FIGURES_PATH))
  757. res_diag_abs_alpha_EyesOpen_1_pre = kmeans_diagnostics(abs_alpha_df_EyesOpen_1_pre, k_values=k_range, transpose=True, save_prefix='AbsAlpha_EyesOpen_1_pre', show=False,save_path=str(FIGURES_PATH))
  758. res_diag_abs_alpha_EyesOpen_2_pre = kmeans_diagnostics(abs_alpha_df_EyesOpen_2_pre, k_values=k_range, transpose=True, save_prefix='AbsAlpha_EyesOpen_2_pre', show=False,save_path=str(FIGURES_PATH))
  759. # %% [markdown]
  760. # After clustering from 1 to 11, we can get some diagnostics - we're looking for k's with the highest silhouette scores - bascially, its 2 clusters that is best for everything. The exception is alpha amp eyes open at time 2, but since we're using time 1 for clustering, we can ignore that. As we will see in the HLMs, it there isn't some unusal pattern to the clustering in terms of age or time effects.
  761. # %%
  762. # Print silhouette scores for k=2 to 10 for each measure and task.
  763. print('Silhouette scores for k=2 to 10 for each measure and task:')
  764. print('Exponent_EyesClosed_1_pre:', np.round(res_diag_exponent_EyesClosed_1_pre['silhouette'][1:9], 3))
  765. print('Exponent_EyesClosed_2_pre:', np.round(res_diag_exponent_EyesClosed_2_pre['silhouette'][1:9], 3))
  766. print('Exponent_EyesOpen_1_pre:', np.round(res_diag_exponent_EyesOpen_1_pre['silhouette'][1:9], 3))
  767. print('Exponent_EyesOpen_2_pre:', np.round(res_diag_exponent_EyesOpen_2_pre['silhouette'][1:9], 3))
  768. print('Offset_EyesClosed_1_pre:', np.round(res_diag_offset_EyesClosed_1_pre['silhouette'][1:9], 3))
  769. print('Offset_EyesClosed_2_pre:', np.round(res_diag_offset_EyesClosed_2_pre['silhouette'][1:9], 3))
  770. print('Offset_EyesOpen_1_pre:', np.round(res_diag_offset_EyesOpen_1_pre['silhouette'][1:9], 3))
  771. print('Offset_EyesOpen_2_pre:', np.round(res_diag_offset_EyesOpen_2_pre['silhouette'][1:9], 3))
  772. print('AlphaFreq_EyesClosed_1_pre:', np.round(res_diag_alpha_freq_EyesClosed_1_pre['silhouette'][1:9], 3))
  773. print('AlphaFreq_EyesClosed_2_pre:', np.round(res_diag_alpha_freq_EyesClosed_2_pre['silhouette'][1:9], 3))
  774. print('AlphaFreq_EyesOpen_1_pre:', np.round(res_diag_alpha_freq_EyesOpen_1_pre['silhouette'][1:9], 3))
  775. print('AlphaFreq_EyesOpen_2_pre:', np.round(res_diag_alpha_freq_EyesOpen_2_pre['silhouette'][1:9], 3))
  776. print('AlphaAmp_EyesClosed_1_pre:', np.round(res_diag_alpha_amp_EyesClosed_1_pre['silhouette'][1:9], 3))
  777. print('AlphaAmp_EyesClosed_2_pre:', np.round(res_diag_alpha_amp_EyesClosed_2_pre['silhouette'][1:9], 3))
  778. print('AlphaAmp_EyesOpen_1_pre:', np.round(res_diag_alpha_amp_EyesOpen_1_pre['silhouette'][1:9], 3))
  779. print('AlphaAmp_EyesOpen_2_pre:', np.round(res_diag_alpha_amp_EyesOpen_2_pre['silhouette'][1:9], 3))
  780. print('AbsAlpha_EyesClosed_1_pre:', np.round(res_diag_abs_alpha_EyesClosed_1_pre['silhouette'][1:9], 3))
  781. print('AbsAlpha_EyesClosed_2_pre:', np.round(res_diag_abs_alpha_EyesClosed_2_pre['silhouette'][1:9], 3))
  782. print('AbsAlpha_EyesOpen_1_pre:', np.round(res_diag_abs_alpha_EyesOpen_1_pre['silhouette'][1:9], 3))
  783. print('AbsAlpha_EyesOpen_2_pre:', np.round(res_diag_abs_alpha_EyesOpen_2_pre['silhouette'][1:9],3))
  784. diag_dfs = []
  785. for res_diag, name in zip([res_diag_exponent_EyesClosed_1_pre, res_diag_exponent_EyesClosed_2_pre, res_diag_exponent_EyesOpen_1_pre, res_diag_exponent_EyesOpen_2_pre,
  786. res_diag_offset_EyesClosed_1_pre, res_diag_offset_EyesClosed_2_pre, res_diag_offset_EyesOpen_1_pre, res_diag_offset_EyesOpen_2_pre,
  787. res_diag_alpha_freq_EyesClosed_1_pre, res_diag_alpha_freq_EyesClosed_2_pre, res_diag_alpha_freq_EyesOpen_1_pre, res_diag_alpha_freq_EyesOpen_2_pre,
  788. res_diag_alpha_amp_EyesClosed_1_pre, res_diag_alpha_amp_EyesClosed_2_pre, res_diag_alpha_amp_EyesOpen_1_pre, res_diag_alpha_amp_EyesOpen_2_pre,
  789. res_diag_abs_alpha_EyesClosed_1_pre, res_diag_abs_alpha_EyesClosed_2_pre, res_diag_abs_alpha_EyesOpen_1_pre, res_diag_abs_alpha_EyesOpen_2_pre],
  790. ['Exponent_EyesClosed_1_pre', 'Exponent_EyesClosed_2_pre', 'Exponent_EyesOpen_1_pre', 'Exponent_EyesOpen_2_pre',
  791. 'Offset_EyesClosed_1_pre', 'Offset_EyesClosed_2_pre', 'Offset_EyesOpen_1_pre', 'Offset_EyesOpen_2_pre',
  792. 'AlphaFreq_EyesClosed_1_pre', 'AlphaFreq_EyesClosed_2_pre', 'AlphaFreq_EyesOpen_1_pre', 'AlphaFreq_EyesOpen_2_pre',
  793. 'AlphaAmp_EyesClosed_1_pre', 'AlphaAmp_EyesClosed_2_pre', 'AlphaAmp_EyesOpen_1_pre', 'AlphaAmp_EyesOpen_2_pre',
  794. 'AbsAlpha_EyesClosed_1_pre', 'AbsAlpha_EyesClosed_2_pre', 'AbsAlpha_EyesOpen_1_pre', 'AbsAlpha_EyesOpen_2_pre']):
  795. sil = np.array(res_diag['silhouette'], dtype=float)[1:]
  796. row = pd.DataFrame([np.round(sil, 3)],
  797. columns=[f'k={k}' for k in range(2, 2 + len(sil))])
  798. row.insert(0, 'measure_task', name)
  799. diag_dfs.append(row)
  800. diag_results_df = pd.concat(diag_dfs, ignore_index=True)
  801. diag_results_df.to_csv(RESULTS_PATH / 'kmeans_diagnostics.csv', index=False)
  802. # %%
  803. #Assign channels to k=2 clusters based on kmeans clustering
  804. from utils import kmeans_cluster_and_save
  805. res_km_exponent_EyesClosed_1_pre = kmeans_cluster_and_save(exponent_df_EyesClosed_1_pre, k=2, transpose=True,
  806. chan_names=chan_info['ch_names'], save_prefix='Exponent_EyesClosed_1_pre',
  807. save_figs=True, save_path=str(FIGURES_PATH))
  808. res_km_exponent_EyesClosed_2_pre = kmeans_cluster_and_save(exponent_df_EyesClosed_2_pre, k=2, transpose=True,
  809. chan_names=chan_info['ch_names'], save_prefix='Exponent_EyesClosed_2_pre',
  810. save_figs=True, save_path=str(FIGURES_PATH))
  811. res_km_exponent_EyesOpen_1_pre = kmeans_cluster_and_save(exponent_df_EyesOpen_1_pre, k=2, transpose=True,
  812. chan_names=chan_info['ch_names'], save_prefix='Exponent_EyesOpen_1_pre',
  813. save_figs=True, save_path=str(FIGURES_PATH))
  814. res_km_exponent_EyesOpen_2_pre = kmeans_cluster_and_save(exponent_df_EyesOpen_2_pre, k=2, transpose=True,
  815. chan_names=chan_info['ch_names'], save_prefix='Exponent_EyesOpen_2_pre',
  816. save_figs=True, save_path=str(FIGURES_PATH))
  817. res_km_offset_EyesClosed_1_pre = kmeans_cluster_and_save(offset_df_EyesClosed_1_pre, k=2, transpose=True,
  818. chan_names=chan_info['ch_names'], save_prefix='Offset_EyesClosed_1_pre',
  819. save_figs=True, save_path=str(FIGURES_PATH))
  820. res_km_offset_EyesClosed_2_pre = kmeans_cluster_and_save(offset_df_EyesClosed_2_pre, k=2, transpose=True,
  821. chan_names=chan_info['ch_names'], save_prefix='Offset_EyesClosed_2_pre',
  822. save_figs=True, save_path=str(FIGURES_PATH))
  823. res_km_offset_EyesOpen_1_pre = kmeans_cluster_and_save(offset_df_EyesOpen_1_pre, k=2, transpose=True,
  824. chan_names=chan_info['ch_names'], save_prefix='Offset_EyesOpen_1_pre',
  825. save_figs=True, save_path=str(FIGURES_PATH))
  826. res_km_offset_EyesOpen_2_pre = kmeans_cluster_and_save(offset_df_EyesOpen_2_pre, k=2, transpose=True,
  827. chan_names=chan_info['ch_names'], save_prefix='Offset_EyesOpen_2_pre',
  828. save_figs=True, save_path=str(FIGURES_PATH))
  829. res_km_alpha_freq_EyesClosed_1_pre = kmeans_cluster_and_save(alpha_freq_df_EyesClosed_1_pre, k=2, transpose=True,
  830. chan_names=chan_info['ch_names'], save_prefix='AlphaFreq_EyesClosed_1_pre',
  831. save_figs=True, save_path=str(FIGURES_PATH))
  832. res_km_alpha_freq_EyesClosed_2_pre = kmeans_cluster_and_save(alpha_freq_df_EyesClosed_2_pre, k=2, transpose=True,
  833. chan_names=chan_info['ch_names'], save_prefix='AlphaFreq_EyesClosed_2_pre',
  834. save_figs=True, save_path=str(FIGURES_PATH))
  835. res_km_alpha_freq_EyesOpen_1_pre = kmeans_cluster_and_save(alpha_freq_df_EyesOpen_1_pre, k=2, transpose=True,
  836. chan_names=chan_info['ch_names'], save_prefix='AlphaFreq_EyesOpen_1_pre',
  837. save_figs=True, save_path=str(FIGURES_PATH))
  838. res_km_alpha_freq_EyesOpen_2_pre = kmeans_cluster_and_save(alpha_freq_df_EyesOpen_2_pre, k=2, transpose=True,
  839. chan_names=chan_info['ch_names'], save_prefix='AlphaFreq_EyesOpen_2_pre',
  840. save_figs=True, save_path=str(FIGURES_PATH))
  841. res_km_alpha_amp_EyesClosed_1_pre = kmeans_cluster_and_save(alpha_amp_df_EyesClosed_1_pre, k=2, transpose=True,
  842. chan_names=chan_info['ch_names'], save_prefix='AlphaAmp_EyesClosed_1_pre',
  843. save_figs=True, save_path=str(FIGURES_PATH))
  844. res_km_alpha_amp_EyesClosed_2_pre = kmeans_cluster_and_save(alpha_amp_df_EyesClosed_2_pre, k=2, transpose=True,
  845. chan_names=chan_info['ch_names'], save_prefix='AlphaAmp_EyesClosed_2_pre',
  846. save_figs=True, save_path=str(FIGURES_PATH))
  847. res_km_alpha_amp_EyesOpen_1_pre = kmeans_cluster_and_save(alpha_amp_df_EyesOpen_1_pre, k=2, transpose=True,
  848. chan_names=chan_info['ch_names'], save_prefix='AlphaAmp_EyesOpen_1_pre',
  849. save_figs=True, save_path=str(FIGURES_PATH))
  850. res_km_alpha_amp_EyesOpen_2_pre = kmeans_cluster_and_save(alpha_amp_df_EyesOpen_2_pre, k=2, transpose=True,
  851. chan_names=chan_info['ch_names'], save_prefix='AlphaAmp_EyesOpen_2_pre',
  852. save_figs=True, save_path=str(FIGURES_PATH))
  853. res_km_abs_alpha_EyesClosed_1_pre = kmeans_cluster_and_save(abs_alpha_df_EyesClosed_1_pre, k=2, transpose=True,
  854. chan_names=chan_info['ch_names'], save_prefix='AbsAlpha_EyesClosed_1_pre',
  855. save_figs=True, save_path=str(FIGURES_PATH))
  856. res_km_abs_alpha_EyesClosed_2_pre = kmeans_cluster_and_save(abs_alpha_df_EyesClosed_2_pre, k=2, transpose=True,
  857. chan_names=chan_info['ch_names'], save_prefix='AbsAlpha_EyesClosed_2_pre',
  858. save_figs=True, save_path=str(FIGURES_PATH))
  859. res_km_abs_alpha_EyesOpen_1_pre = kmeans_cluster_and_save(abs_alpha_df_EyesOpen_1_pre, k=2, transpose=True,
  860. chan_names=chan_info['ch_names'], save_prefix='AbsAlpha_EyesOpen_1_pre',
  861. save_figs=True, save_path=str(FIGURES_PATH))
  862. res_km_abs_alpha_EyesOpen_2_pre = kmeans_cluster_and_save(abs_alpha_df_EyesOpen_2_pre, k=2, transpose=True,
  863. chan_names=chan_info['ch_names'], save_prefix='AbsAlpha_EyesOpen_2_pre',
  864. save_figs=True, save_path=str(FIGURES_PATH))
  865. # %% [markdown]
  866. # Here we generate some topomaps of the clusters for each measure and task.
  867. # %%
  868. import numpy as np
  869. import matplotlib.pyplot as plt
  870. import mne
  871. from matplotlib.colors import ListedColormap, BoundaryNorm
  872. def plot_dual_cluster_topomap(cluster1_mask, cluster2_mask, title_prefix, chan_info):
  873. assert np.all(cluster1_mask ^ cluster2_mask), "Masks must be disjoint and cover all channels"
  874. data = np.where(cluster2_mask, 1, 0)
  875. cmap = ListedColormap(["#d33ce732", "#2e86de5a"])
  876. fig, ax = plt.subplots(figsize=(6, 5))
  877. im, _ = mne.viz.plot_topomap(
  878. data,
  879. chan_info,
  880. axes=ax,
  881. contours=0,
  882. image_interp='nearest',
  883. cmap=cmap,
  884. show=False,
  885. names=chan_info['ch_names'],
  886. sensors = False,
  887. extrapolate='box',
  888. size=15
  889. )
  890. ax.set_title(f'{title_prefix}')
  891. for text in ax.texts:
  892. text.set_fontsize(6)
  893. plt.tight_layout()
  894. fig.savefig(FIGURES_PATH / f'{title_prefix}_Both_Clusters_Topomap.png', dpi=600)
  895. plt.close()
  896. #Exponent
  897. plot_dual_cluster_topomap(res_km_exponent_EyesClosed_1_pre['labels'] == 0, res_km_exponent_EyesClosed_1_pre['labels'] == 1, 'Exponent EC', chan_info)
  898. plot_dual_cluster_topomap(res_km_exponent_EyesOpen_1_pre['labels'] == 0, res_km_exponent_EyesOpen_1_pre['labels'] == 1, 'Exponent EO', chan_info)
  899. #Offset
  900. plot_dual_cluster_topomap(res_km_offset_EyesClosed_1_pre['labels'] == 0, res_km_offset_EyesClosed_1_pre['labels'] == 1, 'Offset EC', chan_info)
  901. plot_dual_cluster_topomap(res_km_offset_EyesOpen_1_pre['labels'] == 0, res_km_offset_EyesOpen_1_pre['labels'] == 1, 'Offset EO', chan_info)
  902. #Alpha Frequency
  903. plot_dual_cluster_topomap(res_km_alpha_freq_EyesClosed_1_pre['labels'] == 0, res_km_alpha_freq_EyesClosed_1_pre['labels'] == 1, 'IAPF EC', chan_info)
  904. plot_dual_cluster_topomap(res_km_alpha_freq_EyesOpen_1_pre['labels'] == 0, res_km_alpha_freq_EyesOpen_1_pre['labels'] == 1, 'IAPF EO', chan_info)
  905. #Alpha Amplitude
  906. plot_dual_cluster_topomap(res_km_alpha_amp_EyesClosed_1_pre['labels'] == 0, res_km_alpha_amp_EyesClosed_1_pre['labels'] == 1, 'Alpha Power EC', chan_info)
  907. plot_dual_cluster_topomap(res_km_alpha_amp_EyesOpen_1_pre['labels'] == 0, res_km_alpha_amp_EyesOpen_1_pre['labels'] == 1, 'Alpha Power EO', chan_info)
  908. #Absolute Alpha Power
  909. plot_dual_cluster_topomap(res_km_abs_alpha_EyesClosed_1_pre['labels'] == 0, res_km_abs_alpha_EyesClosed_1_pre['labels'] == 1, 'Absolute \u03B1 Power EC', chan_info)
  910. plot_dual_cluster_topomap(res_km_abs_alpha_EyesOpen_1_pre['labels'] == 0, res_km_abs_alpha_EyesOpen_1_pre['labels'] == 1, 'Absolute \u03B1 Power EO', chan_info)
  911. def plot_dual_cluster_topomap_subplot(cluster1_mask, cluster2_mask, title_prefix, chan_info, ax):
  912. assert np.all(cluster1_mask ^ cluster2_mask), "Masks must be disjoint and cover all channels"
  913. data = np.where(cluster2_mask, 1, 0)
  914. cmap = ListedColormap(["#d33ce732", "#2e86de5a"])
  915. im, _ = mne.viz.plot_topomap(
  916. data,
  917. chan_info,
  918. axes=ax,
  919. contours=0,
  920. image_interp='nearest',
  921. cmap=cmap,
  922. show=False,
  923. names=chan_info['ch_names'],
  924. sensors=False,
  925. extrapolate='box',
  926. size=15
  927. )
  928. ax.set_title(title_prefix)
  929. for text in ax.texts:
  930. text.set_fontsize(6)
  931. fig, axs = plt.subplots(2, 4, figsize=(16, 8))
  932. plot_dual_cluster_topomap_subplot(res_km_exponent_EyesClosed_1_pre['labels'] == 0, res_km_exponent_EyesClosed_1_pre['labels'] == 1, 'Exponent EC', chan_info, axs[0,0])
  933. plot_dual_cluster_topomap_subplot(res_km_exponent_EyesOpen_1_pre['labels'] == 0, res_km_exponent_EyesOpen_1_pre['labels'] == 1, 'Exponent EO', chan_info, axs[0,1])
  934. plot_dual_cluster_topomap_subplot(res_km_offset_EyesClosed_1_pre['labels'] == 0, res_km_offset_EyesClosed_1_pre['labels'] == 1, 'Offset EC', chan_info, axs[0,2])
  935. plot_dual_cluster_topomap_subplot(res_km_offset_EyesOpen_1_pre['labels'] == 0, res_km_offset_EyesOpen_1_pre['labels'] == 1, 'Offset EO', chan_info, axs[0,3])
  936. plot_dual_cluster_topomap_subplot(res_km_alpha_freq_EyesClosed_1_pre['labels'] == 0, res_km_alpha_freq_EyesClosed_1_pre['labels'] == 1, 'IAPF EC', chan_info, axs[1,0])
  937. plot_dual_cluster_topomap_subplot(res_km_alpha_freq_EyesOpen_1_pre['labels'] == 0, res_km_alpha_freq_EyesOpen_1_pre['labels'] == 1, 'IAPF EO', chan_info, axs[1,1])
  938. plot_dual_cluster_topomap_subplot(res_km_alpha_amp_EyesClosed_1_pre['labels'] == 0, res_km_alpha_amp_EyesClosed_1_pre['labels'] == 1, ' \u03B1 PW EC', chan_info, axs[1,2])
  939. plot_dual_cluster_topomap_subplot(res_km_alpha_amp_EyesOpen_1_pre['labels'] == 0, res_km_alpha_amp_EyesOpen_1_pre['labels'] == 1, '\u03B1 PW EO', chan_info, axs[1,3])
  940. plt.tight_layout()
  941. plt.savefig(FIGURES_PATH / 'All_Measures_Clusters_Topomap.png', dpi=600)
  942. plt.close()
  943. # %% [markdown]
  944. # Lastly, we create dataframes that put each channel into an appropriate cluster, for each measure and task.
  945. # This is applied to all subjects, because we 1) didn't use bad subjects to define clusters, and 2) we want to use the same clusters for all subjects,
  946. # and 3) we will drop them from the ICC and HLM data anyway
  947. # %%
  948. from utils import cluster_params_to_dfs
  949. #Do time 1 first
  950. exponent_EyesClosed_1_pre_clusters = cluster_params_to_dfs(res_km_exponent_EyesClosed_1_pre, fooof_params_dict_EyesClosed_1_pre['exponent_mean_interp'])
  951. exponent_EyesOpen_1_pre_clusters = cluster_params_to_dfs(res_km_exponent_EyesOpen_1_pre, fooof_params_dict_EyesOpen_1_pre['exponent_mean_interp'])
  952. offset_EyesClosed_1_pre_clusters = cluster_params_to_dfs(res_km_offset_EyesClosed_1_pre, fooof_params_dict_EyesClosed_1_pre['offset_mean_interp'])
  953. offset_EyesOpen_1_pre_clusters = cluster_params_to_dfs(res_km_offset_EyesOpen_1_pre, fooof_params_dict_EyesOpen_1_pre['offset_mean_interp'])
  954. alpha_freq_EyesClosed_1_pre_clusters = cluster_params_to_dfs(res_km_alpha_freq_EyesClosed_1_pre, fooof_params_dict_EyesClosed_1_pre['alpha_freq_mean_interp'])
  955. alpha_freq_EyesOpen_1_pre_clusters = cluster_params_to_dfs(res_km_alpha_freq_EyesOpen_1_pre, fooof_params_dict_EyesOpen_1_pre['alpha_freq_mean_interp'])
  956. alpha_amp_EyesClosed_1_pre_clusters = cluster_params_to_dfs(res_km_alpha_amp_EyesClosed_1_pre, fooof_params_dict_EyesClosed_1_pre['alpha_amp_mean_interp'])
  957. alpha_amp_EyesOpen_1_pre_clusters = cluster_params_to_dfs(res_km_alpha_amp_EyesOpen_1_pre, fooof_params_dict_EyesOpen_1_pre['alpha_amp_mean_interp'])
  958. alpha_abs_amp_EyesClosed_1_pre_clusters = cluster_params_to_dfs(res_km_abs_alpha_EyesClosed_1_pre, fooof_params_dict_EyesClosed_1_pre['alpha_absolute'])
  959. alpha_abs_amp_EyesOpen_1_pre_clusters = cluster_params_to_dfs(res_km_abs_alpha_EyesOpen_1_pre, fooof_params_dict_EyesOpen_1_pre['alpha_absolute'])
  960. #Then time 2, using time 1 cluster info
  961. exponent_EyesClosed_2_pre_clusters = cluster_params_to_dfs(res_km_exponent_EyesClosed_1_pre, fooof_params_dict_EyesClosed_2_pre['exponent_mean_interp'])
  962. exponent_EyesOpen_2_pre_clusters = cluster_params_to_dfs(res_km_exponent_EyesOpen_1_pre, fooof_params_dict_EyesOpen_2_pre['exponent_mean_interp'])
  963. offset_EyesClosed_2_pre_clusters = cluster_params_to_dfs(res_km_offset_EyesClosed_1_pre, fooof_params_dict_EyesClosed_2_pre['offset_mean_interp'])
  964. offset_EyesOpen_2_pre_clusters = cluster_params_to_dfs(res_km_offset_EyesOpen_1_pre, fooof_params_dict_EyesOpen_2_pre['offset_mean_interp'])
  965. alpha_freq_EyesClosed_2_pre_clusters = cluster_params_to_dfs(res_km_alpha_freq_EyesClosed_1_pre, fooof_params_dict_EyesClosed_2_pre['alpha_freq_mean_interp'])
  966. alpha_freq_EyesOpen_2_pre_clusters = cluster_params_to_dfs(res_km_alpha_freq_EyesOpen_1_pre, fooof_params_dict_EyesOpen_2_pre['alpha_freq_mean_interp'])
  967. alpha_amp_EyesClosed_2_pre_clusters = cluster_params_to_dfs(res_km_alpha_amp_EyesClosed_1_pre, fooof_params_dict_EyesClosed_2_pre['alpha_amp_mean_interp'])
  968. alpha_amp_EyesOpen_2_pre_clusters = cluster_params_to_dfs(res_km_alpha_amp_EyesOpen_1_pre, fooof_params_dict_EyesOpen_2_pre['alpha_amp_mean_interp'])
  969. alpha_abs_amp_EyesClosed_2_pre_clusters = cluster_params_to_dfs(res_km_abs_alpha_EyesClosed_2_pre, fooof_params_dict_EyesClosed_2_pre['alpha_absolute'])
  970. alpha_abs_amp_EyesOpen_2_pre_clusters = cluster_params_to_dfs(res_km_abs_alpha_EyesOpen_2_pre, fooof_params_dict_EyesOpen_2_pre['alpha_absolute'])
  971. #calculate row means for each cluster, for each measure, for each task, for each timepoint
  972. exponent_EyesClosed_1_pre_cluster1_means = exponent_EyesClosed_1_pre_clusters[0].mean(axis=1)
  973. exponent_EyesClosed_1_pre_cluster2_means = exponent_EyesClosed_1_pre_clusters[1].mean(axis=1)
  974. exponent_EyesClosed_2_pre_cluster1_means = exponent_EyesClosed_2_pre_clusters[0].mean(axis=1)
  975. exponent_EyesClosed_2_pre_cluster2_means = exponent_EyesClosed_2_pre_clusters[1].mean(axis=1)
  976. exponent_EyesOpen_1_pre_cluster1_means = exponent_EyesOpen_1_pre_clusters[0].mean(axis=1)
  977. exponent_EyesOpen_1_pre_cluster2_means = exponent_EyesOpen_1_pre_clusters[1].mean(axis=1)
  978. exponent_EyesOpen_2_pre_cluster1_means = exponent_EyesOpen_2_pre_clusters[0].mean(axis=1)
  979. exponent_EyesOpen_2_pre_cluster2_means = exponent_EyesOpen_2_pre_clusters[1].mean(axis=1)
  980. offset_EyesClosed_1_pre_cluster1_means = offset_EyesClosed_1_pre_clusters[0].mean(axis=1)
  981. offset_EyesClosed_1_pre_cluster2_means = offset_EyesClosed_1_pre_clusters[1].mean(axis=1)
  982. offset_EyesClosed_2_pre_cluster1_means = offset_EyesClosed_2_pre_clusters[0].mean(axis=1)
  983. offset_EyesClosed_2_pre_cluster2_means = offset_EyesClosed_2_pre_clusters[1].mean(axis=1)
  984. offset_EyesOpen_1_pre_cluster1_means = offset_EyesOpen_1_pre_clusters[0].mean(axis=1)
  985. offset_EyesOpen_1_pre_cluster2_means = offset_EyesOpen_1_pre_clusters[1].mean(axis=1)
  986. offset_EyesOpen_2_pre_cluster1_means = offset_EyesOpen_2_pre_clusters[0].mean(axis=1)
  987. offset_EyesOpen_2_pre_cluster2_means = offset_EyesOpen_2_pre_clusters[1].mean(axis=1)
  988. alpha_freq_EyesClosed_1_pre_cluster1_means = alpha_freq_EyesClosed_1_pre_clusters[0].mean(axis=1)
  989. alpha_freq_EyesClosed_1_pre_cluster2_means = alpha_freq_EyesClosed_1_pre_clusters[1].mean(axis=1)
  990. alpha_freq_EyesClosed_2_pre_cluster1_means = alpha_freq_EyesClosed_2_pre_clusters[0].mean(axis=1)
  991. alpha_freq_EyesClosed_2_pre_cluster2_means = alpha_freq_EyesClosed_2_pre_clusters[1].mean(axis=1)
  992. alpha_freq_EyesOpen_1_pre_cluster1_means = alpha_freq_EyesOpen_1_pre_clusters[0].mean(axis=1)
  993. alpha_freq_EyesOpen_1_pre_cluster2_means = alpha_freq_EyesOpen_1_pre_clusters[1].mean(axis=1)
  994. alpha_freq_EyesOpen_2_pre_cluster1_means = alpha_freq_EyesOpen_2_pre_clusters[0].mean(axis=1)
  995. alpha_freq_EyesOpen_2_pre_cluster2_means = alpha_freq_EyesOpen_2_pre_clusters[1].mean(axis=1)
  996. alpha_amp_EyesClosed_1_pre_cluster1_means = alpha_amp_EyesClosed_1_pre_clusters[0].mean(axis=1)
  997. alpha_amp_EyesClosed_1_pre_cluster2_means = alpha_amp_EyesClosed_1_pre_clusters[1].mean(axis=1)
  998. alpha_amp_EyesClosed_2_pre_cluster1_means = alpha_amp_EyesClosed_2_pre_clusters[0].mean(axis=1)
  999. alpha_amp_EyesClosed_2_pre_cluster2_means = alpha_amp_EyesClosed_2_pre_clusters[1].mean(axis=1)
  1000. alpha_amp_EyesOpen_1_pre_cluster1_means = alpha_amp_EyesOpen_1_pre_clusters[0].mean(axis=1)
  1001. alpha_amp_EyesOpen_1_pre_cluster2_means = alpha_amp_EyesOpen_1_pre_clusters[1].mean(axis=1)
  1002. alpha_amp_EyesOpen_2_pre_cluster1_means = alpha_amp_EyesOpen_2_pre_clusters[0].mean(axis=1)
  1003. alpha_amp_EyesOpen_2_pre_cluster2_means = alpha_amp_EyesOpen_2_pre_clusters[1].mean(axis=1)
  1004. alpha_abs_amp_EyesClosed_1_pre_cluster1_means = alpha_abs_amp_EyesClosed_1_pre_clusters[0].mean(axis=1)
  1005. alpha_abs_amp_EyesClosed_1_pre_cluster2_means = alpha_abs_amp_EyesClosed_1_pre_clusters[1].mean(axis=1)
  1006. alpha_abs_amp_EyesClosed_2_pre_cluster1_means = alpha_abs_amp_EyesClosed_2_pre_clusters[0].mean(axis=1)
  1007. alpha_abs_amp_EyesClosed_2_pre_cluster2_means = alpha_abs_amp_EyesClosed_2_pre_clusters[1].mean(axis=1)
  1008. alpha_abs_amp_EyesOpen_1_pre_cluster1_means = alpha_abs_amp_EyesOpen_1_pre_clusters[0].mean(axis=1)
  1009. alpha_abs_amp_EyesOpen_1_pre_cluster2_means = alpha_abs_amp_EyesOpen_1_pre_clusters[1].mean(axis=1)
  1010. alpha_abs_amp_EyesOpen_2_pre_cluster1_means = alpha_abs_amp_EyesOpen_2_pre_clusters[0].mean(axis=1)
  1011. alpha_abs_amp_EyesOpen_2_pre_cluster2_means = alpha_abs_amp_EyesOpen_2_pre_clusters[1].mean(axis=1)
  1012. # %%
  1013. #Merge everything.
  1014. demographic_data_hlm = demographic_data.copy()
  1015. demographic_data_hlm.set_index('participant_id', inplace=True)
  1016. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesClosed_1_pre_cluster1_means.rename('Exponent_EyesClosed_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1017. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesClosed_1_pre_cluster2_means.rename('Exponent_EyesClosed_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1018. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesClosed_2_pre_cluster1_means.rename('Exponent_EyesClosed_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1019. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesClosed_2_pre_cluster2_means.rename('Exponent_EyesClosed_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1020. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesOpen_1_pre_cluster1_means.rename('Exponent_EyesOpen_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1021. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesOpen_1_pre_cluster2_means.rename('Exponent_EyesOpen_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1022. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesOpen_2_pre_cluster1_means.rename('Exponent_EyesOpen_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1023. demographic_data_hlm = demographic_data_hlm.merge(exponent_EyesOpen_2_pre_cluster2_means.rename('Exponent_EyesOpen_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1024. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesClosed_1_pre_cluster1_means.rename('Offset_EyesClosed_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1025. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesClosed_1_pre_cluster2_means.rename('Offset_EyesClosed_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1026. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesClosed_2_pre_cluster1_means.rename('Offset_EyesClosed_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1027. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesClosed_2_pre_cluster2_means.rename('Offset_EyesClosed_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1028. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesOpen_1_pre_cluster1_means.rename('Offset_EyesOpen_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1029. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesOpen_1_pre_cluster2_means.rename('Offset_EyesOpen_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1030. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesOpen_2_pre_cluster1_means.rename('Offset_EyesOpen_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1031. demographic_data_hlm = demographic_data_hlm.merge(offset_EyesOpen_2_pre_cluster2_means.rename('Offset_EyesOpen_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1032. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesClosed_1_pre_cluster1_means.rename('AlphaFreq_EyesClosed_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1033. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesClosed_1_pre_cluster2_means.rename('AlphaFreq_EyesClosed_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1034. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesClosed_2_pre_cluster1_means.rename('AlphaFreq_EyesClosed_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1035. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesClosed_2_pre_cluster2_means.rename('AlphaFreq_EyesClosed_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1036. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesOpen_1_pre_cluster1_means.rename('AlphaFreq_EyesOpen_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1037. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesOpen_1_pre_cluster2_means.rename('AlphaFreq_EyesOpen_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1038. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesOpen_2_pre_cluster1_means.rename('AlphaFreq_EyesOpen_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1039. demographic_data_hlm = demographic_data_hlm.merge(alpha_freq_EyesOpen_2_pre_cluster2_means.rename('AlphaFreq_EyesOpen_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1040. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesClosed_1_pre_cluster1_means.rename('AlphaAmp_EyesClosed_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1041. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesClosed_1_pre_cluster2_means.rename('AlphaAmp_EyesClosed_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1042. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesClosed_2_pre_cluster1_means.rename('AlphaAmp_EyesClosed_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1043. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesClosed_2_pre_cluster2_means.rename('AlphaAmp_EyesClosed_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1044. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesOpen_1_pre_cluster1_means.rename('AlphaAmp_EyesOpen_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1045. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesOpen_1_pre_cluster2_means.rename('AlphaAmp_EyesOpen_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1046. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesOpen_2_pre_cluster1_means.rename('AlphaAmp_EyesOpen_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1047. demographic_data_hlm = demographic_data_hlm.merge(alpha_amp_EyesOpen_2_pre_cluster2_means.rename('AlphaAmp_EyesOpen_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1048. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesClosed_1_pre_cluster1_means.rename('AbsAlpha_EyesClosed_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1049. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesClosed_1_pre_cluster2_means.rename('AbsAlpha_EyesClosed_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1050. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesClosed_2_pre_cluster1_means.rename('AbsAlpha_EyesClosed_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1051. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesClosed_2_pre_cluster2_means.rename('AbsAlpha_EyesClosed_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1052. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesOpen_1_pre_cluster1_means.rename('AbsAlpha_EyesOpen_1_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1053. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesOpen_1_pre_cluster2_means.rename('AbsAlpha_EyesOpen_1_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1054. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesOpen_2_pre_cluster1_means.rename('AbsAlpha_EyesOpen_2_pre_Cluster1'), left_index=True, right_index=True, how='left')
  1055. demographic_data_hlm = demographic_data_hlm.merge(alpha_abs_amp_EyesOpen_2_pre_cluster2_means.rename('AbsAlpha_EyesOpen_2_pre_Cluster2'), left_index=True, right_index=True, how='left')
  1056. demographic_data_hlm.to_csv(RESULTS_PATH / 'demographic_data_hlm_pre.csv')
  1057. # %% [markdown]
  1058. # ## 5. ICCs
  1059. # For these, we will first do them at the channel level, because we're not as worried about multiple comparisons as we are about reliability in general, followed by cluster level tests.
  1060. # %%
  1061. #Set to be ICC(2,1) - two way random effects, absolute agreement, single rater/measurement
  1062. icc_type = ["twoway", "agreement", "single"]
  1063. icc_chan_ec_exponent, icc_f_ec_exponent, icc_p_ec_exponent,icc_lower_ec_exponent,icc_uppwer_ec_exponent, icc_sample_size_ec_exponent = utils.icc_chans_max(np.array(exponent_df_EyesClosed_1_pre), np.array(exponent_df_EyesClosed_2_pre), icc_type)
  1064. icc_chan_eo_exponent, icc_f_eo_exponent, icc_p_eo_exponent, icc_lower_eo_exponent, icc_upper_ec_exponent, icc_sample_size_eo_exponent = utils.icc_chans_max(np.array(exponent_df_EyesOpen_1_pre), np.array(exponent_df_EyesOpen_2_pre), icc_type)
  1065. icc_chan_ec_offset, icc_f_ec_offset, icc_p_ec_offset, icc_ec_lower_offset,icc_ec_upper_offset, icc_sample_size_ec_offset = utils.icc_chans_max(np.array(offset_df_EyesClosed_1_pre), np.array(offset_df_EyesClosed_2_pre), icc_type)
  1066. icc_chan_eo_offset, icc_f_eo_offset, icc_p_eo_offset, icc_eo_lower_offset, icc_eo_upper_offset,icc_sample_size_eo_offset = utils.icc_chans_max(np.array(offset_df_EyesOpen_1_pre), np.array(offset_df_EyesOpen_2_pre), icc_type)
  1067. icc_chan_ec_af, icc_f_ec_af, icc_p_ec_af, icc_lower_ec_af, icc_upper_ec_af, icc_sample_size_ec_af = utils.icc_chans_max(np.array(alpha_freq_df_EyesClosed_1_pre), np.array(alpha_freq_df_EyesClosed_2_pre), icc_type)
  1068. icc_chan_eo_af, icc_f_eo_af, icc_p_eo_af, icc_lower_eo_af, icc_upper_eo_af, icc_sample_size_eo_af = utils.icc_chans_max(np.array(alpha_freq_df_EyesOpen_1_pre), np.array(alpha_freq_df_EyesOpen_2_pre), icc_type)
  1069. icc_chan_ec_aa, icc_f_ec_aa, icc_p_ec_aa, icc_lower_ec_aa, icc_upper_ec_aa, icc_sample_size_ec_aa = utils.icc_chans_max(np.array(alpha_amp_df_EyesClosed_1_pre), np.array(alpha_amp_df_EyesClosed_2_pre), icc_type)
  1070. icc_chan_eo_aa, icc_f_eo_aa, icc_p_eo_aa, icc_lower_eo_aa, icc_upper_eo_aa, icc_sample_size_eo_aa = utils.icc_chans_max(np.array(alpha_amp_df_EyesOpen_1_pre), np.array(alpha_amp_df_EyesOpen_2_pre), icc_type)
  1071. #Absolute alpha power ICCs
  1072. icc_chan_ec_abs_alpha, icc_f_ec_abs_alpha, icc_p_ec_abs_alpha, icc_lower_ec_abs_alpha, icc_upper_ec_abs_alpha, icc_sample_size_ec_abs_alpha = utils.icc_chans_max(np.array(abs_alpha_df_EyesClosed_1_pre), np.array(abs_alpha_df_EyesClosed_2_pre), icc_type)
  1073. icc_chan_eo_abs_alpha, icc_f_eo_abs_alpha, icc_p_eo_abs_alpha, icc_lower_eo_abs_alpha, icc_upper_eo_abs_alpha, icc_sample_size_eo_abs_alpha = utils.icc_chans_max(np.array(abs_alpha_df_EyesOpen_1_pre), np.array(abs_alpha_df_EyesOpen_2_pre), icc_type)
  1074. #ICCs at the cluster level
  1075. icc_type = ["twoway", "agreement", "single"]
  1076. icc_ec_exponent_cluster1, icc_f_ec_exponent_cluster1, icc_p_ec_exponent_cluster1, icc_lower_ec_exponent_cluster1, icc_upper_ec_exponent_cluster1, icc_sample_size_ec_exponent_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['Exponent_EyesClosed_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['Exponent_EyesClosed_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1077. icc_ec_exponent_cluster2, icc_f_ec_exponent_cluster2, icc_p_ec_exponent_cluster2, icc_lower_ec_exponent_cluster2, icc_upper_ec_exponent_cluster2, icc_sample_size_ec_exponent_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['Exponent_EyesClosed_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['Exponent_EyesClosed_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1078. icc_eo_exponent_cluster1, icc_f_eo_exponent_cluster1, icc_p_eo_exponent_cluster1, icc_lower_eo_exponent_cluster1, icc_upper_eo_exponent_cluster1, icc_sample_size_eo_exponent_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['Exponent_EyesOpen_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['Exponent_EyesOpen_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1079. icc_eo_exponent_cluster2, icc_f_eo_exponent_cluster2, icc_p_eo_exponent_cluster2, icc_lower_eo_exponent_cluster2, icc_upper_eo_exponent_cluster2, icc_sample_size_eo_exponent_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['Exponent_EyesOpen_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['Exponent_EyesOpen_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1080. icc_ec_offset_cluster1, icc_f_ec_offset_cluster1, icc_p_ec_offset_cluster1, icc_lower_ec_offset_cluster1, icc_upper_ec_offset_cluster1, icc_sample_size_ec_offset_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['Offset_EyesClosed_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['Offset_EyesClosed_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1081. icc_ec_offset_cluster2, icc_f_ec_offset_cluster2, icc_p_ec_offset_cluster2, icc_lower_ec_offset_cluster2, icc_upper_ec_offset_cluster2, icc_sample_size_ec_offset_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['Offset_EyesClosed_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['Offset_EyesClosed_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1082. icc_eo_offset_cluster1, icc_f_eo_offset_cluster1, icc_p_eo_offset_cluster1, icc_lower_eo_offset_cluster1, icc_upper_eo_offset_cluster1, icc_sample_size_eo_offset_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['Offset_EyesOpen_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['Offset_EyesOpen_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1083. icc_eo_offset_cluster2, icc_f_eo_offset_cluster2, icc_p_eo_offset_cluster2, icc_lower_eo_offset_cluster2, icc_upper_eo_offset_cluster2, icc_sample_size_eo_offset_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['Offset_EyesOpen_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['Offset_EyesOpen_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1084. icc_ec_af_cluster1, icc_f_ec_af_cluster1, icc_p_ec_af_cluster1, icc_lower_ec_af_cluster1, icc_upper_ec_af_cluster1, icc_sample_size_ec_af_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaFreq_EyesClosed_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['AlphaFreq_EyesClosed_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1085. icc_ec_af_cluster2, icc_f_ec_af_cluster2, icc_p_ec_af_cluster2, icc_lower_ec_af_cluster2, icc_upper_ec_af_cluster2, icc_sample_size_ec_af_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaFreq_EyesClosed_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['AlphaFreq_EyesClosed_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1086. icc_eo_af_cluster1, icc_f_eo_af_cluster1, icc_p_eo_af_cluster1, icc_lower_eo_af_cluster1, icc_upper_eo_af_cluster1, icc_sample_size_eo_af_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaFreq_EyesOpen_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['AlphaFreq_EyesOpen_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1087. icc_eo_af_cluster2, icc_f_eo_af_cluster2, icc_p_eo_af_cluster2, icc_lower_eo_af_cluster2, icc_upper_eo_af_cluster2, icc_sample_size_eo_af_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaFreq_EyesOpen_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['AlphaFreq_EyesOpen_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1088. icc_ec_aa_cluster1, icc_f_ec_aa_cluster1, icc_p_ec_aa_cluster1, icc_lower_ec_aa_cluster1, icc_upper_ec_aa_cluster1, icc_sample_size_ec_aa_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaAmp_EyesClosed_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['AlphaAmp_EyesClosed_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1089. icc_ec_aa_cluster2, icc_f_ec_aa_cluster2, icc_p_ec_aa_cluster2, icc_lower_ec_aa_cluster2, icc_upper_ec_aa_cluster2, icc_sample_size_ec_aa_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaAmp_EyesClosed_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['AlphaAmp_EyesClosed_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1090. icc_eo_aa_cluster1, icc_f_eo_aa_cluster1, icc_p_eo_aa_cluster1, icc_lower_eo_aa_cluster1, icc_upper_eo_aa_cluster1, icc_sample_size_eo_aa_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaAmp_EyesOpen_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['AlphaAmp_EyesOpen_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1091. icc_eo_aa_cluster2, icc_f_eo_aa_cluster2, icc_p_eo_aa_cluster2, icc_lower_eo_aa_cluster2, icc_upper_eo_aa_cluster2, icc_sample_size_eo_aa_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['AlphaAmp_EyesOpen_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['AlphaAmp_EyesOpen_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1092. icc_ec_abs_alpha_cluster1, icc_f_ec_abs_alpha_cluster1, icc_p_ec_abs_alpha_cluster1, icc_lower_ec_abs_alpha_cluster1, icc_upper_ec_abs_alpha_cluster1, icc_sample_size_ec_abs_alpha_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['AbsAlpha_EyesClosed_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['AbsAlpha_EyesClosed_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1093. icc_eo_abs_alpha_cluster1, icc_f_eo_abs_alpha_cluster1, icc_p_eo_abs_alpha_cluster1, icc_lower_eo_abs_alpha_cluster1, icc_upper_eo_abs_alpha_cluster1, icc_sample_size_eo_abs_alpha_cluster1 = utils.icc_chans_max(np.array(demographic_data_hlm[['AbsAlpha_EyesOpen_1_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['AbsAlpha_EyesOpen_2_pre_Cluster1']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1094. icc_ec_abs_alpha_cluster2, icc_f_ec_abs_alpha_cluster2, icc_p_ec_abs_alpha_cluster2, icc_lower_ec_abs_alpha_cluster2, icc_upper_ec_abs_alpha_cluster2, icc_sample_size_ec_abs_alpha_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['AbsAlpha_EyesClosed_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), np.array(demographic_data_hlm[['AbsAlpha_EyesClosed_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesClosed_1_pre'] & demographic_data_hlm['Good_Fits_EyesClosed_2_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_1_pre'] & demographic_data_hlm['Good_Alpha_EyesClosed_2_pre'] & demographic_data_hlm['Good_Ref_EyesClosed'] & demographic_data_hlm['Good_Retention_EyesClosed']]), icc_type)
  1095. icc_eo_abs_alpha_cluster2, icc_f_eo_abs_alpha_cluster2, icc_p_eo_abs_alpha_cluster2, icc_lower_eo_abs_alpha_cluster2, icc_upper_eo_abs_alpha_cluster2, icc_sample_size_eo_abs_alpha_cluster2 = utils.icc_chans_max(np.array(demographic_data_hlm[['AbsAlpha_EyesOpen_1_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), np.array(demographic_data_hlm[['AbsAlpha_EyesOpen_2_pre_Cluster2']][demographic_data_hlm['Good_Fits_EyesOpen_1_pre'] & demographic_data_hlm['Good_Fits_EyesOpen_2_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_1_pre'] & demographic_data_hlm['Good_Alpha_EyesOpen_2_pre'] & demographic_data_hlm['Good_Ref_EyesOpen'] & demographic_data_hlm['Good_Retention_EyesOpen']]), icc_type)
  1096. icc_cluster_results = pd.DataFrame({
  1097. 'Measure': ['Exponent', 'Exponent', 'Exponent', 'Exponent', 'Offset', 'Offset', 'Offset', 'Offset', 'AlphaFreq', 'AlphaFreq', 'AlphaFreq', 'AlphaFreq', 'AlphaAmp', 'AlphaAmp', 'AlphaAmp', 'AlphaAmp','AbsAlpha', 'AbsAlpha', 'AbsAlpha', 'AbsAlpha'],
  1098. 'Task': ['EyesClosed', 'EyesClosed', 'EyesOpen', 'EyesOpen', 'EyesClosed', 'EyesClosed', 'EyesOpen', 'EyesOpen', 'EyesClosed', 'EyesClosed', 'EyesOpen', 'EyesOpen', 'EyesClosed', 'EyesClosed', 'EyesOpen', 'EyesOpen','EyesClosed', 'EyesClosed', 'EyesOpen', 'EyesOpen'],
  1099. 'Cluster': ['Cluster1', 'Cluster2', 'Cluster1', 'Cluster2', 'Cluster1', 'Cluster2', 'Cluster1', 'Cluster2', 'Cluster1', 'Cluster2', 'Cluster1', 'Cluster2', 'Cluster1', 'Cluster2', 'Cluster1', 'Cluster2','Cluster1', 'Cluster2', 'Cluster1', 'Cluster2'],
  1100. 'ICC': [icc_ec_exponent_cluster1, icc_ec_exponent_cluster2, icc_eo_exponent_cluster1, icc_eo_exponent_cluster2,
  1101. icc_ec_offset_cluster1, icc_ec_offset_cluster2, icc_eo_offset_cluster1, icc_eo_offset_cluster2,
  1102. icc_ec_af_cluster1, icc_ec_af_cluster2, icc_eo_af_cluster1, icc_eo_af_cluster2,
  1103. icc_ec_aa_cluster1, icc_ec_aa_cluster2, icc_eo_aa_cluster1, icc_eo_aa_cluster2,
  1104. icc_ec_abs_alpha_cluster1, icc_ec_abs_alpha_cluster2, icc_eo_abs_alpha_cluster1, icc_eo_abs_alpha_cluster2],
  1105. 'F-value': [icc_f_ec_exponent_cluster1, icc_f_ec_exponent_cluster2, icc_f_eo_exponent_cluster1, icc_f_eo_exponent_cluster2,
  1106. icc_f_ec_offset_cluster1, icc_f_ec_offset_cluster2, icc_f_eo_offset_cluster1, icc_f_eo_offset_cluster2,
  1107. icc_f_ec_af_cluster1, icc_f_ec_af_cluster2, icc_f_eo_af_cluster1, icc_f_eo_af_cluster2,
  1108. icc_f_ec_aa_cluster1, icc_f_ec_aa_cluster2, icc_f_eo_aa_cluster1, icc_f_eo_aa_cluster2,
  1109. icc_f_ec_abs_alpha_cluster1, icc_f_ec_abs_alpha_cluster2, icc_f_eo_abs_alpha_cluster1, icc_f_eo_abs_alpha_cluster2],
  1110. 'p-value': [icc_p_ec_exponent_cluster1, icc_p_ec_exponent_cluster2, icc_p_eo_exponent_cluster1, icc_p_eo_exponent_cluster2,
  1111. icc_p_ec_offset_cluster1, icc_p_ec_offset_cluster2, icc_p_eo_offset_cluster1, icc_p_eo_offset_cluster2,
  1112. icc_p_ec_af_cluster1, icc_p_ec_af_cluster2, icc_p_eo_af_cluster1, icc_p_eo_af_cluster2,
  1113. icc_p_ec_aa_cluster1, icc_p_ec_aa_cluster2, icc_p_eo_aa_cluster1, icc_p_eo_aa_cluster2,
  1114. icc_p_ec_abs_alpha_cluster1, icc_p_ec_abs_alpha_cluster2, icc_p_eo_abs_alpha_cluster1, icc_p_eo_abs_alpha_cluster2],
  1115. 'Lower CI': [icc_lower_ec_exponent_cluster1, icc_lower_ec_exponent_cluster2, icc_lower_eo_exponent_cluster1, icc_lower_eo_exponent_cluster2,
  1116. icc_lower_ec_offset_cluster1, icc_lower_ec_offset_cluster2, icc_lower_eo_offset_cluster1, icc_lower_eo_offset_cluster2,
  1117. icc_lower_ec_af_cluster1, icc_lower_ec_af_cluster2, icc_lower_eo_af_cluster1, icc_lower_eo_af_cluster2,
  1118. icc_lower_ec_aa_cluster1, icc_lower_ec_aa_cluster2, icc_lower_eo_aa_cluster1, icc_lower_eo_aa_cluster2,
  1119. icc_lower_ec_abs_alpha_cluster1, icc_lower_ec_abs_alpha_cluster2, icc_lower_eo_abs_alpha_cluster1, icc_lower_eo_abs_alpha_cluster2],
  1120. 'Upper CI': [icc_upper_ec_exponent_cluster1, icc_upper_ec_exponent_cluster2, icc_upper_eo_exponent_cluster1, icc_upper_eo_exponent_cluster2,
  1121. icc_upper_ec_offset_cluster1, icc_upper_ec_offset_cluster2, icc_upper_eo_offset_cluster1, icc_upper_eo_offset_cluster2,
  1122. icc_upper_ec_af_cluster1, icc_upper_ec_af_cluster2, icc_upper_eo_af_cluster1, icc_upper_eo_af_cluster2,
  1123. icc_upper_ec_aa_cluster1, icc_upper_ec_aa_cluster2, icc_upper_eo_aa_cluster1, icc_upper_eo_aa_cluster2,
  1124. icc_upper_ec_abs_alpha_cluster1, icc_upper_ec_abs_alpha_cluster2, icc_upper_eo_abs_alpha_cluster1, icc_upper_eo_abs_alpha_cluster2],
  1125. 'N': [icc_sample_size_ec_exponent_cluster1, icc_sample_size_ec_exponent_cluster2, icc_sample_size_eo_exponent_cluster1, icc_sample_size_eo_exponent_cluster2,
  1126. icc_sample_size_ec_offset_cluster1, icc_sample_size_ec_offset_cluster2, icc_sample_size_eo_offset_cluster1, icc_sample_size_eo_offset_cluster2,
  1127. icc_sample_size_ec_af_cluster1, icc_sample_size_ec_af_cluster2, icc_sample_size_eo_af_cluster1, icc_sample_size_eo_af_cluster2,
  1128. icc_sample_size_ec_aa_cluster1, icc_sample_size_ec_aa_cluster2, icc_sample_size_eo_aa_cluster1, icc_sample_size_eo_aa_cluster2,
  1129. icc_sample_size_ec_abs_alpha_cluster1, icc_sample_size_ec_abs_alpha_cluster2, icc_sample_size_eo_abs_alpha_cluster1, icc_sample_size_eo_abs_alpha_cluster2]
  1130. })
  1131. icc_cluster_results['ICC'] = icc_cluster_results['ICC'].apply(lambda x: x[0] if isinstance(x, (list, np.ndarray)) else x)
  1132. icc_cluster_results['F-value'] = icc_cluster_results['F-value'].apply(lambda x: x[0] if isinstance(x, (list, np.ndarray)) else x)
  1133. icc_cluster_results['p-value'] = icc_cluster_results['p-value'].apply(lambda x: x[0] if isinstance(x, (list, np.ndarray)) else x)
  1134. icc_cluster_results['Lower CI'] = icc_cluster_results['Lower CI'].apply(lambda x: x[0] if isinstance(x, (list, np.ndarray)) else x)
  1135. icc_cluster_results['Upper CI'] = icc_cluster_results['Upper CI'].apply(lambda x: x[0] if isinstance(x, (list, np.ndarray)) else x)
  1136. icc_cluster_results['N'] = icc_cluster_results['N'].apply(lambda x: x[0] if isinstance(x, (list, np.ndarray)) else x)
  1137. icc_cluster_results.to_csv(RESULTS_PATH / 'icc_cluster_results_pre.csv', index=False)
  1138. # %%
  1139. #Plot the ICCs for each measure and task, as a topography
  1140. import matplotlib.pyplot as plt
  1141. import mne
  1142. mapping = 'Blues'
  1143. contours = 0
  1144. image_interp = 'cubic'
  1145. size = 5
  1146. vlim = (0, 1)
  1147. fig, axs = plt.subplots(2, 4)
  1148. im,cm = mne.viz.plot_topomap(icc_chan_ec_exponent, chan_info, axes=axs[0,0], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1149. im,cm = mne.viz.plot_topomap(icc_chan_eo_exponent, chan_info, axes=axs[0,1], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1150. im,cm = mne.viz.plot_topomap(icc_chan_ec_offset, chan_info, axes=axs[0,2], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1151. im,cm = mne.viz.plot_topomap(icc_chan_eo_offset, chan_info, axes=axs[0,3], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1152. im,cm = mne.viz.plot_topomap(icc_chan_ec_af, chan_info, axes=axs[1,0], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1153. im,cm = mne.viz.plot_topomap(icc_chan_eo_af, chan_info, axes=axs[1,1], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1154. im,cm = mne.viz.plot_topomap(icc_chan_ec_aa, chan_info, axes=axs[1,2], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1155. im,cm = mne.viz.plot_topomap(icc_chan_eo_aa, chan_info, axes=axs[1,3], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1156. # Plot colorbar
  1157. ax_x_start = 0.12
  1158. ax_y_start = 0.1
  1159. ax_width = 0.8
  1160. ax_height = 0.02
  1161. cbar_ax = fig.add_axes([ax_x_start, ax_y_start, ax_width, ax_height])
  1162. clb = fig.colorbar(im, cax=cbar_ax, orientation='horizontal')
  1163. clb.set_label('ICC', fontsize=12)
  1164. clb.ax.tick_params(labelsize=12)
  1165. axs[0,0].set_title('Exponent EC')
  1166. axs[0,1].set_title('Exponent EO')
  1167. axs[0,2].set_title('Offset EC')
  1168. axs[0,3].set_title('Offset EO')
  1169. axs[1,0].set_title('IAPF EC')
  1170. axs[1,1].set_title('IAPF EO')
  1171. axs[1,2].set_title('\u03B1 PW EC')
  1172. axs[1,3].set_title('\u03B1 PW EO')
  1173. for i in range(0, 4):
  1174. axs[0, i].images[0].set_cmap(mapping)
  1175. axs[1, i].images[0].set_cmap(mapping)
  1176. fig.set_size_inches(8, 5)
  1177. fig.subplots_adjust(wspace=0.02, hspace=0.02)
  1178. plt.savefig(FIGURES_PATH / 'ICCs_pre.png', dpi=600)
  1179. plt.close()
  1180. # %%
  1181. #Here, we just plot the ICCs for absolute alpha.
  1182. import matplotlib.pyplot as plt
  1183. import mne
  1184. mapping = 'Blues'
  1185. contours = 0
  1186. image_interp = 'cubic'
  1187. size = 5
  1188. vlim = (0, 1)
  1189. fig, axs = plt.subplots(1, 2)
  1190. im,cm = mne.viz.plot_topomap(icc_chan_ec_abs_alpha, chan_info, axes=axs[0], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1191. im,cm = mne.viz.plot_topomap(icc_chan_eo_abs_alpha, chan_info, axes=axs[1], show=False, contours=contours, image_interp=image_interp, size=size, vlim=vlim)
  1192. # Plot colorbar
  1193. ax_x_start = 0.12
  1194. ax_y_start = 0.1
  1195. ax_width = 0.8
  1196. ax_height = 0.02
  1197. cbar_ax = fig.add_axes([ax_x_start, ax_y_start, ax_width, ax_height])
  1198. clb = fig.colorbar(im, cax=cbar_ax, orientation='horizontal')
  1199. clb.set_label('ICC', fontsize=12)
  1200. clb.ax.tick_params(labelsize=12)
  1201. axs[0].set_title('Absolute \u03B1 Power EC')
  1202. axs[1].set_title('Absolute \u03B1 Power EO')
  1203. for i in range(0, 1):
  1204. axs[0].images[0].set_cmap(mapping)
  1205. axs[1].images[0].set_cmap(mapping)
  1206. fig.set_size_inches(5, 3)
  1207. fig.subplots_adjust(wspace=0.02, hspace=0.02)
  1208. plt.savefig(FIGURES_PATH / 'ICCs_abs_alpha_pre.png', dpi=600)
  1209. plt.close()
  1210. # %%
  1211. # Plot the ICCs for each measure and task, as a topography (binned/flat shades)
  1212. import matplotlib.pyplot as plt
  1213. import matplotlib.colors as mcolors
  1214. import numpy as np
  1215. import mne
  1216. contours = 0
  1217. image_interp = 'cubic'
  1218. size = 5
  1219. vlim = (0, 1)
  1220. bounds = [0.0, 0.4, 0.59, 0.74, 1.0]
  1221. tick_locs = [0.2, 0.495, 0.665, 0.875]
  1222. tick_labels = ['Poor', 'Fair', 'Good', 'Excellent']
  1223. cmap = plt.get_cmap('Blues', 5)
  1224. cmap = mcolors.ListedColormap(cmap(np.arange(5)))
  1225. norm = mcolors.BoundaryNorm(bounds, cmap.N)
  1226. fig, axs = plt.subplots(2, 4)
  1227. def _tp(data, ax):
  1228. try:
  1229. im, cm = mne.viz.plot_topomap(
  1230. data, chan_info, axes=ax, show=False,
  1231. contours=contours, image_interp=image_interp,
  1232. size=size, vlim=vlim, cmap=cmap, cnorm=norm
  1233. )
  1234. except TypeError:
  1235. im, cm = mne.viz.plot_topomap(
  1236. data, chan_info, axes=ax, show=False,
  1237. contours=contours, image_interp=image_interp,
  1238. size=size, vlim=vlim, cmap=cmap
  1239. )
  1240. im.set_norm(norm)
  1241. return im
  1242. im = _tp(icc_chan_ec_exponent, axs[0,0])
  1243. _tp(icc_chan_eo_exponent, axs[0,1])
  1244. _tp(icc_chan_ec_offset, axs[0,2])
  1245. _tp(icc_chan_eo_offset, axs[0,3])
  1246. _tp(icc_chan_ec_af, axs[1,0])
  1247. _tp(icc_chan_eo_af, axs[1,1])
  1248. _tp(icc_chan_ec_aa, axs[1,2])
  1249. _tp(icc_chan_eo_aa, axs[1,3])
  1250. # Colorbar (categorical)
  1251. ax_x_start, ax_y_start, ax_width, ax_height = 0.12, 0.10, 0.80, 0.02
  1252. cbar_ax = fig.add_axes([ax_x_start, ax_y_start, ax_width, ax_height])
  1253. clb = fig.colorbar(im, cax=cbar_ax, orientation='horizontal',
  1254. boundaries=bounds, ticks=tick_locs)
  1255. clb.set_label('ICC (binned)', fontsize=12)
  1256. clb.ax.set_xticklabels(tick_labels)
  1257. clb.ax.tick_params(labelsize=12)
  1258. # Titles
  1259. axs[0,0].set_title('Exponent EC')
  1260. axs[0,1].set_title('Exponent EO')
  1261. axs[0,2].set_title('Offset EC')
  1262. axs[0,3].set_title('Offset EO')
  1263. axs[1,0].set_title('IAPF EC')
  1264. axs[1,1].set_title('IAPF EO')
  1265. axs[1,2].set_title('\u03B1 PW EC')
  1266. axs[1,3].set_title('\u03B1 PW EO')
  1267. # Size/spacing
  1268. fig.set_size_inches(8, 5)
  1269. fig.subplots_adjust(wspace=0.02, hspace=0.02)
  1270. plt.savefig(FIGURES_PATH / 'ICCs_pre_discrete.png', dpi=600)
  1271. plt.close()
  1272. # %%
  1273. # Plot the ICCs for absolute alpha, as a topography (binned/flat shades)
  1274. import matplotlib.pyplot as plt
  1275. import mne
  1276. mapping = 'Blues'
  1277. contours = 0
  1278. image_interp = 'nearest'
  1279. size = 5
  1280. vlim = (0, 1)
  1281. fig, axs = plt.subplots(1, 2)
  1282. im = _tp(icc_chan_ec_abs_alpha, axs[0])
  1283. _tp(icc_chan_eo_abs_alpha, axs[1])
  1284. # Colorbar (categorical)
  1285. ax_x_start, ax_y_start, ax_width, ax_height = 0.12, 0.10, 0.80, 0.02
  1286. cbar_ax = fig.add_axes([ax_x_start, ax_y_start, ax_width, ax_height])
  1287. clb = fig.colorbar(im, cax=cbar_ax, orientation='horizontal',
  1288. boundaries=bounds, ticks=tick_locs)
  1289. clb.set_label('ICC (binned)', fontsize=12)
  1290. clb.ax.set_xticklabels(tick_labels)
  1291. clb.ax.tick_params(labelsize=12)
  1292. axs[0].set_title('Absolute \u03B1 Power EC')
  1293. axs[1].set_title('Absolute \u03B1 Power EO')
  1294. fig.set_size_inches(5, 3)
  1295. fig.subplots_adjust(wspace=0.02, hspace=0.02)
  1296. plt.savefig(FIGURES_PATH / 'ICCs_abs_alpha_pre_discrete.png', dpi=600)
  1297. plt.close()
  1298. # %% [markdown]
  1299. # ## 6. Final Sample Details, Baseline, Correlations and HLMs
  1300. # These analyses are all handled at the cluster level. Aside from some data-wrangling, the actual analyses are in the IfAdo_analyses rmarkdown notebook.
  1301. # %%
  1302. #If we don't have demographic_data_hlm, reload it from the csv
  1303. if 'demographic_data_hlm' not in locals():
  1304. demographic_data_hlm = pd.read_csv(RESULTS_PATH / 'demographic_data_hlm_pre.csv', index_col=0)
  1305. df_hlm = demographic_data_hlm.reset_index()
  1306. # %%
  1307. # Create a filtered dataframe with participants who have good fits
  1308. df_hlm_filtered = df_hlm.copy()
  1309. # For participants with session2 = 'yes', require good fits in all 4 pre tasks
  1310. # For participants with session2 = 'no', require good fits in session 1 pre tasks
  1311. condition_session2_yes = (
  1312. (df_hlm_filtered['session2'] == 'yes') &
  1313. (df_hlm_filtered['Good_Fits_EyesClosed_1_pre'] == True) &
  1314. (df_hlm_filtered['Good_Fits_EyesClosed_2_pre'] == True) &
  1315. (df_hlm_filtered['Good_Fits_EyesOpen_1_pre'] == True) &
  1316. (df_hlm_filtered['Good_Fits_EyesOpen_2_pre'] == True) &
  1317. (df_hlm_filtered['Good_Retention_EyesClosed'] == True) &
  1318. (df_hlm_filtered['Good_Retention_EyesOpen'] == True) &
  1319. (df_hlm_filtered['Good_Ref_EyesClosed'] == True) &
  1320. (df_hlm_filtered['Good_Ref_EyesOpen'] == True)
  1321. )
  1322. condition_session2_no = (
  1323. (df_hlm_filtered['session2'] == 'no') &
  1324. (df_hlm_filtered['Good_Fits_EyesClosed_1_pre'] == True) &
  1325. (df_hlm_filtered['Good_Fits_EyesOpen_1_pre'] == True)
  1326. )
  1327. df_hlm_good_fits = df_hlm_filtered[condition_session2_yes | condition_session2_no].copy()
  1328. print(f"Original dataframe: {len(df_hlm_filtered)} rows, {df_hlm_filtered['participant_id'].nunique()} participants")
  1329. print(f"Filtered dataframe: {len(df_hlm_good_fits)} rows, {df_hlm_good_fits['participant_id'].nunique()} participants")
  1330. session2_yes_count = df_hlm_good_fits[df_hlm_good_fits['session2'] == 'yes']['participant_id'].nunique()
  1331. session2_no_count = df_hlm_good_fits[df_hlm_good_fits['session2'] == 'no']['participant_id'].nunique()
  1332. print(f"Participants with session2 = 'yes': {session2_yes_count}")
  1333. print(f"Participants with session2 = 'no': {session2_no_count}")
  1334. print(f"Total participants with good fits: {session2_yes_count + session2_no_count}")
  1335. # %%
  1336. import re
  1337. import pandas as pd
  1338. import numpy as np
  1339. def make_hlm_df(measure, condition, cluster=None, df=df_hlm, require_session2=True, require_good_fits=True, require_alpha_fits = False, dropna=False):
  1340. df = df_hlm
  1341. m = str(measure).strip().lower()
  1342. measure_map = {
  1343. 'exponent': 'Exponent',
  1344. 'offset': 'Offset',
  1345. 'alpha_freq': 'AlphaFreq', 'alphafreq': 'AlphaFreq', 'alpha-frequency': 'AlphaFreq',
  1346. 'alpha_amp': 'AlphaAmp', 'alphaamp': 'AlphaAmp', 'alpha-amplitude': 'AlphaAmp', 'absolute-alpha': 'AbsAlpha',
  1347. 'fit': 'Fit', 'error': 'Error'
  1348. }
  1349. measure_key = measure_map.get(m, None)
  1350. if measure_key is None:
  1351. measure_key = str(measure).strip().replace(' ', '').title()
  1352. c = str(condition).strip().lower()
  1353. if 'closed' in c:
  1354. cond_key = 'EyesClosed'
  1355. elif 'open' in c:
  1356. cond_key = 'EyesOpen'
  1357. else:
  1358. cond_key = str(condition).strip().replace(' ', '').title()
  1359. good1 = f"Good_Fits_{cond_key}_1_pre"
  1360. good2 = f"Good_Fits_{cond_key}_2_pre"
  1361. good3 = f"Good_Retention_{cond_key}"
  1362. good4 = f"Good_Ref_{cond_key}"
  1363. for rc in (good1, good2, good3, good4):
  1364. if rc not in df.columns:
  1365. df[rc] = False
  1366. if require_alpha_fits:
  1367. good1_alpha = f"Good_Alpha_{cond_key}_1_pre"
  1368. good2_alpha = f"Good_Alpha_{cond_key}_2_pre"
  1369. if good1_alpha not in df.columns:
  1370. df[good1_alpha] = False
  1371. if good2_alpha not in df.columns:
  1372. df[good2_alpha] = False
  1373. # find all cluster-specific columns for the measure+condition across timepoints
  1374. # expected pattern: <Measure>_<Condition>_<time>_pre_Cluster<NUM>
  1375. pat = re.compile(rf"^{re.escape(measure_key)}_{re.escape(cond_key)}_[12]_pre_Cluster(\d+)$", flags=re.IGNORECASE)
  1376. matched_cols = [col for col in df.columns if pat.match(col)]
  1377. if cluster is not None:
  1378. if isinstance(cluster, int):
  1379. want = f"Cluster{cluster}"
  1380. else:
  1381. cs = str(cluster).strip()
  1382. want = cs if cs.lower().startswith('cluster') else f"Cluster{cs}"
  1383. matched_cols = [c for c in matched_cols if c.endswith(want)]
  1384. if not matched_cols:
  1385. col_t1 = f"{measure_key}_{cond_key}_1_pre_Cluster{cluster if cluster is not None else ''}".rstrip('_')
  1386. col_t2 = f"{measure_key}_{cond_key}_2_pre_Cluster{cluster if cluster is not None else ''}".rstrip('_')
  1387. for rc in (col_t1, col_t2):
  1388. if rc not in df.columns:
  1389. df[rc] = np.nan
  1390. matched_cols = [c for c in (col_t1, col_t2) if c in df.columns]
  1391. required_ids = ['participant_id', 'age', 'sex', 'session2', good1, good2, good3, good4]
  1392. if require_alpha_fits:
  1393. required_ids += [good1_alpha, good2_alpha]
  1394. for rc in required_ids:
  1395. if rc not in df.columns:
  1396. if rc in (good1, good2, good3, good4) + (() if not require_alpha_fits else (good1_alpha, good2_alpha)):
  1397. df[rc] = False
  1398. else:
  1399. df[rc] = np.nan
  1400. # melt across all matched cluster columns
  1401. id_vars = ['participant_id', 'age', 'sex', 'session2', good1, good2, good3, good4]
  1402. if require_alpha_fits:
  1403. id_vars += [good1_alpha, good2_alpha]
  1404. melted = df[id_vars + matched_cols].melt(
  1405. id_vars=id_vars,
  1406. value_vars=matched_cols,
  1407. var_name='orig_measure_col',
  1408. value_name='value'
  1409. )
  1410. # extract time (1 or 2) from the original column name
  1411. melted['time'] = melted['orig_measure_col'].str.extract(r'_(1|2)_pre_', expand=False).astype(float)
  1412. # extract cluster number and make it nullable integer
  1413. melted['cluster'] = pd.to_numeric(melted['orig_measure_col'].str.extract(r'Cluster(\d+)', expand=False), errors='coerce').astype('Int64')
  1414. # center age
  1415. melted['age'] = pd.to_numeric(melted['age'], errors='coerce')
  1416. melted['age_c'] = melted['age'] - melted['age'].mean()
  1417. # set measure name to canonical measure_key
  1418. melted['measure'] = measure_key
  1419. # optional filtering
  1420. if require_session2 and require_good_fits:
  1421. melted = melted[
  1422. (melted['session2'] == 'yes') &
  1423. (melted[good1] == True) &
  1424. (melted[good2] == True) &
  1425. (melted[good3] == True) & (melted[good4] == True)
  1426. ].copy()
  1427. elif require_session2:
  1428. melted = melted[melted['session2'] == 'yes'].copy()
  1429. elif require_good_fits:
  1430. melted = melted[(melted[good1] == True) & (melted[good2] == True) & (melted[good3] == True)].copy()
  1431. if require_alpha_fits:
  1432. melted = melted[
  1433. (melted[good1_alpha] == True) &
  1434. (melted[good2_alpha] == True)
  1435. ].copy()
  1436. if dropna:
  1437. melted = melted.dropna(subset=['value'])
  1438. # drop the helper original column before returning
  1439. melted = melted.drop(columns=['orig_measure_col'])
  1440. return melted
  1441. # %%
  1442. #Create data frames for each measure, task, and cluster, using make_hlm_df function. Note that we don't need to specify require session 2, or good fits as they're defaults
  1443. #Exponents
  1444. exponents_both_clusters_eyesclosed_hlm = make_hlm_df('Exponent', 'EyesClosed', cluster=None, dropna=True)
  1445. exponents_both_clusters_eyesopen_hlm = make_hlm_df('Exponent', 'EyesOpen', cluster=None, dropna=True)
  1446. exponent_cluster1_eyesclosed_hlm = make_hlm_df('Exponent', 'EyesClosed', 'Cluster1',dropna=True)
  1447. exponent_cluster2_eyesclosed_hlm = make_hlm_df('Exponent', 'EyesClosed', 'Cluster2', dropna=True)
  1448. exponent_cluster1_eyesopen_hlm = make_hlm_df('Exponent', 'EyesOpen', 'Cluster1', dropna=True)
  1449. exponent_cluster2_eyesopen_hlm = make_hlm_df('Exponent', 'EyesOpen', 'Cluster2', dropna=True)
  1450. #Offsets
  1451. offset_cluster1_eyesclosed_hlm = make_hlm_df('Offset', 'EyesClosed', 'Cluster1', dropna=True)
  1452. offset_cluster2_eyesclosed_hlm = make_hlm_df('Offset', 'EyesClosed', 'Cluster2', dropna=True)
  1453. offset_cluster1_eyesopen_hlm = make_hlm_df('Offset', 'EyesOpen', 'Cluster1', dropna=True)
  1454. offset_cluster2_eyesopen_hlm = make_hlm_df('Offset', 'EyesOpen', 'Cluster2', dropna=True)
  1455. #alpha frequency
  1456. alpha_freq_eyesclosed_hlm = make_hlm_df('AlphaFreq', 'EyesClosed', cluster=None, require_alpha_fits=True, dropna=True)
  1457. alpha_freq_eyesopen_hlm = make_hlm_df('AlphaFreq', 'EyesOpen', cluster=None, require_alpha_fits=True, dropna=True)
  1458. alpha_freq_eyesclosed_cluster1_hlm = make_hlm_df('AlphaFreq', 'EyesClosed', 'Cluster1', require_alpha_fits=True, dropna=True)
  1459. alpha_freq_eyesclosed_cluster2_hlm = make_hlm_df('AlphaFreq', 'EyesClosed', 'Cluster2', require_alpha_fits=True, dropna=True)
  1460. alpha_freq_eyesopen_cluster1_hlm = make_hlm_df('AlphaFreq', 'EyesOpen', 'Cluster1', require_alpha_fits=True, dropna=True)
  1461. alpha_freq_eyesopen_cluster2_hlm = make_hlm_df('AlphaFreq', 'EyesOpen', 'Cluster2', require_alpha_fits=True, dropna=True)
  1462. #alpha amplitude
  1463. alpha_amp_eyesclosed_hlm = make_hlm_df('AlphaAmp', 'EyesClosed', cluster=None, require_alpha_fits=True, dropna=True)
  1464. alpha_amp_eyesopen_hlm = make_hlm_df('AlphaAmp', 'EyesOpen', cluster=None, require_alpha_fits=True, dropna=True)
  1465. alpha_amp_eyesclosed_cluster1_hlm = make_hlm_df('AlphaAmp', 'EyesClosed', 'Cluster1', require_alpha_fits=True, dropna=True)
  1466. alpha_amp_eyesclosed_cluster2_hlm = make_hlm_df('AlphaAmp', 'EyesClosed', 'Cluster2', require_alpha_fits=True, dropna=True)
  1467. alpha_amp_eyesopen_cluster1_hlm = make_hlm_df('AlphaAmp', 'EyesOpen', 'Cluster1', require_alpha_fits=True, dropna=True)
  1468. alpha_amp_eyesopen_cluster2_hlm = make_hlm_df('AlphaAmp', 'EyesOpen', 'Cluster2', require_alpha_fits=True, dropna=True)
  1469. #Absolute alpha
  1470. abs_alpha_eyesclosed_hlm = make_hlm_df('AbsAlpha', 'EyesClosed', cluster=None, require_alpha_fits=True, dropna=True)
  1471. abs_alpha_eyesopen_hlm = make_hlm_df('AbsAlpha', 'EyesOpen', cluster=None, require_alpha_fits=True, dropna=True)
  1472. abs_alpha_eyesclosed_cluster1_hlm = make_hlm_df('AbsAlpha', 'EyesClosed', 'Cluster1', require_alpha_fits=True, dropna=True)
  1473. abs_alpha_eyesclosed_cluster2_hlm = make_hlm_df('AbsAlpha', 'EyesClosed', 'Cluster2', require_alpha_fits=True, dropna=True)
  1474. abs_alpha_eyesopen_cluster1_hlm = make_hlm_df('AbsAlpha', 'EyesOpen', 'Cluster1', require_alpha_fits=True, dropna=True)
  1475. abs_alpha_eyesopen_cluster2_hlm = make_hlm_df('AbsAlpha', 'EyesOpen', 'Cluster2', require_alpha_fits=True, dropna=True)
  1476. hlm_dfs = {
  1477. 'exponent_cluster1_eyesclosed': exponent_cluster1_eyesclosed_hlm,
  1478. 'exponent_cluster2_eyesclosed': exponent_cluster2_eyesclosed_hlm,
  1479. 'exponent_cluster1_eyesopen': exponent_cluster1_eyesopen_hlm,
  1480. 'exponent_cluster2_eyesopen': exponent_cluster2_eyesopen_hlm,
  1481. 'offset_cluster1_eyesclosed': offset_cluster1_eyesclosed_hlm,
  1482. 'offset_cluster2_eyesclosed': offset_cluster2_eyesclosed_hlm,
  1483. 'offset_cluster1_eyesopen': offset_cluster1_eyesopen_hlm,
  1484. 'offset_cluster2_eyesopen': offset_cluster2_eyesopen_hlm,
  1485. 'alpha_freq_cluster1_eyesclosed': alpha_freq_eyesclosed_cluster1_hlm,
  1486. 'alpha_freq_cluster2_eyesclosed': alpha_freq_eyesclosed_cluster2_hlm,
  1487. 'alpha_freq_cluster1_eyesopen': alpha_freq_eyesopen_cluster1_hlm,
  1488. 'alpha_freq_cluster2_eyesopen': alpha_freq_eyesopen_cluster2_hlm,
  1489. 'alpha_amp_cluster1_eyesclosed': alpha_amp_eyesclosed_cluster1_hlm,
  1490. 'alpha_amp_cluster2_eyesclosed': alpha_amp_eyesclosed_cluster2_hlm,
  1491. 'alpha_amp_cluster1_eyesopen': alpha_amp_eyesopen_cluster1_hlm,
  1492. 'alpha_amp_cluster2_eyesopen': alpha_amp_eyesopen_cluster2_hlm,
  1493. 'abs_alpha_cluster1_eyesclosed': abs_alpha_eyesclosed_cluster1_hlm,
  1494. 'abs_alpha_cluster2_eyesclosed': abs_alpha_eyesclosed_cluster2_hlm,
  1495. 'abs_alpha_cluster1_eyesopen': abs_alpha_eyesopen_cluster1_hlm,
  1496. 'abs_alpha_cluster2_eyesopen': abs_alpha_eyesopen_cluster2_hlm
  1497. }
  1498. hlm_dfs_all = {
  1499. 'exponents_both_clusters_eyesclosed': exponents_both_clusters_eyesclosed_hlm,
  1500. 'exponents_both_clusters_eyesopen': exponents_both_clusters_eyesopen_hlm,
  1501. 'offsets_both_clusters_eyesclosed': make_hlm_df('Offset', 'EyesClosed', cluster=None, dropna=True),
  1502. 'offsets_both_clusters_eyesopen': make_hlm_df('Offset', 'EyesOpen', cluster=None, dropna=True),
  1503. 'alpha_freq_both_clusters_eyesclosed': alpha_freq_eyesclosed_hlm,
  1504. 'alpha_freq_both_clusters_eyesopen': alpha_freq_eyesopen_hlm,
  1505. 'alpha_amp_both_clusters_eyesclosed': alpha_amp_eyesclosed_hlm,
  1506. 'alpha_amp_both_clusters_eyesopen': alpha_amp_eyesopen_hlm,
  1507. 'abs_alpha_both_clusters_eyesclosed': abs_alpha_eyesclosed_hlm,
  1508. 'abs_alpha_both_clusters_eyesopen': abs_alpha_eyesopen_hlm
  1509. }
  1510. # %%
  1511. # Sample size diagnostics - count participants for each analysis type
  1512. from contextlib import redirect_stdout
  1513. #Save the printed info to a text file
  1514. with open('sample_size_demographics.txt', 'w') as f:
  1515. with redirect_stdout(f):
  1516. print("Original Sample Size and Demographics")
  1517. print("=" * 60)
  1518. # 1. Overall sample sizes
  1519. print("\n1. Sample Size")
  1520. print("-" * 30)
  1521. print(f"Total participants in dataset: {len(demographic_data)}")
  1522. print(f"Participants with session2 = 'yes': {(demographic_data['session2'] == 'yes').sum()}")
  1523. print(f"Participants with session2 = 'no': {(demographic_data['session2'] == 'no').sum()}")
  1524. print(f"Age: M={demographic_data['age'].mean():.2f}, SD={demographic_data['age'].std():.2f}, Range={demographic_data['age'].min():.0f}-{demographic_data['age'].max():.0f}")
  1525. print(f"Sex: {(demographic_data['sex'] == 'F').sum()} Female, {(demographic_data['sex'] == 'M').sum()} Male")
  1526. # 2. Specparam fits sample sizes
  1527. print("\n2. Specparam Fits - overall, not means")
  1528. print("-" * 30)
  1529. for task in ['EyesClosed', 'EyesOpen']:
  1530. for session in ['1', '2']:
  1531. col_name = f'Good_Fits_{task}_{session}_pre'
  1532. if col_name in demographic_data.columns:
  1533. count = demographic_data[col_name].sum()
  1534. print(f"Good fits - {task} Session {session}: {count}")
  1535. # 2.1 Mean and SD of R^2 for each task and session, for those with good fits Mean_Fits_EyesClosed_1_pre
  1536. print("\n2.1 Mean and SD of R^2 for each task and session (good fits only)")
  1537. print("-" * 50)
  1538. for task in ['EyesClosed', 'EyesOpen']:
  1539. for session in ['1', '2']:
  1540. col_good = f'Good_Fits_{task}_{session}_pre'
  1541. col_r2 = f'Mean_Fits_{task}_{session}_pre'
  1542. if col_good in demographic_data.columns and col_r2 in demographic_data.columns:
  1543. good_r2 = demographic_data.loc[demographic_data[col_good] == True, col_r2]
  1544. mean_r2 = good_r2.mean()
  1545. sd_r2 = good_r2.std()
  1546. print(f"{task} Session {session} - Mean R^2: {mean_r2:.4f}, SD R^2: {sd_r2:.4f}")
  1547. #2.2 Mean and SD of error for each task and session, for those with good fits
  1548. print("\n2.2 Mean and SD of Error for each task and session (good fits only)")
  1549. print("-" * 50)
  1550. for task in ['EyesClosed', 'EyesOpen']:
  1551. for session in ['1', '2']:
  1552. col_good = f'Good_Fits_{task}_{session}_pre'
  1553. col_error = f'Mean_Error_{task}_{session}_pre'
  1554. if col_good in demographic_data.columns and col_error in demographic_data.columns:
  1555. good_error = demographic_data.loc[demographic_data[col_good] == True, col_error]
  1556. mean_error = good_error.mean()
  1557. sd_error = good_error.std()
  1558. print(f"{task} Session {session} - Mean Error: {mean_error:.4f}, SD Error: {sd_error:.4f}")
  1559. # Count participants with good fits in all 4 pre tasks (our main analysis sample)
  1560. all_good_fits = (
  1561. (demographic_data['Good_Fits_EyesClosed_1_pre'] == True) &
  1562. (demographic_data['Good_Fits_EyesClosed_2_pre'] == True) &
  1563. (demographic_data['Good_Fits_EyesOpen_1_pre'] == True) &
  1564. (demographic_data['Good_Fits_EyesOpen_2_pre'] == True) &
  1565. (demographic_data['session2'] == 'yes')
  1566. )
  1567. # 3. Alpha peak detection sample sizes
  1568. print("\n3. Alpha peak detection - overall, not means")
  1569. print("-" * 30)
  1570. for task in ['EyesClosed', 'EyesOpen']:
  1571. for session in ['1', '2']:
  1572. col_name = f'Good_Alpha_{task}_{session}_pre'
  1573. if col_name in demographic_data.columns:
  1574. count = demographic_data[col_name].sum()
  1575. print(f"Good alpha peaks - {task} Session {session}: {count}")
  1576. all_good_alpha = (
  1577. (demographic_data['Good_Alpha_EyesClosed_1_pre'] == True) &
  1578. (demographic_data['Good_Alpha_EyesClosed_2_pre'] == True) &
  1579. (demographic_data['Good_Alpha_EyesOpen_1_pre'] == True) &
  1580. (demographic_data['Good_Alpha_EyesOpen_2_pre'] == True) &
  1581. (demographic_data['session2'] == 'yes')
  1582. )
  1583. # 4. Task-specific breakdowns for session2=yes participants
  1584. print("\n4. Task specific sample sizes (session2 = 'yes' only)")
  1585. print("-" * 50)
  1586. session2_yes = demographic_data[demographic_data['session2'] == 'yes'].copy()
  1587. for task in ['EyesClosed', 'EyesOpen']:
  1588. print(f"\n{task}:")
  1589. # Both timepoints good
  1590. both_good_fits = (
  1591. (session2_yes[f'Good_Fits_{task}_1_pre'] == True) &
  1592. (session2_yes[f'Good_Fits_{task}_2_pre'] == True)
  1593. )
  1594. both_good_alpha = (
  1595. (session2_yes[f'Good_Alpha_{task}_1_pre'] == True) &
  1596. (session2_yes[f'Good_Alpha_{task}_2_pre'] == True)
  1597. )
  1598. print(f" Good fits both timepoints: {both_good_fits.sum()}")
  1599. print(f" Good alpha both timepoints: {both_good_alpha.sum()}")
  1600. print(f" Both criteria both timepoints: {(both_good_fits & both_good_alpha).sum()}")
  1601. #Put chan stuff here.
  1602. for task in ['EyesClosed', 'EyesOpen']:
  1603. print(f"\n{task} - Channels removed during preprocessing (session2 = 'yes' only):")
  1604. for session in ['1', '2']:
  1605. col_name = f'Channels_Removed_task-{task}_ses-{session}_acq-pre'
  1606. if col_name in session2_yes.columns:
  1607. removed_counts = session2_yes[col_name].dropna()
  1608. removed_counts = removed_counts[removed_counts != 99]
  1609. mean_removed = removed_counts.mean()
  1610. sd_removed = removed_counts.std()
  1611. max_removed = removed_counts.max()
  1612. print(f" Session {session}: Mean channels removed: {mean_removed:.2f}, SD: {sd_removed:.2f}, Max: {max_removed}")
  1613. # 5. Demographics for the samples used in the HLMs. Note that these are all session2.
  1614. print("\n5. Demographics for analysis samples (session2 = 'yes' only), with fit/error info")
  1615. print("-" * 40)
  1616. for task in ['EyesClosed', 'EyesOpen']:
  1617. print(f"\n{task}:")
  1618. task_sample = session2_yes[session2_yes[f'Good_Fits_{task}_1_pre'] & session2_yes[f'Good_Fits_{task}_2_pre'] & session2_yes[f'Good_Ref_{task}'] & session2_yes[f'Good_Retention_{task}']].copy()
  1619. alpha_task_sample = session2_yes[session2_yes[f'Good_Fits_{task}_1_pre'] & session2_yes[f'Good_Fits_{task}_2_pre'] & session2_yes[f'Good_Alpha_{task}_1_pre'] & session2_yes[f'Good_Alpha_{task}_2_pre'] & session2_yes[f'Good_Ref_{task}'] & session2_yes[f'Good_Retention_{task}']].copy()
  1620. if len(task_sample) > 0:
  1621. print(f" Participants in exponent/offset HLMs (n={len(task_sample)}):")
  1622. print(f" Age: M={task_sample['age'].mean():.2f}, SD={task_sample['age'].std():.2f}, Range={task_sample['age'].min():.0f}-{task_sample['age'].max():.0f}")
  1623. print(f" Sex: {(task_sample['sex'] == 'F').sum()} Female, {(task_sample['sex'] == 'M').sum()} Male")
  1624. #mean fits and error
  1625. print(f" Mean R^2 Session 1: {task_sample[f'Mean_Fits_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Fits_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].std():.2f}")
  1626. print(f" Mean R^2 Session 2: {task_sample[f'Mean_Fits_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Fits_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].std():.2f}")
  1627. print(f" Fit Range Session 1: {task_sample[f'Mean_Fits_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].min():.2f} - {task_sample[f'Mean_Fits_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].max():.2f}")
  1628. print(f" Fit Range Session 2: {task_sample[f'Mean_Fits_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].min():.2f} - {task_sample[f'Mean_Fits_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].max():.2f}")
  1629. print(f" Mean Error Session 1: {task_sample[f'Mean_Error_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Error_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].std():.2f}")
  1630. print(f" Mean Error Session 2: {task_sample[f'Mean_Error_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Error_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].std():.2f}")
  1631. print(f" Error Range Session 1: {task_sample[f'Mean_Error_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre']].min():.2f} - {task_sample[f'Mean_Error_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre']].max():.2f}")
  1632. print(f" Error Range Session 2: {task_sample[f'Mean_Error_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].min():.2f} - {task_sample[f'Mean_Error_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].max():.2f}")
  1633. #And using the interpolated means
  1634. print(f" Mean R^2 Session 1 (interp): {task_sample[f'Mean_Fits_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Fits_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].std():.2f}")
  1635. print(f" Mean R^2 Session 2 (interp): {task_sample[f'Mean_Fits_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Fits_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].std():.2f}")
  1636. print(f" Fit Range Session 1 (interp): {task_sample[f'Mean_Fits_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].min():.3f} - {task_sample[f'Mean_Fits_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].max():.2f}")
  1637. print(f" Fit Range Session 2 (interp): {task_sample[f'Mean_Fits_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].min():.2f} - {task_sample[f'Mean_Fits_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].max():.2f}")
  1638. print(f" Mean Error Session 1 (interp): {task_sample[f'Mean_Error_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Error_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].std():.2f}")
  1639. print(f" Mean Error Session 2 (interp): {task_sample[f'Mean_Error_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].mean():.2f}, SD: {task_sample[f'Mean_Error_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].std():.2f}")
  1640. print(f" Error Range Session 1 (interp): {task_sample[f'Mean_Error_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].min():.2f} - {task_sample[f'Mean_Error_interp_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].max():.2f}")
  1641. print(f" Error Range Session 2 (interp): {task_sample[f'Mean_Error_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].min():.2f} - {task_sample[f'Mean_Error_interp_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].max():.2f}")
  1642. #And Epoch counts
  1643. print(f" Original Epochs Session 1: {task_sample[f'EpochsOriginal_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].mean():.2f}, SD: {task_sample[f'EpochsOriginal_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].std():.2f}")
  1644. print(f" Original Epochs Session 2: {task_sample[f'EpochsOriginal_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].mean():.2f}, SD: {task_sample[f'EpochsOriginal_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].std():.2f}")
  1645. print(f" Cleaned Epochs Session 1: {task_sample[f'EpochsRetained_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].mean():.2f}, SD: {task_sample[f'EpochsRetained_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].std():.2f}")
  1646. print(f" Cleaned Epochs Session 2: {task_sample[f'EpochsRetained_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].mean():.2f}, SD: {task_sample[f'EpochsRetained_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].std():.2f}")
  1647. #Min and Max Epochs
  1648. print(f" Original Epochs Range Session 1: {task_sample[f'EpochsOriginal_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].min():.0f} - {task_sample[f'EpochsOriginal_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].max():.0f}")
  1649. print(f" Original Epochs Range Session 2: {task_sample[f'EpochsOriginal_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].min():.0f} - {task_sample[f'EpochsOriginal_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].max():.0f}")
  1650. print(f" Cleaned Epochs Range Session 1: {task_sample[f'EpochsRetained_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].min():.0f} - {task_sample[f'EpochsRetained_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].max():.0f}")
  1651. print(f" Cleaned Epochs Range Session 2: {task_sample[f'EpochsRetained_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].min():.0f} - {task_sample[f'EpochsRetained_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].max():.0f}")
  1652. #Proportion of epochs retained
  1653. print(f" Proportion of Epochs Retained Session 1: {task_sample[f'EpochsProportion_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].mean()*100:.2f}, SD: {task_sample[f'EpochsProportion_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].std()*100:.2f}")
  1654. print(f" Proportion of Epochs Retained Session 2: {task_sample[f'EpochsProportion_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].mean()*100:.2f}, SD: {task_sample[f'EpochsProportion_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].std()*100:.2f}")
  1655. #min and max proportion
  1656. print(f" Proportion of Epochs Retained Range Session 1: {task_sample[f'EpochsProportion_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].min()*100:.2f} - {task_sample[f'EpochsProportion_{task}_1_pre'][task_sample[f'Good_Fits_{task}_1_pre'] == True].max()*100:.2f}")
  1657. print(f" Proportion of Epochs Retained Range Session 2: {task_sample[f'EpochsProportion_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].min()*100:.2f} - {task_sample[f'EpochsProportion_{task}_2_pre'][task_sample[f'Good_Fits_{task}_2_pre'] == True].max()*100:.2f}")
  1658. if len(alpha_task_sample) > 0:
  1659. print(f" Participants in alpha HLMs (n={len(alpha_task_sample)}):")
  1660. print(f" Age: M={alpha_task_sample['age'].mean():.2f}, SD={alpha_task_sample['age'].std():.2f}, Range={alpha_task_sample['age'].min():.0f}-{alpha_task_sample['age'].max():.0f}")
  1661. print(f" Sex: {(alpha_task_sample['sex'] == 'F').sum()} Female, {(alpha_task_sample['sex'] == 'M').sum()} Male")
  1662. print(f" Mean R^2 Session 1: {alpha_task_sample[f'Mean_Fits_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Fits_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].std():.2f}")
  1663. print(f" Mean R^2 Session 2: {alpha_task_sample[f'Mean_Fits_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Fits_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].std():.2f}")
  1664. print(f" Fit Range Session 1: {alpha_task_sample[f'Mean_Fits_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].min():.2f} - {alpha_task_sample[f'Mean_Fits_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].max():.2f}")
  1665. print(f" Fit Range Session 2: {alpha_task_sample[f'Mean_Fits_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].min():.2f} - {alpha_task_sample[f'Mean_Fits_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].max():.2f}")
  1666. print(f" Mean Error Session 1: {alpha_task_sample[f'Mean_Error_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Error_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].std():.2f}")
  1667. print(f" Mean Error Session 2: {alpha_task_sample[f'Mean_Error_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Error_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].std():.2f}")
  1668. print(f" Error Range Session 1: {alpha_task_sample[f'Mean_Error_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].min():.2f} - {alpha_task_sample[f'Mean_Error_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].max():.2f}")
  1669. print(f" Error Range Session 2: {alpha_task_sample[f'Mean_Error_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].min():.2f} - {alpha_task_sample[f'Mean_Error_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].max():.2f}")
  1670. print(f" Mean R^2 Session 1 (interp): {alpha_task_sample[f'Mean_Fits_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Fits_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].std():.2f}")
  1671. print(f" Mean R^2 Session 2 (interp): {alpha_task_sample[f'Mean_Fits_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Fits_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].std():.2f}")
  1672. print(f" Fit Range Session 1 (interp): {alpha_task_sample[f'Mean_Fits_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].min():.2f} - {alpha_task_sample[f'Mean_Fits_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].max():.2f}")
  1673. print(f" Fit Range Session 2 (interp): {alpha_task_sample[f'Mean_Fits_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].min():.2f} - {alpha_task_sample[f'Mean_Fits_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].max():.2f}")
  1674. print(f" Mean Error Session 1 (interp): {alpha_task_sample[f'Mean_Error_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Error_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].std():.2f}")
  1675. print(f" Mean Error Session 2 (interp): {alpha_task_sample[f'Mean_Error_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].mean():.2f}, SD: {alpha_task_sample[f'Mean_Error_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].std():.2f}")
  1676. print(f" Error Range Session 1 (interp): {alpha_task_sample[f'Mean_Error_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].min():.2f} - {alpha_task_sample[f'Mean_Error_interp_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].max():.2f}")
  1677. print(f" Error Range Session 2 (interp): {alpha_task_sample[f'Mean_Error_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].min():.2f} - {alpha_task_sample[f'Mean_Error_interp_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].max():.2f}")
  1678. #And Epoch counts
  1679. print(f" Original Epochs Session 1: {alpha_task_sample[f'EpochsOriginal_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].mean():.2f}, SD: {alpha_task_sample[f'EpochsOriginal_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].std():.2f}")
  1680. print(f" Original Epochs Session 2: {alpha_task_sample[f'EpochsOriginal_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].mean():.2f}, SD: {alpha_task_sample[f'EpochsOriginal_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].std():.2f}")
  1681. print(f" Cleaned Epochs Session 1: {alpha_task_sample[f'EpochsRetained_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].mean():.2f}, SD: {alpha_task_sample[f'EpochsRetained_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].std():.2f}")
  1682. print(f" Cleaned Epochs Session 2: {alpha_task_sample[f'EpochsRetained_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].mean():.2f}, SD: {alpha_task_sample[f'EpochsRetained_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].std():.2f}")
  1683. #Min and Max Epochs
  1684. print(f" Original Epochs Range Session 1: {alpha_task_sample[f'EpochsOriginal_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].min():.0f} - {alpha_task_sample[f'EpochsOriginal_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].max():.0f}")
  1685. print(f" Original Epochs Range Session 2: {alpha_task_sample[f'EpochsOriginal_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].min():.0f} - {alpha_task_sample[f'EpochsOriginal_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].max():.0f}")
  1686. print(f" Cleaned Epochs Range Session 1: {alpha_task_sample[f'EpochsRetained_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].min():.0f} - {alpha_task_sample[f'EpochsRetained_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].max():.0f}")
  1687. print(f" Cleaned Epochs Range Session 2: {alpha_task_sample[f'EpochsRetained_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].min():.0f} - {alpha_task_sample[f'EpochsRetained_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].max():.0f}")
  1688. #Proportion of epochs retained
  1689. print(f" Proportion of Epochs Retained Session 1: {alpha_task_sample[f'EpochsProportion_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].mean()*100:.2f}, SD: {alpha_task_sample[f'EpochsProportion_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].std()*100:.2f}")
  1690. print(f" Proportion of Epochs Retained Session 2: {alpha_task_sample[f'EpochsProportion_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].mean()*100:.2f}, SD: {alpha_task_sample[f'EpochsProportion_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].std()*100:.2f}")
  1691. #min and max proportion
  1692. print(f" Proportion of Epochs Retained Range Session 1: {alpha_task_sample[f'EpochsProportion_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].min()*100:.2f} - {alpha_task_sample[f'EpochsProportion_{task}_1_pre'][alpha_task_sample[f'Good_Fits_{task}_1_pre']].max()*100:.2f}")
  1693. print(f" Proportion of Epochs Retained Range Session 2: {alpha_task_sample[f'EpochsProportion_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].min()*100:.2f} - {alpha_task_sample[f'EpochsProportion_{task}_2_pre'][alpha_task_sample[f'Good_Fits_{task}_2_pre']].max()*100:.2f}")
  1694. pass
  1695. # %% [markdown]
  1696. # ### 6.2 HLMs predicting FOOOF parameters from time, age and sex are implemented in R.
  1697. # %%
  1698. #Save each of the hlm dataframes to a csv file
  1699. for name, hlm_df in hlm_dfs.items():
  1700. hlm_df.to_csv(RESULTS_PATH / f'{name}_for_hlm.csv', index=False)

IfAdo_preprocessing.ipynb at commit e056acf, under MIT · at the source

Overview

Authors: Polina Politanskaia1, Jacinta Bywater1, Anna J Finley2, Hannah A D Keage3, Nicholas J Kelley4, Daniel J McKeown1, Victor R Schinazi1, Douglas J Angus1
  1. Bond University, Gold Coast, Queensland, Australia
  2. North Dakota State University, Fargo, North Dakota, United States of America
  3. Adelaide University, Adelaide, South Australia, Australia
  4. University of Southampton, Southampton, United Kingdom
Institutions: Bond University (Australia); North Dakota State University (United States); Adelaide University (Australia); The University of Adelaide (Australia); University of Southampton (United Kingdom)
Journal: Cerebral cortex (New York, N.Y. : 1991), volume 36, issue 7, article bhag113
Dates: received 3 March 2026; accepted 28 June 2026; published online 10 August 2026; in print July 2026
Type: Research article · Language: English
License: CC BY-NC
Identifiers: DOI 10.1093/cercor/bhag113 · PMID 42574751 · PMCID PMC13456336 · OpenAlex W7202125338
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: EEG (modality), human (organism), methods / tools (subfield)
Methods: Spectral & time-frequency, Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing, fMRI & imaging
Keywords: EEG, aperiodic, alpha, ageing, reliability
MeSH: Aging*, Brain*, Electroencephalography*, Rest*, Adult, Aged, Alpha Rhythm, Female, Follow-Up Studies, Humans, Male, Middle Aged, Reproducibility of Results, Young Adult (* major topic)
Topic: Neural dynamics and brain function (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: Bond University Postgraduate Research Scholarship; National Research Foundation Singapore; Campus for Research Excellence and Technological Enterprise
Citations: cited by 1 paper (Europe PMC); 65 references in the paper

Abstract

Several aspects of parameterized neural activity, including the aperiodic exponent and individual peak alpha frequency, have emerged as promising biomarkers for ageing, pathology, and cognitive decline. Their potential clinical application is tempered by a lack of evidence on long-term temporal stability. Existing investigations have largely relied on cross-sectional designs or considered stability for up to 90 days. Here, we examined five-year reliability, stability, and age-associated changes in periodic and aperiodic neural activity using electroencephalography in adults aged 20-70 years. Resting-state EEG was recorded in two sessions, approximately five years apart. We extracted the aperiodic exponent, aperiodic offset, peak alpha power, and individual alpha peak frequency and examined test-retest reliability at both the channel and cluster levels. All parameters demonstrated fair to excellent test-retest reliability (intraclass correlations = 0.51-0.88). Linear mixed models revealed that individual peak alpha frequency decreased, the aperiodic exponent flattened, and parameterized alpha power remained unchanged. There were no interactions between time and age. Our findings suggest that parameterized activity is reliable over long timeframes, and demonstrates changes consistent with ageing-related processes. Spectral parameterization may provide a means of characterizing within-person neurophysiological changes across adulthood. Future research should explore the utility of identifying deviations that may indicate pathology.

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

Repository

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

MindSpaceLab/Aperiodic_Test_Retest_5year

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: e056acf5244f1b0becfc82591736031431f59ab3, 16 June 2026
Languages: Python (4), R (1), Jupyter (1)
Size: 56 files, 6 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README, license file, environment (pyproject.toml), 2 notebooks
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: MNE-Python (4 files), specparam (formerly FOOOF) (4 files), ICLabel (3 files), Matplotlib (3 files), NumPy (3 files), pandas (3 files), MNE-BIDS (2 files), PyPREP (2 files), scikit-learn (2 files), SciPy (2 files), broom (1 file), cowplot (1 file), easystats (1 file), emmeans (1 file), ggplot2 (1 file), ggpubr (1 file), lme4 (1 file), lmerTest (1 file), NeuroDSP (1 file), psych (1 file), seaborn (1 file), tidyverse (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
8 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:

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

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

Data

Datasets cited

Data availability

All code used for all analyses and plots is publicly available at https://github.com/MindSpaceLab/Aperiodic_Test_Retest_5year. Raw data are available at https://doi.org/10.18112/openneuro.ds005385.v1.0.2.

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

Versions

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

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 8 authors, 5 keywords, 14 MeSH terms, 3 funders, 63 references.

Cite

This paper

Politanskaia, P., Bywater, J., Finley, A. J., Keage, H. A. D., Kelley, N. J., McKeown, D. J., Schinazi, V. R., & Angus, D. J. (2026). Long-term reliability and stability of parameterized resting state EEG: evidence from a five-year follow-up. Cerebral cortex (New York, N.Y. : 1991), 36(7), bhag113. https://doi.org/10.1093/cercor/bhag113

BibTeX

@article{politanskaia2026long,
author = {Politanskaia, Polina and Bywater, Jacinta and Finley, Anna J and Keage, Hannah A D and Kelley, Nicholas J and McKeown, Daniel J and Schinazi, Victor R and Angus, Douglas J},
title = {{Long-term reliability and stability of parameterized resting state EEG: evidence from a five-year follow-up}},
journal = {Cerebral cortex (New York, N.Y. : 1991)},
year = {2026},
month = jul,
volume = {36},
number = {7},
pages = {bhag113},
publisher = {Oxford University Press},
issn = {1047-3211},
doi = {10.1093/cercor/bhag113},
url = {https://doi.org/10.1093/cercor/bhag113},
pmid = {42574751},
pmcid = {PMC13456336}
}

RIS

TY - JOUR
AU - Politanskaia, Polina
AU - Bywater, Jacinta
AU - Finley, Anna J
AU - Keage, Hannah A D
AU - Kelley, Nicholas J
AU - McKeown, Daniel J
AU - Schinazi, Victor R
AU - Angus, Douglas J
TI - Long-term reliability and stability of parameterized resting state EEG: evidence from a five-year follow-up
T2 - Cerebral cortex (New York, N.Y. : 1991)
J2 - Cereb Cortex
PY - 2026
DA - 2026/07/01
VL - 36
IS - 7
SP - bhag113
SN - 1047-3211
PB - Oxford University Press
DO - 10.1093/cercor/bhag113
UR - https://doi.org/10.1093/cercor/bhag113
LA - en
ER -

CSL-JSON

{
"id": "10.1093/cercor/bhag113",
"type": "article-journal",
"title": "Long-term reliability and stability of parameterized resting state EEG: evidence from a five-year follow-up",
"container-title": "Cerebral cortex (New York, N.Y. : 1991)",
"author": [
{
"family": "Politanskaia",
"given": "Polina"
},
{
"family": "Bywater",
"given": "Jacinta"
},
{
"family": "Finley",
"given": "Anna J"
},
{
"family": "Keage",
"given": "Hannah A D"
},
{
"family": "Kelley",
"given": "Nicholas J"
},
{
"family": "McKeown",
"given": "Daniel J"
},
{
"family": "Schinazi",
"given": "Victor R"
},
{
"family": "Angus",
"given": "Douglas J"
}
],
"container-title-short": "Cereb Cortex",
"volume": "36",
"issue": "7",
"page": "bhag113",
"DOI": "10.1093/cercor/bhag113",
"PMID": "42574751",
"PMCID": "PMC13456336",
"ISSN": "1047-3211",
"publisher": "Oxford University Press",
"URL": "https://doi.org/10.1093/cercor/bhag113",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
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.1111/ejn.70255 [code]
A Systematic Review of Aperiodic Neural Activity in Clinical Investigations
Journal: n/a
In common: NeuroDSP, specparam (formerly FOOOF), seaborn, 3 other tools, EEG, 18 references
[2] 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: PyPREP, MNE-BIDS, easystats, 14 other tools, 1 reference
[3] doi:10.7554/elife.100605 [code]
Age-related changes in ‘cortical’ 1/f dynamics are linked to cardiac activity
Journal: n/a
In common: NeuroDSP, MNE-BIDS, specparam (formerly FOOOF), 7 other tools, 6 references
[4] doi:10.1093/cercor/bhag077 [code]
The longitudinal development of intrinsic timescales in infancy and their relation to alpha brain rhythm.
Journal: Cerebral cortex (New York, N.Y. : 1991)
In common: psych, easystats, MNE-Python, 11 other tools, EEG, 3 references
[5] doi:10.1162/imag.a.1321 [code]
Phase similarity between similar objects indicates representational merging across retrieval training but not sleep.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: easystats, broom, emmeans, 11 other tools, EEG, 2 references
[6] doi:10.1038/s41597-026-07350-9 [code]
An open multi-center MEG-EEG dataset for studying conscious visual perception.
Journal: Scientific data
In common: PyPREP, MNE-BIDS, easystats, 11 other tools, EEG
[7] doi:10.1162/imag.a.1245 [code]
Towards precision EEG connectomics: Evaluating the benefits of dense sampling.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: ICLabel, psych, MNE-Python, 11 other tools, EEG, 1 reference
[8] doi:10.1038/s41597-026-07377-y [code]
An open-access multi-site fMRI dataset for investigating conscious visual perception.
Journal: Scientific data
In common: PyPREP, MNE-BIDS, easystats, 11 other tools
[9] doi:10.1097/j.pain.0000000000004044 [code]
No effect of rhythmic visual stimulation on experimental pain perception.
Journal: Pain
In common: PyPREP, MNE-BIDS, specparam (formerly FOOOF), 7 other tools, EEG, 2 references
[10] doi:10.1093/braincomms/fcag351 [code]
Time-resolved aperiodic dynamics in event segmentation in attention-deficit/hyperactivity disorder.
Journal: Brain communications
In common: specparam (formerly FOOOF), ICLabel, easystats, 7 other tools, EEG, 3 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.