OSCR

Hippocampal ripples initiate cortical dimensionality expansion for memory retrieval.

Code ↔ Paper

6 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 6 matches
  1. [1] § Methods › Theta-gamma phase-amplitude coupling ↔ ripple_stage_05b_PAC_across_time_shuffled_final.m, lines 650–709 · score 0.72 · theta frequency, 140 Hz, hippocampal channels, wavelet, Hanning, taper
  2. [2] § Methods › Theta-gamma phase-amplitude coupling ↔ ripple_stage_05a_PAC_final.m, lines 607–664 · score 0.71 · theta frequency, 140 Hz, hippocampal channels, wavelet, Hanning, taper
  3. [3] § Methods › Method details › Dimensionality transformation ↔ ripple_stage_04c_dimensionality_LME_final.m, lines 603–718 · score 0.61 · elbow point, explained variance, eigenvalues, threshold, PCA, component
  4. [4] § Methods › Method details › Dimensionality transformation ↔ ripple_stage_04a_dimensionality_final.m, lines 531–662 · score 0.59 · elbow point, explained variance, eigenvalues, PCA, component, transformation
  5. [5] § Results › Hippocampal ripple-induced dimensionality expansion increases the separability of cortical representations ↔ ripple_stage_04a_dimensionality_final.m, lines 531–662 · score 0.51 · sliding windows, latent, elbow, eigenvalue, variance, overlap
  6. [6] § Results › Hippocampal ripple-induced dimensionality expansion increases the separability of cortical representations ↔ ripple_stage_04a_dimensionality_shuffled_final.m, lines 530–647 · score 0.51 · sliding windows, latent, elbow, eigenvalue, variance, overlap

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

MATLAB · 1,223 lines · 43 KB · no license · 2 matches

  1. %%
  2. % [ripple_stage_04a_dimensionality] find ripples in hippocampal channels,
  3. % extract and realign data based on ripple events.
  4. % Do PCA to estimate dimensionality of correct and incorrect.
  5. % Bernhard Staresina [[email hidden]]
  6. % Casper Kerren [[email hidden]]
  7. clear
  8. restoredefaultpath
  9. addpath('/Users/kerrenadmin/Desktop/Postdoc/Project_1/Analyses_matlab/general_scripts_matlab/fieldtrip-20230422')
  10. ft_defaults
  11. % [~,ftpath]=ft_version;
  12. %% path settings
  13. settings = [];
  14. settings.base_path_castle = '/Users/kerrenadmin/Desktop/Other_projects/Dimensionality_ripples_Casper_and_Bernhard/'; % '/castles/nr/projects/w/wimberm-ieeg-compute/';
  15. settings.subjects = char('CF', 'JM', 'SO', 'AH','FC', 'HW', 'AM', 'MH','FS', 'AS', 'CB', 'KK');
  16. settings.SubjectIDs = char('01_CF', '02_JM', '03_SO', '06_AH','07_FC', '08_HW', '09_AM', '10_MH','11_FS', '12_AS', '13_CB', '14_KK');
  17. load("colour_scheme.mat")
  18. settings.colour_scheme = colour_scheme;
  19. settings.data_dir = [settings.base_path_castle,'preprocessing/artifact_rejected_data/'];
  20. settings.save_dir = [settings.base_path_castle,'output_data/decoding/'];
  21. settings.data_dir_channels = [settings.base_path_castle,'ripple_project_publication_for_replication/templates'];
  22. settings.anatomy_dir = [settings.base_path_castle,'ripple_project_publication_for_replication/additional_analyses/visualisation/'];
  23. settings.AAL_dir = fullfile(settings.base_path_castle,'ripple_project_publication_for_replication/subfunctions/AAL3');
  24. settings.SPM_dir = fullfile(settings.base_path_castle,'/ripple_project_publication_for_replication/subfunctions/spm12');
  25. settings.scalp_channels = {'C3' 'C4' 'Cz' 'T3' 'T4' 'T5' 'T6' 'O1' 'O2' 'Oz' 'F3' 'F4' 'Fz' 'Cb1' 'Cb2'};
  26. subjects = {'CF', 'JM', 'SO', 'AH','FC', 'HW', 'AM', 'MH','FS', 'AS', 'CB', 'KK'};
  27. SubjectIDs = {'01_CF', '02_JM', '03_SO', '06_AH','07_FC', '08_HW', '09_AM', '10_MH','11_FS', '12_AS', '13_CB', '14_KK'};
  28. settings.nu_rep = [1, 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, 1];
  29. settings.healthyhemi = {'R' 'LR' 'R' 'L' 'L' 'R' 'R' 'R' 'R' 'R' 'R' 'LR'};
  30. addpath(genpath('/Users/kerrenadmin/Desktop/Postdoc/Project_1/Analyses_matlab/general_scripts_matlab/MVPA-Light-master'))
  31. addpath(genpath([settings.base_path_castle,'ripple_project_publication_for_replication/main_analyses/Slythm']))
  32. addpath([settings.base_path_castle,'ripple_project_publication_for_replication/subfunctions'])
  33. addpath(genpath('/Users/kerrenadmin/Desktop/Postdoc/Project_1/Analyses_matlab/help_functions'))
  34. addpath(genpath('/Users/kerrenadmin/Desktop/Postdoc/Project_1/Analyses_matlab/general_scripts_matlab/plotting'))
  35. %% pre-decoding settings
  36. % ripple extraction
  37. settings.remove_falsepositives = 1; % decide whether or not to exclude ripples deemed false positives based on spectral peak detection
  38. settings.full_enc_trial = 1; % set to 0 if you want encoding trial to end with RT and to 1 if it should end at 3 sec
  39. settings.remove_ripple_duplicates = 1; % remove co-occuring ripples
  40. settings.time_to_excl_RT = .25; % exclude last 250 ms of trials, to make sure ripple event was in trial
  41. settings.solo_ripple = 1; % pick one (maxEnv) ripple per trial if multiple ripple events are found
  42. settings.ripple_latency = [.25 5]; % define time window at retrieval in which the ripple events need to occur
  43. settings.do_surrogates = 0; % switch time of ripples between trials, 1 == for all trials, 2 == for correct trials only
  44. %% decoding settings
  45. % data preprocessing
  46. settings.ori_fsample = 1000; % original sample frequency
  47. settings.do_resample = 100; % [] or sample frequency
  48. % baseline and zscoring settings
  49. settings.zscore_data4class = 1;
  50. settings.bs_correct = 1;
  51. settings.bs_period = [-.2 0]; % [-.5 -.1]
  52. settings.bs_trim = 0; % can be 0. amount of % to trim away when calculating the baseling
  53. % smoothing options
  54. settings.do_smoothdata = 1; % use matlabs smoothdata function for running average
  55. settings.smooth_win = .200; % .100, [] running average time window in seconds;
  56. % time of interest
  57. settings.TOI_train = [-.5 3];
  58. settings.timesteps_train = ((settings.ori_fsample/settings.do_resample)/settings.ori_fsample); % in s. if you want it to take less sample points multiply [e.g., ((settings.ori_fsample/settings.do_resample)/settings.ori_fsample)*2
  59. settings.TOI_test = [-1.2 1.2]; % time around ripple (I take this time window to get a proper estimate around the edges too. Only look at -1 to 1 later.
  60. settings.timesteps_test = ((settings.ori_fsample/settings.do_resample)/settings.ori_fsample); % in s
  61. settings.classifier = 'lda';
  62. settings.metric = 'auc';
  63. %% channel settings
  64. settings.channel_sel = 1; % 1 exclude hippo, 2 only hippo, 3 all channels
  65. %% settings pca
  66. settings.smooth_before_dim = 1; % smooth Nans before doing dim reduction
  67. settings.nu_time_points = 60; % time of sliding window in ms
  68. settings.prc_overlap = .9; % percentage overlap sliding window
  69. settings.decode_components = 1; % Decode the PCA components I picked.
  70. %% start for loop
  71. timeaxis = settings.TOI_test(1):settings.timesteps_test:settings.TOI_test(2);
  72. freqaxis = settings.TOI_train(1):settings.timesteps_train:settings.TOI_train(2);
  73. perf = cell(1,numel(subjects));
  74. channs = cell(1,numel(subjects));
  75. RT_all_subj_correct = cell(1,numel(subjects));
  76. RT_all_subj_incorrect = cell(1,numel(subjects));
  77. ripple_time_correct = cell(1,numel(subjects));
  78. ripple_time_incorrect = cell(1,numel(subjects));
  79. numWorkers = 8; %
  80. parpool('local', numWorkers);
  81. tic
  82. parfor isubject = 1:numel(subjects)
  83. fprintf('processing subject %01d/%02d\n',isubject,numel(subjects));
  84. %% LOAD data
  85. tmp = load([settings.data_dir,'eeg_session01_all_chan_nobadchan_cmntrim_artdet_',subjects{isubject}]);
  86. data = tmp.data;
  87. onsets_session = tmp.onsets_session;
  88. tmp = [];
  89. %% load channels and restrict to channels that are in hippocampus
  90. SubjectID = SubjectIDs{isubject};
  91. tmp = load([settings.base_path_castle,'ripple_project_publication_for_replication/templates/channels_hipp_ripples']);
  92. channels_hipp_ripples = tmp.channels_hipp_ripples;
  93. tmp = [];
  94. channels = channels_hipp_ripples(isubject,:);
  95. channels = channels(~cellfun(@isempty,channels));
  96. cfg = [];
  97. cfg.channel = channels;
  98. data = ft_selectdata(cfg, data);
  99. % If NaNs in recording, interpolate these.
  100. cfg = [];
  101. cfg.prewindow = 3;
  102. cfg.postwindow = 3;
  103. data = ft_interpolatenan(cfg,data);
  104. %% Find ripples
  105. [inData,alldat] = detect_ripples(data, settings);
  106. %% remove false positives from ripple data
  107. if settings.remove_falsepositives
  108. alldat = remove_false_positives(alldat);
  109. end
  110. %% delete co-occuring ripples
  111. if settings.remove_ripple_duplicates
  112. alldat = remove_ripple_duplicates(alldat);
  113. end
  114. %% Load data to realign based on cue onset encoding and based on ripples
  115. data_in = load([settings.data_dir,'eeg_session01_all_chan_nobadchan_cmntrim_artdet_',subjects{isubject}],'data','onsets_session');
  116. data = data_in.data;
  117. data_in = [];
  118. onsets = onsets_session - data.time{1}(1)*inData.fsample;
  119. data.sampleinfo = 1+data.sampleinfo - data.sampleinfo(1);
  120. data.time{1} = 1/data.fsample+data.time{1}-data.time{1}(1);
  121. %% channel selection for data
  122. % 1 exclude hippo, 2 only hippo, 3 all channels
  123. tmp = load([settings.data_dir_channels,'/channels_to_exclude_all_hipp_both_hem.mat']);
  124. channels_to_exclude_all_hipp = tmp.channels_to_exclude_all_hipp;
  125. tmp = load([settings.data_dir_channels,'/channels_hipp_ripples.mat']);
  126. channels_hipp_ripples = tmp.channels_hipp_ripples;
  127. cfg = [];
  128. cfg.channel = data.label;
  129. switch settings.channel_sel
  130. case 1
  131. cfg.channel = setdiff(setdiff([data.label],char(channels_to_exclude_all_hipp{isubject,:})),settings.scalp_channels);
  132. case 2
  133. cfg.channel = intersect(cellstr(setdiff([data.label],settings.scalp_channels)),char(channels_hipp_ripples{isubject,:}));
  134. case 3
  135. cfg.channel = setdiff(data.label,settings.scalp_channels);
  136. end
  137. %--- load channel info and coordinates
  138. [~,~,entries] = xlsread(fullfile(settings.anatomy_dir,'well01_ripples_ROIs_w_labels.xlsx'));
  139. subject_colums = entries(1,:);
  140. column_names = entries(2,:);
  141. these_labels = entries(3:end,strcmp(subject_colums,['s' SubjectIDs{isubject}]) & strcmp(column_names,'label'));
  142. these_x = cell2mat(entries(3:end,strcmp(subject_colums,['s' SubjectIDs{isubject}]) & strcmp(column_names,'x')));
  143. these_y = cell2mat(entries(3:end,strcmp(subject_colums,['s' SubjectIDs{isubject}]) & strcmp(column_names,'y')));
  144. these_z = cell2mat(entries(3:end,strcmp(subject_colums,['s' SubjectIDs{isubject}]) & strcmp(column_names,'z')));
  145. data = ft_selectdata(cfg, data);
  146. % write out info of retained channels
  147. for ichannel = 1:numel(data.label)
  148. idx = strcmp(data.label{ichannel},these_labels);
  149. channs{isubject}(ichannel).names = data.label{ichannel};
  150. channs{isubject}(ichannel).coords = [these_x(idx) these_y(idx) these_z(idx)];
  151. end
  152. %% Load subject file and change RT
  153. [numbers,strings] = xlsread([settings.base_path_castle,'well01_behavior_all.xls']);
  154. strings = strings(2:end,:);
  155. if isnan(numbers(1,1))
  156. numbers = numbers(2:end,:);
  157. end
  158. sel = find(strcmp(strings(:,2),SubjectID));
  159. sel = sel(1:numel(onsets));
  160. trls_enc = strcmp(strings(sel,4),'encoding');
  161. trls_ret = strcmp(strings(sel,4),'retrieval');
  162. Memory = cell(size(strings(sel,12))); % Convert Memory to a cell array of the same size
  163. Memory = strings(sel,12);
  164. Memory = Memory(trls_ret);
  165. idx_trial = find(trls_ret);
  166. Memory(:, 2) = num2cell(idx_trial); % Assign idx_trial to the second column
  167. RT = numbers(sel,11);
  168. RT(RT==-1 & trls_enc==1) = 3; % -1 no press in time - set to 3s at encoding
  169. if settings.full_enc_trial
  170. RT(trls_enc==1) = 3; % [optional] set all encoding to 3s
  171. end
  172. RT(RT==-1 & trls_ret==1) = 5; % -1 no press in time - set to 5s at encoding and 5s at retrieval
  173. trialinfo = [];
  174. for itrial = 1:numel(sel)
  175. trialinfo(itrial).SubjectID = strings(sel(itrial),2);
  176. trialinfo(itrial).RunNumber = numbers(sel(itrial),3);
  177. trialinfo(itrial).ExpPhase = strings(sel(itrial),4);
  178. trialinfo(itrial).TrialNumber = numbers(sel(itrial),5);
  179. trialinfo(itrial).EventNumber = itrial;
  180. trialinfo(itrial).BlockType = strings(sel(itrial),6);
  181. trialinfo(itrial).Subcat = strings(sel(itrial),7);
  182. trialinfo(itrial).Word = strings(sel(itrial),8);
  183. trialinfo(itrial).OldNew = strings(sel(itrial),9);
  184. trialinfo(itrial).Response = strings(sel(itrial),10);
  185. trialinfo(itrial).Memory = strings(sel(itrial),12);
  186. trialinfo(itrial).RT = RT(itrial);
  187. end
  188. sel = [];
  189. strings = [];
  190. %% create onset matrices (remove last 250ms to ensure ripple event in trial)
  191. onsetmat = [onsets; onsets+(RT'.*data.fsample)-(settings.time_to_excl_RT*data.fsample)]';
  192. %% Extract ripples (optional to select long and short duration ripples)
  193. trl_ripple = [];
  194. cnt = 0;
  195. for ichannel = 1:numel(alldat)
  196. evs = alldat{ichannel}.evtIndiv.maxTime;
  197. envSum = alldat{ichannel}.evtIndiv.envSum;
  198. ripple_dur = alldat{ichannel}.evtIndiv.duration;
  199. duration_sel = logical(ones(1,numel(ripple_dur)));
  200. evs = evs(duration_sel);
  201. envSum = envSum(duration_sel);
  202. %% for each detected ripple, find the corresponding trial
  203. for iripple = 1:numel(evs)
  204. this_event = evs(iripple) >= onsetmat(:,1) & evs(iripple) <= onsetmat(:,2);
  205. if any(this_event)
  206. cnt=cnt+1;
  207. trl_ripple(cnt,1) = find(this_event); % note down corresponding event number
  208. trl_ripple(cnt,2) = evs(iripple); % note down ripple sample
  209. trl_ripple(cnt,3) = (evs(iripple) - onsetmat(this_event,1))/data.fsample; % note down time of ripple in trial
  210. trl_ripple(cnt,4) = ichannel; % note down channel
  211. trl_ripple(cnt,5) = envSum(iripple);
  212. end
  213. end
  214. end
  215. trl_ripple = sortrows(trl_ripple,1);
  216. %% restrict ripple data to retrieval
  217. trl_ripple = trl_ripple(ismember(trl_ripple(:,1),find(trls_ret)),:);
  218. %% [optional] do surrogates by taking time of ripple from other trial (for all or only for correct trials)
  219. correct_mem = cell2mat(Memory(ismember(Memory(:,1),'SourceCorrect'),2));
  220. idx_correct = ismember(trl_ripple(:,1), correct_mem);
  221. trl_ripple_correct = trl_ripple(idx_correct,:);
  222. if settings.do_surrogates == 1 % 1 for all trials
  223. randtrials = circshift(1:size(trl_ripple,1),1);
  224. tmp = trl_ripple(:,2)-round(trl_ripple(:,3).*data.fsample); % find cue onset
  225. tmp = tmp+round(trl_ripple(randtrials,3).*data.fsample); % add another ripple's event time
  226. trl_ripple(:,2) = tmp;
  227. trl_ripple(:,3) = trl_ripple(randtrials,3);
  228. elseif settings.do_surrogates == 2 % 2 for only correct trials
  229. randtrials_correct = circshift(1:size(trl_ripple_correct,1),-1);
  230. tmp_correct = trl_ripple_correct(:,2) - round(trl_ripple_correct(:,3) .* data.fsample); % find cue onset
  231. tmp_correct = tmp_correct + round(trl_ripple_correct(randtrials_correct,3) .* data.fsample); % add shuffled ripple event time
  232. trl_ripple_correct(:,2) = tmp_correct;
  233. trl_ripple_correct(:,3) = trl_ripple_correct(randtrials_correct,3);
  234. trl_ripple(idx_correct,:) = trl_ripple_correct; % add to original structure with only correct trials swapped
  235. end
  236. %% Create trial structure around ripples
  237. pretrig = round(abs(settings.TOI_test(1)) * data.fsample); % enough time to baseline correct later
  238. posttrig = round(abs(settings.TOI_test(2)) * data.fsample);
  239. cfg = [];
  240. cfg.trl = [trl_ripple(:,2)-pretrig trl_ripple(:,2)+posttrig -pretrig*ones(size(trl_ripple,1),1)];
  241. data_ripples = ft_redefinetrial(cfg,data);
  242. % add trial info for each ripple trial, accounting for multiple ripples
  243. % per trial
  244. data_ripples.trialinfo = [];
  245. for itrial = 1:numel(data_ripples.trial)
  246. corresponding_trialinfo = find([trialinfo.EventNumber]==trl_ripple(itrial,1));
  247. trl_info = trialinfo(corresponding_trialinfo);
  248. trl_info.sample_ripple = trl_ripple(itrial,2);
  249. trl_info.time_ripple = trl_ripple(itrial,3);
  250. trl_info.channel = trl_ripple(itrial,4);
  251. trl_info.name_channel = {alldat{trl_ripple(itrial,4)}.evtIndiv.label};
  252. trl_info.envSum = trl_ripple(itrial,5);
  253. data_ripples.trialinfo{itrial,1} = trl_info;
  254. end
  255. %% [optional] if there are multiple ripples per trial - pick the one ripple with highest activity (captured in the summed envelope metric)
  256. if settings.solo_ripple
  257. tmp_trl_info = data_ripples.trialinfo;
  258. % find the trials in which there were more than one ripple
  259. trlinfo = cell2mat(data_ripples.trialinfo);
  260. ripple_trial = [trlinfo.EventNumber];
  261. envSum = [trlinfo.envSum];
  262. idx_unique = unique(ripple_trial);
  263. sel = [];
  264. counter = 1;
  265. for itrial = 1:numel(idx_unique)
  266. idx = find(ripple_trial==idx_unique(itrial));
  267. [~,max_effect] = max(envSum(idx));
  268. sel(counter) = idx(max_effect);
  269. counter = counter+1;
  270. end
  271. cfg = [];
  272. cfg.trials = sel;
  273. data_ripples = ft_selectdata(cfg, data_ripples);
  274. end
  275. %% [optional] pick ripples in a specific time window
  276. if any(settings.ripple_latency)
  277. trlinfo = cell2mat(data_ripples.trialinfo);
  278. sel = [trlinfo.time_ripple] > settings.ripple_latency(1) & [trlinfo.time_ripple] < settings.ripple_latency(2);
  279. cfg = [];
  280. cfg.trials = sel;
  281. data_ripples = ft_selectdata(cfg, data_ripples);
  282. end
  283. %% Realign trials based on stimulus onsets
  284. pretrig = round(abs(settings.TOI_train(1)) * data.fsample);
  285. posttrig = round(abs(settings.TOI_train(2)) * data.fsample);
  286. cfg = [];
  287. cfg.trl = [onsets'-pretrig onsets'+posttrig -pretrig*ones(numel(onsets),1)];
  288. data_stimuli = ft_redefinetrial(cfg,data);
  289. data_stimuli.trialinfo = {};
  290. for itrial = 1:numel(trialinfo)
  291. data_stimuli.trialinfo{itrial,1} = trialinfo(itrial);
  292. end
  293. %% [optional] resample
  294. if any(settings.do_resample)
  295. cfg = [];
  296. cfg.resamplefs = settings.do_resample;
  297. data_ripples = ft_resampledata(cfg, data_ripples);
  298. data_stimuli = ft_resampledata(cfg, data_stimuli);
  299. end
  300. %% Time-lock data
  301. cfg = [];
  302. cfg.keeptrials = 'yes';
  303. cfg.removemean = 'no';
  304. data_ripples = ft_timelockanalysis(cfg, data_ripples);
  305. data_stimuli = ft_timelockanalysis(cfg, data_stimuli);
  306. fsample = round(1/(data_ripples.time(2)-data_ripples.time(1)));
  307. %% [optional] Running mean filter
  308. if ~isempty(settings.smooth_win)
  309. data_ripples.trial = smoothdata(data_ripples.trial,3,'movmean',settings.smooth_win*fsample);
  310. data_stimuli.trial = smoothdata(data_stimuli.trial,3,'movmean',settings.smooth_win*fsample);
  311. end
  312. %% [optional] BL correct (us pre-stim baseline also for ripple-locked data)
  313. if settings.bs_correct == 1
  314. % training data
  315. bl_idx = nearest(data_stimuli.time,settings.bs_period(1)):nearest(data_stimuli.time,settings.bs_period(2));
  316. bldat = trimmean(data_stimuli.trial(:,:,bl_idx),settings.bs_trim,'round',3);
  317. blmat = repmat(bldat,[1 1 size(data_stimuli.trial,3)]);
  318. data_stimuli.trial = data_stimuli.trial - blmat;
  319. % testing data
  320. trlinfo = cell2mat(data_ripples.trialinfo);
  321. orig_events = [trlinfo.EventNumber];
  322. bl4ripples = nan(size(data_ripples.trial,1),size(bldat,2));
  323. for iripple = 1:size(data_ripples.trial,1)
  324. bl4ripples(iripple,:) = bldat(orig_events(iripple),:);
  325. end
  326. blmat = repmat(bl4ripples,[1 1 size(data_ripples.trial,3)]);
  327. data_ripples.trial = data_ripples.trial - blmat;
  328. end
  329. %% time-lock data
  330. cfg = [];
  331. cfg.keeptrials = 'yes';
  332. data_stimuli = ft_timelockanalysis(cfg,data_stimuli);
  333. data_ripples = ft_timelockanalysis(cfg,data_ripples);
  334. %% calculate time of ripple and RT for those trials
  335. ripple_tmp = [];
  336. RT_tmp = [];
  337. for itrial = 1:numel(data_ripples.trialinfo)
  338. ripple_tmp(itrial) = data_ripples.trialinfo{itrial, 1}.time_ripple;
  339. RT_tmp(itrial) = data_ripples.trialinfo{itrial, 1}.RT;
  340. end
  341. trlinfo = cell2mat(data_ripples.trialinfo);
  342. sel1 = ismember([trlinfo.ExpPhase],'retrieval') & ismember([trlinfo.Memory],'SourceCorrect');
  343. sel2 = ismember([trlinfo.ExpPhase],'retrieval') &(ismember([trlinfo.Memory],{'SourceIncorrect' ,'SourceDunno' 'dunno'}));
  344. RT_all_subj_correct{isubject} = RT_tmp(sel1);
  345. RT_all_subj_incorrect{isubject} = RT_tmp(sel2);
  346. ripple_time_correct{isubject} = ripple_tmp(sel1);
  347. ripple_time_incorrect{isubject} = ripple_tmp(sel2);
  348. %%
  349. %%%%%%%%%%%%%%%%%%%%%%
  350. %%%%%%%%% PCA %%%%%%%%
  351. %%%%%%%%%%%%%%%%%%%%%%
  352. trlinfo = cell2mat(data_stimuli.trialinfo);
  353. sel1 = ismember([trlinfo.ExpPhase],'encoding') & ismember([trlinfo.BlockType],'color');
  354. sel2 = ismember([trlinfo.ExpPhase],'encoding') & ismember([trlinfo.BlockType],'scene');
  355. dataToClassifyTraining = cat(1,data_stimuli.trial(sel1,:,:),data_stimuli.trial(sel2,:,:));
  356. clabelTraining = cat(1,1*ones(sum(sel1),1),2*ones(sum(sel2),1));
  357. samples_train = nearest(data_stimuli.time,settings.TOI_train(1)):settings.timesteps_train*fsample:nearest(data_stimuli.time,settings.TOI_train(2));
  358. dataToClassifyTraining = dataToClassifyTraining(:,:,samples_train);
  359. %% 1. category PCA [colours vs. scenes; coarse]
  360. trlinfo = cell2mat(data_ripples.trialinfo);
  361. %% PCA on the data to get the eigenvalues that explain 90% of the variance.
  362. % Do it with a sliding window of 60ms with 100Hz)
  363. for icond = 1:2
  364. if icond == 1
  365. sel1 = ismember([trlinfo.ExpPhase],'retrieval') & ismember([trlinfo.BlockType],'color') & ismember([trlinfo.Memory],'SourceCorrect');
  366. sel2 = ismember([trlinfo.ExpPhase],'retrieval') & ismember([trlinfo.BlockType],'scene') & ismember([trlinfo.Memory],'SourceCorrect') ;
  367. perf{isubject}.correct.trl_num_test = [sum(sel1) sum(sel2)];
  368. elseif icond == 2
  369. sel1 = ismember([trlinfo.ExpPhase],'retrieval') & ismember([trlinfo.BlockType],'color')& (ismember([trlinfo.Memory],{'SourceIncorrect' ,'SourceDunno' 'dunno'}));
  370. sel2 = ismember([trlinfo.ExpPhase],'retrieval') & ismember([trlinfo.BlockType],'scene')& (ismember([trlinfo.Memory],{'SourceIncorrect' ,'SourceDunno' 'dunno'}));
  371. perf{isubject} .incorrect.trl_num_test = [sum(sel1) sum(sel2)];
  372. end
  373. clabelTest = cat(1,1*ones(sum(sel1),1),2*ones(sum(sel2),1));
  374. dataToClassifyTest = cat(1,data_ripples.trial(sel1,:,:),data_ripples.trial(sel2,:,:));
  375. sumsel1 = sum(sel1);
  376. sumsel2 = sum(sel2);
  377. fs = 1/(data_ripples.time(2)-data_ripples.time(1));
  378. chunk_size = round(settings.nu_time_points/((1/fs)*1000)); % Size of each chunk
  379. overlap_size = round(chunk_size * settings.prc_overlap);
  380. num_chunks = floor((size(dataToClassifyTest, 3) - overlap_size) / (chunk_size - overlap_size));
  381. TOI_ripple = linspace(data_ripples.time(1), data_ripples.time(end),num_chunks);
  382. explained_variances = [];
  383. how_much_variance = [];
  384. accuracy = [];
  385. ripple_to_decode = [];
  386. for i = 1:num_chunks
  387. % Calculate the start and end indices of the current chunk
  388. start_idx = (i - 1) * (chunk_size - overlap_size) + 1;
  389. end_idx = start_idx + chunk_size - 1;
  390. % Extract data for the current chunk and reshape
  391. chunk_data = dataToClassifyTest(:, :, start_idx:end_idx);
  392. chunk_data = reshape(chunk_data, size(dataToClassifyTest, 1), []);
  393. if settings.smooth_before_dim == 1 % smooth NaNs through linear interpolation
  394. for ismooth = 1:size(chunk_data, 1)
  395. valid_indices = ~isnan(chunk_data(ismooth, :));
  396. chunk_data(ismooth, :) = interp1(find(valid_indices), chunk_data(ismooth, valid_indices), 1:size(chunk_data, 2), 'linear', 'extrap');
  397. end
  398. end
  399. % Perform PCA and compute explained variance
  400. [coefficients, ~, latent, ~, explained] = pca(chunk_data);
  401. % Compute explained variance
  402. explained_variance_pca = latent / sum(latent);
  403. % use a data-driven approach to get the first elbow point where
  404. % least variance is explained.
  405. curvature = diff(diff(explained_variance_pca));
  406. [~, elbow_index] = max(curvature);
  407. elbow_component = elbow_index + 1; % Add 1 because of diff operation
  408. explained_variances(i, :) = elbow_component;
  409. how_much_variance(i,:) = sum(explained_variance_pca(1:elbow_component));
  410. % Do PCA inverse transformation for later decoding
  411. if settings.decode_components == 1
  412. selected_components = coefficients(:,1:elbow_component);
  413. transformed_data = chunk_data * selected_components;
  414. reconstructed_data = transformed_data * selected_components';
  415. reconstructed_data = reshape(reconstructed_data,[size(dataToClassifyTest(:, :, start_idx:end_idx))]);
  416. ripple_to_decode(:,:,i) = nanmean(reconstructed_data,3); % take mean of those time points used in sliding window
  417. end
  418. end
  419. accuracy = explained_variances;
  420. if settings.decode_components == 1
  421. if settings.zscore_data4class
  422. dataToClassifyTraining = zscore(dataToClassifyTraining);
  423. ripple_to_decode = zscore(ripple_to_decode);
  424. end
  425. cfg = [];
  426. cfg.classifier = settings.classifier;
  427. cfg.metric = settings.metric;
  428. [accuracy_dec, ~] = mv_classify_timextime(cfg, dataToClassifyTraining, clabelTraining, ripple_to_decode, clabelTest);
  429. end
  430. if icond == 1
  431. perf{isubject}.correct.accuracy = accuracy;
  432. perf{isubject}.correct.exl_var = how_much_variance;
  433. if settings.decode_components == 1
  434. perf{isubject}.dec.correct.accuracy = accuracy_dec;
  435. end
  436. elseif icond == 2
  437. perf{isubject}.incorrect.accuracy = accuracy;
  438. perf{isubject}.incorrect.exl_var = how_much_variance;
  439. if settings.decode_components == 1
  440. perf{isubject}.dec.incorrect.accuracy = accuracy_dec;
  441. end
  442. end
  443. % add info
  444. perf{isubject}.channelcount_test = size(data_ripples.trial,2);
  445. perf{isubject}.channels_test = data_ripples.label;
  446. perf{isubject}.time_train = TOI_ripple;
  447. perf{isubject}.time_test = TOI_ripple;
  448. end
  449. % clearvars -except perf settings isubject subjects SubjectIDs RT_all_subj_correct RT_all_subj_incorrect
  450. end
  451. toc
  452. delete(gcp);
  453. return
  454. %% Stats and plots
  455. RT_correct = cellfun(@mean, RT_all_subj_correct);
  456. RT_max_correct = cellfun(@max, RT_all_subj_correct);
  457. RT_min_correct = cellfun(@min, RT_all_subj_correct);
  458. RT_incorrect = cellfun(@mean, RT_all_subj_incorrect);
  459. RT_max_incorrect = cellfun(@max, RT_all_subj_incorrect);
  460. RT_min_incorrect = cellfun(@min, RT_all_subj_incorrect);
  461. perf{1}.RT.correct = RT_all_subj_correct;
  462. perf{1}.RT.incorrect = RT_all_subj_incorrect;
  463. RT = RT_correct;
  464. ripple_time_mean = cellfun(@mean, ripple_time_correct);
  465. ripple_time_max = cellfun(@max, ripple_time_correct);
  466. ripple_time_min = cellfun(@min, ripple_time_correct);
  467. perf{1}.ripple_times.correct = ripple_time_correct;
  468. perf{1}.ripple_times.incorrect = ripple_time_incorrect;
  469. ripples_to_plot = [];
  470. rt_to_plot = [];
  471. explained_var_corr = [];
  472. explained_var_incorr = [];
  473. for participant = 1:numel(subjects)
  474. ripples_to_plot = [ripples_to_plot, ripple_time_correct{participant}];
  475. rt_to_plot = [rt_to_plot, RT_all_subj_correct{participant}];
  476. explained_var_corr(participant,:) = perf{1, participant}.correct.exl_var;
  477. explained_var_incorr(participant,:) = perf{1, participant}.incorrect.exl_var;
  478. end
  479. delay_ripple_rt = rt_to_plot-ripples_to_plot;
  480. figure;
  481. subplot(3,1,1)
  482. nhist(ripples_to_plot','proportion','color',settings.colour_scheme(8,:))
  483. title('Time of ripples')
  484. xlabel('time of ripples')
  485. ylabel('proportion')
  486. set(gca,'FontSize',14)
  487. set(gca,'TickDir','out')
  488. title(sprintf('Time of ripples, median = %.2fms',median(ripple_time_mean*1000)),'interpreter','none')
  489. subplot(3,1,2)
  490. nhist(rt_to_plot','proportion','color',settings.colour_scheme(8,:))
  491. title('Reaction time in trials of ripples')
  492. xlabel('Reaction time')
  493. ylabel('proportion')
  494. set(gca,'FontSize',14)
  495. set(gca,'TickDir','out')
  496. title(sprintf('Reaction time in trials of ripples, median = %.2fms',median(RT*1000)),'interpreter','none')
  497. subplot(3,1,3)
  498. nhist(delay_ripple_rt','proportion','color',settings.colour_scheme(8,:))
  499. xlabel('Delay ripple RT')
  500. ylabel('proportion')
  501. set(gca,'FontSize',14)
  502. set(gca,'TickDir','out')
  503. title(sprintf('Delay ripples RT, median = %.2fms',median(delay_ripple_rt*1000)),'interpreter','none')
  504. for participant = 1:numel(subjects)
  505. trl_num_test = perf{participant}.correct.trl_num_test;
  506. trl_correct(participant) = sum(trl_num_test);
  507. trl_num_test = perf{participant}.incorrect.trl_num_test;
  508. trl_incorrect(participant) = sum(trl_num_test);
  509. end
  510. data_nu_trl = {};
  511. data_nu_trl{1,1} = trl_correct;
  512. data_nu_trl{2,1} = trl_incorrect;
  513. [~,p_val,~,stats] = ttest(trl_correct,trl_incorrect)
  514. figure;
  515. h = rm_raincloud(data_nu_trl, [settings.colour_scheme(6,:)],0,'ks',[],settings.colour_scheme);
  516. h.p{1, 1}.FaceColor = settings.colour_scheme(1,:);
  517. h.s{1, 1}.MarkerFaceColor = settings.colour_scheme(1,:);
  518. h.m(1, 1).MarkerFaceColor = settings.colour_scheme(1,:);
  519. h.p{2, 1}.FaceColor = settings.colour_scheme(10,:);
  520. h.s{2, 1}.MarkerFaceColor = settings.colour_scheme(10,:);
  521. h.m(2, 1).MarkerFaceColor = settings.colour_scheme(10,:);
  522. hold on
  523. title(sprintf('number of trials for the two conditions, t-stat = %.2f', stats.tstat))
  524. set(gca,'TickDir','out')
  525. xlabel('number of trials')
  526. yticklabels({sprintf('incorrect %d',mean(trl_incorrect)), sprintf('correct %d',floor(mean(trl_correct)))})
  527. ylabel('condition')
  528. set(gca,'FontSize',20)
  529. axis tight
  530. [~,p_val,~,stats] = ttest(trl_correct,trl_incorrect)
  531. correct_incorrect = {};
  532. for isubject = 1:numel(subjects)
  533. correct_incorrect{1}.label = {'Channels'};
  534. correct_incorrect{1}.time = perf{1,1}.time_train;
  535. correct_incorrect{1}.individual(isubject,1,:) = perf{isubject}.correct.accuracy;
  536. % correct_incorrect{1}.individual(isubject,1,:) = explained_var_corr(isubject,:);
  537. correct_incorrect{1}.dimord = 'subj_chan_time';
  538. end
  539. correct_incorrect{1}.avg = squeeze(correct_incorrect{1}.individual);
  540. correct_incorrect{2} = correct_incorrect{1};
  541. for isubject = 1:numel(subjects)
  542. correct_incorrect{2}.individual(isubject,1,:) = perf{isubject}.incorrect.accuracy;
  543. % correct_incorrect{2}.individual(isubject,1,:) = explained_var_incorr(isubject,:);
  544. end
  545. correct_incorrect{2}.avg = squeeze(correct_incorrect{2}.individual);
  546. % decoding
  547. if settings.decode_components == 1
  548. correct = cell(1,numel(subjects));
  549. incorrect = cell(1,numel(subjects));
  550. correct_dec = [];
  551. incorrect_dec = [];
  552. for isubject = 1:numel(subjects)
  553. % correct
  554. correct{1,isubject} = struct;
  555. correct{1,isubject}.label = {'chan'};
  556. correct{1,isubject}.dimord = 'chan_freq_time';
  557. correct{1,isubject}.freq = freqaxis;
  558. correct{1,isubject}.time = perf{1, 1}.time_test;
  559. correct{1,isubject}.powspctrm(1,:,:) = perf{isubject}.dec.correct.accuracy;
  560. % incorrect
  561. incorrect{1,isubject} = correct{1,isubject};
  562. incorrect{1,isubject}.powspctrm(1,:,:) = perf{isubject}.dec.incorrect.accuracy;
  563. baseline_all{1,isubject} = correct{1,isubject};
  564. baseline_all{1,isubject}.powspctrm(1,:,:) = .5*ones(size(perf{isubject}.dec.correct.accuracy));
  565. correct_dec(isubject,:,:) = perf{isubject}.dec.correct.accuracy;
  566. incorrect_dec(isubject,:,:) = perf{isubject}.dec.incorrect.accuracy;
  567. end
  568. end
  569. %% FT stats (dimensionality)
  570. xlimits = nearest(correct_incorrect{1, 1}.time, -1):nearest(correct_incorrect{1, 1}.time, 1);
  571. xlimits = correct_incorrect{1, 1}.time(xlimits);
  572. cfg = [];
  573. cfg.latency = [xlimits(1) xlimits(end)];
  574. cfg.channel = 'all';
  575. cfg.statistic = 'depsamplesT';
  576. cfg.method = 'montecarlo'; % 'montecarlo' 'analytic';
  577. cfg.correctm = 'cluster'; % 'no', cluster;
  578. cfg.alpha = .05;
  579. cfg.clusteralpha = .05;
  580. cfg.tail = 0;
  581. cfg.correcttail = 'alpha'; % alpha prob no
  582. cfg.neighbours = [];
  583. cfg.minnbchan = 0;
  584. cfg.avgovertime = 'no'; % 'no' 'yes'
  585. cfg.avgoverchan = 'no';
  586. cfg.computecritval = 'yes';
  587. cfg.numrandomization = 'all';%'all';
  588. cfg.clusterstatistic = 'maxsum'; % 'maxsum', 'maxsize', 'wcm'
  589. cfg.clustertail = cfg.tail;
  590. cfg.parameter = 'individual';
  591. nSub = size(correct_incorrect{1, 1}.individual ,1);
  592. % set up design matrix
  593. design = zeros(2,2*nSub);
  594. for i = 1:nSub
  595. design(1,i) = i;
  596. end
  597. for i = 1:nSub
  598. design(1,nSub+i) = i;
  599. end
  600. design(2,1:nSub) = 1;
  601. design(2,nSub+1:2*nSub) = 2;
  602. cfg.design = design;
  603. cfg.uvar = 1;
  604. cfg.ivar = 2;
  605. % run stats
  606. [Fieldtripstats] = ft_timelockstatistics(cfg, correct_incorrect{:});
  607. length(find(Fieldtripstats.mask))
  608. %% plot significant vals
  609. d = squeeze(correct_incorrect{1}.individual);
  610. m = nanmean(d);
  611. s = nanstd(d)./sqrt(size(d,1));
  612. figure;
  613. boundedline(perf{1,1}.time_train,m,s,'b');
  614. plot(perf{1,1}.time_train,m,'k','linewidth',2);
  615. hold on
  616. d = squeeze(correct_incorrect{2}.individual);
  617. m = nanmean(d);
  618. s = nanstd(d)./sqrt(size(d,1));
  619. boundedline(perf{1,1}.time_train,m,s,'r');
  620. plot(perf{1,1}.time_train,m,'k','linewidth',2);
  621. hold on
  622. stats_time = nearest(correct_incorrect{1}.time,cfg.latency(1)):nearest(correct_incorrect{1}.time,cfg.latency(2));
  623. sigline = nan(1,numel(correct_incorrect{1}.time));
  624. % sigline(stats_time(Fieldtripstats.mask==1)) = m(stats_time(Fieldtripstats.mask==1));
  625. sigline(stats_time(Fieldtripstats.mask==1)) = 2;
  626. plot(correct_incorrect{1}.time,sigline,'r','linewidth',4);
  627. set(gca,'FontSize',16,'FontName','Arial')
  628. xlabel('ripple time (s)')
  629. ylabel('dimensionality difference')
  630. set(gca,'TickDir','out')
  631. axis tight
  632. vline(0)
  633. xlim([cfg.latency(1), cfg.latency(end)])
  634. m_exp_corr = mean(explained_var_corr(:,sigline==2),2);
  635. m_exp_incorr = mean(explained_var_incorr(:,sigline==2),2);
  636. mean(m_exp_corr)
  637. mean(m_exp_incorr)
  638. std(m_exp_corr)
  639. std(m_exp_incorr)
  640. [~,p_val_expl_var,~,stat] = ttest(m_exp_corr,m_exp_incorr)
  641. %% relate sigline to reaction time on a group level
  642. d = squeeze(correct_incorrect{1}.individual);
  643. RT = RT_correct;
  644. dimensionality_change = mean(d(:,sigline==2),2);
  645. figure;
  646. scatter(RT, dimensionality_change, 'filled', 'MarkerFaceColor', '#0072BD');
  647. xlabel('RT', 'FontSize', 12);
  648. ylabel('Dimensionality Change', 'FontSize', 12);
  649. title('Correlation', 'FontSize', 14);
  650. grid on;
  651. box on;
  652. hold on;
  653. % Fit a linear regression line
  654. p = polyfit(RT, dimensionality_change, 1);
  655. f = polyval(p, RT);
  656. plot(RT, f, 'r-', 'LineWidth', 1.5);
  657. % Add legend
  658. legend('Data', 'Linear Fit', 'Location', 'best');
  659. % Customize the plot appearance
  660. set(gca, 'FontSize', 10); % Set font size for axis labels
  661. set(gcf, 'Color', 'w'); % Set background color of the figure to white
  662. yfit = polyval(p, RT);
  663. yresid = dimensionality_change - yfit;
  664. SSresid = sum(yresid.^2);
  665. SStotal = (length(dimensionality_change)-1) * var(dimensionality_change);
  666. rsq = 1 - SSresid/SStotal;
  667. disp(['R-squared: ', num2str(rsq)]);
  668. [rho_rt_dim, p_rt_dim] = corr(RT',dimensionality_change, 'type', 'Spearman');
  669. %% correlate RT across time
  670. data_to_correlate = squeeze(correct_incorrect{1}.individual);
  671. RT = RT_correct;
  672. fs = 1/(correct_incorrect{1, 1}.time(2)-correct_incorrect{1, 1}.time(1));
  673. chunk_size = round(100/((1/fs)*1000)); % Size of each chunk
  674. overlap_size = round(chunk_size * settings.prc_overlap);
  675. num_chunks = floor((size(correct_incorrect{1}.individual, 3) - overlap_size) / (chunk_size - overlap_size));
  676. TOI_corr = linspace(correct_incorrect{1, 1}.time(1), correct_incorrect{1, 1}.time(end),num_chunks);
  677. rho_across_time = [];
  678. p_across_time = [];
  679. for itime = 1:num_chunks
  680. start_idx = (itime - 1) * (chunk_size - overlap_size) + 1;
  681. end_idx = start_idx + chunk_size - 1;
  682. [rho_tmp, p_tmp] = corr(mean(data_to_correlate(:,start_idx:end_idx),2), RT','type', 'spearman');
  683. rho_across_time(itime) = rho_tmp;
  684. p_across_time(itime) = p_tmp;
  685. end
  686. rho_across_time_perm = [];
  687. p_across_time_perm = [];
  688. for nu_perm = 1:500
  689. rand_rt = randperm(12);
  690. for itime = 1:num_chunks
  691. start_idx = (itime - 1) * (chunk_size - overlap_size) + 1;
  692. end_idx = start_idx + chunk_size - 1;
  693. [rho_tmp, p_tmp] = corr(mean(data_to_correlate(rand_rt,start_idx:end_idx),2), RT','type', 'spearman');
  694. rho_across_time_perm(nu_perm,itime) = rho_tmp;
  695. p_across_time_perm(nu_perm,itime) = p_tmp;
  696. end
  697. end
  698. xlimits_ind = nearest(TOI_corr, -1):nearest(TOI_corr, 1);
  699. alpha = 0.05;
  700. z_threshold = norminv(1 - alpha/2);
  701. zvalue_rho = (rho_across_time(xlimits_ind)-(mean(rho_across_time_perm(:,xlimits_ind))))./std(rho_across_time_perm(:,xlimits_ind));
  702. xlimits = TOI_corr(xlimits_ind);
  703. plot(xlimits, zvalue_rho,'linewidth', 3)
  704. hold on
  705. plot(xlimits, z_threshold * ones(size(xlimits)), 'r--'); % positive threshold
  706. plot(xlimits, -z_threshold * ones(size(xlimits)), 'r--'); % negative threshold
  707. below_threshold = zvalue_rho < -z_threshold;
  708. scatter(xlimits(below_threshold), zvalue_rho(below_threshold), 'r', 'filled', 'MarkerFaceAlpha', 0.5)
  709. ylim([-3 3])
  710. xlim([-1 1])
  711. set(gca,'FontSize',16)
  712. set(gca,'TickDir','out')
  713. hold off
  714. xlabel('Ripple time (sec)')
  715. ylabel('Z-transformed correlation')
  716. title('Z-value of correlation Across Time')
  717. legend('Z-value', 'Positive Threshold', 'Negative Threshold', 'Location', 'NorthEast')
  718. %% plot t line
  719. figure
  720. plot(Fieldtripstats.time,Fieldtripstats.stat,'k','linewidth',2)
  721. hold on
  722. sigline05 = nan(1,numel(Fieldtripstats.prob));
  723. sigline01 = nan(1,numel(Fieldtripstats.prob));
  724. p05 = Fieldtripstats.prob < .05;
  725. p01 = Fieldtripstats.prob < .01;
  726. sigline05(p05) = Fieldtripstats.stat(p05);
  727. sigline01(p01) = Fieldtripstats.stat(p01);
  728. plot(Fieldtripstats.time,sigline05,'r','linewidth',5)
  729. plot(Fieldtripstats.time,sigline01,'y','linewidth',2)
  730. vline(0)
  731. hline(0)
  732. set(gca,'FontSize',8)
  733. xlabel('time (sec)')
  734. set(gca,'TickDir','out')
  735. title('dimensionality');
  736. xlim([cfg.latency(1), cfg.latency(end)])
  737. %% correlate fine-grained decoding with dimensionality
  738. % FT stats (dimensionality)
  739. xlimits = nearest(correct_incorrect{1, 1}.time, -1):nearest(correct_incorrect{1, 1}.time, 1);
  740. xlimits = correct_incorrect{1, 1}.time(xlimits);
  741. cfg = [];
  742. cfg.latency = [xlimits(1) xlimits(end)];
  743. cfg.channel = 'all';
  744. cfg.statistic = 'depsamplesT';
  745. cfg.method = 'montecarlo'; % 'montecarlo' 'analytic';
  746. cfg.correctm = 'cluster'; % 'no', cluster;
  747. cfg.alpha = .05;
  748. cfg.clusteralpha = .05;
  749. cfg.tail = 0;
  750. cfg.correcttail = 'alpha'; % alpha prob no
  751. cfg.neighbours = [];
  752. cfg.minnbchan = 0;
  753. cfg.avgovertime = 'no'; % 'no' 'yes'
  754. cfg.avgoverchan = 'no';
  755. cfg.computecritval = 'yes';
  756. cfg.numrandomization = 500;%'all';
  757. cfg.clusterstatistic = 'maxsum'; % 'maxsum', 'maxsize', 'wcm'
  758. cfg.clustertail = cfg.tail;
  759. cfg.parameter = 'individual';
  760. nSub = size(correct_incorrect{1, 1}.individual ,1);
  761. % set up design matrix
  762. design = zeros(2,2*nSub);
  763. for i = 1:nSub
  764. design(1,i) = i;
  765. end
  766. for i = 1:nSub
  767. design(1,nSub+i) = i;
  768. end
  769. design(2,1:nSub) = 1;
  770. design(2,nSub+1:2*nSub) = 2;
  771. cfg.design = design;
  772. cfg.uvar = 1;
  773. cfg.ivar = 2;
  774. % run stats
  775. [Fieldtripstats] = ft_timelockstatistics(cfg, correct_incorrect{:});
  776. length(find(Fieldtripstats.mask))
  777. %% plot significant vals
  778. d = squeeze(correct_incorrect{1}.individual-correct_incorrect{2}.individual);
  779. m = nanmean(d);
  780. s = nanstd(d)./sqrt(size(d,1));
  781. figure;
  782. boundedline(perf{1,1}.time_train,m,s,'k');
  783. plot(perf{1,1}.time_train,m,'k','linewidth',2);
  784. hold on
  785. stats_time = nearest(correct_incorrect{1}.time,cfg.latency(1)):nearest(correct_incorrect{1}.time,cfg.latency(2));
  786. sigline = nan(1,numel(correct_incorrect{1}.time));
  787. % sigline(stats_time(Fieldtripstats.mask==1)) = m(stats_time(Fieldtripstats.mask==1));
  788. sigline(stats_time(Fieldtripstats.mask==1)) = 0;
  789. plot(correct_incorrect{1}.time,sigline,'r','linewidth',4);
  790. set(gca,'FontSize',16,'FontName','Arial')
  791. xlabel('ripple time (s)')
  792. ylabel('dimensionality difference')
  793. set(gca,'TickDir','out')
  794. axis tight
  795. vline(0)
  796. hline(0)
  797. xlim([cfg.latency(1), cfg.latency(end)])
  798. % load original data
  799. load('mask_t_vals_fine_enc_ripple.mat')
  800. mask_t_vals_fine = squeeze(mask_t_vals_fine_enc_ripple);
  801. % Load data from decoding analysis
  802. load('correct_dec_fine_all.mat')
  803. load('incorrect_dec_fine_all.mat')
  804. correct_dec_all = correct_dec_fine_all;
  805. incorrect_dec_all = incorrect_dec_fine_all;
  806. time_dec = linspace(-.5,3,351);
  807. idx_time = nearest(time_dec,-.2):nearest(time_dec,3);
  808. correct_dec_all = correct_dec_all(:,idx_time,:);
  809. incorrect_dec_all = incorrect_dec_all(:,idx_time,:);
  810. for isubject = 1:size(settings.subjects,1)
  811. tmp_sub = [];
  812. tmp_sub = squeeze(correct_dec_all(isubject, :,:));
  813. tmp_sub(~mask_t_vals_fine) = NaN;
  814. correct_dec_all(isubject, :,:) = tmp_sub;
  815. tmp_sub = [];
  816. tmp_sub = squeeze(incorrect_dec_all(isubject, :,:));
  817. tmp_sub(~mask_t_vals_fine) = NaN;
  818. incorrect_dec_all(isubject, :,:) = tmp_sub;
  819. end
  820. correct_dec_mean = nanmean(nanmean(correct_dec_all,3),2);
  821. incorrect_dec_mean = nanmean(nanmean(incorrect_dec_all,3),2);
  822. dimensionality_change = mean(d(:,sigline==0),2);
  823. [r, p] = corr(dimensionality_change,correct_dec_mean-incorrect_dec_mean,'tail','right');
  824. % Number of data points
  825. n = numel(dimensionality_change);
  826. % Convert r to Fisher's z-score
  827. z = atanh(r);
  828. effect_size = z * sqrt(n - 3);
  829. disp(['Effect size (Cohen''s d): ' num2str(effect_size)]);
  830. %% visualise included channels
  831. % elec_size = 15;
  832. % transp = 0.25;
  833. % extracolor = .5;
  834. %
  835. % epos_all = [];
  836. %
  837. % for isubject = 1:numel(subjects)
  838. % epos_all = [epos_all;cell2mat({channs{isubject}.coords}')];
  839. % end
  840. %
  841. % colvec = ones(1,size(epos_all,1));
  842. %
  843. % views = [-90 0;0 0;180 -90];
  844. %
  845. % for iview=1:size(views,1)
  846. % figure
  847. % plot_ecog(colvec, ...
  848. % fullfile(ftpath,'template/anatomy/'),...
  849. % epos_all,[-max(colvec) max(colvec)+extracolor], transp, views(iview,:), elec_size);
  850. % colorbar off
  851. % end
  852. %%

ripple_stage_04a_dimensionality_final.m at commit 1d7307a, no license · at the source

Overview

  1. Max Planck Institute for Human Cognitive and Brain Sciences,Leipzig, Germany
  2. Department of Psychology, New York University,New York, NY USA
  3. Kavli Institute for Systems Neuroscience, Centre for Neural Computation, Egil and Pauline Braathen and Fred Kavli Centre for Cortical Microcircuits, Jebsen Centre for Alzheimer’s Disease, NTNU Norwegian University of Science and Technology,Trondheim, Norway
Journal: Nature communications, volume 17, issue 1, article 6677
Dates: received 22 September 2025; accepted 26 June 2026; published online 20 July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s41467-026-75345-6 · PMID 42476972 · PMCID PMC13385388 · OpenAlex W4410097547
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: EEG (modality), human (organism), epilepsy (population), cognitive (subfield)
Methods: Spectral & time-frequency, Connectivity, Statistics, Smoothing, state filtering, decompositions, Machine learning, Preprocessing, Evoked potentials, Single-unit activity, calcium imaging, Physiology & signal measures
Keywords: Learning and memory, Hippocampus
MeSH: Cerebral Cortex*, Hippocampus*, Mental Recall*, Drug Resistant Epilepsy, Electroencephalography, Female, Humans, Male, Memory, Episodic, Reaction Time (* major topic)
Topic: Memory and Neural Mechanisms (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Citations: not cited yet (Europe PMC); 105 references in the paper

Abstract

How are past experiences reconstructed from memory? Learning is thought to compress external inputs into low-dimensional hippocampal representations, later expanded into high-dimensional cortical activity during recall. Hippocampal ripples, brief high-frequency bursts linked to retrieval, may initiate this expansion. Analysing intracranial EEG data from patients with pharmacoresistant epilepsy during an episodic memory task, we found that cortical dimensionality increased following ripple events during correct, but not incorrect, retrieval. This expansion correlated with faster reaction times and reinstatement of the target association. Crucially, hippocampal theta and cortical gamma phase-amplitude coupling emerged after ripples but before cortical expansion, suggesting a mechanism for ripple-driven communication. Ripple events also marked the separation of task-relevant variables in cortical state space, revealing how hippocampal output reshapes the geometry of memory representations to support successful recall.

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

Repository

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

kerrencasper/Hippocampal-ripples-initiate-cortical-dimensionality-expansion-for-memory-retrieval

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 1d7307a6037aa27e50b7b489b04f79bb0aa77aa9, 16 April 2025
Languages: MATLAB (18)
Size: 60 files, 18 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
19 files

Code availability

The code that supports the conclusions of this study is available at GitHub (https://github.com/kerrencasper/Hippocampal-ripples-initiate-cortical-dimensionality-expansion-for-memory-retrieval.git).

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

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

The data that support the conclusions of this study are available at Zenodo (10.5281/zenodo.18490239).

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

Versions

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

Version 2, 28 September 2026

  • Funding: added Deutsche Forschungsgemeinschaft: 437219953; Max-Planck-Gesellschaft

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 3 authors, 2 keywords, 10 MeSH terms, 101 references.

Cite

This paper

Kerrén, C., Michelmann, S., & Doeller, C. F. (2026). Hippocampal ripples initiate cortical dimensionality expansion for memory retrieval. Nature communications, 17(1), 6677. https://doi.org/10.1038/s41467-026-75345-6

BibTeX

@article{kerren2026hippocampal,
author = {Kerrén, Casper and Michelmann, Sebastian and Doeller, Christian F.},
title = {{Hippocampal ripples initiate cortical dimensionality expansion for memory retrieval}},
journal = {Nature communications},
year = {2026},
month = jul,
volume = {17},
number = {1},
pages = {6677},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/s41467-026-75345-6},
url = {https://doi.org/10.1038/s41467-026-75345-6},
pmid = {42476972},
pmcid = {PMC13385388}
}

RIS

TY - JOUR
AU - Kerrén, Casper
AU - Michelmann, Sebastian
AU - Doeller, Christian F.
TI - Hippocampal ripples initiate cortical dimensionality expansion for memory retrieval
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/07/20
VL - 17
IS - 1
SP - 6677
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/s41467-026-75345-6
UR - https://doi.org/10.1038/s41467-026-75345-6
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41467-026-75345-6",
"type": "article-journal",
"title": "Hippocampal ripples initiate cortical dimensionality expansion for memory retrieval",
"container-title": "Nature communications",
"author": [
{
"family": "Kerrén",
"given": "Casper"
},
{
"family": "Michelmann",
"given": "Sebastian"
},
{
"family": "Doeller",
"given": "Christian F."
}
],
"container-title-short": "Nat Commun",
"volume": "17",
"issue": "1",
"page": "6677",
"DOI": "10.1038/s41467-026-75345-6",
"PMID": "42476972",
"PMCID": "PMC13385388",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41467-026-75345-6",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
20
]
]
}
}

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

Similar papers

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

[1] doi:10.7554/elife.100642 [code]
Disrupted hippocampal theta-gamma coupling and spike-field coherence following experimental traumatic brain injury.
Journal: eLife
In common: Parallel Computing Toolbox, Image Processing Toolbox, Signal Processing Toolbox, 11 references
[2] doi:10.1038/s41593-026-02345-6 [code]
Human hippocampal ripples tune cortical responses based on predicted uncertainty.
Journal: Nature neuroscience
In common: boundedline, FieldTrip, Image Processing Toolbox, 2 other tools, cognitive, 9 references
[3] doi:10.1093/braincomms/fcag041
Exercise enhances hippocampal-cortical ripple interactions in the human brain.
Journal: Brain communications
In common: 13 references
[4] doi:10.7554/elife.110795 [code]
REM sleep prefrontal high-frequency oscillation chains mediate distinct cortical - hippocampal reactivation patterns compared to NREM sleep.
Journal: eLife
In common: boundedline, FieldTrip, Image Processing Toolbox, 2 other tools, 8 references
[5] doi:10.1038/s41467-026-70633-7 [code]
Global coincident bursts of high frequency oscillations across the human cortex coordinate large-scale memory processing.
Journal: Nature communications
In common: epilepsy, cognitive, 10 references
[6] doi:10.1162/imag.a.1218 [code]
Reliability and signal comparison of OPM-MEG, fMRI &amp; iEEG in a repeated movie viewing paradigm.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: FieldTrip, Image Processing Toolbox, Statistics and Machine Learning Toolbox, 5 references, author Sebastian Michelmann
[7] doi:10.1016/j.celrep.2026.117845 [code]
Hippocampal skill memory expansion drives online performance dynamics during skill learning.
Journal: Cell reports
In common: Image Processing Toolbox, cognitive, 9 references
[8] doi:10.7554/elife.108023 [code]
Challenges in replay detection by TDLM in post-encoding resting state.
Journal: eLife
In common: Statistics and Machine Learning Toolbox, 9 references
[9] doi:10.1038/s42003-025-08618-3 [code]
Physical activity simultaneously improves working memory and ripple-spindle coupling
Journal: n/a
In common: Statistics and Machine Learning Toolbox, EEG, cognitive, 8 references
[10] doi:10.1016/j.celrep.2026.117646 [code]
Medial entorhinal-hippocampal desynchronization parallels the emergence of memory impairment in a mouse model of Alzheimer's disease pathology.
Journal: Cell reports
In common: boundedline, FieldTrip, Parallel Computing Toolbox, 3 other tools, 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.