We performed source analysis in the theta band using a DICS beamformer
Source localisation was conducted using Dynamical Imaging of Coherent Sources (DICS, Gross et al., 2001), which applies a spatial filter to the MEG data at each grid point, in order to maximise signal from that location whilst attenuating signals elsewhere. The spatial filter was calculated from the cross-spectral densities for a time–frequency tile centred on the effects found at sensor level (3-6Hz; 0–650ms; gradiometer channels only). Beamforming approaches have been shown to reduce the influence of external artefacts (e.g. EOG, ECG and muscle activity) by localising these outside the brain (Herdman & Cheyne, 2009; Hillebrand & Barnes, 2005). For all analyses, a common filter across baseline and active periods was used and a regularisation parameter of lambda 5% was applied.
Script: group_beamformer_2_multiplesubject_PS.m
%% Specify subject list, good channals and condition labels
subject = sort({'XX','XY','XZ'...});
% Only include the gradiometers and remove bad channels
chans_included = {'MEGGRAD', '-MEG0322', '-MEG2542','-MEG0111','-MEG0532'};
% Condition List
condition = {'LR_hard','LR_easy','VO_easy','VO_hard'};
%% Loop for all subjects & all conditions
for i=1:length(subject)
%% Load variable required for source analysis
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\mri_realigned.mat',subject{i}))
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sens.mat',subject{i}))
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\seg.mat',subject{i}))
%% Create leadfields in subject's brain warped to MNI space
% Load template sourcemodel
load('D:\fieldtrip-20160208\template\sourcemodel\standard_sourcemodel3d8mm.mat');
template_grid = sourcemodel;
template_grid = ft_convert_units(template_grid,'mm')
clear sourcemodel;
% construct the volume conductor model (i.e. head model) for each
% subject
cfg = [];
cfg.method = 'singleshell';
headmodel = ft_prepare_headmodel(cfg, seg);
% create the subject specific grid, using the template grid that has
% just been created
cfg = [];
cfg.grid.warpmni = 'yes';
cfg.grid.template = template_grid;
cfg.grid.nonlinear = 'yes'; % use non-linear normalization
cfg.mri = mri_realigned;
cfg.grid.unit ='mm';
grid = ft_prepare_sourcemodel(cfg);
% make a figure of the single subject headmodel, and grid positions
figure; hold on;
ft_plot_vol(headmodel, 'edgecolor', 'none', 'facesourceloc', 0.4);
ft_plot_mesh(grid.pos(grid.inside,:));
ft_plot_sens(sens, 'style', 'r*')
% create leadfield
cfg = [];
cfg.channel = chans_included;
cfg.grid = grid;
cfg.vol = headmodel;
cfg.grad = sens;
cfg.normalize = 'yes';
lf = ft_prepare_leadfield(cfg)
%% Now perform DICS using prepared leadfields for all 4 conditions
for con = 1:length(condition)
%% Load the required variables you computed earlier
load(sprintf('D:\\pilot\\%s\\PS\\data_%s.mat',subject{i},condition{con}))
%% CD to correct place
cd(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\',subject{i}))
%% Load in data variable of interest
data_clean = eval(sprintf('data_%s',condition{con}));
%% Compensate for Maxfilter & keep the manuualy keep the rank of the data under 64
covar = zeros(numel(data_clean.label));
for itrial = 1:numel(data_clean.trial)
currtrial = data_clean.trial{itrial};
covar = covar + currtrial*currtrial.';
end
[V, D] = eig(covar);
D = sort(diag(D),'descend');
D = D ./ sum(D);
Dcum = cumsum(D);
numcomponent = find(Dcum>.99,1,'first') +1; % number of components accounting for 99% of variance in covar matrix
disp(sprintf('\n Reducing the data to %d components \n',numcomponent));
cfg = [];
cfg.method = 'pca';
cfg.updatesens = 'yes';
cfg.channel = 'MEG';
comp = ft_componentanalysis(cfg, data_clean);
cfg = [];
cfg.updatesens = 'yes';
cfg.component = comp.label(numcomponent:end);
data_fix = ft_rejectcomponent(cfg, comp);
%% Downsample to 250Hz
cfg = [];
cfg.resamplefs = 250;
cfg.detrend = 'no';
data_clean_250 = ft_resampledata(cfg,data_fix);
%% Cut out windows of interest
cfg = [];
cfg.toilim = [-0.65 0];
dataPre = ft_redefinetrial(cfg, data_clean_250);
cfg.toilim = [0 0.65];
dataPost = ft_redefinetrial(cfg, data_clean_250);
cfg.toilim = [-0.65 0.65];
dataAll = ft_redefinetrial(cfg, data_clean_250);
%% Do sensor-level frequency analysis
cfg = [];
cfg.grad = sens;
cfg.taper = 'hanning';
cfg.method = 'mtmfft';
cfg.output = 'powandcsd';
cfg.foilim = [3 6];
cfg.pad = 4;
freqAll = ft_freqanalysis(cfg, dataAll);
freqPre = ft_freqanalysis(cfg, dataPre);
freqPost = ft_freqanalysis(cfg, dataPost);
%% Do ya beamforming
% Source reconstruction for the whole trial
cfg=[];
cfg.grad = sens;
cfg.method='dics';
cfg.dics.lambda = '5%';
cfg.dics.projectnoise = 'yes';
cfg.grid=lf;
cfg.vol=headmodel;
cfg.keepfilter = 'yes';
cfg.channel = chans_included;
sourceAll=ft_sourceanalysis(cfg, freqAll);
% DICS for your conditions of interest using the common filter
% computed aboce.
cfg=[];
cfg.grad = sens;
cfg.method='dics';
cfg.dics.lambda = '5%';
cfg.dics.projectnoise = 'yes';
cfg.grid=lf;
cfg.grid.filter = sourceAll.avg.filter;
cfg.vol=headmodel;
cfg.keepfilter='no';
cfg.channel = chans_included;
sourcePre=ft_sourceanalysis(cfg, freqPre);
sourcePost=ft_sourceanalysis(cfg, freqPost);
% Make sure your field positions match the template grid
sourcePre.pos=template_grid.pos; % right(?)
sourcePost.pos=template_grid.pos; % right(?)
% SAVE
save(sprintf('sourcePre_%s',condition{con}), 'sourcePre', '-v7.3');
save(sprintf('sourcePost_%s',condition{con}), 'sourcePost', '-v7.3');
% Compute the power difference between baseline and active period
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = '((x1-x2)./x2)*100';
sourceR=ft_math(cfg,sourcePost,sourcePre);
% Interpolate
mri = ft_read_mri('D:\fieldtrip-20160208\template\anatomy\single_subj_T1.nii');
cfg = [];
cfg.voxelcoord = 'no';
cfg.parameter = 'pow';
cfg.interpmethod = 'nearest';
sourceI = ft_sourceinterpolate(cfg, sourceR, mri);
% Plot + Save
cfg = [];
cfg.funparameter = 'pow'
cfg.location = 'max';
ft_sourceplot(cfg,sourceI)
colormap(jet)
set(gcf,'name',sprintf('%s>Baseline Subject: %s',condition{con},subject{i}))
saveas(gcf,sprintf('SourceI_%s.png',condition{con}));
clear data_clean data_fix sourceR sourceI sourcePre sourcePost
clc
end
%% Now we calculate Left/Right 160 versus Visible Occluded 160
load sourcePost_LR_hard.mat
load sourcePre_LR_hard.mat
% Baseline correct LR_hard
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = '((x1-x2)./x2)*100';
sourcePost_LRhard_bc =ft_math(cfg,sourcePost,sourcePre);
load sourcePost_LR_easy.mat
load sourcePre_LR_easy.mat
% Baseline correct LR_easy
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = '((x1-x2)./x2)*100';
sourcePre_LReasy_bc =ft_math(cfg,sourcePost,sourcePre);
% Take LR_hard activity away from LR_easy
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = 'subtract';
sourceR=ft_math(cfg,sourcePost_LRhard_bc,sourcePre_LReasy_bc);
% Interpolate onto MRI with atlas
mri = ft_read_mri('D:\fieldtrip-20160208\template\anatomy\single_subj_T1.nii');
cfg = [];
cfg.voxelcoord = 'no';
cfg.parameter = 'pow';
cfg.interpmethod = 'nearest';
sourceI = ft_sourceinterpolate(cfg, sourceR, mri);
cfg=[];
cfg.method = 'ortho';
cfg.funparameter = 'pow';
cfg.funcolormap = 'jet';
ft_sourceplot(cfg,sourceI);
set(gcf,'name',sprintf('160 vs 60 L/R Subject = %s',subject{i}))
saveas(gcf,'160 vs 60 L_R.png')
clear seg sens mri_realigned lf grid template_grid
end
Left/Right and Visible/Occluded theta power whole-brain maps are then loaded, baseline corrected and statistically compared using non-parametric cluster statistics.
% For each condition I am loading in pre + post soure localisation
% variables and subtracting the post-grating power frome pre-grating power
grandavg_LR_hard = []; % LR_hard
for i=1:length(subject)
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePost_LR_hard.mat',subject{i}))
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePre_LR_hard.mat',subject{i}))
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = 'subtract';
sourceR=ft_math(cfg,sourcePost,sourcePre);
sourceR.subject = subject{i};
grandavg_LR_hard{i} = sourceR; disp(['Added Subject ' subject{i}]);
end
clear alldata cfg sourceR
grandavg_LR_easy = []; % LR_easy
for i=1:length(subject)
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePost_LR_easy.mat',subject{i}))
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePre_LR_easy.mat',subject{i}))
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = 'subtract';
sourceR=ft_math(cfg,sourcePost,sourcePre);
sourceR.subject = subject{i};
grandavg_LR_easy{i} = sourceR; disp(['Added Subject ' subject{i}]);
end
clear alldata cfg sourceR
grandavg_VO_hard = []; % VO_hard
for i=1:length(subject)
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePost_VO_hard.mat',subject{i}))
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePre_VO_hard.mat',subject{i}))
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = 'subtract';
sourceR=ft_math(cfg,sourcePost,sourcePre);
sourceR.subject = subject{i};
grandavg_VO_hard{i} = sourceR; disp(['Added Subject ' subject{i}]);
end
clear alldata cfg sourceR
grandavg_VO_easy = []; % VO_easy
for i=1:length(subject)
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePost_VO_easy.mat',subject{i}))
load(sprintf('D:\\pilot\\%s\\PS\\sourceloc\\sourcePre_VO_easy.mat',subject{i}))
cfg = [];
cfg.parameter = 'avg.pow';
cfg.operation = 'subtract';
sourceR=ft_math(cfg,sourcePost,sourcePre);
sourceR.subject = subject{i};
grandavg_VO_easy{i} = sourceR; disp(['Added Subject ' subject{i}]);
end
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% STATS
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Run Statistics
% run statistics over subjects
cfg=[];
cfg.dim = grandavg_LR_hard{1}.dim;
cfg.method = 'montecarlo';
cfg.statistic = 'ft_statfun_depsamplesT';
cfg.parameter = 'pow';
cfg.correctm = 'cluster';
cfg.computecritval = 'yes';
cfg.numrandomization = 1000;
cfg.tail = 0; % Two sided testing
% Design Matrix
nsubj=numel(grandavg_LR_hard);
cfg.design(1,:) = [1:nsubj 1:nsubj];
cfg.design(2,:) = [ones(1,nsubj) ones(1,nsubj)*2];
cfg.uvar = 1; % row of design matrix that contains unit variable (in this case: subjects)
cfg.ivar = 2; % row of design matrix that contains independent variable (the conditions)
% Perform statistical analysis separsetly for LR & VO conditions
[stat_LR] = ft_sourcestatistics(cfg,grandavg_LR_hard{:}, grandavg_LR_easy{:});
[stat_VO] = ft_sourcestatistics(cfg,grandavg_VO_hard{:}, grandavg_VO_easy{:});
[stat] = ft_sourcestatistics(cfg,grandavgA{:}, grandavgB{:});
%save('stat','stat','-v7.3')
% Show raw source level statistics
cfg = [];
cfg.method = 'ortho';
cfg.funparameter = 'stat';
cfg.location = 'max';
%cfg.maskparameter = 'mask';%turn on to show mask
cfg.funcolormap = 'jet';
ft_sourceplot(cfg,stat);
The results are interpolated onto the standard SPM T1 brain and exported to .nii and .gii (Human Connectome Project) formats.
%% Interpolate onto SPM T1 brain
mri = ft_read_mri('D:\fieldtrip-20160208\template\anatomy\single_subj_T1.nii');
cfg = [];
cfg.voxelcoord = 'no';
cfg.parameter = 'stat';
cfg.interpmethod = 'nearest';
statint = ft_sourceinterpolate(cfg, stat_LR, mri); %your FT variable corresponding to the subject specific nifti
cfg.parameter = 'mask';
maskint = ft_sourceinterpolate(cfg, stat_LR,mri);
statint.mask = maskint.mask;
%% Plot interpolated results
cfg = [];
cfg.funparameter = 'stat';
cfg.maskparameter = 'mask';
cfg.location = 'max';
cfg.funcolormap = 'jet';
ft_sourceplot(cfg,statint);
%% Export to nifti
% Run this if you want to export the clusters rather than the raw stats
statint.stat(isnan(statint.stat)) = 0;
statint.stat = (statint.stat(:).*statint.mask(:)) %masks
% Use ft_sourcewrite to export to nifti
cfg = [];
cfg.filetype = 'nifti';
cfg.filename = 'group_PS_stats_3_6Hz_clustered';
cfg.parameter = 'stat';
ft_sourcewrite(cfg,statint);
Dependencies: Fieldtrip Toolbox