├── +ROInets ├── +examples │ ├── many_subjects.m │ ├── single_subject.m │ ├── synthetic.m │ └── task_analysis.m ├── +test │ ├── README.md │ ├── run.m │ ├── test_phase_surrogates.m │ └── test_rank_error.m ├── BayesGLasso_Columnwise.m ├── Contents.m ├── Fisher_r_to_z.m ├── ROI_variance_analysis_rest.m ├── aec.m ├── apply_function_to_structure.m ├── call_fsl_wrapper.m ├── check_inputs.m ├── cholinv.m ├── closest_orthogonal_matrix.m ├── col_sum.m ├── cols.m ├── convert_correlations_to_normal_variables.m ├── convert_precision_to_pcorr.m ├── demean.m ├── do_group_level_glm.m ├── do_group_level_statistics.m ├── do_pairwise_calculation.m ├── do_subject_level_glm.m ├── dp_glasso.m ├── envelope_data.m ├── estimate_AR_coeffs.m ├── false_discovery_rate.m ├── fast_svds.m ├── find_empirical_H0_distribution_width.m ├── find_empirical_dev.m ├── find_permutation_H0_distribution_width.m ├── generate_phase_surrogates.m ├── get_edges.m ├── get_node_tcs.m ├── glasso_cv.m ├── glasso_frequentist.m ├── householder_orthogonalise.m ├── isposdef.m ├── logdet.m ├── make_directory.m ├── mean_connectivity_statistic_group_level.m ├── network_based_statistic_group_level.m ├── nii_parcel_quicksave.m ├── num_nodes.m ├── p_to_z_two_tailed.m ├── perform_glm_with_randomise.m ├── pli.m ├── plot_group_level_results.m ├── plv.m ├── randgamma.m ├── read_vest.m ├── reformat_results.m ├── regression.m ├── remove_source_leakage.m ├── row_sum.m ├── rows.m ├── run_correlation_analysis.m ├── run_individual_network_analysis.m ├── run_individual_network_analysis_task.m ├── run_network_analysis.m ├── save_vest.m ├── scale_cols.m ├── scale_rows.m ├── scomponents.m ├── setdiff_pos_int.m ├── symmetric_orthogonalise.m ├── test_me.m ├── tikhonov_inverse.m ├── univariate_edge_test.m ├── unvectorize.m └── z_to_p_two_tailed.m ├── .gitignore ├── README.md └── licenses.txt /+ROInets/+examples/many_subjects.m: -------------------------------------------------------------------------------- 1 | function correlationMats = many_subjects(Dlist,parcelFile,outDir,sessionName) 2 | % Example analysis for many subjects 3 | % 4 | % INPUTS 5 | % - Dlist: array of meeg objects or filenames for meeg objects 6 | % - parcelFile: choose a binary ROI map. Take care that the resolution of the 7 | % nifti file matches that of the source reconstruction. 8 | % - outdir: choose a results directory 9 | % - sessionName: Optionally specify name of sessions (one for each entry in Dlist) 10 | 11 | if nargin < 4 || isempty(sessionName) 12 | sessionName = arrayfun(@(x) sprintf('session_%d',x),1:length(Dlist),'UniformOutput',false); 13 | end 14 | 15 | % Set up the ROI network settings 16 | Settings = struct(); 17 | Settings.spatialBasisSet = parcelFile; % a binary file which holds the voxel allocation for each ROI - voxels x ROIs 18 | Settings.gridStep = 8; % mm % resolution of source recon and nifti parcellation file 19 | Settings.Regularize.do = true; % use regularization on partial correlation matrices using the graphical lasso. 20 | Settings.Regularize.path = logspace(-9,2,80); % This specifies a single, or vector, of possible rho-parameters controlling the strength of regularization. 21 | Settings.Regularize.method = 'Friedman'; % Regularization approach to take. {'Friedman' or 'Bayesian'} 22 | Settings.Regularize.adaptivePath = true; % adapth the regularization path if necessary 23 | Settings.leakageCorrectionMethod = 'closest'; % choose from 'closest', 'symmetric', 'pairwise' or 'none'. 24 | Settings.nEmpiricalSamples = 8; % convert correlations to standard normal z-statistics using a simulated empirical distribution. This controls how many times we simulate data of the same size as the analysis dataset 25 | Settings.ARmodelOrder = 1; % We tailor the empirical data to have the same temporal smoothness as the MEG data. An order of 1 should be ok. 26 | Settings.EnvelopeParams.windowLength = 2; % s % sliding window length for power envelope calculation. See Brookes 2011, 2012 and Luckhoo 2012. 27 | Settings.EnvelopeParams.useFilter = true; % use a more sophisticated filter than a sliding window average 28 | Settings.EnvelopeParams.takeLogs = true; % perform analysis on logarithm of envelope. This improves normality assumption 29 | Settings.frequencyBands = {[8 13], [13 30], []}; % a set of frequency bands for analysis. Set to empty to use broadband. The bandpass filtering is performed before orthogonalisation. 30 | Settings.timecourseCreationMethod = 'spatialBasis'; % 'PCA', 'peakVoxel' or 'spatialBasis' 31 | Settings.outputDirectory = outDir; % Set a directory for the results output 32 | Settings.groupStatisticsMethod = 'fixed-effects'; % 'mixed-effects' or 'fixed-effects' 33 | Settings.FDRalpha = 0.05; % false determination rate significance threshold 34 | Settings.sessionName = sessionName; 35 | Settings.SaveCorrected = struct('timeCourses', false, ... % save corrected timecourses 36 | 'envelopes', true, ... % save corrected power envelopes 37 | 'variances', false); % save mean power in each ROI before correction 38 | 39 | % Run the ROI network analysis 40 | correlationMats = ROInets.run_network_analysis(Dlist, Settings); 41 | -------------------------------------------------------------------------------- /+ROInets/+examples/single_subject.m: -------------------------------------------------------------------------------- 1 | function correlationMats = single_subject(dataFile,parcelFile,outDir,sessionName) 2 | % Example analysis for a single subject 3 | % 4 | % INPUTS 5 | % - dataFile: A single meeg objects or filenames for an meeg object 6 | % - parcelFile: choose a binary ROI map. Take care that the resolution of the 7 | % nifti file matches that of the source reconstruction. 8 | % - outdir: choose a results directory 9 | % - sessionName: Optionally specify name of sessions (one for each entry in Dlist) 10 | 11 | if nargin < 4 || isempty(sessionName) 12 | sessionName = 'single_subject'; 13 | end 14 | 15 | if isa(dataFile,'meeg') 16 | assert(length(dataFile) == 1,'Only one input file is supported'); 17 | end 18 | 19 | % set a save file name 20 | resultsName = fullfile(outDir, 'myResults'); 21 | 22 | % setup the ROI network settings 23 | Settings = struct(); 24 | Settings.spatialBasisSet = parcelFile; % a binary file which holds the voxel allocation for each ROI - voxels x ROIs 25 | Settings.gridStep = 8; % mm % resolution of source recon and nifti parcellation file 26 | Settings.Regularize.do = true; % use regularization on partial correlation matrices using the graphical lasso. 27 | Settings.Regularize.path = logspace(-9,2,80); % This specifies a single, or vector, of possible rho-parameters controlling the strength of regularization. 28 | Settings.Regularize.method = 'Friedman'; % Regularization approach to take. {'Friedman' or 'Bayesian'} 29 | Settings.Regularize.adaptivePath = true; % adapth the regularization path if necessary 30 | Settings.leakageCorrectionMethod = 'closest'; % choose from 'closest', 'symmetric', 'pairwise' or 'none'. 31 | Settings.nEmpiricalSamples = 8; % convert correlations to standard normal z-statistics using a simulated empirical distribution. This controls how many times we simulate data of the same size as the analysis dataset 32 | Settings.ARmodelOrder = 1; % We tailor the empirical data to have the same temporal smoothness as the MEG data. An order of 1 should be ok. 33 | Settings.EnvelopeParams.windowLength = 2; % s % sliding window length for power envelope calculation. See Brookes 2011, 2012 and Luckhoo 2012. 34 | Settings.EnvelopeParams.useFilter = true; % use a more sophisticated filter than a sliding window average 35 | Settings.EnvelopeParams.takeLogs = true; % perform analysis on logarithm of envelope. This improves normality assumption 36 | Settings.frequencyBands = {[8 13], [13 30], []}; % a set of frequency bands for analysis. Set to empty to use broadband. The bandpass filtering is performed before orthogonalisation. 37 | Settings.timecourseCreationMethod = 'spatialBasis'; % 'PCA', 'peakVoxel' or 'spatialBasis' 38 | Settings.outputDirectory = outDir; % Set a directory for the results output 39 | Settings.groupStatisticsMethod = 'mixed-effects'; % 'mixed-effects' or 'fixed-effects' 40 | Settings.FDRalpha = 0.05; % false determination rate significance threshold 41 | Settings.sessionName = sessionName; 42 | Settings.SaveCorrected = struct('timeCourses', false, ... % save corrected timecourses 43 | 'envelopes', true, ... % save corrected power envelopes 44 | 'variances', false); % save mean power in each ROI before correction 45 | 46 | % Run the ROI network analysis 47 | Settings = ROInets.check_inputs(Settings); 48 | correlationMats = ROInets.run_individual_network_analysis(dataFile, Settings, resultsName); 49 | -------------------------------------------------------------------------------- /+ROInets/+examples/synthetic.m: -------------------------------------------------------------------------------- 1 | function correlationMats = synthetic(outDir) 2 | % Example analysis using synthetic data (does not require an input meeg object 3 | % 4 | % INPUTS 5 | % - outDir: Directory in which to write synthetic parcel timecourses and analysis output 6 | if nargin < 1 || isempty(outDir) 7 | outDir = fullfile(pwd,'ROInets_synthetic_example'); 8 | fprintf('Writing output to %s\n',outDir); 9 | mkdir(outDir) 10 | end 11 | 12 | % Write synthetic parcel timecourses 13 | dataFile = fullfile(outDir,'synthetic_data.mat'); 14 | % Use an autoregressive model to generate smooth data 15 | % follow http://www.mathworks.co.uk/help/signal/examples/linear-prediction-and-autoregressive-modeling.html 16 | Fs = 100; %Hz 17 | duration = 60; %s 18 | time = 0:1.0/Fs:duration; 19 | nSamples = length(time); 20 | b = fir1(1024, 0.5); 21 | nVoxels = 3; 22 | [ARfilterTerms, ARnoiseVar] = lpc(b, 7); 23 | % Generate data from a covariance matrix and smooth 24 | C = [1 -0.1 0.6 25 | -0.1 1 0.3 26 | 0.6 0.3 1] * ARnoiseVar; 27 | u = chol(C)' * randn(nVoxels, nSamples); 28 | data = filter(1, ARfilterTerms, u.').'; 29 | figure('Name', 'Input data', 'Color', 'w'); 30 | plot(time.', data.'); 31 | % Save to file 32 | sampleRateInHz = Fs; 33 | save(dataFile, 'data', 'time', 'sampleRateInHz'); 34 | 35 | % Choose a binary ROI map. 36 | spatialBasis = eye(3); 37 | 38 | % set a save file name 39 | resultsName = fullfile(outDir, 'syntheticResults'); 40 | sessionName = 'myBestSubject'; 41 | 42 | % setup the ROI network settings 43 | Settings = struct(); 44 | Settings.spatialBasisSet = spatialBasis; % a binary file which holds the voxel allocation for each ROI - voxels x ROIs 45 | Settings.gridStep = 8; % mm % resolution of source recon and nifti parcellation file 46 | Settings.Regularize.do = true; % use regularization on partial correlation matrices using the graphical lasso. 47 | Settings.Regularize.path = logspace(-9,2,80); % This specifies a single, or vector, of possible rho-parameters controlling the strength of regularization. 48 | Settings.Regularize.method = 'Friedman'; % Regularization approach to take. {'Friedman' or 'Bayesian'} 49 | Settings.Regularize.adaptivePath = true; % adapth the regularization path if necessary 50 | Settings.leakageCorrectionMethod = 'closest'; % choose from 'closest', 'symmetric', 'pairwise' or 'none'. 51 | Settings.nEmpiricalSamples = 8; % convert correlations to standard normal z-statistics using a simulated empirical distribution. This controls how many times we simulate data of the same size as the analysis dataset 52 | Settings.ARmodelOrder = 1; % We tailor the empirical data to have the same temporal smoothness as the MEG data. An order of 1 should be ok. 53 | Settings.EnvelopeParams.windowLength = 2; % s % sliding window length for power envelope calculation. See Brookes 2011, 2012 and Luckhoo 2012. 54 | Settings.EnvelopeParams.useFilter = true; % use a more sophisticated filter than a sliding window average 55 | Settings.EnvelopeParams.takeLogs = true; 56 | Settings.frequencyBands = {[13 30]}; % a set of frequency bands for analysis. Set to empty to use broadband. The bandpass filtering is performed before orthogonalisation. 57 | Settings.timecourseCreationMethod = 'spatialBasis'; % 'PCA', 'peakVoxel' or 'spatialBasis' 58 | Settings.outputDirectory = outDir; % Set a directory for the results output 59 | Settings.groupStatisticsMethod = 'mixed-effects'; % 'mixed-effects' or 'fixed-effects' 60 | Settings.FDRalpha = 0.05; % false determination rate significance threshold 61 | Settings.sessionName = sessionName; 62 | Settings.SaveCorrected = struct('timeCourses', false, ... % save corrected timecourses 63 | 'envelopes', true, ... % save corrected power envelopes 64 | 'variances', false); % save mean power in each ROI before correction 65 | 66 | % run the ROI network analysis 67 | Settings = ROInets.check_inputs(Settings); 68 | correlationMats = ROInets.run_individual_network_analysis(dataFile, Settings, resultsName); 69 | 70 | % Want to run an analysis on many subjects? Have a look at 71 | % run_network_analysis to see the suggested steps. 72 | 73 | % show results 74 | figure('Name', 'node correlation matrix', 'Color', 'w'); 75 | imagesc(correlationMats{1}.envCorrelation); 76 | axis square 77 | colorbar 78 | 79 | % plot envelopes 80 | load(fullfile(outDir, 'corrected-ROI-timecourses', ... 81 | 'myBestSubject_13-30Hz_ROI_envelope_timecourses.mat')); 82 | figure('Name', 'envelope timecourses', 'Color', 'w'); 83 | plot(time_ds', nodeEnv') 84 | -------------------------------------------------------------------------------- /+ROInets/+examples/task_analysis.m: -------------------------------------------------------------------------------- 1 | function correlationMats = task_analysis(varargin) 2 | %EXAMPLE example task analysis for epoched data from several sessions 3 | % 4 | % correlationMats = EXAMPLE_TASK_ANALYIS() 5 | % for help, type 6 | % `help run_network_analysis' 7 | 8 | % Copyright 2015 OHBA 9 | % This program is free software: you can redistribute it and/or modify 10 | % it under the terms of the GNU General Public License as published by 11 | % the Free Software Foundation, either version 3 of the License, or 12 | % (at your option) any later version. 13 | % 14 | % This program is distributed in the hope that it will be useful, 15 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | % GNU General Public License for more details. 18 | % 19 | % You should have received a copy of the GNU General Public License 20 | % along with this program. If not, see . 21 | 22 | 23 | % $LastChangedBy$ 24 | % $Revision$ 25 | % $LastChangedDate$ 26 | % Contact: giles.colclough@gmail.com 27 | % Originally written on: MACI64 by Giles Colclough, 12-Jan-2015 15:25:47 28 | 29 | 30 | % choose a binary ROI map. Take care that the resolution of the nifti file 31 | % matches that of the source reconstruction. 32 | parcellationDirectory = '/path/to/parcellation/'; 33 | parcelFile = fullfile(parcellationDirectory, ... 34 | 'parcellationFile.nii.gz'); 35 | 36 | % choose a results directory 37 | outDir = '/path/to/results/'; 38 | 39 | % set objects in and names for each session 40 | dataFiles = {'/Users/gilesc/data/MND-malcolm/Motor_beta.oat/concatMfsession12_spm_meeg.mat'; ... 41 | '/Users/gilesc/data/MND-malcolm/Motor_beta.oat/concatMfsession120_spm_meeg.mat'; ... 42 | '/Users/gilesc/data/MND-malcolm/Motor_beta.oat/concatMfsession121_spm_meeg.mat'; ... 43 | '/Users/gilesc/data/MND-malcolm/Motor_beta.oat/concatMfsession122_spm_meeg.mat'}; 44 | 45 | % setup the ROI network settings 46 | Settings = struct(); 47 | Settings.spatialBasisSet = parcelFile; % a binary file which holds the voxel allocation for each ROI - voxels x ROIs 48 | Settings.gridStep = 8; % mm % resolution of source recon and nifti parcellation file 49 | Settings.timeRange = [0.01 3.99]; % range of times to use for analysis 50 | Settings.Regularize.do = true; % use regularization on partial correlation matrices using the graphical lasso. 51 | Settings.Regularize.path = 0.001; % This specifies a single, or vector, of possible rho-parameters controlling the strength of regularization. 52 | Settings.Regularize.method = 'Friedman'; % Regularization approach to take. {'Friedman' or 'Bayesian'} 53 | Settings.Regularize.adaptivePath = false; % adapt the regularization path if necessary 54 | Settings.leakageCorrectionMethod = 'closest'; % choose from 'closest', 'symmetric', 'pairwise' or 'none'. 55 | Settings.nEmpiricalSamples = 1; % convert correlations to standard normal z-statistics using a simulated empirical distribution. This controls how many times we simulate data of the same size as the analysis dataset 56 | Settings.EnvelopeParams.windowLength = 1/40; % s % sliding window length for power envelope calculation. See Brookes 2011, 2012 and Luckhoo 2012. 57 | Settings.EnvelopeParams.useFilter = true; % use a more sophisticated filter than a sliding window average 58 | Settings.EnvelopeParams.takeLogs = true; 59 | Settings.frequencyBands = {[]}; % a set of frequency bands for analysis. Set to empty to use broadband. The bandpass filtering is performed before orthogonalisation. 60 | Settings.timecourseCreationMethod = 'PCA'; % 'PCA', 'mean', 'peakVoxel' or 'spatialBasis' 61 | Settings.outputDirectory = outDir; % Set a directory for the results output 62 | Settings.groupStatisticsMethod = 'mixed-effects'; % 'mixed-effects' or 'fixed-effects' 63 | Settings.FDRalpha = 0.05; % false determination rate significance threshold 64 | Settings.sessionName = {'sess1', 'sess2', 'sess3', 'sess4'}; 65 | Settings.SaveCorrected.timeCourse = false; 66 | Settings.SaveCorrected.envelopes = false; 67 | Settings.SaveCorrected.variances = false; 68 | Settings.SaveCorrected.ROIweightings = false; 69 | Settings.SubjectLevel.conditionLabel = {'longvalidR', 'longvalidL', 'ShortValidRight', 'ShortValidLeft'}; 70 | Settings.SubjectLevel.designSummary = {[1 0 0 0]', [0 1 0 0]', [0 0 1 0]', [0 0 0 1]'}; % summarise the design matrix by condition label 71 | Settings.SubjectLevel.contrasts = {[1 0 1 0]; [1 -1 1 -1]}; % each contrast is a new cell 72 | Settings.GroupLevel.designMatrix = [1 0 % nSubjects x nEVs 73 | 1 0 74 | 0 1 75 | 0 1]; 76 | Settings.GroupLevel.contrasts = [1 1; % contrast 1 77 | 1 -1]; % contrast 2 78 | % run the ROI network analysis 79 | correlationMats = ROInets.run_network_analysis(dataFiles, Settings); 80 | end%example_many_subj 81 | % [EOF] 82 | -------------------------------------------------------------------------------- /+ROInets/+test/README.md: -------------------------------------------------------------------------------- 1 | ### Unit Tests 2 | 3 | To run all tests, use 4 | 5 | ROInets.test.run 6 | 7 | Each file in this directory, with the exception of `run.m`, tests a different feature provided by this package -------------------------------------------------------------------------------- /+ROInets/+test/run.m: -------------------------------------------------------------------------------- 1 | matlab.unittest.TestSuite.fromPackage('ROInets.test').run -------------------------------------------------------------------------------- /+ROInets/+test/test_phase_surrogates.m: -------------------------------------------------------------------------------- 1 | classdef test_phase_surrogates < matlab.unittest.TestCase 2 | 3 | % Some unit tests to verify properties of the phase randomization process 4 | 5 | methods(Test) 6 | function test_odd_samples(self) 7 | cmat = [1 0.5 0.5; 0.5 1 0.5; 0.5 0.5 1]; % Noise covariance 8 | x = mvnrnd([0 1 2],cmat,1001); 9 | 10 | y = ROInets.generate_phase_surrogates(x); 11 | 12 | % Check means are preserved 13 | self.verifyEqual(mean(x),mean(y),'AbsTol',1e-5); 14 | 15 | % Check STD is preserved 16 | self.verifyEqual(std(x,1),std(y,1),'AbsTol',1e-5); 17 | 18 | % Check correlations across channels are destroyed 19 | x_corr = triu(cov(y),1); 20 | self.verifyEqual(x_corr(:),zeros(numel(x_corr),1),'AbsTol',0.15); % They won't be totally destroyed, but should be nowhere near 0.5 21 | 22 | % Check power spectra are the same 23 | self.verifyEqual(abs(fft(x)).^2,abs(fft(y)).^2,'RelTol',1e-5); % 24 | 25 | end 26 | 27 | function test_even_samples(self) 28 | cmat = [1 0.5 0.5; 0.5 1 0.5; 0.5 0.5 1]; % Noise covariance 29 | x = mvnrnd([3 1 2],cmat,1000); 30 | 31 | y = ROInets.generate_phase_surrogates(x); 32 | 33 | % Check means are preserved 34 | self.verifyEqual(mean(x),mean(y),'AbsTol',1e-5); 35 | 36 | % Check STD is preserved 37 | self.verifyEqual(std(x,1),std(y,1),'AbsTol',1e-5); 38 | 39 | % Check correlations across channels are destroyed 40 | x_corr = triu(cov(y),1); 41 | self.verifyEqual(x_corr(:),zeros(numel(x_corr),1),'AbsTol',0.15); % They won't be totally destroyed, but should be nowhere near 0.5 42 | 43 | % Check power spectra are the same 44 | self.verifyEqual(abs(fft(x)).^2,abs(fft(y)).^2,'RelTol',1e-5); 45 | 46 | end 47 | 48 | function test_preserve_correlation(self) 49 | cmat = [1 0.5 0.5; 0.5 1 0.5; 0.5 0.5 1]; % Noise covariance 50 | x = mvnrnd([1 1 2],cmat,1000); 51 | 52 | y = ROInets.generate_phase_surrogates(x,true); 53 | 54 | % Check means are preserved 55 | self.verifyEqual(mean(x),mean(y),'AbsTol',1e-5); 56 | 57 | % Check STD is preserved 58 | self.verifyEqual(std(x,1),std(y,1),'AbsTol',1e-5); 59 | 60 | % Check correlations across channels are PRESERVED now 61 | self.verifyEqual(cov(x),cov(y),'AbsTol',1e-5); 62 | 63 | % Check power spectra are the same 64 | self.verifyEqual(abs(fft(x)).^2,abs(fft(y)).^2,'RelTol',1e-5); 65 | 66 | end 67 | 68 | 69 | 70 | end 71 | end 72 | 73 | -------------------------------------------------------------------------------- /+ROInets/+test/test_rank_error.m: -------------------------------------------------------------------------------- 1 | classdef test_rank_error < matlab.unittest.TestCase 2 | 3 | % Check that rank deficient node timeseries are correctly identified 4 | % Depending on the orthogonalization method, rank errors can be thrown 5 | % at different levels. They should be propagated through remove_source_leakage() 6 | 7 | methods(Test) 8 | function test_rank_deficient(self) 9 | nodeData = [1 0 0 0; 1 0 0 0]; 10 | 11 | self.verifyError(@() ROInets.remove_source_leakage(nodeData,'symmetric'),'ROInets:RankError') 12 | self.verifyError(@() ROInets.remove_source_leakage(nodeData,'closest'),'ROInets:RankError') 13 | self.verifyError(@() ROInets.remove_source_leakage(nodeData,'householder'),'ROInets:RankError') 14 | end 15 | 16 | function test_rank_not_rank_deficient(self) 17 | nodeData = [1 0 0 0; 1 1 0 0]; 18 | 19 | self.verifyWarningFree(@() ROInets.remove_source_leakage(nodeData,'symmetric')) 20 | self.verifyWarningFree(@() ROInets.remove_source_leakage(nodeData,'closest')) 21 | self.verifyWarningFree(@() ROInets.remove_source_leakage(nodeData,'householder')) 22 | end 23 | 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /+ROInets/BayesGLasso_Columnwise.m: -------------------------------------------------------------------------------- 1 | function [Sig_save,C_save,lambda_save] = BayesGLasso_Columnwise(S,n,Sig,C,a_lambda,b_lambda,burnin,nmc) 2 | % Efficient Bayesian Graphical Lasso MCMC sampler using data-augmented 3 | % block (column-wise) Gibbs sampler 4 | %Input: 5 | % S = Y'*Y : sample covariance matrix * n 6 | % n: sample size 7 | % lambda: |C|^(n/2) exp(-1/2 (SC) - lambda/2 ||C||_1 ); 8 | % Sig,C: initial Covariance and precision matrix C = inv(Sig); 9 | % burnin, nmc : number of MCMC burnins and saved samples 10 | % lambda ~ Ga(a_lambda,b_lambda) - shape a_lambda, scale 1/b_lambda 11 | 12 | %Output: 13 | % Sig_save: p by p by nmc matrices of saved posterior samples of covariance 14 | % C_save: p by p by nmc matrices of saved posterior samples of precision 15 | % lambda: 1 by nmc vector of saved posterior samples of lambda 16 | 17 | % Ref: Wang 2012 Bayesain Graphical Lasso and Efficient Posterior 18 | % Computation 19 | 20 | % Written by Hao Wang & U of South Carolina 21 | 22 | [p] = size(S,1); indmx = reshape([1:p^2],p,p); 23 | upperind = indmx(triu(indmx,1)>0); 24 | 25 | indmx_t = indmx'; 26 | lowerind = indmx_t(triu(indmx_t,1)>0); 27 | 28 | 29 | C_save = zeros(p,p,nmc); Sig_save = C_save; 30 | lambda_save = zeros(1,nmc); 31 | tau = zeros(p); 32 | 33 | ind_noi_all = zeros(p-1,p); 34 | for i = 1:p 35 | if i==1 36 | ind_noi = [2:p]'; 37 | elseif i==p 38 | ind_noi = [1:p-1]'; 39 | else 40 | ind_noi = [1:i-1,i+1:p]'; 41 | end 42 | 43 | ind_noi_all(:,i) = ind_noi; 44 | end 45 | 46 | apost = a_lambda + p*(p+1)/2; 47 | 48 | % ft_progress('init', 'text', ''); 49 | % cleanProgressDisplay = onCleanup(@() ft_progress('close')); 50 | for iter = 1: burnin+nmc 51 | 52 | if(mod(iter,1000)==0), 53 | % ft_progress(iter / (burnin+nmc), ... 54 | % ' MCMC iter = %d out of %d\n', ... 55 | % iter, burnin+nmc); 56 | fprintf('iter %i out of %i. \n', iter, burnin+nmc); 57 | end%if 58 | 59 | 60 | % %%% Sample lambda 61 | bpost = b_lambda + sum(abs(C(:)))/2; 62 | lambda = ROInets.randgamma(apost, 1.0/bpost); % replaced gamrnd(apost,1/bpost,1); using Lightspeed Mex file. Uses properties of scale 1/bpost: see code in randgamma, gamrnd, randg, and http://en.wikipedia.org/wiki/Gamma_distribution#Scaling 63 | 64 | %%% sample tau off-diagonal 65 | Cadjust = max(abs(C(upperind)),10^-6); 66 | lambda_prime = lambda^2; 67 | mu_prime = min(lambda./Cadjust,10^12); 68 | 69 | 70 | tau_temp = 1./rand_ig(mu_prime,lambda_prime); 71 | tau(upperind) = tau_temp; 72 | tau(lowerind) = tau_temp; 73 | 74 | 75 | %%% sample Sig and C = inv(Sig) 76 | for i = 1:p 77 | 78 | ind_noi = ind_noi_all(:,i); 79 | 80 | tau_temp = tau(ind_noi,i); 81 | 82 | Sig11 = Sig(ind_noi,ind_noi); Sig12 = Sig(ind_noi,i); 83 | 84 | invC11 = Sig11 - Sig12*Sig12'/Sig(i,i); 85 | 86 | Ci = (S(i,i)+lambda)*invC11+diag(1./tau_temp); 87 | 88 | 89 | Ci_chol = chol(Ci); 90 | 91 | mu_i = -Ci\S(ind_noi,i); 92 | 93 | beta = mu_i+ Ci_chol\randn(p-1,1); 94 | 95 | C(ind_noi,i) = beta; 96 | C(i,ind_noi) = beta; 97 | gam = ((S(i,i)+lambda)\2) .* randgamma(n/2+1); % replaces gamrnd(n/2+1,(S(i,i)+lambda)\2); 98 | 99 | C(i,i) = gam+beta'*invC11*beta; 100 | 101 | %% Below updating Covariance matrix according to one-column change of precision matrix 102 | invC11beta = invC11*beta; 103 | 104 | Sig(ind_noi,ind_noi) = invC11+invC11beta*invC11beta'/gam; 105 | Sig12 = -invC11beta/gam; 106 | Sig(ind_noi,i) = Sig12; 107 | Sig(i,ind_noi) = Sig12'; 108 | Sig(i,i) = 1/gam; 109 | end 110 | 111 | if iter >burnin 112 | Sig_save(:,:,iter-burnin) = Sig; 113 | C_save(:,:,iter-burnin) = C; 114 | lambda_save(iter-burnin) = lambda; 115 | end%if 116 | end 117 | end%BayesGlassoColumnwise 118 | 119 | 120 | 121 | 122 | function y = rand_ig(theta,chi) 123 | %RAND_IG 124 | % THE INVERSE GAUSSIAN DISTRIBUTION 125 | % 126 | % The Inverse Gaussian distribution is left skewed distribution whose 127 | % location is set by the mean with the profile determined by the 128 | % scale factor. The random variable can take a value between zero and 129 | % infinity. The skewness increases rapidly with decreasing values of 130 | % the scale parameter. 131 | % 132 | % 133 | % pdf(y) = sqrt(chi/(2*pi*y^3)) * exp(-chi./(2*y).*(y/theta-1).^2); 134 | % cdf(y) = normcdf(sqrt(chi./y).*(y/theta-1)) + ... 135 | % exp(2*chi/theta)*normcdf(sqrt(chi./y).*(-y/theta-1)); 136 | % 137 | % where normcdf(x) = 0.5*(1+erf(y/sqrt(2))); is the standard normal CDF 138 | % 139 | % Mean = theta; 140 | % Variance = theta^3/chi; 141 | % Skewness = sqrt(9*theta/chi); 142 | % Kurtosis = 15*mean/scale; 143 | % Mode = theta/(2*chi)*(sqrt(9*theta^2+4*chi^2)-3*theta); 144 | % 145 | % PARAMETERS: 146 | % theta - location; (theta>0) 147 | % chi - scale; (chi>0) 148 | % 149 | % SUPPORT: 150 | % y, y>0 151 | % 152 | % CLASS: 153 | % Continuous skewed distribution 154 | % 155 | % NOTES: 156 | % 1. There are several alternate forms for the PDF, 157 | % some of which have more than two parameters 158 | % 2. The Inverse Gaussian distribution is often called the Inverse Normal 159 | % 3. Wald distribution is a special case of The Inverse Gaussian distribution 160 | % where the mean is a constant with the value one. 161 | % 4. The Inverse Gaussian distribution is a special case of The Generalized 162 | % Hyperbolic Distribution 163 | % 164 | % USAGE: 165 | % randraw('ig', [theta, chi], sampleSize) - generate sampleSize number 166 | % of variates from the Inverse Gaussian distribution with 167 | % parameters theta and chi; 168 | % randraw('ig') - help for the Inverse Gaussian distribution; 169 | % 170 | % EXAMPLES: 171 | % 1. y = randraw('ig', [0.1, 1], [1 1e5]); 172 | % 2. y = randraw('ig', [3.2, 10], 1, 1e5); 173 | % 3. y = randraw('ig', [100.2, 6], 1e5 ); 174 | % 4. y = randraw('ig', [10, 10.5], [1e5 1] ); 175 | % 5. randraw('ig'); 176 | % 177 | % SEE ALSO: 178 | % WALD distribution 179 | % END ig HELP END inversegauss HELP END invgauss HELP 180 | 181 | % Method: 182 | % 183 | % There is an efficient procedure that utilizes a transformation 184 | % yielding two roots. 185 | % If Y is Inverse Gauss random variable, then following to [1] 186 | % we can write: 187 | % V = chi*(Y-theta)^2/(Y*theta^2) ~ Chi-Square(1), 188 | % 189 | % i.e. V is distributed as a chi-square random variable with 190 | % one degree of freedom. 191 | % So it can be simply generated by taking a square of a 192 | % standard normal random number. 193 | % Solving this equation for Y yields two roots: 194 | % 195 | % y1 = theta + 0.5*theta/chi * ( theta*V - sqrt(4*theta*chi*V + ... 196 | % theta^2*V.^2) ); 197 | % and 198 | % y2 = theta^2/y1; 199 | % 200 | % In [2] showed that Y can be simulated by choosing y1 with probability 201 | % theta/(theta+y1) and y2 with probability 1-theta/(theta+y1) 202 | %h 203 | % References: 204 | % [1] Shuster, J. (1968). On the Inverse Gaussian Distribution Function, 205 | % Journal of the American Statistical Association 63: 1514-1516. 206 | % 207 | % [2] Michael, J.R., Schucany, W.R. and Haas, R.W. (1976). 208 | % Generating Random Variates Using Transformations with Multiple Roots, 209 | % The American Statistician 30: 88-90. 210 | 211 | sampleSize = max(length(theta),length(chi)); 212 | 213 | chisq1 = randn(sampleSize,1).^2; 214 | y = theta + 0.5.*theta./chi.*(theta.*chisq1 - ... 215 | sqrt(4.*theta.*chi.*chisq1 + theta.^2.*chisq1.^2) ); 216 | 217 | l = rand(sampleSize,1)>= theta./(theta+y); 218 | 219 | if any(l), 220 | y(l) = theta(l).^2./y(l); 221 | end 222 | end%rand_ig 223 | % [EOF] -------------------------------------------------------------------------------- /+ROInets/Contents.m: -------------------------------------------------------------------------------- 1 | % +ROINETS 2 | % 3 | % Files 4 | % apply_function_to_structure - applies function to every field of a structure 5 | % BayesGLasso_Columnwise - Efficient Bayesian Graphical Lasso MCMC sampler 6 | % call_fsl_wrapper - wrapper on call_fsl function to check for errors. 7 | % check_inputs - Checks properties of Settings structure 8 | % cholinv - matrix inverse via cholesky decomposition 9 | % closest_orthogonal_matrix - Computes closest orthogonal matrix 10 | % col_sum - Sum for each column. 11 | % cols - The number of columns. 12 | % convert_correlations_to_normal_variables - converts correlations to z-scores 13 | % convert_precision_to_pcorr - converts to partial correlation 14 | % demean - Remove mean value 15 | % do_group_level_statistics - add group level inference 16 | % do_pairwise_calculation - pairwise source-leakage corrected network matrix 17 | % dp_glasso - graphical lasso 18 | % envelope_data - applies Hilbert envelope to data, without normalisation 19 | % estimate_AR_coeffs - fits an AR model to voxel data and estimates coefficients 20 | % example - example analysis for a single subject 21 | % example_external_use - EXAMPLE example analysis for a single subject, if you're external to OHBA 22 | % example_many_subj - EXAMPLE example analysis for a single subject 23 | % false_discovery_rate - converts standard z-scores to q-scores 24 | % fast_svds - RAM/time-efficient version of SVDS, singular value decomposition 25 | % find_empirical_dev - Build up correlations from empirical null distribtion 26 | % find_empirical_H0_distribution_width - FIND_EMPIRICAL_H0_DISTRIBUTON_WIDTH 27 | % Fisher_r_to_z - Converts correlations to z-scores 28 | % get_node_tcs - extracts ROI time-courses 29 | % glasso_cv - K-fold cross-validation for shrinkage parameter in glasso 30 | % glasso_frequentist - graphical lasso for regularized precision matrix estimation 31 | % householder_orthogonalise - orthogonalisation using householder method 32 | % isposdef - Test for positive definite matrix. 33 | % logdet - Computation of logarithm of determinant of a matrix 34 | % make_directory - Makes directory if not already existing - wrapper on mkdir 35 | % nii_parcel_quicksave - Saves data in parcels as nifti 36 | % p_to_z_two_tailed - Z_TO_P_TWO_TAILED convert p-value to standard z-value in two-tailed test 37 | % randgamma - GC_RANDGAMMA random sample from gamma distribution with shape and scale 38 | % reformat_results - move session correlation mats to frequency band mats 39 | % regression - Solves multivariate regression y = X*b + e using fast mex binaries 40 | % remove_source_leakage - correct ROI time-courses for source leakage 41 | % row_sum - Sum for each row. 42 | % rows - The number of rows. 43 | % run_correlation_analysis - runs various correlations on node data 44 | % run_individual_network_analysis - runs a single session network analysis 45 | % scale_cols - Scale each column of a matrix. 46 | % scale_rows - Scale each row of a matrix. 47 | % scomponents - Compute the strongly connected components of a graph 48 | % setdiff_pos_int - Set difference of two sets of positive integers (much faster than built-in setdiff) 49 | % symmetric_orthogonalise - closest orthogonal matrix 50 | % test_me - test that pipeline runs 51 | % z_to_p_two_tailed - convert standard z-value to p-value in two-tailed test 52 | -------------------------------------------------------------------------------- /+ROInets/Fisher_r_to_z.m: -------------------------------------------------------------------------------- 1 | function [Z, z, p] = Fisher_r_to_z(r, N, rho0, sigma) 2 | %FISHER_R_TO_Z Converts correlations to z-scores 3 | % 4 | % Z = Fisher_r_to_z(r) uses Fisher's Z-transform to convert 5 | % correlations r to a Z value. 6 | % 7 | % Z-stats will be clipped to a very large value, rather than returning 8 | % infinity from r==1. 9 | % 10 | % [Z, z, p] = Fisher_r_to_z(r, N) uses Fisher's Z-transform to convert 11 | % correlations to a Z value, and a z-test statistic for the correlation 12 | % being different from zero, given a sample size N (Two-tailed, only), 13 | % assuming a normal null distribution of correlations 14 | % 15 | % [Z, z, p] = Fisher_r_to_z(r, N, rhoTest) compares to an H0:rho = 16 | % rhoTest instead of H0:rho=0. 17 | % 18 | % [Z, z, p] = Fisher_r_to_z(r, N, rhoTest, SIGMA) uses SIGMA as the 19 | % standard deviation of the null Z-stat distribution, for example, as 20 | % found from an empirical (normal) null. 21 | 22 | % References: 23 | % http://courses.education.illinois.edu/EdPsy580/lectures/correlation-ha.pdf 24 | % http://support.sas.com/documentation/cdl/en/procstat/63104/HTML/default/viewer.htm#procstat_corr_sect018.htm 25 | 26 | % Copyright 2014 OHBA 27 | % This program is free software: you can redistribute it and/or modify 28 | % it under the terms of the GNU General Public License as published by 29 | % the Free Software Foundation, either version 3 of the License, or 30 | % (at your option) any later version. 31 | % 32 | % This program is distributed in the hope that it will be useful, 33 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 34 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 35 | % GNU General Public License for more details. 36 | % 37 | % You should have received a copy of the GNU General Public License 38 | % along with this program. If not, see . 39 | 40 | 41 | % $LastChangedBy: giles.colclough@gmail.com $ 42 | % $Revision: 229 $ 43 | % $LastChangedDate: 2014-08-07 20:51:36 +0100 (Thu, 07 Aug 2014) $ 44 | % Contact: giles.colclough@gmail.com 45 | % Originally written on: GLNXA64 by Giles Colclough, 14-Feb-2014 12:26:45 46 | 47 | 48 | 49 | %%% Constants 50 | 51 | % Bound Z to prevent infinities 52 | LARGE_Z = 35; % 35 is a pretty large z-stat! 53 | 54 | 55 | 56 | 57 | %%% Input checking 58 | 59 | assert(all(abs(r(:))<=1), ... 60 | [mfilename ':InvalidCorrelations'], ... 61 | 'Correlations must be between -1 and 1. \n'); 62 | 63 | if nargout > 1 && nargin == 1, 64 | error([mfilename ':NotEnoughInputs'], ... 65 | 'Not enough input arguments\n'); 66 | end%if 67 | 68 | if nargin > 1, 69 | assert(N > 4, ... 70 | [mfilename ':TooSmallSample'], ... 71 | 'Please use a sample size greater than 4. \n'); 72 | end%if 73 | 74 | if nargin < 3 || isempty(rho0), 75 | rho0 = 0; 76 | else 77 | assert(abs(rho0) <= 1, ... 78 | [mfilename ':InvalidTestCorrelation'], ... 79 | 'rho0 must be between -1 and 1. \n'); 80 | if abs(rho0) == 1, 81 | rho0 = sign(rho0) * tanh(LARGE_Z); % prevent atanh(rho0) being infinity 82 | end%if 83 | end%if 84 | 85 | if nargout > 1 && (nargin < 4 || ~sigma), 86 | % default standard deviation under normal assumptions 87 | sigma = sqrt(1.0 ./ (N-3)); 88 | end%if 89 | 90 | 91 | 92 | 93 | %%% MAIN 94 | 95 | % Transform correlations to Z 96 | Z = convert_r_to_Z(r, LARGE_Z); 97 | 98 | % Do hypothesis testing 99 | if nargout > 1, 100 | z = convert_Z_to_z(Z, N, rho0, sigma, LARGE_Z); 101 | p = ROInets.z_to_p_two_tailed(z); 102 | end%if 103 | end%Fisher_r_to_z 104 | 105 | 106 | 107 | 108 | 109 | 110 | %%% Subfunctions 111 | 112 | function Z = convert_r_to_Z(r, LARGE_Z) 113 | % Fisher's conversion of correlations to Z-distributed statistic 114 | Z = atanh(r); % correlations of +- 1 will be infinite 115 | Z(isinf(Z)) = LARGE_Z .* sign(Z(isinf(Z))); 116 | end%convert_r_to_Z 117 | 118 | function z = convert_Z_to_z(Z, N, rho0, sigma, LARGE_Z) 119 | % construct a normal random variable with mean zero and variance 1/(N-3). 120 | zNoNorm = Z - (repmat(convert_r_to_Z(rho0, LARGE_Z), size(Z)) ... 121 | - repmat(rho0, size(Z)) ./ (2 * (N-1))); 122 | 123 | if 0 == sigma, 124 | % this can happen if there is very low variance in the orthogonalised 125 | % data. It leads to the regularisation compressing the empirical 126 | % distribution to zero. I don't have a good fix for this at the moment. 127 | sigma = 1.0 ./ sqrt(N-3); 128 | warning([mfilename ':PoorlyConditionedEmpiricalDistributionWidth'], ... 129 | ['Width of empirical distribution incorrectly esimated. ', ... 130 | 'Using normal assumptions. \n']); 131 | end%if 132 | 133 | % standard normally distributed variable 134 | z = zNoNorm ./ sigma; 135 | end%convert_Z_to_z 136 | % [EOF] 137 | -------------------------------------------------------------------------------- /+ROInets/ROI_variance_analysis_rest.m: -------------------------------------------------------------------------------- 1 | function [SubjectLevel, GroupLevel] = ROI_variance_analysis_rest(varianceFiles, subjectDesign, groupDesign, contrasts) 2 | %ROI_VARIANCE_ANALYSIS_REST performs power analyis on subjects in resting 3 | % state 4 | % 5 | % [SUBJECTLEVEL, GROUPLEVEL] = ROI_VARIANCE_ANALYSIS_REST(FILES, S_X, G_X,C) 6 | % 7 | % takes in cell array of filenames FILES pointing to variance files. 8 | % There should be nSessions files. Each is a .mat file holding an 9 | % nROIs x 1 array of variances. The subject design matrix, S_X, should be 10 | % nSessions x nSubjects. (Use the identity matrix if there is only one 11 | % session per subject.) 12 | % 13 | % The group design, G_X, should be nSubjects x nEVs. 14 | % Finally, the contrasts should be a matrix of nContrasts x nEVs. 15 | % 16 | % The outputs are SUBJECTLEVEL, a structure with parameters beta which are 17 | % the mean log variance in each ROI for each subject, and GROUPLEVEL, which 18 | % holds the beta values from the group-level regression, together with 19 | % statistics associated with each contrast. 20 | % 21 | % [...] = ROI_VARIANCE_ANALYSIS_REST(MAT, ...) takes in an nROIs by 22 | % nSessions matrix of ROI variances. 23 | % 24 | % Note that analysis is performed on the logarithm of variances: effect 25 | % sizes relate to % changes in variance. 26 | % 27 | % This code uses randomise at the top level: the p-values for any 28 | % whole-group mean effect will be uninformative. 29 | % 30 | % Unlike in randomise's output, the p-values are p-values: a p-value of 31 | % 0.001 is significant. (Not 1-p). 32 | 33 | % parse input and transform to log-space 34 | [variances, nSessions, nROIs] = parse_input(varianceFiles); 35 | assert(all(variances(:) > 0), ... 36 | 'Expecting variances to be greater than 0. What''s up with your data?\n'); 37 | 38 | logVariances = log(variances); 39 | 40 | % check dimensions 41 | [checkMe, nSubjects] = size(subjectDesign); 42 | assert(checkMe == nSessions, ... 43 | 'Subject design should be nSessions x nSubjects. \n'); 44 | [checkMe, nEVs] = size(groupDesign); 45 | assert(checkMe == nSubjects, ... 46 | 'Group design should be nSubjects x nEVs. \n'); 47 | [nContrasts, checkMe] = size(contrasts); 48 | assert(checkMe == nEVs, ... 49 | 'Contrasts should be nContrasts x nEVs. \n'); 50 | 51 | % perform subject-level glm 52 | SubjectLevel = struct(); 53 | SubjectLevel.design = subjectDesign; 54 | SubjectLevel.nSessions = nSessions; 55 | SubjectLevel.nSubjects = nSubjects; 56 | SubjectLevel.nROIs = nROIs; 57 | SubjectLevel.analysisTime = datestr(now); 58 | 59 | SubjectLevel.beta = subjectDesign \ logVariances'; 60 | SubjectLevel.meanVariance = exp(SubjectLevel.beta); 61 | 62 | % perform group-level glm using randomise. 63 | GroupLevel = struct(); 64 | GroupLevel.design = groupDesign; 65 | GroupLevel.nSubjects = nSubjects; 66 | GroupLevel.nEVs = nEVs; 67 | GroupLevel.nContrasts = nContrasts; 68 | GroupLevel.analysisTime = datestr(now); 69 | 70 | GroupLevel.beta = groupDesign \ SubjectLevel.beta; 71 | GroupLevel.parameterEstimates = contrasts * GroupLevel.beta; 72 | [T, OneMinusp, corrOneMinusp] = ROInets.perform_glm_with_randomise(SubjectLevel.beta', groupDesign, contrasts, false); 73 | for iContrast = nContrasts:-1:1, 74 | GroupLevel.contrast(iContrast).contrast = contrasts(iContrast,:); 75 | GroupLevel.contrast(iContrast).T = T(:,iContrast); 76 | GroupLevel.contrast(iContrast).p = 1 - OneMinusp(:,iContrast); 77 | GroupLevel.contrast(iContrast).minusLogp = -log10(1 - OneMinusp(:,iContrast)); 78 | GroupLevel.contrast(iContrast).corrp = 1 - corrOneMinusp(:,iContrast); 79 | GroupLevel.contrast(iContrast).minusLogCorrp = -log10(1 - corrOneMinusp(:,iContrast)); 80 | end%for 81 | end%ROI_variance_analysis_rest 82 | 83 | function [V, nSessions, nROIs] = parse_input(varianceFiles) 84 | %PARSE_INPUT extract variances from files 85 | 86 | if iscell(varianceFiles), 87 | nSessions = length(varianceFiles); 88 | for iSession = nSessions:-1:1, 89 | tmp = load(varianceFiles{iSession}); 90 | 91 | % this assumes that each file will have the same number of elements 92 | % stored 93 | V(:,iSession) = tmp.ROIvariances(:); 94 | end%for 95 | nROIs = ROInets.rows(V); 96 | 97 | elseif ismatrix(varianceFiles), 98 | [nROIs, nSessions] = size(varianceFiles); 99 | V = varianceFiles; 100 | 101 | else 102 | error([mfilename ':BadInput'], ... 103 | 'Expecting first input to be file list or a matrix of variances. \n'); 104 | end%if 105 | end%parse_input 106 | % [EOF] -------------------------------------------------------------------------------- /+ROInets/aec.m: -------------------------------------------------------------------------------- 1 | function c = aec(env) 2 | % Take in envelope timecourse and compute AEC 3 | % 4 | % INPUT 5 | % - env - envelope timecourse, n_times x n_signals 6 | % 7 | % OUTPUT 8 | % - c - AEC connectivity matrix 9 | % 10 | % EXAMPLE USAGE 11 | % 12 | % aec(env) 13 | % 14 | % 15 | % Romesh Abeysuriya 2017 16 | 17 | clean = all(isfinite(env),1); 18 | c = corr(env(:,clean).'); 19 | -------------------------------------------------------------------------------- /+ROInets/apply_function_to_structure.m: -------------------------------------------------------------------------------- 1 | function out = apply_function_to_structure(f, in) 2 | %APPLY_FUNCTION_TO_STRUCTURE applies function to every field of a structure 3 | 4 | 5 | % Copyright 2014 OHBA 6 | % This program is free software: you can redistribute it and/or modify 7 | % it under the terms of the GNU General Public License as published by 8 | % the Free Software Foundation, either version 3 of the License, or 9 | % (at your option) any later version. 10 | % 11 | % This program is distributed in the hope that it will be useful, 12 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | % GNU General Public License for more details. 15 | % 16 | % You should have received a copy of the GNU General Public License 17 | % along with this program. If not, see . 18 | 19 | 20 | % $LastChangedBy: giles.colclough@gmail.com $ 21 | % $Revision: 214 $ 22 | % $LastChangedDate: 2014-07-24 12:40:42 +0100 (Thu, 24 Jul 2014) $ 23 | % Contact: giles.colclough@gmail.com 24 | % Originally written on: MACI64 by Giles Colclough, 17-Mar-2014 23:24:35 25 | 26 | ff = fieldnames(in); 27 | for iff = 1:length(ff), 28 | out.(ff{iff}) = f(in.(ff{iff})); 29 | end%for 30 | 31 | end%apply_function_to_structure 32 | -------------------------------------------------------------------------------- /+ROInets/call_fsl_wrapper.m: -------------------------------------------------------------------------------- 1 | function result = call_fsl_wrapper(fslCommand, quiet) 2 | %CALL_FSL_WRAPPER wrapper on call_fsl function to check for errors. 3 | % 4 | % RESULT = CALL_FSL_WRAPPER(COMMAND) runs an fsl function using terminal command 5 | % line string COMMAND, returning standard output in RESULT. 6 | % 7 | % See also: CALL_FSL. (Part of the fsl distribution.) 8 | 9 | 10 | % Copyright 2013 OHBA 11 | % This program is free software: you can redistribute it and/or modify 12 | % it under the terms of the GNU General Public License as published by 13 | % the Free Software Foundation, either version 3 of the License, or 14 | % (at your option) any later version. 15 | % 16 | % This program is distributed in the hope that it will be useful, 17 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | % GNU General Public License for more details. 20 | % 21 | % You should have received a copy of the GNU General Public License 22 | % along with this program. If not, see . 23 | 24 | 25 | % $LastChangedBy: giles.colclough@gmail.com $ 26 | % $Revision: 313 $ 27 | % $LastChangedDate: 2014-11-11 12:29:19 +0000 (Tue, 11 Nov 2014) $ 28 | % Contact: giles.colclough 'at' magd.ox.ac.uk 29 | % Originally written on: GLNXA64 by Giles Colclough, 07-Nov-2013 12:43:45 30 | if nargin < 2 || ~exist('quiet', 'var') || ~quiet, 31 | fprintf(fslCommand); 32 | fprintf('\n'); 33 | end%if 34 | 35 | [status, result] = call_fsl(fslCommand); 36 | if status, 37 | error([mfilename ':fslCallFailed'], ... 38 | 'Call to fsl failed with message: \n %s \n', result); 39 | end -------------------------------------------------------------------------------- /+ROInets/cholinv.m: -------------------------------------------------------------------------------- 1 | function U = cholinv(M, isSparse) 2 | %CHOLINV matrix inverse via cholesky decomposition 3 | % 4 | % U = CHOLINV(M) inverts M using the cholesky decomposition. M must be 5 | % positive definite. 6 | % 7 | % U = CHOLINV(M, true) assumes M is sparse and uses the suitesparse set of 8 | % algorithms. 9 | % 10 | % If you're going to multiply this matrix onto something else, there are 11 | % better algorithms available. 12 | % 13 | % If you're looking for robust estimation of an inverse covariance, you 14 | % need to check out the graphical lasso, SCAD, adaptive graphical lasso, 15 | % Bayesian lasso or other Bayesian shrinkage estimator. 16 | 17 | 18 | % Copyright 2015 OHBA 19 | % This program is free software: you can redistribute it and/or modify 20 | % it under the terms of the GNU General Public License as published by 21 | % the Free Software Foundation, either version 3 of the License, or 22 | % (at your option) any later version. 23 | % 24 | % This program is distributed in the hope that it will be useful, 25 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | % GNU General Public License for more details. 28 | % 29 | % You should have received a copy of the GNU General Public License 30 | % along with this program. If not, see . 31 | 32 | 33 | % $LastChangedBy$ 34 | % $Revision$ 35 | % $LastChangedDate$ 36 | % Contact: giles.colclough@gmail.com 37 | % Originally written on: MACI64 by Giles Colclough, 02-Mar-2015 20:27:04 38 | 39 | if 1==nargin || ~isSparse, 40 | [R,p] = chol(M); 41 | 42 | if ~p, 43 | Rinv = R \ eye(size(R)); %inv(R); % fast for upper triangular 44 | U = Rinv * Rinv'; 45 | else 46 | warning([mfilename ':NotPosDef'], ... 47 | 'Input matrix not positive definite. Using svd method. \n'); 48 | U = pinv(M); 49 | end%if 50 | else 51 | % run sparse algorithms 52 | U = spinv(M); 53 | end%if 54 | end%cholinv 55 | -------------------------------------------------------------------------------- /+ROInets/closest_orthogonal_matrix.m: -------------------------------------------------------------------------------- 1 | function [L, d, rho, W] = closest_orthogonal_matrix(A) 2 | %CLOSEST_ORTHOGONAL_MATRIX Computes closest orthogonal matrix 3 | % 4 | % L = CLOSEST_ORTHOGONAL_MATRIX(A) returns orthogonal matrix L which 5 | % is closest to A, as measured by the Frobenius norm of (L-A), such that 6 | % transpose(L) * L is non-negative diagonal. 7 | % 8 | % [L, D, RHO] = CLOSEST_ORTHOGONAL_MATRIX(A) also returns the scaling 9 | % factors for each column of A, D, and the final converged square 10 | % distance between L and A, RHO. 11 | 12 | % References: 13 | % R. Evans, 14 | % "http://empslocal.ex.ac.uk/people/staff/reverson/uploads/Site/procrustes.pdf" 15 | % "Orthogonal, but not Orthonormal, Procrustes Problems", 1997 16 | % 17 | % See also: ROInets.symmetric_orthogonalise, svd. 18 | 19 | % Copyright 2014 OHBA 20 | % This program is free software: you can redistribute it and/or modify 21 | % it under the terms of the GNU General Public License as published by 22 | % the Free Software Foundation, either version 3 of the License, or 23 | % (at your option) any later version. 24 | % 25 | % This program is distributed in the hope that it will be useful, 26 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | % GNU General Public License for more details. 29 | % 30 | % You should have received a copy of the GNU General Public License 31 | % along with this program. If not, see . 32 | 33 | % $LastChangedBy$ 34 | % $Revision$ 35 | % $LastChangedDate$ 36 | % Contact: giles.colclough@gmail.com 37 | % Originally written on: MACI64 by Giles Colclough, 07-Aug-2014 13:27:52 38 | % Optimised by JH on Jul 19th 2016 39 | 40 | % settings 41 | MAX_ITER = 2e2; 42 | TOLERANCE = max(1, max(size(A)) * ROInets.fast_svds(A,1)) * eps(class(A)); % the tolerance used for matrices within matlab 43 | DEBUG = false; 44 | 45 | % tests for convergence 46 | reldiff = @(a,b) 2*abs(a-b) / (abs(a)+abs(b)); 47 | absdiff = @(a,b) abs(a-b); % previously used - JH 48 | convergence = @(rho, prevRho) reldiff(rho,prevRho) <= TOLERANCE; 49 | 50 | % declare memory and initialise 51 | iter = 0; 52 | A_b = conj(A); 53 | d = sqrt(dot( A, A_b, 1 )); % alternative: ones( 1, ROInets.cols(A) ) 54 | rho = NaN(MAX_ITER, 1); 55 | 56 | while iter < MAX_ITER, 57 | iter = iter + 1; 58 | 59 | % find orthonormal polar factor 60 | try 61 | V = ROInets.symmetric_orthogonalise(ROInets.scale_cols( A, d )); 62 | catch ME 63 | % use suppression of some variables such that rank is reduced as a 64 | % stopping criterion 65 | if iter > 1 && strcmp(ME.identifier, 'ROInets:RankError'), 66 | % stop the process and use that last L computed. 67 | rank_reduction_warning(); 68 | break 69 | else 70 | throw(ME); % Re-throw the original error 71 | end%if 72 | end%try 73 | 74 | % minimise rho = |A - V D|^2 w.r.t d 75 | d = dot( A_b, V, 1 ); 76 | 77 | % new best orthogonal estimate 78 | L = ROInets.scale_cols( V, d ); 79 | 80 | % compute error term 81 | E = A - L; 82 | rho(iter) = sqrt(sum(dot( E, conj(E), 1 ))); % use sqrt(trace) instead - JH 83 | 84 | if DEBUG, 85 | fprintf('%s: iter %4d \t rho %g \n', mfilename, iter, rho(iter)); %#ok 86 | end 87 | 88 | if iter > 1 && convergence(rho(iter), rho(iter - 1)), 89 | break 90 | end%if 91 | end%convergence loop 92 | 93 | if nargout > 3, 94 | % output linear operator - run once more 95 | [~, ~, ~, W] = ROInets.symmetric_orthogonalise(ROInets.scale_cols( A, d )); 96 | W = diag(d) * W * diag(d); 97 | end%if 98 | 99 | % tidy vector 100 | rho(isnan(rho)) = []; 101 | 102 | if isequal(iter, MAX_ITER), 103 | warning([mfilename ':MaxIterationsHit'], ... 104 | ['%s: hit max iterations: %d. ', ... 105 | 'You may not have found the optimal orthogonal matrix. \n'], ... 106 | mfilename, MAX_ITER); 107 | end%if 108 | 109 | if DEBUG, 110 | figure('Name', 'Convergence path', 'Color', 'w'); %#ok 111 | semilogy(2:(length(rho)-1), rho(2:end-1) - rho(end), 'b+'); 112 | xlabel('iteration'); 113 | ylabel('\rho_i - \rho_{\inf}'); 114 | end%if 115 | 116 | end%closest_orthogonal_matrix 117 | 118 | function [] = rank_reduction_warning() 119 | % This warning is produced when the orthogonalisation process runs out of 120 | % rank. 121 | % This can happen when two vectors are very close to each other but 122 | % different in magnitude. The alignement process can suppress the smaller 123 | % vector down to zero, as there is little residual left. This, in some 124 | % circumstances, can reduce the rank below the dimensionality of the data. 125 | % 126 | % As L at all iterations of the algorithm are orthogonal matrices, and as 127 | % after one iteration the magnitudes do become close to the original set, 128 | % it seems like this is a fair stopping criterion. 129 | warning([mfilename ':RankReducedTooFar'], ... 130 | ['%s: Orthogonalisation optimisation stopped when rank reduced ', ... 131 | 'to limit. \n'], ... 132 | mfilename); 133 | end%rank_reduction_warning 134 | % [EOF] 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | % % % 150 | % % % % test some methods for computation within the iteration loop 151 | % % % 152 | % % % %% Setup 153 | % % % nNodes = 40; 154 | % % % nSamples = 200*600; 155 | % % % nRuns = 20; 156 | % % % 157 | % % % % make covariance matrix 158 | % % % sigmaOff = 0.2 * rand(nNodes); 159 | % % % sigmaOff = sigmaOff' * sigmaOff; 160 | % % % sigma = sigmaOff - diag(diag(sigmaOff)) + eye(nNodes); 161 | % % % 162 | % % % var = 1 + 0.1 * randn(nNodes, 1); 163 | % % % sigma = diag(sqrt(var)) * sigma * diag(sqrt(var)); 164 | % % % 165 | % % % % make mean matrix 166 | % % % mu = 4 + randn(1, nNodes); 167 | % % % 168 | % % % % simulate data stream 169 | % % % A = mvnrnd(mu, sigma, nSamples); 170 | % % % A = ROInets.demean(A); 171 | % % % 172 | % % % V = ROInets.symmetric_orthogonalise(A); 173 | % % % %% A finding a d vector 174 | % % % fprintf('finding d\n'); 175 | % % % 176 | % % % % 1. diag 177 | % % % diag_time = zeros(1,nRuns); 178 | % % % d = zeros(nNodes,1); 179 | % % % 180 | % % % for iRun = 1:nRuns, 181 | % % % tic 182 | % % % d = diag(A' * V); 183 | % % % diag_time(iRun) = toc; 184 | % % % end 185 | % % % 186 | % % % fprintf('Diag time: %g\n', sum(diag_time)); 187 | % % % fprintf('max d: %g\n\n', max(d)); 188 | % % % 189 | % % % % 2. vector norms 190 | % % % loop_time = zeros(1,nRuns); 191 | % % % for iRun = 1:nRuns, 192 | % % % tic 193 | % % % for k = nNodes:-1:1, 194 | % % % d(k) = A(:,k)' * V(:,k); 195 | % % % end 196 | % % % loop_time(iRun) = toc; 197 | % % % end 198 | % % % fprintf('Loop time: %g\n', sum(loop_time)); 199 | % % % fprintf('max d: %g\n\n', max(d)); 200 | % % % 201 | % % % %% B finding rho 202 | % % % fprintf('finding rho\n'); 203 | % % % d = diag(A' * V); 204 | % % % rho = NaN; 205 | % % % % % 1. all in one trace 206 | % % % % AIO_time = zeros(1,nRuns); 207 | % % % % for iRun = 1:nRuns, 208 | % % % % tic 209 | % % % % rho = sum(diag( (A - V * diag(d))' * (A - V * diag(d)) )); % inline trace 210 | % % % % AIO_time(iRun) = toc; 211 | % % % % end 212 | % % % % fprintf('All in one trace time: %g\n', sum(AIO_time)); 213 | % % % 214 | % % % % 2. pre-allocate trace 215 | % % % AIO_time = zeros(1,nRuns); 216 | % % % for iRun = 1:nRuns, 217 | % % % tic 218 | % % % M = A - V * diag(d); 219 | % % % rho = sum(diag( M' * M )); % inline trace 220 | % % % AIO_time(iRun) = toc; 221 | % % % end 222 | % % % fprintf('Pre-allocate trace time: %g\n', sum(AIO_time)); 223 | % % % fprintf('rho: %g\n\n', rho); 224 | % % % 225 | % % % % on the spot 226 | % % % OTS_time = zeros(1,nRuns); 227 | % % % for iRun = 1:nRuns, 228 | % % % tic 229 | % % % M = A - V * diag(d); 230 | % % % rho = sum(sum(M.^2)); % inline trace 231 | % % % OTS_time(iRun) = toc; 232 | % % % end 233 | % % % fprintf('On-the-spot time: %g\n', sum(OTS_time)); 234 | % % % fprintf('rho: %g\n\n', rho); 235 | % % % 236 | % % % % 3. norm 237 | % % % norm_time = zeros(1,nRuns); 238 | % % % for iRun = 1:nRuns, 239 | % % % tic 240 | % % % rho = norm( (A - V * diag(d)), 'fro' )^2; 241 | % % % norm_time(iRun) = toc; 242 | % % % end 243 | % % % fprintf('Norm time: %g\n', sum(norm_time)); 244 | % % % fprintf('rho: %g\n\n', rho); 245 | % % % 246 | % % % % % 4. svd 247 | % % % % svd_time = zeros(1,nRuns); 248 | % % % % for iRun = 1:nRuns, 249 | % % % % tic 250 | % % % % rho = sum(svd(A - V * diag(d)).^2); 251 | % % % % svd_time(iRun) = toc; 252 | % % % % end 253 | % % % % fprintf('SVD time: %g\n', sum(svd_time)); 254 | % % % 255 | % % % % 5. loop 256 | % % % loop_time = zeros(1,nRuns); 257 | % % % for iRun = 1:nRuns, 258 | % % % tic 259 | % % % for k = nNodes:-1:1, 260 | % % % rho_(k) = sum( (A(:,k) - V(:,k) * d(k)).^2 ); 261 | % % % end 262 | % % % rho = sum(rho_); 263 | % % % loop_time(iRun) = toc; 264 | % % % end 265 | % % % fprintf('Loop time: %g\n', sum(loop_time)); 266 | % % % fprintf('rho: %g\n\n', rho); 267 | % % % 268 | % % % %% Check to see if this way is better 269 | % % % [L, d, rhoFinal] = ROInets.closest_orthogonal_matrix(A); 270 | % % % display(rho - rhoFinal(end)); 271 | -------------------------------------------------------------------------------- /+ROInets/col_sum.m: -------------------------------------------------------------------------------- 1 | function s = col_sum(x) 2 | % COL_SUM Sum for each column. 3 | % A more readable alternative to sum(x,1). 4 | s = sum(x,1); 5 | -------------------------------------------------------------------------------- /+ROInets/cols.m: -------------------------------------------------------------------------------- 1 | function c = cols(x) 2 | % COLS The number of columns. 3 | % COLS is a more readable alternative to size(x,2). 4 | c = size(x,2); 5 | -------------------------------------------------------------------------------- /+ROInets/convert_correlations_to_normal_variables.m: -------------------------------------------------------------------------------- 1 | function corrMats = convert_correlations_to_normal_variables(corrMats, ... 2 | sigma, ... 3 | doRegularize) 4 | %CONVERT_CORRELATIONS_TO_NORMAL_VARIABLES converts correlations to z-scores 5 | % 6 | % CORRELATION_MATS = convert_correlations_to_normal_variables(CORRELATION_MATS, SIGMA, DO_REGULARIZE) 7 | 8 | 9 | % Copyright 2014 OHBA 10 | % This program is free software: you can redistribute it and/or modify 11 | % it under the terms of the GNU General Public License as published by 12 | % the Free Software Foundation, either version 3 of the License, or 13 | % (at your option) any later version. 14 | % 15 | % This program is distributed in the hope that it will be useful, 16 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | % GNU General Public License for more details. 19 | % 20 | % You should have received a copy of the GNU General Public License 21 | % along with this program. If not, see . 22 | 23 | 24 | % $LastChangedBy: adambaker86@gmail.com $ 25 | % $Revision: 261 $ 26 | % $LastChangedDate: 2014-10-20 20:19:04 +0100 (Mon, 20 Oct 2014) $ 27 | % Contact: giles.colclough@gmail.com 28 | % Originally written on: MACI64 by Giles Colclough, 17-Apr-2014 11:10:31 29 | 30 | nullHypothesisCorrelation = 0; 31 | 32 | [~, corrMats.envCorrelation_z] = ROInets.Fisher_r_to_z(corrMats.envCorrelation, ... 33 | corrMats.nSamples, ... 34 | nullHypothesisCorrelation, ... 35 | sigma.z); 36 | [~, corrMats.envPartialCorrelation_z] = ROInets.Fisher_r_to_z(corrMats.envPartialCorrelation, ... 37 | corrMats.nSamples, ... 38 | nullHypothesisCorrelation, ... 39 | sigma.z_partial); 40 | 41 | if doRegularize && isfield(corrMats, 'envPartialCorrelationRegularized'), 42 | % we use the unregularised width - best we can do. 43 | % if we use the regularised width, then this is often compressed to 44 | % zero. 45 | [~, corrMats.envPartialCorrelationRegularized_z] = ROInets.Fisher_r_to_z(corrMats.envPartialCorrelationRegularized, ... 46 | corrMats.nSamples, ... 47 | nullHypothesisCorrelation, ... 48 | sigma.z_partial); 49 | end%if 50 | end%convert_correlations_to_normal_variables 51 | -------------------------------------------------------------------------------- /+ROInets/convert_precision_to_pcorr.m: -------------------------------------------------------------------------------- 1 | function [pcorr, invVar, Aguess] = convert_precision_to_pcorr(precision) 2 | %CONVERT_PRECISION_TO_PCORR converts to partial correlation 3 | % 4 | % PCORR = CONVERT_PRECISION_TO_PCORR(PRECISION) converts PRECISION 5 | % matrix to partial correlation matrix PCORR. 6 | % 7 | % [PCORR, INVVAR] = CONVERT_PRECISION_TO_PCORR(PRECISION) outputs the 8 | % inverse of the variance of each variable, INVVAR. 9 | % 10 | % See also: CORR, CORRCOV. 11 | 12 | % Additional functionality: 13 | % [PCORR, INVVAR, ESTNETMAT] = CONVERT_PRECISION_TO_PCORR(PRECISION) 14 | % provides an esimated network matrix under the assumptions of a 15 | % structural equation model (SEM) z=Az + e for variables z, network 16 | % matrix A and Gaussian noise e. 17 | % 18 | % See as a reference, Mark W. Woolrich & Klaas E. Stephan. (2013) 19 | % "Biophysical network models and the human connectome," NeuroImage, 20 | % volume 80, pages 330-338, ISSN 1053-8119, 21 | % http://dx.doi.org/10.1016/j.neuroimage.2013.03.059. 22 | % (http://www.sciencedirect.com/science/article/pii/S1053811913003091) 23 | % The relvant derivation is in Appendix A. 24 | 25 | 26 | % Copyright 2013 OHBA 27 | % This program is free software: you can redistribute it and/or modify 28 | % it under the terms of the GNU General Public License as published by 29 | % the Free Software Foundation, either version 3 of the License, or 30 | % (at your option) any later version. 31 | % 32 | % This program is distributed in the hope that it will be useful, 33 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 34 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 35 | % GNU General Public License for more details. 36 | % 37 | % You should have received a copy of the GNU General Public License 38 | % along with this program. If not, see . 39 | 40 | 41 | % $LastChangedBy: giles.colclough@gmail.com $ 42 | % $Revision: 213 $ 43 | % $LastChangedDate: 2014-07-24 12:39:09 +0100 (Thu, 24 Jul 2014) $ 44 | % Contact: giles.colclough@gmail.com 45 | % Originally written on: GLNXA64 by Giles Colclough, 18-Nov-2013 13:42:28 46 | 47 | % find the inverse variances 48 | invVar = diag(precision); 49 | 50 | % check for blank input 51 | if isempty(precision), 52 | pcorr = []; 53 | Aguess = []; 54 | return 55 | end%if 56 | 57 | % check for zeros 58 | if any(~invVar), 59 | warning([mfilename ':InfiniteVariance'], ... 60 | ['At least one zero was found on the diagonal of the precision ', ... 61 | 'matrix. \nThis is unphysical, corresponding to infinite ', ... 62 | 'variance. There is no corresponding partial correlation ', ... 63 | 'matrix. \n']); 64 | pcorr = NaN(size(precision)); 65 | Aguess = []; 66 | return 67 | end%if zeros on diagonal 68 | 69 | % construct partial correlation matrix 70 | D = diag(sqrt(1.0./invVar)); 71 | 72 | L = D * precision * D; 73 | pcorr = - triu(L, 1) - triu(L, 1)' + diag(ones(size(precision,1),1)); 74 | 75 | 76 | % construct esimated SEM network matrix if desired 77 | if nargout>2, 78 | Aguess = estimate_SEM_network_matrix(precision); 79 | end%if 80 | end%convert_precision_to_pcorr 81 | 82 | function estNetMat = estimate_SEM_network_matrix(precision) 83 | % consider an SEM z=Az + e 84 | % (I - A) prop sqrtm(inv(covariance)) 85 | estNetMat = - sqrtm(precision); 86 | % ignore diagonal elements - set to zero 87 | estNetMat = triu(estNetMat, 1) + triu(estNetMat, 1)'; 88 | end%estimate_network_matrix 89 | % [EOF] 90 | -------------------------------------------------------------------------------- /+ROInets/demean.m: -------------------------------------------------------------------------------- 1 | function centredX = demean(X, dim) 2 | %DEMEAN Remove mean value 3 | % CENTRED_X = DEMEAN(X) removes the column means from X or works along 4 | % first non-zero dimension 5 | % 6 | % CENTRED_X = DEMEAN(X, DIM) removes means from X along dimension DIM. 7 | % 8 | % See also MEAN, BSXFUN. 9 | 10 | 11 | % Copyright 2013 OHBA 12 | % This program is free software: you can redistribute it and/or modify 13 | % it under the terms of the GNU General Public License as published by 14 | % the Free Software Foundation, either version 3 of the License, or 15 | % (at your option) any later version. 16 | % 17 | % This program is distributed in the hope that it will be useful, 18 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | % GNU General Public License for more details. 21 | % 22 | % You should have received a copy of the GNU General Public License 23 | % along with this program. If not, see . 24 | 25 | 26 | % $LastChangedBy: giles.colclough@gmail.com $ 27 | % $Revision: 213 $ 28 | % $LastChangedDate: 2014-07-24 12:39:09 +0100 (Thu, 24 Jul 2014) $ 29 | % Contact: giles.colclough@gmail.com 30 | % Originally written on: GLNXA64 by Giles Colclough, 11-Dec-2013 15:31:14 31 | 32 | if nargin == 1, 33 | if(ROInets.rows(X) > 1) 34 | dim = 1; 35 | elseif(ROInets.cols(X) > 1) 36 | dim = 2; 37 | elseif isscalar(X) 38 | centredX = 0; 39 | return 40 | else 41 | dim = find(size(X), 1, 'first'); 42 | end%if 43 | end%if dim not provided 44 | 45 | centredX = bsxfun(@minus, X, sum(X,dim) ./ size(X,dim)); % inline mean(X, dim); 46 | 47 | % To test speeds: 48 | %bsxfunElapsed time is 16.242885 seconds. 49 | % repmatElapsed time is 22.516901 seconds. 50 | % y = magic(10000); 51 | % dim = 2; 52 | % 53 | % fprintf('bsxfun') 54 | % tic; 55 | % for i = 1:100, 56 | % z = bsxfun(@minus, y, mean(y,dim)); 57 | % end 58 | % toc 59 | % 60 | % fprintf('repmat') 61 | % tic; 62 | % for i = 1:100, 63 | % dims = size(y); 64 | % dimsize = size(y,dim); 65 | % dimrep = ones(1,length(dims)); 66 | % dimrep(dim) = dimsize; 67 | % z = y - repmat(mean(y,dim), dimrep); 68 | % end 69 | % toc 70 | % 71 | % 72 | % end 73 | -------------------------------------------------------------------------------- /+ROInets/do_group_level_glm.m: -------------------------------------------------------------------------------- 1 | function correlationMats = do_group_level_glm(correlationMats, Settings) 2 | %DO_GROUP_LEVEL_GLM run group comparison 3 | % 4 | 5 | 6 | % Copyright 2014 OHBA 7 | % This program is free software: you can redistribute it and/or modify 8 | % it under the terms of the GNU General Public License as published by 9 | % the Free Software Foundation, either version 3 of the License, or 10 | % (at your option) any later version. 11 | % 12 | % This program is distributed in the hope that it will be useful, 13 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | % GNU General Public License for more details. 16 | % 17 | % You should have received a copy of the GNU General Public License 18 | % along with this program. If not, see . 19 | 20 | 21 | % $LastChangedBy: giles.colclough@gmail.com $ 22 | % $Revision: 231 $ 23 | % $LastChangedDate: 2014-08-07 20:53:06 +0100 (Thu, 07 Aug 2014) $ 24 | % Contact: giles.colclough@gmail.com 25 | % Originally written on: MACI64 by Giles Colclough, 10-Apr-2014 13:33:21 26 | 27 | for iFreq = Settings.nFreqBands:-1:1, 28 | if strcmpi(Settings.paradigm, 'rest'), 29 | % univariate edge testing in turn for correlation, partial correlation 30 | % and regularized partials. 31 | [T, p, corrp, COPE] = ROInets.univariate_edge_test(correlationMats{iFreq}.envCorrelation_z, ... 32 | Settings.GroupLevel.designMatrix, ... 33 | Settings.GroupLevel.contrasts); 34 | for iContrast = size(p,3):-1:1, 35 | h(:,:,iContrast) = ROInets.false_discovery_rate(ROInets.p_to_z_two_tailed(p(:,:,iContrast)), ... 36 | Settings.FDRalpha); 37 | end%for 38 | 39 | groupLevel.correlation.T = T; 40 | groupLevel.correlation.p = p; 41 | groupLevel.correlation.FWEp = corrp; 42 | groupLevel.correlation.FDRh = h; 43 | groupLevel.correlation.COPE = COPE; 44 | 45 | [T, p, corrp, COPE] = ROInets.univariate_edge_test(correlationMats{iFreq}.envPartialCorrelation_z, ... 46 | Settings.GroupLevel.designMatrix, ... 47 | Settings.GroupLevel.contrasts); 48 | for iContrast = size(p,3):-1:1, 49 | h(:,:,iContrast) = ROInets.false_discovery_rate(ROInets.p_to_z_two_tailed(p(:,:,iContrast)), ... 50 | Settings.FDRalpha); 51 | end%for 52 | 53 | groupLevel.partialCorrelation.T = T; 54 | groupLevel.partialCorrelation.p = p; 55 | groupLevel.partialCorrelation.FWEp = corrp; 56 | groupLevel.partialCorrelation.FDRh = h; 57 | groupLevel.partialCorrelation.COPE = COPE; 58 | 59 | [T, p, corrp, COPE] = ROInets.univariate_edge_test(correlationMats{iFreq}.envPartialCorrelationRegularized_z, ... 60 | Settings.GroupLevel.designMatrix, ... 61 | Settings.GroupLevel.contrasts); 62 | for iContrast = size(p,3):-1:1, 63 | h(:,:,iContrast) = ROInets.false_discovery_rate(ROInets.p_to_z_two_tailed(p(:,:,iContrast)), ... 64 | Settings.FDRalpha); 65 | end%for 66 | 67 | groupLevel.partialCorrelationRegularized.T = T; 68 | groupLevel.partialCorrelationRegularized.p = p; 69 | groupLevel.partialCorrelationRegularized.FWEp = corrp; 70 | groupLevel.partialCorrelationRegularized.FDRh = h; 71 | groupLevel.partialCorrelationRegularized.COPE = COPE; 72 | 73 | % add in design for reference 74 | groupLevel.designMatrix = Settings.GroupLevel.designMatrix; 75 | groupLevel.contrasts = Settings.GroupLevel.contrasts; 76 | 77 | 78 | elseif strcmpi(Settings.paradigm, 'task'), 79 | % we need to run a separate GLM for each first level contrast. 80 | % And for each group-level contrast. 81 | % What a mare. 82 | for iContrast = length(Settings.FirstLevel.contrasts):-1:1, 83 | % we use parameter estimates from the level below for each of 84 | % correlation, partial correlation and regularised partial 85 | % correlation 86 | if isfield(correlationMats{iFreq}, 'subjectLevel'), 87 | COPE = correlationMats{iFreq}.subjectLevel(iContrast).cope; 88 | elseif isfield(correlationMats{iFreq}, 'firstLevel'), 89 | COPE = correlationMats{iFreq}.firstLevel(iContrast).cope; 90 | else 91 | error([mfilename ':WhereIsTheData'], ... 92 | 'Expected input to have either first level or subject level results. \n'); 93 | end%if 94 | 95 | 96 | %-- correlation 97 | [T, p, corrp, COPE] = ROInets.univariate_edge_test(COPE.correlation, ... 98 | Settings.GroupLevel.designMatrix, ... 99 | Settings.GroupLevel.contrasts); 100 | for iConG = size(p,3):-1:1, 101 | h(:,:,iConG) = ROInets.false_discovery_rate(ROInets.p_to_z_two_tailed(p(:,:,iConG)), ... 102 | Settings.FDRalpha); 103 | end%for 104 | 105 | groupLevel(iContrast).correlation.T = T; 106 | groupLevel(iContrast).correlation.p = p; 107 | groupLevel(iContrast).correlation.FWEp = corrp; 108 | groupLevel(iContrast).correlation.FDRh = h; 109 | groupLevel(iContrast).correlation.COPE = COPE; 110 | 111 | %-- partial correlation 112 | [T, p, corrp, COPE] = ROInets.univariate_edge_test(COPE.partialCorrelation, ... 113 | Settings.GroupLevel.designMatrix, ... 114 | Settings.GroupLevel.contrasts); 115 | for iConG = size(p,3):-1:1, 116 | h(:,:,iConG) = ROInets.false_discovery_rate(ROInets.p_to_z_two_tailed(p(:,:,iConG)), ... 117 | Settings.FDRalpha); 118 | end%for 119 | 120 | groupLevel(iContrast).partialCorrelation.T = T; 121 | groupLevel(iContrast).partialCorrelation.p = p; 122 | groupLevel(iContrast).partialCorrelation.FWEp = corrp; 123 | groupLevel(iContrast).partialCorrelation.FDRh = h; 124 | groupLevel(iContrast).partialCorrelation.COPE = COPE; 125 | 126 | %-- regularised partial correlation 127 | if Settings.Regularize.do, 128 | [T, p, corrp, COPE] = ROInets.univariate_edge_test(COPE.partialCorrelationRegularized, ... 129 | Settings.GroupLevel.designMatrix, ... 130 | Settings.GroupLevel.contrasts); 131 | for iConG = size(p,3):-1:1, 132 | h(:,:,iConG) = ROInets.false_discovery_rate(ROInets.p_to_z_two_tailed(p(:,:,iConG)), ... 133 | Settings.FDRalpha); 134 | end%for 135 | 136 | groupLevel(iContrast).partialCorrelationRegularized.T = T; 137 | groupLevel(iContrast).partialCorrelationRegularized.p = p; 138 | groupLevel(iContrast).partialCorrelationRegularized.FWEp = corrp; 139 | groupLevel(iContrast).partialCorrelationRegularized.FDRh = h; 140 | groupLevel(iContrast).partialCorrelationRegularized.COPE = COPE; 141 | end%if 142 | % add in first levels for reference 143 | groupLevel(iContrast).firstLevelContrast = Settings.SubjectLevel.contrasts{iContrast}; 144 | groupLevel(iContrast).firstLevelConditionLabels = Settings.SubjectLevel.conditionLabel; 145 | 146 | % add in design for reference 147 | groupLevel(iContrast).groupDesignMatrix = Settings.GroupLevel.designMatrix; 148 | groupLevel(iContrast).groupContrasts = Settings.GroupLevel.contrasts; 149 | end%for 150 | 151 | else 152 | error([mfilename 'BadParadigm'], ... 153 | 'Unrecognised paradigm %s. \n', Settings.paradigm); 154 | end%if 155 | 156 | correlationMats{iFreq}.groupLevel = groupLevel; 157 | end%for loop over frequencies 158 | -------------------------------------------------------------------------------- /+ROInets/do_group_level_statistics.m: -------------------------------------------------------------------------------- 1 | function correlationMats = do_group_level_statistics(correlationMats, Settings) 2 | %DO_GROUP_LEVEL_STATISTICS add group level inference for mean over subects 3 | % 4 | % CORRMATS = DO_GROUP_LEVEL_STATISTICS(CORRMATS, SETTINGS) adds group-level 5 | % inference fields to the CORRMATS structure. This tests just for edge 6 | % presence over all subjects 7 | % 8 | % Adds fields groupEnvCorrelation_z, groupEnvPartialCorrelation_z and 9 | % groupEnvPartialCorrelationRegularized_z. 10 | % 11 | % Also adds structure falseDiscoveryRate, holding the standard z-stat 12 | % threshold over which network edge classifications will have a false 13 | % discovery rate of Settings.FDRalpha, and adjusted probabilities for 14 | % each network edge. Type `help ROInets.false_discovery_rate' for more 15 | % information. 16 | % 17 | % A fixed effects (z-test) or mixed effects (t-test) analysis on the mean 18 | % z-stats over all subjects can be performed, set by the 19 | % Settings.groupStatisticsMethod option. 20 | % 21 | % See also: TTEST, ZTEST, ROInets.FALSE_DISCOVERY_RATE. 22 | 23 | 24 | % Copyright 2014 OHBA 25 | % This program is free software: you can redistribute it and/or modify 26 | % it under the terms of the GNU General Public License as published by 27 | % the Free Software Foundation, either version 3 of the License, or 28 | % (at your option) any later version. 29 | % 30 | % This program is distributed in the hope that it will be useful, 31 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | % GNU General Public License for more details. 34 | % 35 | % You should have received a copy of the GNU General Public License 36 | % along with this program. If not, see . 37 | 38 | 39 | % $LastChangedBy: giles.colclough@gmail.com $ 40 | % $Revision: 231 $ 41 | % $LastChangedDate: 2014-08-07 20:53:06 +0100 (Thu, 07 Aug 2014) $ 42 | % Contact: giles.colclough@gmail.com 43 | % Originally written on: MACI64 by Giles Colclough, 10-Apr-2014 13:33:21 44 | 45 | if 1 == Settings.nSessions, 46 | for iFreq = Settings.nFreqBands:-1:1, 47 | correlationMats{iFreq}.groupEnvCorrelation_z = correlationMats{iFreq}.envCorrelation_z; 48 | correlationMats{iFreq}.groupEnvPartialCorrelation_z = correlationMats{iFreq}.envPartialCorrelation_z; 49 | if Settings.Regularize.do && isfield(correlationMats{iFreq}, 'envPartialCorrelationRegularized_z'), 50 | correlationMats{iFreq}.groupEnvPartialCorrelationRegularized_z = correlationMats{iFreq}.envPartialCorrelationRegularized_z; 51 | end%if 52 | end%for 53 | 54 | else 55 | % take average correlations across subjects 56 | average = cellfun(@(C) ROInets.apply_function_to_structure(@take_average_correlations, C), ... 57 | correlationMats, ... 58 | 'UniformOutput', false); 59 | 60 | switch lower(Settings.groupStatisticsMethod) 61 | case 'fixed-effects' 62 | % do z-test on mean of z-stats. This is a group-level fixed-effects 63 | % analysis, testing that the population mean of the z-stats at each node is 64 | % non-zero, under the population variance of 1. Note this is 65 | % well-described: the single-subject z-stats do follow a very good normal 66 | % distribution under the null, as tested with null data! 67 | % significance at 5% is z=1.96 and above 68 | % We don't need a t-test: we do t-tests when we approximate the population 69 | % s.d. with the sample s.d. 70 | % We know the population s.d. under the null here! 71 | sd_of_means = 1.0 ./ sqrt(Settings.nSessions); 72 | for iFreq = Settings.nFreqBands:-1:1, 73 | % this is equivalent to testing (mean(z) - mu) / s, 74 | % for mu=0 and s=1/sqrt(N) under H0: null distribution of sample means. 75 | 76 | correlationMats{iFreq}.groupEnvCorrelation_z = ... 77 | (average{iFreq}.envCorrelation_z ./ sd_of_means); 78 | 79 | correlationMats{iFreq}.groupEnvPartialCorrelation_z = ... 80 | (average{iFreq}.envPartialCorrelation_z ./ sd_of_means); 81 | 82 | if (Settings.Regularize.do && ... 83 | isfield(average{iFreq}, 'envPartialCorrelationRegularized_z')), 84 | 85 | correlationMats{iFreq}.groupEnvPartialCorrelationRegularized_z = ... 86 | average{iFreq}.envPartialCorrelationRegularized_z ... 87 | ./ sd_of_means; 88 | end%if 89 | end%for 90 | 91 | case 'mixed-effects' 92 | % Mixed effects group-level stats: reduces power but allows for 93 | % mistakes/errors in modelling of 1st level stats - e.g. if scaling to 94 | % z-stats has buggered up, a mixed-effects analysis is still robust. 95 | % we use a t-test in this situation 96 | nullMean = 0; 97 | alpha = 0.05; 98 | tail = 'both'; 99 | 100 | for iFreq = Settings.nFreqBands:-1:1, 101 | [~, p] = ttest(shiftdim(correlationMats{iFreq}.envCorrelation_z, 2), ... 102 | nullMean, alpha, tail); 103 | signZ = sign(average{iFreq}.envCorrelation_z); 104 | % convert p-value to two-tailed z-stat 105 | correlationMats{iFreq}.groupEnvCorrelation_z = ... 106 | ROInets.p_to_z_two_tailed(shiftdim(p), signZ); 107 | 108 | [~, p] = ttest(shiftdim(correlationMats{iFreq}.envPartialCorrelation_z, 2), ... 109 | nullMean, alpha, tail); 110 | signZ = sign(average{iFreq}.envPartialCorrelation_z); 111 | correlationMats{iFreq}.groupEnvPartialCorrelation_z = ... 112 | ROInets.p_to_z_two_tailed(shiftdim(p), signZ); 113 | 114 | if (Settings.Regularize.do && ... 115 | isfield(correlationMats{iFreq}, 'envPartialCorrelationRegularized_z')), 116 | 117 | [~, p] = ttest(shiftdim(correlationMats{iFreq}.envPartialCorrelationRegularized_z, 2), ... 118 | nullMean, alpha, tail); 119 | signZ = sign(average{iFreq}.envPartialCorrelationRegularized_z); 120 | correlationMats{iFreq}.groupEnvPartialCorrelationRegularized_z = ... 121 | ROInets.p_to_z_two_tailed(shiftdim(p), signZ); 122 | end%if 123 | end%for 124 | 125 | otherwise 126 | error([mfilename ':UnrecognisedGroupStats'], ... 127 | 'Unrecognised group stats method: %s\n', ... 128 | Settings.groupStatisticsMethod); 129 | end%switch 130 | end%if 131 | 132 | % put group results through false discovery rate conversion to account for 133 | % multiple network edge comparisons 134 | for iFreq = Settings.nFreqBands:-1:1, 135 | % desired false discovery rate threshold 136 | correlationMats{iFreq}.falseDiscoveryRate.alpha = Settings.FDRalpha; 137 | 138 | % calculate equivalent z-threshold, and adjusted probability values on 139 | % each edge 140 | [~, ... 141 | correlationMats{iFreq}.falseDiscoveryRate.zThreshold.groupEnvCorrelation_z, ... 142 | correlationMats{iFreq}.falseDiscoveryRate.pAdjusted.groupEnvCorrelation_z] = ... 143 | ROInets.false_discovery_rate(abs(correlationMats{iFreq}.groupEnvCorrelation_z), ... 144 | Settings.FDRalpha); 145 | 146 | [~, ... 147 | correlationMats{iFreq}.falseDiscoveryRate.zThreshold.groupEnvPartialCorrelation_z, ... 148 | correlationMats{iFreq}.falseDiscoveryRate.pAdjusted.groupEnvPartialCorrelation_z] = ... 149 | ROInets.false_discovery_rate(abs(correlationMats{iFreq}.groupEnvPartialCorrelation_z), ... 150 | Settings.FDRalpha); 151 | 152 | if (Settings.Regularize.do && ... 153 | isfield(correlationMats{iFreq}, 'envPartialCorrelationRegularized_z')), 154 | [~, ... 155 | correlationMats{iFreq}.falseDiscoveryRate.zThreshold.groupEnvPartialCorrelationRegularized_z, ... 156 | correlationMats{iFreq}.falseDiscoveryRate.pAdjusted.groupEnvPartialCorrelationRegularized_z] = ... 157 | ROInets.false_discovery_rate(abs(correlationMats{iFreq}.groupEnvPartialCorrelationRegularized_z), ... 158 | Settings.FDRalpha); 159 | end%if 160 | end%for 161 | 162 | end%do_group_level_statistics 163 | 164 | 165 | 166 | %%% SUBFUNCTION %%% 167 | 168 | function y = take_average_correlations(x) 169 | if isnumeric(x), 170 | y = nanmean(real(x), 3); 171 | else 172 | y = x; 173 | end%if 174 | end%take_average_correlations 175 | % [EOF] 176 | -------------------------------------------------------------------------------- /+ROInets/do_pairwise_calculation.m: -------------------------------------------------------------------------------- 1 | function mats = do_pairwise_calculation(nodeData, ... 2 | time, ... 3 | EnvelopeParams) 4 | % DO_PAIRWISE_CALCULATION pairwise source-leakage corrected network matrix 5 | % 6 | % MATS = DO_PAIRWISE_CALCULATION(DATA, TIME, ENVPARAMS) computes the node 7 | % correlation, envelope correlation and envelope partial correlations 8 | % for nodes corrected all-to-alll in a pairwise orthogonalisation 9 | % manner. 10 | % 11 | % DATA is nNodes x nTimeSamples, TIME is a vector of timepoints and 12 | % ENVPARAMS is a structure with fields: 13 | % - windowLength : length of moving average window in s 14 | % - overlap : fractional overlap of moving average window 15 | % - useHanningWindow : use of a Hanning window function in the moving 16 | % average block 17 | % - useFilter : true/false: use moving average or more 18 | % sophisticated downsampling / resampling filter. 19 | % The passed frequency is 1.0/WINDOWLENGTH, resampled to a Nyquist 20 | % frequency of twice this. 21 | % 22 | % MATS is a structure containing the Correlation, envCorrelation, and 23 | % envPartialCorrelation between nodes after the correction is applied. It 24 | % also contains the nSamples in the enveloped data, and a Regularization 25 | % field for tying into the run_network_analysis pipeline. 26 | % 27 | % extension of voxelwise orthogonalisation methods to ROI time-courses. 28 | % See Brookes et al 2012 29 | 30 | 31 | % Copyright 2014 OHBA 32 | % This program is free software: you can redistribute it and/or modify 33 | % it under the terms of the GNU General Public License as published by 34 | % the Free Software Foundation, either version 3 of the License, or 35 | % (at your option) any later version. 36 | % 37 | % This program is distributed in the hope that it will be useful, 38 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 39 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 40 | % GNU General Public License for more details. 41 | % 42 | % You should have received a copy of the GNU General Public License 43 | % along with this program. If not, see . 44 | 45 | 46 | % $LastChangedBy: giles.colclough@gmail.com $ 47 | % $Revision: 373 $ 48 | % $LastChangedDate: 2015-01-12 16:36:30 +0000 (Mon, 12 Jan 2015) $ 49 | % Contact: giles.colclough@gmail.com 50 | % Originally written on: MACI64 by Giles Colclough, 18-Mar-2014 09:43:55 51 | 52 | % do the calculation using pairwise orthogonalisation and partial 53 | % correlation 54 | 55 | nNodes = ROInets.rows(nodeData); 56 | 57 | for iNode = nNodes:-1:1, 58 | fprintf(' Pairwise correlation analysis for ROI %d out of %d. \n', ... 59 | iNode, nNodes); 60 | 61 | otherNodes = ROInets.setdiff_pos_int(1:nNodes, iNode); 62 | % envelope now to save repeating inside the next loop 63 | iNodeEnv = ROInets.envelope_data(nodeData(iNode,:), ... 64 | time, ... 65 | EnvelopeParams); 66 | 67 | for ioindex = 1:length(otherNodes), 68 | oNode = otherNodes(ioindex); 69 | % remove the node timecourse from all others 70 | tmp = ROInets.householder_orthogonalise([nodeData(iNode,:)', ... 71 | nodeData(oNode,:)']); % first vector is unaffected 72 | % orthNode will hold nodeData(oNode,:) pairwise orthogonalised to 73 | % iNode. It's transposed to a column vector. 74 | orthNode = tmp(:,2); 75 | clear tmp 76 | 77 | % now remove these two from all others to get pcorr 78 | otherNodesSaveTwo = ROInets.setdiff_pos_int((1:nNodes), ... 79 | [iNode,oNode]); 80 | 81 | dataForPCorr = zeros(size(nodeData)); 82 | dataForPCorr(iNode,:) = nodeData(iNode,:); 83 | dataForPCorr(oNode,:) = orthNode; 84 | 85 | % loop over all other nodes save these two 86 | for aoindex = 1:length(otherNodesSaveTwo), 87 | aoNode = otherNodesSaveTwo(aoindex); 88 | tmp = ROInets.householder_orthogonalise([nodeData(iNode,:)', ... 89 | orthNode, ... 90 | nodeData(aoNode,:)'])'; % first two vectors are already orthogonal 91 | dataForPCorr(aoNode,:) = tmp(3,:); 92 | end%for 93 | 94 | % envelope 95 | envsForTwoNodes(otherNodes,:) = ROInets.envelope_data(dataForPCorr(otherNodes,:), ... 96 | time, ... 97 | EnvelopeParams); 98 | envsForTwoNodes(iNode,:) = iNodeEnv; 99 | 100 | % partial correlation 101 | covariance = cov(envsForTwoNodes'); 102 | partialCorrSingleRun = ROInets.convert_precision_to_pcorr(... 103 | pinv(covariance)); 104 | nodePCorr(iNode, oNode) = partialCorrSingleRun(iNode, oNode); 105 | 106 | % marginal correlation 107 | corrTmp = corrcov(covariance([iNode,oNode], [iNode,oNode])); % corr([envsForTwoNodes(iNode,:)', envsForTwoNodes(oNode,:)']); 108 | nodeCorr(iNode, oNode) = corrTmp(1,2); 109 | 110 | % raw correlation 111 | corrTmp = corr([nodeData(iNode,:)', orthNode]); 112 | rawCorr(iNode, oNode) = corrTmp(1,2); 113 | end%loop over other nodes 114 | 115 | nodeCorr(iNode,iNode) = 1; 116 | nodePCorr(iNode,iNode) = 1; 117 | rawCorr(iNode,iNode) = 1; 118 | end%loop over nodes 119 | 120 | mats.nSamples = ROInets.cols(envsForTwoNodes); % this, unhelpfully, should be the number of samples in the enveloped data 121 | mats.correlation = rawCorr; 122 | mats.envCorrelation = nodeCorr; 123 | mats.envPartialCorrelation = nodePCorr; 124 | mats.Regularization.mean = 0; 125 | end%do_pairwise_calculation 126 | % [EOF] 127 | -------------------------------------------------------------------------------- /+ROInets/do_subject_level_glm.m: -------------------------------------------------------------------------------- 1 | function correlationMats = do_subject_level_glm(correlationMats, Settings) 2 | %DO_SUBJECT_LEVEL_GLM run subject level analysis 3 | % 4 | % M = ROInets.DO_SUBJECT_LEVEL_GLM(M, Settings) uses subject design 5 | % matrix Settings.SubjectLevel.subjectDesign to perform a subject-level analysis, and append these results 6 | % as a subject level to the set of correlation matrices M. 7 | % 8 | % The subject design matrix should be an nSessions by nSubjects matrix 9 | % with entries 1 or zeros to indicate allocation of sessions to subjects. 10 | % 11 | % 12 | 13 | 14 | % Copyright 2014 OHBA 15 | % This program is free software: you can redistribute it and/or modify 16 | % it under the terms of the GNU General Public License as published by 17 | % the Free Software Foundation, either version 3 of the License, or 18 | % (at your option) any later version. 19 | % 20 | % This program is distributed in the hope that it will be useful, 21 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | % GNU General Public License for more details. 24 | % 25 | % You should have received a copy of the GNU General Public License 26 | % along with this program. If not, see . 27 | 28 | 29 | % $LastChangedBy: giles.colclough@gmail.com $ 30 | % $Revision: 231 $ 31 | % $LastChangedDate: 2014-08-07 20:53:06 +0100 (Thu, 07 Aug 2014) $ 32 | % Contact: giles.colclough@gmail.com 33 | % Originally written on: MACI64 by Giles Colclough, 10-Apr-2014 13:33:21 34 | 35 | if ~strcmpi(Settings.paradigm, 'task'), 36 | % no need to do anything: only 1 session assumed for RS analysis 37 | return 38 | end%if 39 | 40 | % quick dimension check 41 | nSessions = size(correlationMats{1}.firstLevel(1).cope.correlation,3); 42 | 43 | if ~isfield(Settings, 'SubjectLevel') || isempty(Settings.SubjectLevel) || isempty(Settings.SubjectLevel.subjectDesign), 44 | subjectDesign = eye(nSessions); 45 | else 46 | subjectDesign = Settings.SubjectLevel.subjectDesign; 47 | end%if 48 | 49 | assert(ROInets.rows(subjectDesign) == nSessions, ... 50 | [mfilename ':dimensionMismatch'], ... 51 | 'subjectDesign should have as many rows as there are sessions to analyse. \n'); 52 | 53 | for iFreq = Settings.nFreqBands:-1:1, 54 | % we need to run a separate GLM for each first level contrast. 55 | for iContrast = length(Settings.FirstLevel.contrasts):-1:1, 56 | 57 | % we use parameter estimates from the level below for each of 58 | % correlation, partial correlation and regularised partial 59 | % correlation 60 | if isfield(correlationMats{iFreq}, 'firstLevel'), 61 | COPE = correlationMats{iFreq}.firstLevel(iContrast).cope; 62 | else 63 | error([mfilename ':WhereIsTheData'], ... 64 | 'Expected input to have first level results. \n'); 65 | end%if 66 | 67 | % find any subjects which have nan entries. They will need to be 68 | % removed from the data and design matrix. 69 | % It's a safe assumption that NaNs encode subjects without 70 | % sufficient information in trials to estimate parameters 71 | % They will therefore be nan over all edges, and for each of 72 | % correlation, partialcorr and the regularised version 73 | nanSessions = any(isnan(COPE.correlation),1); 74 | goodSessions = logical(squeeze(~nanSessions(1,1,:))); 75 | cleanDesign = subjectDesign(goodSessions,:); 76 | 77 | 78 | %-- correlation 79 | beta = cleanDesign \ ROInets.get_edges(COPE.correlation(:,:,goodSessions))'; 80 | subjectLevel(iContrast).cope.correlation = ROInets.unvectorize(beta'); 81 | 82 | %-- partial correlation 83 | beta = cleanDesign \ ROInets.get_edges(COPE.partialCorrelation(:,:,goodSessions))'; 84 | subjectLevel(iContrast).cope.partialCorrelation = ROInets.unvectorize(beta'); 85 | 86 | %-- regularised partial correlation 87 | if Settings.Regularize.do, 88 | beta = cleanDesign \ ROInets.get_edges(COPE.partialCorrelationRegularized(:,:,goodSessions))'; 89 | subjectLevel(iContrast).cope.partialCorrelationRegularized = ROInets.unvectorize(beta'); 90 | end%if 91 | % add in first levels for reference 92 | subjectLevel(iContrast).firstLevelContrast = Settings.SubjectLevel.contrasts{iContrast}; 93 | subjectLevel(iContrast).firstLevelConditionLabels = Settings.SubjectLevel.conditionLabel; 94 | 95 | % add in design for reference 96 | subjectLevel(iContrast).designMatrix = subjectDesign; 97 | end%for 98 | 99 | 100 | 101 | correlationMats{iFreq}.subjectLevel = subjectLevel; 102 | end%for loop over frequencies 103 | -------------------------------------------------------------------------------- /+ROInets/estimate_AR_coeffs.m: -------------------------------------------------------------------------------- 1 | function [AR_coeffs, varEst, AR_partial_coeffs] = estimate_AR_coeffs(data, order) 2 | %ESTIMATE_AR_COEFFS fits an AR model to voxel data and estimates coefficients 3 | % [AR_COEFFS, VAR_EST, AR_PARTIAL_COEFFS] = estimate_AR_coeffs(DATA, P) fits 4 | % a P-order AR model to every row of DATA, then provides the median 5 | % coefficients from the fits, AR_COEFFS, the median noise variance VAR_EST 6 | % and the median AR partial coefficients AR_PARTIAL_COEFFS, useful for 7 | % model testing. 8 | % 9 | % AR_COEFFS obey the matlab convention: SUM_{k=0}^{p} AR(k) y(n-k) = e(n) 10 | % where e(n) is a Gaussian noise process. 11 | % 12 | % See also nets_r2z, ARYULE 13 | 14 | % References: http://www.math.utah.edu/~zhorvath/ar1.pdf 15 | 16 | % Copyright 2014 OHBA 17 | % This program is free software: you can redistribute it and/or modify 18 | % it under the terms of the GNU General Public License as published by 19 | % the Free Software Foundation, either version 3 of the License, or 20 | % (at your option) any later version. 21 | % 22 | % This program is distributed in the hope that it will be useful, 23 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | % GNU General Public License for more details. 26 | % 27 | % You should have received a copy of the GNU General Public License 28 | % along with this program. If not, see . 29 | 30 | 31 | % $LastChangedBy: giles.colclough@gmail.com $ 32 | % $Revision: 214 $ 33 | % $LastChangedDate: 2014-07-24 12:40:42 +0100 (Thu, 24 Jul 2014) $ 34 | % Contact: giles.colclough@gmail.com 35 | % Originally written on: GLNXA64 by Giles Colclough, 16-Apr-2014 15:25:19 36 | 37 | % Use matlab convention on signs. 38 | 39 | 40 | fprintf(' Estimating AR model parameters. \n'); 41 | 42 | % parse input order 43 | if 0 == order, 44 | AR_coeffs = 1; 45 | varEst = 0; 46 | AR_partial_coeffs = 1; 47 | return 48 | 49 | elseif order < 0, 50 | error([mfilename ':InvalidOrderParameter'], ... 51 | 'The model order must be a non-negative integer. \n'); 52 | end%if 53 | 54 | nNodes = ROInets.rows(data); 55 | 56 | % fit AR model with Yule-Walker method at each row 57 | for iNode = nNodes:-1:1, 58 | [A(:,iNode), E(iNode), K(:,iNode)] = aryule(data(iNode, :), order); 59 | end%for 60 | 61 | % take estimate over all voxels 62 | AR_coeffs = median(A,2); 63 | varEst = median(E); 64 | 65 | % partial autocorrelation can help with model selection pacf = -K 66 | AR_partial_coeffs = -median(K,2); 67 | 68 | % check stability of model 69 | if order > 0 && ~is_stable(AR_coeffs), 70 | error([mfilename ':UnstableModel'], ... 71 | ['The fitted AR model is unstable. ', ... 72 | 'You could try using more coefficients. \n']); 73 | end%if 74 | end%estimate_AR_coeffs 75 | 76 | function isStable = is_stable(AR_coeffs) 77 | % BOOL = IS_STABLE(AR_COEFFS) checks the stability of AR model with 78 | % coeffecients AR_COEFFS and returns BOOL true or false. 79 | % 80 | % The stability of an AR model is confirmed if the roots of the 81 | % characteristic equation lie within the unit circle. 82 | 83 | % Ref: http://davegiles.blogspot.co.uk/2013/06/when-is-autoregressive-model.html 84 | 85 | r = roots(AR_coeffs); 86 | isStable = all(abs(r) < 1.0); 87 | end%is_stable 88 | % [EOF] 89 | -------------------------------------------------------------------------------- /+ROInets/fast_svds.m: -------------------------------------------------------------------------------- 1 | function [U, S, V] = fast_svds(X, N) 2 | %FAST_SVDS RAM/time-efficient version of SVDS, singular value decomposition 3 | % 4 | % S = FAST_SVDS(X, N) computes N components of the singular value 5 | % decomposition of X: X = U * S * V' where S is diagonal, and returns the 6 | % singular values in S. 7 | % 8 | % [U, S, V] = FAST_SVDS(X, N) computes N components of the singular value 9 | % decomposition of X: X = U * S * V' where S is diagonal. 10 | % If N <= 0, rank(X) - abs(N) components are estimated. 11 | % 12 | % Note: no demeaning of X takes place within this function 13 | 14 | % Copyright 2013-4 OHBA, FMRIB 15 | % This program is free software: you can redirstribute it and/or modify 16 | % it under the terms of the GNU General Public License as published by 17 | % the Free Software Foundation, either version 3 of the License, or 18 | % (at your option) any later version. 19 | % 20 | % This program is distributed in the hope that it will be useful, 21 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 22 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 | % GNU General Public License for more details. 24 | % 25 | % You should have received a copy of the GNU General Public License 26 | % along with this program. If not, see . 27 | 28 | 29 | % $LastChangedBy: giles.colclough@gmail.com $ 30 | % $Revision: 230 $ 31 | % $LastChangedDate: 2014-08-07 20:52:19 +0100 (Thu, 07 Aug 2014) $ 32 | % Contact: giles.colclough 'at' magd.ox.ac.uk 33 | % Originally written by Steve Smith, FMRIB, 2013. 34 | 35 | % parse component selection 36 | if N < 1 37 | N = max(min(size(X)) + N, 1); 38 | end%if 39 | 40 | % Compute svd from eigenvalue decomposition of X'*X. 41 | % Choose which decomposition to take to maximise efficiency. 42 | if ROInets.rows(X) < ROInets.cols(X), 43 | if N < ROInets.rows(X), 44 | [U, d] = eigs(X * X', N); 45 | 46 | else 47 | [U, d] = eig(X * X'); 48 | U = fliplr(U); 49 | d = rot90(d, 2); 50 | end%if 51 | 52 | S = sqrt(abs(d)); 53 | V = X' * (U * diag((1.0 / diag(S)))); 54 | 55 | else 56 | if N < ROInets.cols(X), 57 | [V, d] = eigs(X' * X, N); 58 | 59 | else 60 | [V, d] = eig(X' * X); 61 | V = fliplr(V); 62 | d = rot90(d, 2); 63 | end%if 64 | 65 | S = sqrt(abs(d)); 66 | U = X * (V * diag((1.0 / diag(S)))); 67 | 68 | end%if 69 | 70 | % change output based on required behaviour 71 | if 1 == nargout, 72 | U = S; 73 | end%if 74 | end%fast_svds 75 | % [EOF] -------------------------------------------------------------------------------- /+ROInets/find_empirical_H0_distribution_width.m: -------------------------------------------------------------------------------- 1 | function sigma = find_empirical_H0_distribution_width(nH0Iter, nNodes, nTimeSamples, Settings, RegularizationResults, ARmodel, Fs, Filter, EnvelopeParams) 2 | %FIND_EMPIRICAL_H0_DISTRIBUTON_WIDTH 3 | % 4 | % generation of empirical H0 dataset by using fitted AR model 5 | % and calculating distribution of null correlations. 6 | 7 | % Copyright 2014 OHBA 8 | % This program is free software: you can redistribute it and/or modify 9 | % it under the terms of the GNU General Public License as published by 10 | % the Free Software Foundation, either version 3 of the License, or 11 | % (at your option) any later version. 12 | % 13 | % This program is distributed in the hope that it will be useful, 14 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | % GNU General Public License for more details. 17 | % 18 | % You should have received a copy of the GNU General Public License 19 | % along with this program. If not, see . 20 | 21 | 22 | % $LastChangedBy: giles.colclough@gmail.com $ 23 | % $Revision: 263 $ 24 | % $LastChangedDate: 2014-10-23 11:30:39 +0100 (Thu, 23 Oct 2014) $ 25 | % Contact: giles.colclough@gmail.com 26 | % Originally written on: MACI64 by Giles Colclough, 17-Mar-2014 23:39:39 27 | 28 | 29 | 30 | if nH0Iter, 31 | fprintf(' Generating empirical null correlation distribution. \n'); 32 | % set regularization to be applied to empirical data 33 | if Settings.Regularize.do, 34 | if strcmpi(Settings.Regularize.method, 'Friedman'), 35 | empirical_rho = RegularizationResults.mean; 36 | else % Bayesian 37 | % subsample from posterior of regularization parameters. If 38 | % nH0Iter is greater than the number of posterior samples, 39 | % choose with replacement. 40 | nPosteriorSamples = length(RegularizationResults.posteriorRho); 41 | if nH0Iter <= nPosteriorSamples, 42 | replacement = false; 43 | else 44 | replacement = true; 45 | end%if 46 | empirical_rho = RegularizationResults.posteriorRho(... 47 | randsample(nPosteriorSamples, ... 48 | nH0Iter, replacement)); 49 | end%if 50 | else 51 | empirical_rho = 0; 52 | end%if 53 | 54 | sigma.nH0Iter = nH0Iter; 55 | sigma.nNodes = nNodes; 56 | sigma.nSamples = nTimeSamples; 57 | [sigma.z, ... 58 | sigma.z_partial, ... 59 | sigma.z_partial_reg] = ROInets.find_empirical_dev(nH0Iter, ... 60 | sigma.nNodes, ... 61 | sigma.nSamples, ... 62 | ARmodel, ... 63 | Settings.Regularize.do, ... 64 | empirical_rho, ... 65 | Fs, ... 66 | Filter, ... 67 | EnvelopeParams); 68 | else 69 | % we will assume normality 70 | [sigma.z, ... 71 | sigma.z_partial, ... 72 | sigma.z_partial_reg] = deal([]); 73 | end%if 74 | end%find_empirical_H0_distribution_width 75 | -------------------------------------------------------------------------------- /+ROInets/find_empirical_dev.m: -------------------------------------------------------------------------------- 1 | function [sigma_z, ... 2 | sigma_z_partial, ... 3 | sigma_z_partial_reg] = find_empirical_dev(nIter, ... 4 | nNodes, ... 5 | nSamples, ... 6 | ARmodel, ... 7 | doRegularize, ... 8 | rho, ... 9 | Fs, ... 10 | FilterSettings, ... 11 | EnvelopeParams) 12 | %FIND_EMPIRICAL_DEV Build up correlations from empirical null distribtion 13 | 14 | 15 | % Copyright 2014 OHBA 16 | % This program is free software: you can redistribute it and/or modify 17 | % it under the terms of the GNU General Public License as published by 18 | % the Free Software Foundation, either version 3 of the License, or 19 | % (at your option) any later version. 20 | % 21 | % This program is distributed in the hope that it will be useful, 22 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | % GNU General Public License for more details. 25 | % 26 | % You should have received a copy of the GNU General Public License 27 | % along with this program. If not, see . 28 | 29 | 30 | % $LastChangedBy: giles.colclough@gmail.com $ 31 | % $Revision: 366 $ 32 | % $LastChangedDate: 2014-12-13 19:03:44 +0000 (Sat, 13 Dec 2014) $ 33 | % Contact: giles.colclough@gmail.com 34 | % Originally written on: GLNXA64 by Giles Colclough, 16-Apr-2014 17:09:11 35 | 36 | if nIter < 1, 37 | error([mfilename ':BadnH0Iter'], ... 38 | 'Specify how many simulated data sets to create in nH0Iter'); 39 | end%if 40 | 41 | 42 | if doRegularize, 43 | assert(all(rho(:) >= 0), ... 44 | [mfilename ':BadRegularizationParameter'], ... 45 | 'Regularization parameters must be greater than zero. \n'); 46 | 47 | % check conditioning of rho 48 | if isscalar(rho), 49 | rho = repmat(rho, 1, nIter); 50 | else 51 | assert(isequal(length(rho), nIter), ... 52 | [mfilename ':NumberRegularizationParameters'], ... 53 | ['Please use as many regularization parameters as ', ... 54 | 'iterations, or just set one. \n']); 55 | end%if 56 | 57 | else 58 | rho = 0; 59 | end%if 60 | 61 | 62 | % produce AR random data 63 | randData = filter(1, ... 64 | ARmodel.coeffs, ... 65 | sqrt(ARmodel.varianceEstimate) .* randn(nSamples, nNodes, nIter)); 66 | t = (0:1:nSamples-1) ./ Fs; 67 | 68 | for iIter = nIter:-1:1, 69 | % bandpass filter the data 70 | if isempty(FilterSettings.band), 71 | filteredData = transpose(randData(:,:,iIter)); 72 | else 73 | filteredData = ft_preproc_bandpassfilter(transpose(randData(:,:,iIter)), ... 74 | Fs, ... 75 | FilterSettings.band, ... 76 | FilterSettings.order, ... 77 | FilterSettings.type, ... 78 | FilterSettings.direction); 79 | end%if 80 | 81 | % envelope the data 82 | testData = ROInets.envelope_data(filteredData, t, EnvelopeParams)'; 83 | clear filteredData 84 | 85 | % take covariance 86 | rCov = real(cov(testData)); 87 | rCorr = corrcov(rCov,1); 88 | 89 | % we've had a weird error with infinities. catch that 90 | isOutOfBounds = any(isinf(rCov(:)) | isnan(rCov(:))) || ... 91 | any(isinf(testData(:)) | isnan(testData(:))); 92 | if isOutOfBounds, 93 | error([mfilename 'CorrelationOutOfBounds'], ... 94 | ['Something''s wrong here. check me out. \n ', ... 95 | 'Possibly application of AR model to random data is failing. \n']); 96 | elseif ~ROInets.isposdef(rCov), 97 | c = rCov - rCov'; 98 | if max(abs(c(:))) > eps, 99 | error([mfilename ':AsymmetricCorrelation'], ... 100 | 'We have generated an asymmetric covariance matrix. \n'); 101 | else 102 | warning([mfilename ':NonPosDefCorrelation'], ... 103 | 'We have generated a non-pos-def covariance matrix. \n'); 104 | end%if 105 | end%if 106 | 107 | clear testData 108 | 109 | % extract only unique values 110 | uniqueInd = triu(true(size(rCorr)), 1); 111 | rEmpirical(:,iIter) = rCorr(uniqueInd); 112 | 113 | % repeat for partial correlations 114 | r_partial = ROInets.convert_precision_to_pcorr(pinv(rCov)); 115 | rEmpirical_partial(:,iIter) = r_partial(uniqueInd); 116 | 117 | % repeat for regularized partial correlations 118 | if doRegularize 119 | rPrecision = ROInets.dp_glasso(rCov, [], rho(iIter)); 120 | r_reg = ROInets.convert_precision_to_pcorr(rPrecision); 121 | 122 | rEmpirical_partial_reg(:,iIter) = r_reg(uniqueInd); 123 | else 124 | rEmpirical_partial_reg = []; 125 | end 126 | end%for 127 | 128 | sigma_z = std(ROInets.Fisher_r_to_z(rEmpirical(:))); 129 | sigma_z_partial = std(ROInets.Fisher_r_to_z(rEmpirical_partial(:))); 130 | sigma_z_partial_reg = std(ROInets.Fisher_r_to_z(rEmpirical_partial_reg(:))); 131 | 132 | end%find_empirical_dev 133 | % [EOF] 134 | -------------------------------------------------------------------------------- /+ROInets/find_permutation_H0_distribution_width.m: -------------------------------------------------------------------------------- 1 | function sigma = find_permutation_H0_distribution_width(envData, nPerms, Regularize, transform) 2 | %FIND_PERMUTATION_H0_DISTRIBUTION_WIDTH use random shuffles to build a null 3 | % 4 | % SIGMA = FIND_PERMUTATION_H0_DISTRIBUTION_WIDTH(ENVDATA, NPERMS, REGULARIZE, TRANSFORM) 5 | % finds the standard deviation of z-transformed correlations for null 6 | % data sharing the same temporal smoothness as input ENVDATA. NPERMS 7 | % complete datasets of the same size as ENVDATA are generated to build 8 | % this empirical null. 9 | % 10 | % If TRANSORM is true, a logarithmic transform is used to improve the 11 | % match between normally-generated variables and the input data. 12 | % 13 | % SEE ALSO: FIND_EMPIRICAL_H0_DISTRIBUTION. 14 | 15 | 16 | % Copyright 2014 OHBA 17 | % This program is free software: you can redistribute it and/or modify 18 | % it under the terms of the GNU General Public License as published by 19 | % the Free Software Foundation, either version 3 of the License, or 20 | % (at your option) any later version. 21 | % 22 | % This program is distributed in the hope that it will be useful, 23 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | % GNU General Public License for more details. 26 | % 27 | % You should have received a copy of the GNU General Public License 28 | % along with this program. If not, see . 29 | 30 | 31 | % $LastChangedBy: giles.colclough@gmail.com $ 32 | % $Revision: 368 $ 33 | % $LastChangedDate: 2014-12-13 19:05:24 +0000 (Sat, 13 Dec 2014) $ 34 | % Contact: giles.colclough 'at' magd.ox.ac.uk 35 | % Originally written on: GLNXA64 by Giles Colclough, 07-Nov-2013 12:43:45 36 | if ~nPerms, 37 | % we will assume normality 38 | [sigma.z, ... 39 | sigma.z_partial, ... 40 | sigma.z_partial_reg] = deal([]); 41 | return 42 | end%if 43 | 44 | if nargin < 3 || isempty(Regularize), 45 | Regularize.do = false; 46 | end%if 47 | if Regularize.do, 48 | assert(isfield(Regularize, 'rho') && Regularize.rho > 0, ... 49 | [mfilename ':BadRegularizationParam'], ... 50 | 'Regularization parameter must be non-zero\n'); 51 | end%if 52 | if nargin < 4 53 | transform = false; 54 | end%if 55 | 56 | nTrials = size(envData, 3); 57 | nPermsTotal = nTrials * nPerms; 58 | 59 | for iPerm = nPermsTotal:-1:1, 60 | iTrial = mod(iPerm-1, nTrials)+1; 61 | surrogateData = make_surrogate_data(envData(:,:,iTrial), ... 62 | transform); 63 | [corr(:,iPerm), pcorr(:,iPerm), pcorrReg(:,iPerm)] = compute_correlations(surrogateData, ... 64 | Regularize); 65 | end%permutations 66 | 67 | sigma.z = std(ROInets.Fisher_r_to_z(corr(:))); 68 | sigma.z_partial = std(ROInets.Fisher_r_to_z(pcorr(:))); 69 | sigma.z_partial_reg = std(ROInets.Fisher_r_to_z(pcorrReg(:))); 70 | 71 | end%find_H0_permutation_distribution_width 72 | 73 | function newData = make_surrogate_data(envData, transform) 74 | %MAKE_SURROGATE_DATA generates one round of surrogates of ENVDATA 75 | nModes = ROInets.rows(envData); 76 | 77 | % make more normal 78 | if transform, envData = log(envData); end 79 | 80 | % phase randomisation 81 | for iMode = nModes:-1:1, 82 | newData(:,iMode) = ROInets.generate_phase_surrogates(envData(iMode,:)',false,0); % Note transposition 83 | end%for 84 | 85 | % undo transform 86 | if transform, newData = e.^newData; end 87 | end%make_surrogate_data 88 | 89 | function [r, pr, prR] = compute_correlations(data, Regularize) 90 | %COMPUTE_CORRELATIONS generates correlation, partial correlation and 91 | % regularized partial correlation 92 | % 93 | 94 | upperTriInds = triu(true(ROInets.cols(data)),1); 95 | 96 | rCov = cov(data); 97 | cr = corrcov(rCov, 1); 98 | cpr = ROInets.convert_precision_to_pcorr(ROInets.cholinv(rCov)); 99 | if Regularize.do, 100 | cprR = ROInets.convert_precision_to_pcorr(ROInets.dp_glasso(rCov, [], Regularize.rho)); 101 | else 102 | cprR = cpr; 103 | end%if 104 | 105 | r = cr(upperTriInds); 106 | pr = cpr(upperTriInds); 107 | prR = cprR(upperTriInds); 108 | end%compute_correlations 109 | -------------------------------------------------------------------------------- /+ROInets/generate_phase_surrogates.m: -------------------------------------------------------------------------------- 1 | function surrogate = generate_phase_surrogates(data, preserve_correlation,use_box_cox) 2 | %% Generate surrogate data though method 3 in Hurtado et al 2004. Statistical 3 | % method for detection of phase locking episodes in neural oscillations. J 4 | % Neurophysiol. 10.1152/jn.00853.2003. 5 | % 6 | % This scrambles the phase spectrum whilst preserving the amplitude spectrum 7 | % The surrogate data is rescaled to the same mean and standard deviation 8 | % 9 | % surrogate = generate_phase_surrogate(data, preserve_correlation, mean_term, std_term) 10 | % 11 | % INPUTS 12 | % - data is the original timeseries, [nsamples x nchannels] 13 | % - preserve_correlation - apply the same phase offset to each channel. False by default 14 | % - use_box_cox. false by default. If this is true, then boxcox1.m from OSL will be used to 15 | % transform the data. Each channel gets its own transformation. The inverse transform will 16 | % automatically be applied. Offsets are applied prior to doing the transform and inverse 17 | % transform, following Adam Baker's implementation. However, the final rescaling of the mean 18 | % could lead to the surrogate containing negative values, even if the original data does not. 19 | % This should be fine for checking correlations, since none of the rescalings affect the correlation 20 | % except that if a subsequent analysis assumes the surrogate envelopes are positive, this may fail 21 | % unless an additional offset is applied 22 | % 23 | % OUTPUTS 24 | % - surrogate - the surrogate time series, same size as data 25 | % 26 | % Romesh Abeysuriya Nov 2016 27 | % Andrew Quinn August 2015 28 | % Adjusted by GC to improve speed of fft 29 | 30 | if nargin < 3 || isempty(use_box_cox) 31 | use_box_cox = false; 32 | end 33 | 34 | if nargin < 2 || isempty(preserve_correlation) 35 | preserve_correlation = false; % Generate a different phase offset vector for each channel 36 | end 37 | 38 | [nSamples, nVars] = size(data); 39 | mean_term = mean(data); 40 | std_term = std(data,1); % Use N-1 standard deviation to match original code 41 | 42 | if use_box_cox 43 | if ~exist('boxcox1') 44 | error('boxcox1.m not found. Has OSL been added to the path?'); 45 | end 46 | 47 | lambda = nan(nVars,1); 48 | c = nan(nVars,1); 49 | for j = 1:nVars 50 | [~,lambda(j),c(j)] = boxcox1(data(:,j),0); 51 | data(:,j) = (data(:,1)+c(j)).^lambda(j); 52 | end 53 | end 54 | 55 | % get amplitude spectrum 56 | amp_spec = fft(data, nSamples,1); % no need to take abs as we can just add random phases 57 | 58 | % Generate phase noise 59 | % first element of spectrum is DC component, subsequent elements mirror 60 | % each other about the Nyquist frequency, if present 61 | n_components = floor((nSamples-1)/2); % Number of components that *aren't* DC or Nyquist 62 | if preserve_correlation 63 | noise = bsxfun(@times,ones(1,nVars),rand(n_components,1).* (2 * pi)); % Same phase shift for each channel 64 | else 65 | noise = rand(n_components, nVars) .* (2 * pi); 66 | end 67 | 68 | if rem(nSamples,2) % If odd number of samples, then Nyquist frequency is NOT present 69 | newPhase = [zeros(1,nVars); 1i .* noise; -1i .* flipud(noise)]; % second half uses conjugate phases, mirrored in order 70 | else % Otherwise, include zero phase shift for the Nyquist frequency 71 | newPhase = [zeros(1,nVars); 1i .* noise; zeros(1,nVars); -1i .* flipud(noise)]; % second half uses conjugate phases, mirrored in order 72 | end 73 | 74 | % Make new phase scrabled spectrum 75 | rand_spec = exp(newPhase).*amp_spec; 76 | 77 | % Create new time_course 78 | surrogate = ifft(rand_spec, nSamples); 79 | 80 | % If we rescaled the timeseries, undo the transformation, following 81 | % Adam's thesis where raising to the power of 1/lambda covers all cases 82 | if use_box_cox 83 | for j = 1:nVars 84 | surrogate(:,j) = surrogate(:,j) - min(surrogate(:,j)) + min(data(:,j)); 85 | surrogate(:,j) = surrogate(:,j).^(1/lambda(j)); 86 | end 87 | end 88 | 89 | % demean 90 | surrogate = bsxfun(@minus, surrogate, mean(surrogate)); 91 | 92 | % Normalise time_series 93 | surrogate = bsxfun(@times, surrogate, std_term./std(surrogate,1)); 94 | 95 | % Reset the mean 96 | surrogate = bsxfun(@plus, surrogate, mean_term); 97 | 98 | end%generate_phase_surrogates -------------------------------------------------------------------------------- /+ROInets/get_edges.m: -------------------------------------------------------------------------------- 1 | function e = get_edges(W) 2 | %GET_EDGES retrieves edges from network matrix 3 | % 4 | % E = GET_EDGES(W) retrieves the edges from square symmetric network matrix 5 | % W. 6 | 7 | % reshape W 8 | [nEdges, ~, nSubs] = size(W); 9 | edgeInd = repmat(triu(true(nEdges),1), [1 1 nSubs]); 10 | e = reshape(W(edgeInd), [], nSubs); 11 | end%get_edges -------------------------------------------------------------------------------- /+ROInets/glasso_frequentist.m: -------------------------------------------------------------------------------- 1 | function [P, W] = glasso_frequentist(S, rho, verbose) 2 | %GLASSO_FREQUENTIST graphical lasso for regularized precision matrix estimation 3 | % 4 | % P = GLASSO_FREQUENTIST(S, RHO) outputs the regularized precision 5 | % matrix, P, from the sample covariance matrix S, using L1 norm 6 | % regularization parameter RHO. 7 | % 8 | % [P, W] = GLASSO_FREQUENTIST(S, RHO) also returns the regularised 9 | % covariance matrix W. 10 | % 11 | % P = GLASSO_FREQUENTIST(S, RHO, VERBOSE) switches between three levels 12 | % of verbosity: choose between 0,1,2. 13 | % See also glasso_FTH, L1precisionBCD, glasso. 14 | 15 | % References: 16 | % Jerome Friedman, Trevor Hastie and Robert Tibshirani Sparse inverse covariance estimation with the graphical lasso. Biostatistics, December 12, 2007 17 | % 18 | % http://www.di.ens.fr/~mschmidt/Software/L1precision.html 19 | % http://statweb.stanford.edu/~tibs/glasso/index.html 20 | % http://www.stat.sc.edu/~wang345/RESEARCH/Bglasso/bglasso.html 21 | 22 | % This software builds heavily on contributions by Mark Schmidt 23 | % (L1precisionBCD) and Hao Wang (glasso_FTH). See references above. 24 | % Neither code package was released with a license, beyond author 25 | % acknowledgment. 26 | 27 | % Copyright 2014 OHBA 28 | % This program is free software: you can redistribute it and/or modify 29 | % it under the terms of the GNU General Public License as published by 30 | % the Free Software Foundation, either version 3 of the License, or 31 | % (at your option) any later version. 32 | % 33 | % This program is distributed in the hope that it will be useful, 34 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 35 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 | % GNU General Public License for more details. 37 | % 38 | % You should have received a copy of the GNU General Public License 39 | % along with this program. If not, see . 40 | 41 | 42 | % QPAS licence (called as subfunction): 43 | % +----------------------------------------------+ 44 | % | Written by Adrian Wills, | 45 | % | School of Elec. Eng. & Comp. Sci. | 46 | % | University of Newcastle, | 47 | % | Callaghan, NSW, 2308, AUSTRALIA | 48 | % | | 49 | % | Last Revised 25 May 2007. | 50 | % | | 51 | % | Copyright (C) Adrian Wills. | 52 | % +----------------------------------------------+ 53 | % 54 | % The current version of this software is free of charge and 55 | % openly distributed, BUT PLEASE NOTE: 56 | % 57 | % This software must be referenced when used in a published work. 58 | % 59 | % This software may not be re-distributed as a part of a commercial product. 60 | % If you distribute it in a non-commercial products, please contact me first, 61 | % to make sure you ship the most recent version. 62 | % 63 | % This software is distributed in the hope that it will be useful, 64 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 65 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 66 | % 67 | % IF IT FAILS TO WORK, IT'S YOUR LOSS AND YOUR PROBLEM. 68 | 69 | % $LastChangedBy: giles.colclough@gmail.com $ 70 | % $Revision: 214 $ 71 | % $LastChangedDate: 2014-07-24 12:40:42 +0100 (Thu, 24 Jul 2014) $ 72 | % Contact: giles.colclough@gmail.com 73 | % Originally written on: GLNXA64 by Giles Colclough, 19-Feb-2014 16:56:06 74 | 75 | % lock function in memory and define variable which will flag only on first 76 | % run 77 | mlock 78 | persistent PRINT_SPEED_WARNING 79 | 80 | if ~exist('PRINT_SPEED_WARNING', 'var') || isempty(PRINT_SPEED_WARNING), 81 | % This is the first time this function is run this session 82 | PRINT_SPEED_WARNING = true; 83 | end%if 84 | 85 | 86 | % check if no regularisation 87 | if 0 == rho, 88 | if nargout > 1, 89 | W = S; 90 | end%if 91 | P = pinv(S); 92 | return 93 | elseif ~isscalar(rho), 94 | error([mfilename ':NonScalarRho'], ... 95 | '%s: Expected scalar regularization parameter. \n', ... 96 | mfilename); 97 | end%if 98 | 99 | % Check for qp mex file 100 | if exist('qpas', 'file') == 3, 101 | useQP = 1; 102 | PRINT_SPEED_WARNING = false; 103 | else 104 | useQP = 0; 105 | end%if 106 | 107 | % Tell the user they should get compiled mex files 108 | if PRINT_SPEED_WARNING, 109 | warning([mfilename ':GetMexFiles'], ... 110 | ['%s will run much faster using the compiled qpas mex files ', ... 111 | 'by Adrian Wills. \n', ... 112 | 'They are obtainable under an attribution, non-commercial ', ... 113 | 'license from \n ', ... 114 | '', ... 115 | 'http://sigpromu.org/quadprog/index.html. \n'], ... 116 | mfilename); 117 | PRINT_SPEED_WARNING = false; % prevent from displaying on repeated runs 118 | end%if 119 | 120 | % switch off display 121 | if nargin < 3, verbose = 1; end 122 | 123 | if verbose == 2, 124 | fprintf(' Running glasso:\n'); 125 | end%if 126 | 127 | optTol = 1e-5; 128 | 129 | if useQP % use method from L1precisionBCD, a little stripped down 130 | 131 | p = size(S,1); 132 | maxIter = 10; 133 | A = [eye(p-1,p-1); -eye(p-1,p-1)]; 134 | f = zeros(p-1,1); 135 | 136 | % Initial W 137 | W = S + rho * eye(p,p); 138 | 139 | % qp parameters 140 | qpSolver = @qpas; 141 | qpArgs = {[],[],[],[]}; 142 | 143 | for iter = 1 : (maxIter+1), 144 | % Check Primal-Dual gap 145 | P = ROInets.cholinv(W); %W ^ -1; % W should be PD 146 | gap = trace(S*P) + rho * sum(sum(abs(P))) - p; 147 | if verbose == 2, 148 | fprintf(' Iter = %d, OptCond = %.5f\n', iter, gap); 149 | end%if 150 | if gap < optTol 151 | if verbose, 152 | fprintf(' glasso: Solution Found\n'); 153 | end%if 154 | return 155 | end%if 156 | 157 | for i = 1:p 158 | if verbose == 2, % this has the potential to cut short a run over all columns ... not necessarily a good idea... 159 | P = pinv(W); %W^-1; % W should be PD 160 | gap = trace(S*P) + rho * sum(sum(abs(P))) - p; 161 | fprintf(' Column = %d, OptCond = %.5f\n', i, gap); 162 | if gap < optTol 163 | fprintf(' glasso: Solution Found\n'); 164 | break 165 | end%if 166 | end%if 167 | 168 | % Compute Needed Partitions of W and S 169 | s_12 = S(ROInets.setdiff_pos_int(1:p, i), i); 170 | 171 | H = 2 * pinv(W(ROInets.setdiff_pos_int(1:p,i), ROInets.setdiff_pos_int(1:p,i))); 172 | b = rho * ones(2 * (p-1), 1) + [s_12; -s_12]; 173 | w = qpSolver((H + H')/2, f, A, b, qpArgs{:}); 174 | 175 | % Un-Permute 176 | W(ROInets.setdiff_pos_int(1:p,i), i) = w; 177 | W(i, ROInets.setdiff_pos_int(1:p,i)) = w'; 178 | end%for 179 | end%for 180 | 181 | else % use Wang's matlab method, as it seems to be a little faster 182 | 183 | % glasso_FTH(sample covariance matrix, rho: L1 regulariztion parameter) 184 | % called as: P = glasso_FTH(S, rho); 185 | 186 | % N.B. This algorithm may collapse in certain cases, especially when p large, as no positive 187 | % definite contraints are imposed in the glasso algorithm. 188 | 189 | % Written by Hao Wang @ U of South Carolina 190 | 191 | p = size(S,1); 192 | if isscalar(rho), 193 | rho = rho*ones(p); 194 | end%if 195 | 196 | avgSoff = mean(S(~eye(p))); % average of off-diagonal elements of empirical covariance matrix S 197 | 198 | t = 1e-4; % a fixed threshold ; 199 | 200 | % Initial value 201 | W = S + diag(diag(rho)); 202 | 203 | % Maximum number of iterations 204 | Max1 = 30; % across column 205 | Max2 = 30; % Within column, gradient descend 206 | 207 | for iter1 = 1:Max1 208 | if verbose == 2, 209 | fprintf(' Iter = %d\n',iter1); 210 | end%if 211 | 212 | W_old = W; 213 | 214 | for i = 1:p 215 | if i == 1 216 | ind_noi = (2:p)'; 217 | elseif i==p 218 | ind_noi = (1:p-1)'; 219 | else 220 | ind_noi = [1:i-1, i+1:p]'; 221 | end%if 222 | 223 | V = W(ind_noi,ind_noi); 224 | s12 = S(:,i); 225 | s12(i) = []; 226 | w12 = W(ind_noi,i); 227 | 228 | beta = V \ w12; 229 | 230 | % below Pathwise coordinate descent 231 | for iter2 = 1:Max2 232 | if verbose == 2, 233 | fprintf(' Column = %d, OptTol = %.5f\n', ... 234 | iter2, optTol); 235 | end%if 236 | 237 | beta_old = beta; 238 | % Speed-up trial which didn't work! 239 | % %%% GC replaces code below 240 | % j = (1:p-1)'; 241 | % x = s12(j) - V(j,:)*beta + diag(V(j,j)) .* beta(j); 242 | % 243 | % signx = sign(x); 244 | % signx(x==0) = rand(sum(x==0),1); %- this line was being called lots. Led to massive time increase and warnings about poor scaling in L60 beta = V\w12. 245 | % 246 | % beta(j) = max(0, abs(x) - rho(i, ind_noi(j))') .* signx ./ diag(V(j,j)); 247 | % %---replaces this 248 | for j = 1:p-1 249 | x = s12(j) - V(j,:) * beta + V(j,j) * beta(j); 250 | if 0 == x, 251 | signx = rand(1); 252 | else 253 | signx = sign(x); 254 | end%if 255 | 256 | beta(j) = max(0, abs(x) - rho(i,ind_noi(j))) ... 257 | * signx / V(j,j); 258 | end%for 259 | % END of GC trial 260 | 261 | if max(abs(beta_old - beta)) < optTol, 262 | break 263 | end%if 264 | end%for 265 | 266 | w12 = V * beta; 267 | W(i,ind_noi) = w12'; 268 | W(ind_noi,i) = w12; 269 | end%for 270 | 271 | chg = abs(W_old - W); 272 | 273 | if mean(chg(:)) < (t * avgSoff), 274 | break 275 | end%if 276 | end%for 277 | 278 | P = ROInets.cholinv(W); 279 | 280 | if verbose, 281 | fprintf(' glasso: Solution Found\n'); 282 | end%if 283 | end%if 284 | end%glasso_frequentist 285 | % [EOF] 286 | -------------------------------------------------------------------------------- /+ROInets/householder_orthogonalise.m: -------------------------------------------------------------------------------- 1 | function [O, Q, R, W] = householder_orthogonalise(A) 2 | %HOUSEHOLDER_ORTHOGONALISE orthogonalisation using householder method 3 | % O = HOUSEHOLDER_ORTHOGONALISE(A) Computes orthogonal set of vectors O 4 | % from input A. Vectors must form columns of A. 5 | % 6 | % [O, Q, R, W] = HOUSEHOLDER_ORTHOGONALISE(A) Computes orthonormal vectors 7 | % in Q, and R such that A = Q*R, and W such that O = A * W; 8 | % 9 | % See also: ROInets.symmetric_orthogonalise 10 | 11 | % Copyright 2013 OHBA 12 | % This program is free software: you can redistribute it and/or modify 13 | % it under the terms of the GNU General Public License as published by 14 | % the Free Software Foundation, either version 3 of the License, or 15 | % (at your option) any later version. 16 | % 17 | % This program is distributed in the hope that it will be useful, 18 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | % GNU General Public License for more details. 21 | % 22 | % You should have received a copy of the GNU General Public License 23 | % along with this program. If not, see . 24 | 25 | 26 | % $LastChangedBy: giles.colclough@gmail.com $ 27 | % $Revision: 239 $ 28 | % $LastChangedDate: 2014-08-15 14:58:49 +0100 (Fri, 15 Aug 2014) $ 29 | % Contact: giles.colclough@gmail.com 30 | % Originally written on: GLNXA64 by Giles Colclough, 06-Nov-2013 13:35:17 31 | 32 | [Q, R] = qr(A,0); 33 | 34 | O = Q * (diag(diag(R))); 35 | 36 | if nargout > 3, 37 | W = inv(R) * diag(diag(R)); %#ok inverse acceptable for upper triangular matrix 38 | end%if 39 | -------------------------------------------------------------------------------- /+ROInets/isposdef.m: -------------------------------------------------------------------------------- 1 | function b = isposdef(a) 2 | % ISPOSDEF Test for positive definite matrix. 3 | % ISPOSDEF(A) returns 1 if A is positive definite, 0 otherwise. 4 | % Using chol is much more efficient than computing eigenvectors. 5 | 6 | % Written by Tom Minka 7 | 8 | [~,p] = chol(a); 9 | b = (p == 0); 10 | -------------------------------------------------------------------------------- /+ROInets/logdet.m: -------------------------------------------------------------------------------- 1 | function v = logdet(A, op) 2 | %LOGDET Computation of logarithm of determinant of a matrix 3 | % 4 | % v = logdet(A); 5 | % computes the logarithm of determinant of A. 6 | % 7 | % Here, A should be a square matrix of double or single class. 8 | % If A is singular, it will returns -inf. 9 | % 10 | % Theoretically, this function should be functionally 11 | % equivalent to log(det(A)). However, it avoids the 12 | % overflow/underflow problems that are likely to 13 | % happen when applying det to large matrices. 14 | % 15 | % The key idea is based on the mathematical fact that 16 | % the determinant of a triangular matrix equals the 17 | % product of its diagonal elements. Hence, the matrix's 18 | % log-determinant is equal to the sum of their logarithm 19 | % values. By keeping all computations in log-scale, the 20 | % problem of underflow/overflow caused by product of 21 | % many numbers can be effectively circumvented. 22 | % 23 | % The implementation is based on LU factorization. 24 | % 25 | % v = logdet(A, 'chol'); 26 | % If A is positive definite, you can tell the function 27 | % to use Cholesky factorization to accomplish the task 28 | % using this syntax, which is substantially more efficient 29 | % for positive definite matrix. 30 | % 31 | % Remarks 32 | % ------- 33 | % logarithm of determinant of a matrix widely occurs in the 34 | % context of multivariate statistics. The log-pdf, entropy, 35 | % and divergence of Gaussian distribution typically comprises 36 | % a term in form of log-determinant. This function might be 37 | % useful there, especially in a high-dimensional space. 38 | % 39 | % Theoretially, LU, QR can both do the job. However, LU 40 | % factorization is substantially faster. So, for generic 41 | % matrix, LU factorization is adopted. 42 | % 43 | % For positive definite matrices, such as covariance matrices, 44 | % Cholesky factorization is typically more efficient. And it 45 | % is STRONGLY RECOMMENDED that you use the chol (2nd syntax above) 46 | % when you are sure that you are dealing with a positive definite 47 | % matrix. 48 | % 49 | % Examples 50 | % -------- 51 | % % compute the log-determinant of a generic matrix 52 | % A = rand(1000); 53 | % v = logdet(A); 54 | % 55 | % % compute the log-determinant of a positive-definite matrix 56 | % A = rand(1000); 57 | % C = A * A'; % this makes C positive definite 58 | % v = logdet(C, 'chol'); 59 | % 60 | 61 | % Copyright 2008, Dahua Lin, MIT 62 | % Email: dhlin@mit.edu 63 | % 64 | % This file can be freely modified or distributed for any kind of 65 | % purposes. 66 | % 67 | 68 | %% argument checking 69 | 70 | assert(isfloat(A) && ismatrix(A) && size(A,1) == size(A,2), ... 71 | 'logdet:invalidarg', ... 72 | 'A should be a square matrix of double or single class.'); 73 | 74 | if nargin < 2 75 | use_chol = 0; 76 | else 77 | assert(strcmpi(op, 'chol'), ... 78 | 'logdet:invalidarg', ... 79 | 'The second argument can only be a string ''chol'' if it is specified.'); 80 | use_chol = 1; 81 | end 82 | 83 | %% computation 84 | 85 | if use_chol 86 | v = 2 * sum(log(diag(chol(A)))); 87 | else 88 | [~, U, P] = lu(A); 89 | du = diag(U); 90 | c = det(P) * prod(sign(du)); 91 | v = log(c) + sum(log(abs(du))); 92 | end 93 | -------------------------------------------------------------------------------- /+ROInets/make_directory.m: -------------------------------------------------------------------------------- 1 | function [] = make_directory(directoryName) 2 | %MAKE_DIRECTORY Makes directory if not already existing - wrapper on mkdir 3 | % 4 | % MAKE_DIRECTORY(NAME) checks for the existence of directory NAME and 5 | % creates it if it doesn't exist. 6 | % Any errors are caught and displayed. 7 | % 8 | % See also MKDIR. 9 | 10 | % Copyright 2014 OHBA 11 | % This program is free software: you can redistribute it and/or modify 12 | % it under the terms of the GNU General Public License as published by 13 | % the Free Software Foundation, either version 3 of the License, or 14 | % (at your option) any later version. 15 | % 16 | % This program is distributed in the hope that it will be useful, 17 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | % GNU General Public License for more details. 20 | % 21 | % You should have received a copy of the GNU General Public License 22 | % along with this program. If not, see . 23 | 24 | 25 | % $LastChangedBy: giles.colclough@gmail.com $ 26 | % $Revision: 214 $ 27 | % $LastChangedDate: 2014-07-24 12:40:42 +0100 (Thu, 24 Jul 2014) $ 28 | % Contact: giles.colclough@gmail.com 29 | % Originally written on: GLNXA64 by Giles Colclough, 14-Apr-2014 16:53:27 30 | 31 | if ~isdir(directoryName), 32 | [success, errMsg, errId] = mkdir(directoryName); 33 | if ~success, 34 | error([mfilename ':' errId], ... 35 | ['Creation failed of directory: \n', ... 36 | ' %s\n\n ', ... 37 | 'Error message:\n%s\n'], ... 38 | directoryName, errMsg); 39 | end%if 40 | end%if 41 | 42 | end%make_directory 43 | -------------------------------------------------------------------------------- /+ROInets/mean_connectivity_statistic_group_level.m: -------------------------------------------------------------------------------- 1 | function [p, beta] = mean_connectivity_statistic_group_level(netmat, Settings, iContrast) 2 | %MEAN_CONNECTIVITY_STATISTIC_GROUP_LEVEL 3 | % 4 | % [p, beta] = mean_connectivity_statistic_group_level(netmat, Settings, iContrast) 5 | % 6 | % Tests for a difference in mean connectivity over all edges 7 | % 8 | % The model is set up by choosing a contrast from the group level 9 | % statistics contrasts in Settings. 10 | % 11 | % Netmat needs to contain an nNodes x nNodes x nSubjects matrix, e.g. 12 | % envCorrelation_z. 13 | % 14 | % Returns the parameter estimate from the contrast , beta 15 | % and the p-value associated with this under permutation 16 | 17 | meanConn = get_mean_connectivity(netmat); 18 | 19 | 20 | % Use NBS as a hacky shortcut to run the permuted GLM 21 | GLM = setup_glm(meanConn, Settings, iContrast); 22 | testStat = NBSglm(GLM); 23 | 24 | % parameter estimate and p-value 25 | [beta, p] = estimate(testStat, GLM); 26 | 27 | end 28 | 29 | 30 | 31 | function meanConn = get_mean_connectivity(netmat) 32 | % input dimensions 33 | [nNodes, checkMe, nSubjects] = size(netmat); 34 | assert(checkMe == nNodes, ... 35 | [mfilename ':BadNetmatInput'], ... 36 | 'Please input 3D netmat. \n'); 37 | 38 | triUpperInd = logical(triu(ones(nNodes), 1)); 39 | 40 | % extract mean connectivity for each subject 41 | for iSubject = nSubjects:-1:1, 42 | tmp = netmat(:,:,iSubject); 43 | meanConn(iSubject) = mean(tmp(triUpperInd)); 44 | end%for 45 | 46 | end%get_mean_connectivity 47 | 48 | function GLM = setup_glm(m, Settings, iContrast) 49 | %SETUP_GLM creates structure in appropriate size 50 | 51 | GLM.y = m(:); 52 | GLM.perms = 5000; 53 | GLM.X = Settings.GroupLevel.designMatrix; 54 | GLM.contrast = Settings.GroupLevel.contrasts(iContrast,:); 55 | GLM.test = 'ttest'; 56 | end%setup_glm 57 | 58 | function [b, p] = estimate(testStat, GLM) 59 | 60 | % compute the parameter estimate 61 | b = GLM.contrast * (GLM.X \ GLM.y); 62 | 63 | % compute p-value based on permutations of test statistic 64 | p = mean(testStat(1) <= testStat(2:end)); 65 | end%estimate -------------------------------------------------------------------------------- /+ROInets/network_based_statistic_group_level.m: -------------------------------------------------------------------------------- 1 | function [nComponents, adjacency, pVals] = network_based_statistic_group_level(netmat, Settings, iContrast, threshold, alpha) 2 | %network_based_statistic_group_level 3 | % 4 | % Runs a post-hoc NBS (Zalesky, 2012) on one netmat. 5 | % 6 | % NETWORK_BASED_STATISTIC_GROUP_LEVEL(NETMAT, SETTINGS, CONTRAST_NO, THRESH, ALPHA) runs on 7 | % the nNodes x nNodes x nSubjects netmat (e.g. envCorrelation_z) using 8 | % Settings structure, choosing contrast CONTRAST_NO from the group-level 9 | % design. 10 | % 11 | % THRESH is the cut-off for the Z-stat on each edge. Try 2? 12 | % ALPHA is the FWER significance level. 13 | % 14 | % 15 | % Sorry it's so clunky. 16 | % 17 | % You need the NBS toolbox to run this: https://sites.google.com/site/bctnet/comparison/nbs 18 | 19 | 20 | % Setup and run GLM 21 | GLM = setup_glm(netmat, Settings, iContrast); 22 | testStat = NBSglm(GLM); 23 | 24 | % show histogram of z-stats 25 | plot_z_and_threshold(testStat(1,:), threshold); 26 | 27 | % Setup and run stats 28 | STATS = setup_stats(testStat, ROInets.rows(netmat), threshold, alpha); 29 | [nComponents, sigComponents, pVals] = NBSstats(STATS); 30 | for iC = nComponents:-1:1, 31 | tmp = full(sigComponents{iC}); 32 | adjacency(:,:,iC) = tmp + tmp.'; 33 | end%for 34 | 35 | end%network_based_statistic_group_level 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | function GLM = setup_glm(netmat, Settings, iContrast) 46 | %SETUP_GLM creates structure in appropriate size 47 | 48 | GLM.y = reformat_netmat(netmat); 49 | GLM.perms = 5000; 50 | GLM.X = Settings.GroupLevel.designMatrix; 51 | GLM.contrast = Settings.GroupLevel.contrasts(iContrast,:); 52 | GLM.test = 'ttest'; 53 | end%setup_glm 54 | 55 | function STATS = setup_stats(testStat, nModes, threshold, alpha) 56 | %SETUP_STATS creates STATS parameters tructure for NBS 57 | STATS.thresh = threshold; % threshold for forming connected components 58 | STATS.alpha = alpha; % significance threshold for FWER 59 | STATS.N = nModes; 60 | STATS.test_stat = testStat; 61 | STATS.size = 'intensity'; % 'extent' or 'intensity' - measures either size of component, or total connectivity in component. 62 | end%setup_stats 63 | 64 | function y = reformat_netmat(data) 65 | % turn netmats into a single row, taking only upper triangular part 66 | 67 | [nNodes, checkMe, nSubjects] = size(data); 68 | assert(checkMe == nNodes, ... 69 | [mfilename ':BadNetmatInput'], ... 70 | 'Please input 3D netmat. \n'); 71 | 72 | triUpperInd = logical(triu(ones(nNodes), 1)); 73 | 74 | for iS = nSubjects:-1:1, 75 | tmp = data(:,:,iS); 76 | y(iS,:) = tmp(triUpperInd); 77 | end%for 78 | 79 | end%reformat_netmat 80 | 81 | function plot_z_and_threshold(z, threshold) 82 | figure('Name', 'Z-stats input to NBS', 'Color', 'w'); 83 | hist(z, 30); 84 | vline(threshold); 85 | xlabel('Z'); 86 | end%plot -------------------------------------------------------------------------------- /+ROInets/nii_parcel_quicksave.m: -------------------------------------------------------------------------------- 1 | function fileNameOut = nii_parcel_quicksave(data, parcelFlag, filename, varargin) 2 | %NII_PARCEL_QUICKSAVE Saves data in parcels as nifti 3 | % NII_PARCEL_QUICKSAVE(DATA, PARCELFLAG, FILENAME, OPTIONS) or 4 | % saves DATA in nifti file FILENAME using the spatial resolution specified in 5 | % OPTIONS.INPUT_SPAT_RES (in mm). See nii_quicksave for other OPTIONS. The 6 | % DATA form an (nParcels) x (nVolumes) matrix and PARCELFLAG (nVoxels) x 7 | % (nParcels) is a binary matrix identifying the membership of voxels in 8 | % parcels, or a matrix indicating the membership of each voxel in a 9 | % parcel. 10 | % OR: 11 | % NII_PARCEL_QUICKSAVE(DATA, PARCELFLAG, FILENAME, SPATIALRES, RESAMP, INTERP) 12 | % Is the old interface. Where is the output spatial resolution specified 13 | % (in mm). See nii_quicksave for other settings 14 | % 15 | % See also: NII_QUICKSAVE. 16 | 17 | 18 | % Copyright 2013 OHBA 19 | % This program is free software: you can redistribute it and/or modify 20 | % it under the terms of the GNU General Public License as published by 21 | % the Free Software Foundation, either version 3 of the License, or 22 | % (at your option) any later version. 23 | % 24 | % This program is distributed in the hope that it will be useful, 25 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | % GNU General Public License for more details. 28 | % 29 | % You should have received a copy of the GNU General Public License 30 | % along with this program. If not, see . 31 | 32 | 33 | % $LastChangedBy: giles.colclough@gmail.com $ 34 | % $Revision: 213 $ 35 | % $LastChangedDate: 2014-07-24 12:39:09 +0100 (Thu, 24 Jul 2014) $ 36 | % Contact: giles.colclough@gmail.com 37 | % Originally written on: GLNXA64 by Giles Colclough, 10-Dec-2013 10:07:57 38 | 39 | NII_MAX_SIZE = 32767; 40 | 41 | % check save location exists 42 | saveDir = fileparts(filename); 43 | if ~exist(saveDir, 'dir'), 44 | warning([mfilename ':CreatingSaveDirectory'], ... 45 | 'Save location did not exist. Creating directory \n %s\n', ... 46 | saveDir); 47 | ROInets.make_directory(saveDir); 48 | end%if 49 | 50 | % strip extension from filename - actually, don't. This takes apart names 51 | % with periods in the middle, but no explicit extension. 52 | % filename = fullfile(saveDir, fileStem); 53 | 54 | % check data sizes match 55 | [nVoxels, nParcels] = size(parcelFlag); 56 | assert(isequal(ROInets.rows(data), nParcels), ... 57 | [mfilename ':InputSizeMismatch'], ... 58 | 'data must have rows equal to number of columns in parcelFlag. \n'); 59 | 60 | % declare memory to ensure correct size 61 | rePackedData = zeros(nVoxels, ROInets.cols(data)); 62 | 63 | % repack data into voxel form 64 | if islogical(parcelFlag), 65 | disp('Computing maps from binary parcellation'); 66 | 67 | % check that each voxel is only a member of one parcel 68 | assert(~any(ROInets.row_sum(parcelFlag) > 1), ... 69 | [mfilename ':MultipleParcelOccupancy'], ... 70 | 'Each voxel can be a member of at most one parcel. \n'); 71 | 72 | for iParcel = nParcels:-1:1, 73 | insertInds = parcelFlag(:, iParcel); 74 | rePackedData(insertInds, :) = repmat(data(iParcel, :), ... 75 | sum(insertInds), 1); 76 | end; 77 | else 78 | disp('Computing maps from spatial basis weights'); 79 | rePackedData=parcelFlag*data; 80 | end; 81 | 82 | if ROInets.cols(rePackedData) < NII_MAX_SIZE, 83 | % save using osl function 84 | fileNameOut=nii.quicksave(rePackedData, filename, varargin{:}); 85 | 86 | else 87 | fprintf('Nii file limit exceeded, saving as .mat \n'); 88 | fileNameOut = [filename '.mat']; % overwrite previous extension 89 | save(fileNameOut, 'rePackedData', '-v7.3'); 90 | end%if 91 | 92 | end%nii_parcel_quicksave 93 | % [EOF] 94 | -------------------------------------------------------------------------------- /+ROInets/num_nodes.m: -------------------------------------------------------------------------------- 1 | function n = num_nodes(nEdges) 2 | %NUM_NODES returns the number of nodes in a network based on the number of 3 | %edges 4 | % 5 | % N = NUM_NODES(NEDGES) returns the number of nodes (variables) in a square 6 | % symmetric network matrix with NEDGES upper off-diagonal elements 7 | n = (sqrt(8*nEdges + 1) + 1) ./ 2; 8 | end%num_nodes -------------------------------------------------------------------------------- /+ROInets/p_to_z_two_tailed.m: -------------------------------------------------------------------------------- 1 | function z = p_to_z_two_tailed(p, sign_z) 2 | %Z_TO_P_TWO_TAILED convert p-value to standard z-value in two-tailed test 3 | % 4 | % Z = Z_TO_P_TWO_TAILED(P, SIGN_Z) converts P to standard Z-values, 5 | % interpreting P as a two-tailed p-value. Z values are returned with 6 | % signs provided in SIGN_Z. 7 | 8 | if nargin < 2 || isempty(sign_z), 9 | sign_z = 1; 10 | end%if 11 | 12 | assert(isscalar(sign_z) || all(size(p) == size(sign_z)), ... 13 | [mfilename ':IncompatibleInputSizes'], ... 14 | 'SIGN_Z should be the same size as P, or a scalar. \n'); 15 | assert(all(abs(sign_z(:)) == 1 | sign_z(:) == 0), ... 16 | [mfilename ':UnrecognisedSignInput'], ... 17 | 'SIGN_Z should contain +1s, -1s or zeros. \n'); 18 | 19 | z = sign_z .* norminv(1.0 - p./2.0 - eps, 0, 1); 20 | % [EOF] -------------------------------------------------------------------------------- /+ROInets/perform_glm_with_randomise.m: -------------------------------------------------------------------------------- 1 | function [Ttmp, ptmp, corrptmp, COPE] = perform_glm_with_randomise(edges, designMatrix, contrasts, standardise) 2 | %PERFORM_GLM_WITH_RANDOMISE 3 | % 4 | % [T, P, CORRP] = PERFORM_GLM_WITH_RANDOMISE(DATA, X, CONTRASTS, STANDARDISE) 5 | % runs a GLM of DATA = X Beta + noise. Returns T-statistics, uncorrected 6 | % and permutation-corrected p-values associated with the regression. 7 | % 8 | % [T, P, CORRP, COPE] = ... also returns the contrast of parameter 9 | % estimates from the regression. 10 | % 11 | % Randomise actually returns 1-p values in p and corrp. 12 | % 13 | % Note: randomise automatically demeans, so this is not helpful for finding 14 | % mean effect over all subjects / sessions 15 | 16 | nSessions = ROInets.cols(edges); 17 | 18 | [checkMe, nEVs] = size(designMatrix); 19 | assert(checkMe == nSessions, ... 20 | [mfilename ':BadDesign'], ... 21 | 'Design matrix must have as many rows as the data have columns. \n'); 22 | 23 | [nContrasts, checkMe] = size(contrasts); 24 | assert(checkMe == nEVs, ... 25 | [mfilename ':BadContrasts'], ... 26 | 'Contrasts must have as many columns as EVs in the design matrix. \n'); 27 | 28 | if nargin < 4 || ~exist('standardise', 'var'), 29 | standardise = false; 30 | else 31 | assert(islogical(standardise), ... 32 | [mfilename ':BadStandardise'], ... 33 | 'Standardise input must be a logical value. \n'); 34 | end%if 35 | 36 | %% Save out edges into nifti 37 | resultsDir = tempdir; 38 | inputNifti = fullfile(resultsDir, 'network_edges.nii.gz'); 39 | 40 | % check for NaNs - treat as subjects to be ignored. This is a good 41 | % representation for missing data. 42 | % 43 | % What will we do with NaNs? Maybe impute the value as the group mean? 44 | % Maybe EM imputation? 45 | % Instead, let's remove them outright. 46 | badSubjects = any(isnan(edges),1); 47 | cleanEdges = edges; 48 | cleanEdges(:,badSubjects) = []; 49 | 50 | for iS = ROInets.cols(cleanEdges):-1:1, 51 | formattedEdges(:,1,1,iS) = cleanEdges(:,iS); 52 | end 53 | save_avw(formattedEdges, inputNifti, 'f', [1 1 1 1]); 54 | Ci = onCleanup(@() delete(inputNifti)); 55 | 56 | 57 | %% Construct design matrix 58 | designMatrix(badSubjects,:) = []; 59 | if standardise, 60 | % demean and variance normalise 61 | X = bsxfun(@rdivide, bsxfun(@minus, designMatrix, mean(designMatrix)), ... 62 | std(designMatrix)); 63 | else 64 | X = designMatrix; 65 | end%if 66 | 67 | % save out 68 | designFile = fullfile(resultsDir, 'univariate_edge_test_design.mat'); 69 | ROInets.save_vest(X, designFile); 70 | Cd = onCleanup(@() delete(designFile)); 71 | 72 | %% Construct contrasts 73 | contrastFile = fullfile(resultsDir, 'univariate_edge_test_design.con'); 74 | ROInets.save_vest(contrasts, contrastFile); 75 | Cc = onCleanup(@() delete(contrastFile)); 76 | 77 | %% Run randomise 78 | outputNifti = fullfile(resultsDir, 'univariate_edge_test'); 79 | 80 | % call to randomise 81 | command = sprintf('randomise -i %s -o %s -d %s -t %s -x --norcmask', ... 82 | inputNifti, outputNifti, designFile, contrastFile); 83 | 84 | % submit to terminal 85 | ROInets.call_fsl_wrapper(command); 86 | 87 | Co = onCleanup(@() delete([outputNifti '*.nii.gz'])); 88 | 89 | %% Produce nice COPEs for each edge 90 | % a cope is the difference in mean Z-converted correlations between each 91 | % group 92 | pinvxtx = pinv(designMatrix' * designMatrix); 93 | pinvx = pinvxtx * designMatrix'; 94 | 95 | for iEdge = ROInets.rows(edges):-1:1, 96 | COPE(iEdge,:) = contrasts * pinvx * edges(iEdge, :).'; 97 | end%for 98 | 99 | %% Retrieve results 100 | for iCon = nContrasts:-1:1, 101 | TstatFile{iCon} = [outputNifti '_tstat' num2str(iCon) '.nii.gz']; 102 | pFile{iCon} = [outputNifti '_vox_p_tstat' num2str(iCon) '.nii.gz']; 103 | corrpFile{iCon} = [outputNifti '_vox_corrp_tstat' num2str(iCon) '.nii.gz']; 104 | 105 | Ttmp(:,iCon) = read_avw (TstatFile{iCon}); 106 | ptmp(:,iCon) = read_avw (pFile{iCon}); 107 | corrptmp(:,iCon) = read_avw (corrpFile{iCon}); 108 | end%for 109 | end%perform_glm_with_randomise 110 | % [EOF] -------------------------------------------------------------------------------- /+ROInets/pli.m: -------------------------------------------------------------------------------- 1 | function c = pli(phase) 2 | % Take in phase timecourses and compute phase lag index 3 | % 4 | % INPUT 5 | % - phase - phase timecourse, n_signals x n_times 6 | % 7 | % OUTPUT 8 | % - c - PLI connectivity matrix 9 | % 10 | % EXAMPLE USAGE 11 | % 12 | % pli(phase) 13 | % 14 | % Romesh Abeysuriya 2017 15 | 16 | % Remove any NaNs (or Infs) 17 | clean = all(isfinite(phase),1); 18 | phase = phase(:,clean); 19 | 20 | assert(all(isfinite(phase(:)))); 21 | plv_out = zeros(size(phase,1)); 22 | [a,b] = meshgrid(1:size(phase,1)); 23 | a = triu(a,1); 24 | b = triu(b,1); 25 | a = a(:); 26 | b = b(:); 27 | idx = find(a~=0 & b~=0); 28 | 29 | c = zeros(size(phase,1)); 30 | phase_diffs = zeros(size(phase,2),length(idx)); 31 | 32 | for l = 1:length(idx) 33 | phase_diffs(:,l) = phase(a(idx(l)),:)-phase(b(idx(l)),:); 34 | end 35 | 36 | tmp = abs(mean(sign(sin(phase_diffs)),1)); 37 | 38 | for l = 1:length(idx) 39 | c(a(idx(l)),b(idx(l))) = tmp(l); 40 | end 41 | 42 | c = tril(c)+tril(c)'; 43 | 44 | 45 | -------------------------------------------------------------------------------- /+ROInets/plot_group_level_results.m: -------------------------------------------------------------------------------- 1 | function plot_group_level_results(correlationMats, Settings, netType, correctionType, ROInames, ROIorder, firstLevelConsToPlot, groupLevelConsToPlot) 2 | %PLOT_GROUP_LEVEL_RESULTS 3 | % 4 | % PLOT_GROUP_LEVEL_RESULTS(CORRELATIONMATS, SETTINGS, NETTYPE, CORRECTIONTYPE, 5 | % ROINAMES, ROIORDER, FIRSTLEVELCONSTOPLOT, 6 | % GROUPLEVELCONSTOPLOT) 7 | % plots results from group-level network analysis. 8 | % 9 | % Takes in CORRELATIONMATS, output from run_network_analysis. Plots 10 | % heatmaps of significance using multiple comparisons CORRECTIONTYPE 11 | % for network analysis method NETTYPE. Plots will be labelled using cell 12 | % array ROINAMES, with optional permutation of ROIs ROIORDER. Chooses 13 | % which FIRSTLEVELCONSTOPLOT and GROUPLEVELCONSTOPLOT. 14 | % 15 | % The last four options can be omitted or set to [] to receive defaults 16 | % 17 | % NETTYPE: - 'correlation' 18 | % - 'partialCorrelation' 19 | % - 'partialCorrelationRegularized' 20 | % 21 | % CORRECTIONTYPE - 'T' - plot T-stats 22 | % - 'p' - plot uncorrected p-values 23 | % - 'FDRh' - threshold uncorrected p-values at FDR = 0.05 24 | % - 'FWEp' - plot family-wise error corrected p-values 25 | % 26 | % Be aware: p-values are displayed as -log10(p). 27 | % e.g. 0.05 -> 1.3 28 | % 0.01 -> 2 29 | % 0.001 -> 3 30 | % 31 | % 32 | % Example usage: 33 | % 34 | % fmri_d100reduced_labels(); 35 | % ROInets.plot_grou_level_results(correlationmats, Settings, 'correlation', 'FWEp', LABELS(NEW_ORDER), NEW_ORDER); 36 | 37 | %% parse inputs 38 | if ~iscell(correlationMats), 39 | error([mfilename ':NonCellInput'], ... 40 | 'Expecting a cell array of structures as first input. \n'); 41 | end%if 42 | 43 | 44 | isTask = isfield(correlationMats{1}, 'firstLevel') ... 45 | && strcmpi(Settings.paradigm, 'task'); 46 | 47 | nFreqs = Settings.nFreqBands; 48 | nFreqsCheck = length(correlationMats); 49 | nGroupLevelContrasts = ROInets.rows(Settings.GroupLevel.contrasts); 50 | nGroupLevelContrastsCheck = size(correlationMats{1}.groupLevel(1).correlation.T,3); 51 | 52 | assert(nFreqsCheck == nFreqs && ... 53 | nGroupLevelContrastsCheck == nGroupLevelContrasts, ... 54 | [mfilename ':MatSettingsMismatch'], ... 55 | 'The Settings file and CorrelationMats file do not seem to match up. \n'); 56 | 57 | if isTask, 58 | nROIs = ROInets.rows(correlationMats{1}.firstLevel(1).cope.correlation); 59 | else 60 | nROIs = ROInets.rows(correlationMats{1}.correlation); 61 | end%if 62 | 63 | validatestring(netType, {'correlation', 'partialCorrelation', ... 64 | 'partialCorrelationRegularized'}, ... 65 | mfilename, 'netType', 3); 66 | validatestring(correctionType, {'T', 'p', 'FDRh', 'FWEp'}, ... 67 | mfilename, 'correctionType', 4); 68 | if nargin >= 5 && ~isempty(ROInames), 69 | assert((iscell(ROInames) && length(ROInames) == nROIs)); 70 | else 71 | ROInames = 1:nROIs; 72 | end%if 73 | if nargin >= 6 && ~isempty(ROIorder), 74 | assert(length(ROIorder) == nROIs); 75 | else 76 | ROIorder = 1:nROIs; 77 | end%if 78 | if nargin >= 8 && ~isempty(groupLevelConsToPlot), 79 | assert(all(ismember(groupLevelConsToPlot, 1:nGroupLevelContrasts))); 80 | else 81 | groupLevelConsToPlot = 1:nGroupLevelContrasts; 82 | end%if 83 | 84 | 85 | FONTSIZE = 12; % MP edit from 15 86 | 87 | 88 | if isTask, 89 | nFirstLevelContrasts = length(Settings.SubjectLevel.contrasts); 90 | nFirstLevelContrastsCheck = length(correlationMats{1}.firstLevel); 91 | assert(nFirstLevelContrastsCheck == nFirstLevelContrasts, ... 92 | [mfilename ':FirstLevelMismatch'], ... 93 | 'The Settings file and CorrelationMats file do not seem to match up. \n'); 94 | if nargin >= 7 && ~isempty(firstLevelConsToPlot), 95 | assert(all(ismember(firstLevelConsToPlot, 1:nFirstLevelContrasts))); 96 | else 97 | firstLevelConsToPlot = 1:nFirstLevelContrasts; 98 | end%if 99 | 100 | %% Let's plot away 101 | for iFreq = 1:nFreqs, 102 | for iConFirst = firstLevelConsToPlot, 103 | matBase = correlationMats{iFreq}.groupLevel(iConFirst).(netType); 104 | for iConGroup = groupLevelConsToPlot, 105 | CAXIS = [0 3]; 106 | switch correctionType 107 | case 'T' 108 | plotMat = matBase.(correctionType)(ROIorder,ROIorder,iConGroup); 109 | CAXIS = [0 15]; 110 | 111 | case {'p', 'FWEp'} 112 | plotMat = -log10(matBase.(correctionType)(ROIorder,ROIorder,iConGroup)); 113 | 114 | case 'FDRh' 115 | % threshold original p-values by FDR-corrected bound 116 | h = matBase.(correctionType)(ROIorder,ROIorder,iConGroup); 117 | p = matBase.p(ROIorder,ROIorder,iConGroup); 118 | threshP = p; 119 | threshP(p~=h) = 1; 120 | plotMat = -log10(threshP); 121 | otherwise 122 | error([mfilename ':BadCorrectionType'], ... 123 | 'Unrecognised correction type. \n'); 124 | end 125 | plotTitle = sprintf('Freq band %d, First level contrast %d, Group level contrast %d, %s %s results', ... 126 | iFreq, iConFirst, iConGroup, netType, correctionType); 127 | figure('Name', plotTitle, 'Color', 'w'); 128 | imagesc(plotMat(ROIorder,ROIorder)); 129 | set(gca, 'YTick', 1:nROIs, 'YTickLabel', ROInames); 130 | set(gca,... 131 | 'FontName', 'Helvetica', ... 132 | 'FontSize', FONTSIZE, ... 133 | 'Box', 'on', ... 134 | 'YGrid', 'off', ... 135 | 'XGrid', 'off', ... 136 | 'TickDir', 'in', ... 137 | 'TickLength', [0.005 0.005], ... 138 | 'XMinorTick', 'off', ... 139 | 'YMinorTick', 'off', ... 140 | 'XColor', [0.3 0.3 0.3], ... 141 | 'YColor', [0.3 0.3 0.3], ... 142 | 'LineWidth', 2); 143 | caxis(CAXIS); 144 | colormap(bluewhitered); 145 | axis square 146 | colorbar; 147 | end%for 148 | end%for 149 | end%for 150 | 151 | else % not task 152 | %% Let's plot away 153 | for iFreq = 1:nFreqs, 154 | matBase = correlationMats{iFreq}.groupLevel.(netType); 155 | for iConGroup = groupLevelConsToPlot, 156 | CAXIS = [0 3]; 157 | switch correctionType 158 | case 'T' 159 | plotMat = matBase.(correctionType)(ROIorder,ROIorder,iConGroup); 160 | CAXIS = [0 15]; 161 | 162 | case {'p', 'FWEp'} 163 | plotMat = -log10(matBase.(correctionType)(ROIorder,ROIorder,iConGroup)); 164 | 165 | case 'FDRh' 166 | % threshold original p-values by FDR-corrected bound 167 | h = matBase.(correctionType)(ROIorder,ROIorder,iConGroup); 168 | p = matBase.p(ROIorder,ROIorder,iConGroup); 169 | threshP = p; 170 | threshP(p~=h) = 1; 171 | plotMat = -log10(threshP); 172 | otherwise 173 | error([mfilename ':BadCorrectionType'], ... 174 | 'Unrecognised correction type. \n'); 175 | end 176 | plotTitle = sprintf('Freq band %d, Group level contrast %d, %s %s results', ... 177 | iFreq, iConGroup, netType, correctionType); 178 | figure('Name', plotTitle, 'Color', 'w'); 179 | imagesc(plotMat(ROIorder,ROIorder)); 180 | set(gca, 'YTick', 1:nROIs, 'YTickLabel', ROInames); 181 | set(gca, 'XTick', 1:nROIs, 'XTickLabel', ROInames); 182 | set(gca,... 183 | 'FontName', 'Helvetica', ... 184 | 'FontSize', FONTSIZE, ... 185 | 'Box', 'on', ... 186 | 'YGrid', 'off', ... 187 | 'XGrid', 'off', ... 188 | 'TickDir', 'in', ... 189 | 'TickLength', [0.005 0.005], ... 190 | 'XMinorTick', 'off', ... 191 | 'YMinorTick', 'off', ... 192 | 'XColor', [0.3 0.3 0.3], ... 193 | 'YColor', [0.3 0.3 0.3], ... 194 | 'LineWidth', 2); 195 | caxis(CAXIS); 196 | colormap(bluewhitered); 197 | axis square 198 | colorbar; 199 | end%for 200 | end%for 201 | end%if istask 202 | 203 | end%plot_froup_level_results 204 | % [EOF] 205 | -------------------------------------------------------------------------------- /+ROInets/plv.m: -------------------------------------------------------------------------------- 1 | function c = plv(phase) 2 | % Take in phase timecourses and compute phase lag index 3 | % 4 | % INPUT 5 | % - phase - phase timecourse, n_signals x n_times 6 | % 7 | % OUTPUT 8 | % - c - PLV connectivity matrix 9 | % 10 | % EXAMPLE USAGE 11 | % 12 | % plv(phase) 13 | % 14 | % Romesh Abeysuriya 2017 15 | 16 | % Remove any NaNs (or Infs) 17 | clean = all(isfinite(phase),1); 18 | phase = phase(:,clean); 19 | 20 | assert(all(isfinite(phase(:)))); 21 | plv_out = zeros(size(phase,1)); 22 | [a,b] = meshgrid(1:size(phase,1)); 23 | a = triu(a,1); 24 | b = triu(b,1); 25 | a = a(:); 26 | b = b(:); 27 | idx = find(a~=0 & b~=0); 28 | 29 | c = zeros(size(phase,1)); 30 | phase_diffs = zeros(size(phase,2),length(idx)); 31 | 32 | for l = 1:length(idx) 33 | phase_diffs(:,l) = phase(a(idx(l)),:)-phase(b(idx(l)),:); 34 | end 35 | 36 | tmp = abs(mean(exp(1i*phase_diffs),1)); 37 | 38 | for l = 1:length(idx) 39 | c(a(idx(l)),b(idx(l))) = tmp(l); 40 | end 41 | 42 | c = tril(c)+tril(c)'; 43 | 44 | -------------------------------------------------------------------------------- /+ROInets/randgamma.m: -------------------------------------------------------------------------------- 1 | function x = randgamma(shape, scale) 2 | %GC_RANDGAMMA random sample from gamma distribution with shape and scale 3 | % 4 | % X = GC_RANDGAMMA(A, B) returns a matrix, the same size as A and B, where 5 | % X(i,j) is a sample from a Gamma(A(i,j), B(i,j)) distribution. 6 | % 7 | % A and B must be the same size. 8 | % 9 | % Gamma(a,b) has density function p(x) = x^(a-1) * exp(-x/b) ... 10 | % / (b^(a) * gamma(a)). 11 | % 12 | % Mean: a*b 13 | % Variance: a*b^2 14 | % Skewness: 2/sqrt(a) 15 | % Kurtosis: 6/a 16 | % Mode: b*(a-1) 17 | % 18 | % Same pdf as Matlab's gamrnd 19 | % 20 | % Tom Minka's lightspeed toolbox must be installed, compiled, and on the 21 | % Matlab path. 22 | % 23 | % See also gamrnd, randg, randgamma. 24 | 25 | % References: 26 | % http://en.wikipedia.org/wiki/Gamma_distribution#Scaling 27 | 28 | 29 | % Copyright 2014 Giles Colclough 30 | % This program is free software: you can redistribute it and/or modify 31 | % it under the terms of the GNU General Public License as published by 32 | % the Free Software Foundation, either version 3 of the License, or 33 | % (at your option) any later version. 34 | % 35 | % This program is distributed in the hope that it will be useful, 36 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 37 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 38 | % GNU General Public License for more details. 39 | % 40 | % You should have received a copy of the GNU General Public License 41 | % along with this program. If not, see . 42 | 43 | 44 | % $LastChangedBy$ 45 | % $Revision$ 46 | % $LastChangedDate$ 47 | % Contact: giles.colclough@gmail.com 48 | % Originally written on: GLNXA64 by Giles Colclough, 19-Feb-2014 16:42:31 49 | mlock 50 | persistent USE_LIGHTSPEED 51 | 52 | if ~exist('USE_LIGHTSPEED', 'var'), 53 | % check for lightspeed mex file 54 | USE_LIGHTSPEED = (3 == exist('randgamma', 'file')); % this check is slow if in a loop 55 | end%if 56 | 57 | assert(isequal(size(shape), size(scale)), ... 58 | [mfilename ':InconsistentInputDimensions'], ... 59 | 'Input dimensions must be the same size. \n'); 60 | 61 | % Check for lightspeed mex file 62 | if USE_LIGHTSPEED, 63 | % This code replaces gamrnd(shape, scale) or scale.*randg(shape) 64 | % using Lightspeed Mex file from a compiled lightspeed toolbox (See Tom 65 | % Minka's website). 66 | x = scale .* randgamma(shape); 67 | 68 | else 69 | % native matlab 70 | x = scale .* randg(shape); 71 | end%if 72 | 73 | end%GC_randgamma 74 | -------------------------------------------------------------------------------- /+ROInets/read_vest.m: -------------------------------------------------------------------------------- 1 | function x = read_vest(filename) 2 | 3 | fp=fopen(filename,'r'); 4 | 5 | while(1) 6 | line = fgetl(fp); 7 | if(strcmp(line,'/Matrix')) 8 | break; 9 | end; 10 | end; 11 | 12 | str = fgetl(fp); 13 | x = read_vest_line(str); 14 | str = fgetl(fp); 15 | 16 | c=1; 17 | while(isstr(str)) 18 | c=c+1; 19 | x(c,:) = read_vest_line(str); 20 | str = fgetl(fp); 21 | end; 22 | 23 | fclose(fp); 24 | 25 | %%%%%%%%%%%%%%%% 26 | 27 | function val = read_vest_line(str) 28 | 29 | indDelim = sort([0,findstr(str,sprintf('\t')),findstr(str,sprintf(' '))]); 30 | val = zeros(1,length(indDelim)-1); 31 | s=0; 32 | 33 | for ctDelim = 1:length(indDelim)-1, 34 | s=s+1; 35 | val(s) = str2double(str(indDelim(ctDelim)+1:indDelim(ctDelim+1)-1)); 36 | end 37 | 38 | if any(isnan(val)), 39 | val=NaN; 40 | end 41 | 42 | -------------------------------------------------------------------------------- /+ROInets/reformat_results.m: -------------------------------------------------------------------------------- 1 | function correlationMats = reformat_results(mats, Settings) 2 | %REFORMAT_RESULTS move session correlation mats to frequency band mats 3 | % 4 | % FREQ_MATS = REFORMAT_RESULTS(MATS, SETTINGS) moves session-specific 5 | % correlation matrices in MATS{iSession}{iFrequency}.correlationMatrix 6 | % into a new format, 7 | % FREQ_MATS{iFrequency}.correlationMatrix(:,:,iSession). The SETTINGS 8 | % structure from oil.ROInetworks must be provided. 9 | 10 | % Copyright 2014 OHBA 11 | % This program is free software: you can redistribute it and/or modify 12 | % it under the terms of the GNU General Public License as published by 13 | % the Free Software Foundation, either version 3 of the License, or 14 | % (at your option) any later version. 15 | % 16 | % This program is distributed in the hope that it will be useful, 17 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | % GNU General Public License for more details. 20 | % 21 | % You should have received a copy of the GNU General Public License 22 | % along with this program. If not, see . 23 | 24 | 25 | % $LastChangedBy: adambaker86@gmail.com $ 26 | % $Revision: 261 $ 27 | % $LastChangedDate: 2014-10-20 20:19:04 +0100 (Mon, 20 Oct 2014) $ 28 | % Contact: giles.colclough@gmail.com 29 | % Originally written on: GLNXA64 by Giles Colclough, 11-Apr-2014 12:14:41 30 | 31 | for iSession = Settings.nSessions:-1:1, 32 | for iFreq = Settings.nFreqBands:-1:1, 33 | if strcmpi(Settings.paradigm, 'rest'), 34 | % reformat all of the results 35 | correlationMats{iFreq}.correlation(:,:,iSession) = mats{iSession}{iFreq}.correlation; 36 | correlationMats{iFreq}.envCorrelation(:,:,iSession) = mats{iSession}{iFreq}.envCorrelation; 37 | correlationMats{iFreq}.envCovariance(:,:,iSession) = mats{iSession}{iFreq}.envCovariance; 38 | correlationMats{iFreq}.envPrecision(:,:,iSession) = mats{iSession}{iFreq}.envPrecision; 39 | correlationMats{iFreq}.envPartialCorrelation(:,:,iSession) = mats{iSession}{iFreq}.envPartialCorrelation; 40 | correlationMats{iFreq}.envCorrelation_z(:,:,iSession) = mats{iSession}{iFreq}.envCorrelation_z; 41 | correlationMats{iFreq}.envPartialCorrelation_z(:,:,iSession) = mats{iSession}{iFreq}.envPartialCorrelation_z; 42 | if Settings.Regularize.do, 43 | correlationMats{iFreq}.envPartialCorrelationRegularized(:,:,iSession) = mats{iSession}{iFreq}.envPartialCorrelationRegularized; 44 | correlationMats{iFreq}.envPrecisionRegularized(:,:,iSession) = mats{iSession}{iFreq}.envPrecisionRegularized; 45 | correlationMats{iFreq}.envPartialCorrelationRegularized_z(:,:,iSession) = mats{iSession}{iFreq}.envPartialCorrelationRegularized_z; 46 | correlationMats{iFreq}.Regularization(iSession) = mats{iSession}{iFreq}.Regularization; 47 | end%if 48 | end%if 49 | if isfield(mats{iSession}{iFreq}, 'ARmodel'), 50 | correlationMats{iFreq}.ARmodel(iSession) = mats{iSession}{iFreq}.ARmodel; 51 | end%if 52 | if isfield(mats{iSession}{iFreq}, 'H0Sigma'), 53 | correlationMats{iFreq}.H0Sigma(iSession) = mats{iSession}{iFreq}.H0Sigma; 54 | end%if 55 | correlationMats{iFreq}.nEnvSamples(iSession) = mats{iSession}{iFreq}.nSamples; 56 | correlationMats{iFreq}.sessionNames{iSession} = mats{iSession}{iFreq}.sessionName; 57 | end%for 58 | 59 | if strcmpi(Settings.paradigm, 'task'), 60 | % only store the results from the GLM. 61 | % if the user wants results averaged over all trials, they should 62 | % provide a contrast specifically for that. 63 | 64 | for iContrast = length(mats{1}{1}.firstLevel):-1:1, 65 | correlationMats{iFreq}.firstLevel(iContrast).cope.correlation(:,:,iSession) = mats{iSession}{iFreq}.firstLevel(iContrast).cope.correlation; 66 | correlationMats{iFreq}.firstLevel(iContrast).cope.partialCorrelation(:,:,iSession) = mats{iSession}{iFreq}.firstLevel(iContrast).cope.partialCorrelation; 67 | correlationMats{iFreq}.firstLevel(iContrast).cope.partialCorrelationRegularized(:,:,iSession) = mats{iSession}{iFreq}.firstLevel(iContrast).cope.partialCorrelationRegularized; 68 | correlationMats{iFreq}.firstLevel(iContrast).contrast = Settings.SubjectLevel.contrasts{iContrast}; 69 | correlationMats{iFreq}.firstLevel(iContrast).conditionLabel = Settings.SubjectLevel.conditionLabel; 70 | end%for 71 | end%if 72 | 73 | correlationMats{iFreq}.frequencyBand = Settings.frequencyBands{iFreq}; 74 | end%for 75 | 76 | 77 | 78 | end%reformat_results 79 | -------------------------------------------------------------------------------- /+ROInets/regression.m: -------------------------------------------------------------------------------- 1 | function beta = regression(y, X, nocheck) 2 | %REGRESSION Solves multivariate regression y = X*b + e using fast mex binaries 3 | % 4 | % BETA = regression(Y, X) solves 5 | % BETA = pinv(X) * Y <==> y = X * BETA + e, minimising lsq error on e, 6 | % using a fast qpas mex routine. 7 | % If X is not full rank, or Y is a matrix rather than a vector, the 8 | % function defaults to Matlab's pinv function 9 | % 10 | % BETA = regression(Y, X, 'nocheck') skips the check on the 11 | % rank of X, which is quicker if X is known to be full rank in advance. 12 | 13 | % References: 14 | % http://www.stat.colostate.edu/~meyer/hingerev2.pdf 15 | % 16 | 17 | % Never use beta = X\y: it is much slower than pinv route, and does not 18 | % handle incomplete rank cases in a sensible manner. 19 | 20 | % Copyright 2014 OHBA 21 | % This program is free software: you can redistribute it and/or modify 22 | % it under the terms of the GNU General Public License as published by 23 | % the Free Software Foundation, either version 3 of the License, or 24 | % (at your option) any later version. 25 | % 26 | % This program is distributed in the hope that it will be useful, 27 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | % GNU General Public License for more details. 30 | % 31 | % You should have received a copy of the GNU General Public License 32 | % along with this program. If not, see . 33 | 34 | 35 | % $LastChangedBy: giles.colclough@gmail.com $ 36 | % $Revision: 214 $ 37 | % $LastChangedDate: 2014-07-24 12:40:42 +0100 (Thu, 24 Jul 2014) $ 38 | % Contact: giles.colclough@gmail.com 39 | % Originally written on: GLNXA64 by Giles Colclough, 14-Apr-2014 12:24:16 40 | 41 | 42 | % lock function in memory and define variable which will flag only on first 43 | % run 44 | mlock 45 | persistent PRINT_SPEED_WARNING 46 | 47 | if ~exist('PRINT_SPEED_WARNING', 'var') || isempty(PRINT_SPEED_WARNING), 48 | % This is the first time this function is run this session 49 | PRINT_SPEED_WARNING = true; 50 | end%if 51 | 52 | % Check for qp mex file 53 | if exist('qpas', 'file') == 3, 54 | useQP = 1; 55 | PRINT_SPEED_WARNING = false; 56 | else 57 | useQP = 0; 58 | end%if 59 | 60 | % Tell the user they should get compiled mex files 61 | if PRINT_SPEED_WARNING, 62 | warning([mfilename ':GetMexFiles'], ... 63 | '% will run much faster using the compiled qpas mex files ', ... 64 | 'by Adrian Wills. \n', ... 65 | 'They are obtainable under an attribution, non-commercial ', ... 66 | 'license from \n http://sigpromu.org/quadprog/index.html. \n', ... 67 | mfilename); 68 | PRINT_SPEED_WARNING = false; % prevent from displaying on repeated runs 69 | end%if 70 | 71 | % check for rank deficiency 72 | if nargin >= 3 && strcmpi(nocheck, 'nocheck'), 73 | isRankDeficient = false; 74 | else 75 | isRankDeficient = ROInets.cols(X) > ROInets.rows(X) || rank(X) < ROInets.cols(X); 76 | end%if 77 | 78 | % solve problem 79 | if ~useQP || isRankDeficient || ROInets.cols(y) > 1, 80 | if isRankDeficient, 81 | fprintf(['%s - X is rank deficient: ', ... 82 | 'using pseudoinverse to calculate regression. \n'], ... 83 | mfilename); 84 | end%if 85 | 86 | % use pseudoinverse 87 | beta = pinv(X) * y; 88 | 89 | else 90 | % Reformat as quadratic programming problem, 91 | % minimise beta' H beta - 2 c' beta 92 | % for H = X'X and c = X'y 93 | 94 | % note - for y with more than one column, use pinv. For larger 95 | % problems, that is much faster than looping over columns and using qpas. 96 | 97 | H = X' * X; 98 | f = - X' * y; 99 | beta = qpas(H, f); 100 | end%if 101 | end%regression 102 | -------------------------------------------------------------------------------- /+ROInets/remove_source_leakage.m: -------------------------------------------------------------------------------- 1 | function nodeData = remove_source_leakage(nodeDataOrig, protocol) 2 | %REMOVE_SOURCE_LEAKAGE correct ROI time-courses for source leakage 3 | % 4 | % NODEDATA = REMOVE_SOURCE_LEAKAGE(NODEDATAORIG, PROTOCOL) 5 | % produces orthogonalised node time-courses NODEDATA from 6 | % uncorrected node time-courses NODEDATAORIG 7 | % 8 | % PROTOCOL is a string to switch between various all-to-all 9 | % orthogonalisation methods for source-spread correction. It can be: 10 | % 'none' - No correction applied. 11 | % 'symmetric' - Apply orthogonalisation on the parcel time-courses. 12 | % This produces orthonormal parcel time-courses 13 | % which are as close as possible to the original 14 | % time-courses. 15 | % 'closest' - Apply orthogonalisation on the parcel time-courses. 16 | % Start as for the symmetric method, then converge to 17 | % a (not orthonormal) orthogonal matrix which is as 18 | % close as possible to the original time-courses. 19 | % 'householder' - Orthogonalise using a more numerically stable 20 | % alternative to the Gram-Schmidt process, dealing 21 | % with ROI time-courses in a random order. 22 | % 23 | 24 | 25 | % Copyright 2014 OHBA 26 | % This program is free software: you can redirstribute it and/or modify 27 | % it under the terms of the GNU General Public License as published by 28 | % the Free Software Foundation, either version 3 of the License, or 29 | % (at your option) any later version. 30 | % 31 | % This program is distributed in the hope that it will be useful, 32 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 33 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 34 | % GNU General Public License for more details. 35 | % 36 | % You should have received a copy of the GNU General Public License 37 | % along with this program. If not, see . 38 | 39 | 40 | % $LastChangedBy: giles.colclough@gmail.com $ 41 | % $Revision: 235 $ 42 | % $LastChangedDate: 2014-08-07 22:09:15 +0100 (Thu, 07 Aug 2014) $ 43 | % Contact: giles.colclough 'at' magd.ox.ac.uk 44 | % Originally written on: GLNXA64 by Giles Colclough and Stephen Smith. 45 | 46 | switch protocol 47 | case 'none' 48 | % no orthogonalisation applied to parcel time-courses 49 | nodeData = nodeDataOrig; 50 | 51 | case 'closest' 52 | % finds closest orthogonal set of vectors by applying symmetric 53 | % orthogonalisation then iterating to find closest orthogonal matrix 54 | nodeData = find_closest_orthogonal_matrix(nodeDataOrig,@ROInets.closest_orthogonal_matrix,protocol); 55 | 56 | case 'symmetric' 57 | % finds closest orthonormal matrix 58 | nodeData = find_closest_orthogonal_matrix(nodeDataOrig,@ROInets.symmetric_orthogonalise,protocol); 59 | 60 | case 'householder' 61 | nodeData = find_orthogonal_matrix_by_householder_reflections(nodeDataOrig); 62 | 63 | otherwise 64 | error([mfilename ':UnrecognisedOrthMethod'], ... 65 | 'Unrecognised parcel orthogonalisation protocol. \n'); 66 | end%switch 67 | end%remove_source_leakage 68 | % ------------------------------------------------------------------------- 69 | function nodeData = find_closest_orthogonal_matrix(nodeDataOrig,orthogFunction,protocol) 70 | %FIND_CLOSEST_ORTHOGONAL_MATRIX wrapper on orthogonalisation functions 71 | nParcels = ROInets.rows(nodeDataOrig); 72 | 73 | if isa(nodeDataOrig, 'meeg') 74 | currentMontage = nodeDataOrig.montage('getmontage'); 75 | if isempty(currentMontage) 76 | name = sprintf('%s leakage correction',protocol); 77 | else 78 | name = sprintf('%s leakage correction - %s',protocol,currentMontage.name); 79 | end 80 | 81 | [~, ~, ~, W] = orthogFunction(transpose(nodeDataOrig(:,:))); 82 | nodeData = add_montage(nodeDataOrig, W',name,nodeDataOrig.chanlabels); 83 | else 84 | nodeData = transpose(orthogFunction(transpose(nodeDataOrig))); 85 | end%if 86 | 87 | end%find_closest_orthogonal_matrix 88 | % ------------------------------------------------------------------------- 89 | function nodeData = find_orthogonal_matrix_by_householder_reflections(nodeDataOrig, rankErrorMessage) 90 | %FIND_ORTHOGONAL_MATRIX_BY_HOUSEHOLDER_REFLECTIONS wrapper on householder 91 | % orthogonalisation 92 | 93 | nParcels = ROInets.rows(nodeDataOrig); 94 | 95 | isRankDeficient = rank(nodeDataOrig) < nParcels; 96 | if isRankDeficient 97 | rankErrorMessage = ['The ROI time-course matrix is not full rank. \n', ... 98 | ' This prevents you from using an all-to-all ', ... 99 | 'orthogonalisation method. \n', ... 100 | ' Your data have rank %d, and you are looking ', ... 101 | 'at %d ROIs. \n', ... 102 | ' You could try reducing the number of ROIs, or ', ... 103 | 'using an alternative orthogonalisation method. \n']; 104 | error('ROInets:RankError',rankErrorMessage,rank(nodeDataOrig), nParcels); 105 | end%if 106 | 107 | permutation = randperm(nParcels); 108 | permutationInverse(permutation) = 1:nParcels; 109 | 110 | if isa(nodeDataOrig, 'meeg') 111 | % bugger the permutation bit for this faff 112 | [~, ~, ~, W] = ROInets.householder_orthogonalise(nodeDataOrig(:,:).'); 113 | nodeData = add_montage(nodeDataOrig, W', 'householder',nodeDataOrig.chanlabels); 114 | else 115 | nodeData = ROInets.householder_orthogonalise(nodeDataOrig(permutation,:).').'; 116 | nodeData = nodeData(permutationInverse,:); 117 | end 118 | 119 | end%find_orthogonal_matrix_by_householder_reflections 120 | 121 | -------------------------------------------------------------------------------- /+ROInets/row_sum.m: -------------------------------------------------------------------------------- 1 | function s = row_sum(x) 2 | % ROW_SUM Sum for each row. 3 | % A faster and more readable alternative to sum(x,2). 4 | 5 | % unfortunately, this removes any sparseness of x. 6 | s = x*ones(ROInets.cols(x),1); 7 | 8 | -------------------------------------------------------------------------------- /+ROInets/rows.m: -------------------------------------------------------------------------------- 1 | function r = rows(x) 2 | % ROWS The number of rows. 3 | % ROWS is a more readable alternative to size(x,1). 4 | r = size(x,1); 5 | -------------------------------------------------------------------------------- /+ROInets/run_correlation_analysis.m: -------------------------------------------------------------------------------- 1 | function mats = run_correlation_analysis(nodeData, envData, Regularize) 2 | %RUN_CORRELATION_ANALYSIS runs various correlations on node data 3 | % MATS = RUN_CORRELATION_ANALYSIS(NODEDATA, ENVDATA) 4 | % produces matrices in structure MATS which give the correlation of the 5 | % raw node timecourses, NODEDATA, and the marginal correlation and 6 | % partial correlation of the envelope timecourses ENVDATA. 7 | % 8 | % MATS = RUN_CORRELATION_ANALYSIS(NODEDATA, ENVDATA, REGULARIZE) applies 9 | % regularization to the estimation of partial correlation matrices using 10 | % parameters set in REGULARIZE: 11 | % .do : true or false: controls use of 12 | % regularization. [false] 13 | % .method : 'Bayesian' or 'Friedman': use Wang (2012)'s 14 | % Bayesian graphical lasso or Friedman (2007)'s 15 | % graphical lasso 16 | % .path : path of regularization parameters controlling 17 | % the strength of the regularization, the best 18 | % value being found using 10-fold CV. 19 | % [Friedman only] 20 | % .adaptivePath : true or false: adapt path if the best 21 | % regularization parameter is on the edge of 22 | % the path [Friedman only] 23 | % .Prior : structure with fields controlling the shape 24 | % of the gamma(x; a, 1/b) hyperprior on the 25 | % regularization parameter. 26 | % If this field is not set, the default Kerman 27 | % (2011) neutral hyperprior is used (a=1/3, b=0) 28 | % [Bayesian only] 29 | % .a - shape 30 | % .b - 1/scale 31 | % There is no proper uninformative prior which is flat in 32 | % log x. Common minimally informative priors are a=epsilon, 33 | % b=epsilon, which is not flat in log x or proper in the 34 | % limit epsilon -> 0. It can be very informative in datasets 35 | % with small variances. 36 | % The Kerman prior is more uniform in log x and allows the 37 | % median of the posterior distribution to be specified by 38 | % the data alone. 39 | % 40 | % References on techniques used: 41 | % Friedman, J. and Hastie, T and Tibshirani, R. "Sparse inverse covariance 42 | % estimation with the graphical lasso", Biostatistics 9(3), 432-441, 43 | % (2008). 44 | % 45 | % Wang, H. "Bayesian graphical lasso models and efficient posterior 46 | % computation", Bayesian Analysis 7(2), 771-790 (2012). 47 | % 48 | % Kerman, J. "Neutral noninformative and informative conjugate beta and 49 | % gamma prior distributions", Electronic Journal of Statistics 5, 50 | % 1450-1470 (2011). 51 | % 52 | % Gelman, A. "Prior distributions for variance parameters in hierarchical 53 | % models", Bayesian Analysis 1(3), 515-533 (2006). 54 | 55 | 56 | % Copyright 2014 OHBA 57 | % This program is free software: you can redistribute it and/or modify 58 | % it under the terms of the GNU General Public License as published by 59 | % the Free Software Foundation, either version 3 of the License, or 60 | % (at your option) any later version. 61 | % 62 | % This program is distributed in the hope that it will be useful, 63 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 64 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 65 | % GNU General Public License for more details. 66 | % 67 | % You should have received a copy of the GNU General Public License 68 | % along with this program. If not, see . 69 | 70 | 71 | % $LastChangedBy: giles.colclough@gmail.com $ 72 | % $Revision: 368 $ 73 | % $LastChangedDate: 2014-12-13 19:05:24 +0000 (Sat, 13 Dec 2014) $ 74 | % Contact: giles.colclough 'at' magd.ox.ac.uk 75 | % Originally written on: GLNXA64 by Giles Colclough, 07-Nov-2013 12:43:45 76 | 77 | if nargin < 2 || isempty(Regularize), 78 | Regularize.do = false; 79 | end%if 80 | 81 | nTrials = size(envData, 3); 82 | 83 | if ~isempty(nodeData), 84 | % raw correlations 85 | for iTrial = size(nodeData,3):-1:1, 86 | rawCorr(:,:,iTrial) = corr(nodeData(:,:,iTrial)'); 87 | end 88 | clear nodeData 89 | else 90 | rawCorr = []; 91 | end%if 92 | 93 | fprintf(' Running correlation analysis on enveloped data: \n'); 94 | for iTrial = nTrials:-1:1, 95 | nodeCov(:,:,iTrial) = cov(real(envData(:,:,iTrial)')); 96 | nodeCorr(:,:,iTrial) = corrcov(nodeCov(:,:,iTrial)); 97 | nodePrecision(:,:,iTrial) = ROInets.cholinv(nodeCov(:,:,iTrial)); 98 | nodePCorr(:,:,iTrial) = ROInets.convert_precision_to_pcorr(nodePrecision(:,:,iTrial)); 99 | end%for 100 | 101 | nSamples = ROInets.cols(envData); 102 | 103 | mats.nSamples = nSamples; 104 | mats.correlation = rawCorr; 105 | mats.envCovariance = nodeCov; 106 | mats.envCorrelation = nodeCorr; 107 | mats.envPrecision = nodePrecision; 108 | mats.envPartialCorrelation = nodePCorr; 109 | 110 | if Regularize.do, 111 | switch Regularize.method 112 | case 'Bayesian' 113 | % do Bayesian Graphical L1 Lasso using Wang (2012)'s method and code 114 | fprintf(' Regularizing using Bayesian graphical lasso \n'); 115 | % default - use Kerman neutral hyperprior (2011) 116 | a_lambda = Regularize.Prior.a; % def: 1/3 117 | b_lambda = Regularize.Prior.b; % def: 0 118 | 119 | % MCMC parameters 120 | burnIn = 3000; %iterations 121 | nMC = 8000; %iterations 122 | 123 | for iTrial = nTrials:-1:1, 124 | [~, ... 125 | postPrecision, ... 126 | postLambda] = ROInets.BayesGLasso_Columnwise(nodeCov .* nSamples, ... 127 | nSamples, ... 128 | nodeCov, ... 129 | nodePrecision, ... 130 | a_lambda, ... 131 | b_lambda, ... 132 | burnIn, ... 133 | nMC); 134 | 135 | regPrec(:,:,iTrial) = mean(postPrecision, 3); 136 | regPCorr(:,:,iTrial) = ROInets.convert_precision_to_pcorr(regPrec); 137 | meanRho(:,:,iTrial) = mean(postLambda) ./ nSamples; % lambda = N * rho 138 | fprintf(' Mean regularization rho: %g. \n', meanRho); 139 | mats.Regularization.posteriorRho(:,:,iTrial) = postLambda ./nSamples; 140 | end%for 141 | 142 | mats.envPartialCorrelationRegularized = regPCorr; 143 | mats.envPrecisionRegularized = regPrec; 144 | mats.Regularization.mean = mean(meanRho,3); 145 | 146 | 147 | case 'Friedman' 148 | % do Friedman (2008)'s graphical lasso. May be faster. 149 | fprintf(' Regularizing using graphical lasso and x-validation \n'); 150 | Kfold = 10; 151 | for iTrial = nTrials:-1:1, 152 | [regPrecision(:,:,iTrial), rhoOpt(:,:,iTrial)] = ... 153 | ROInets.glasso_cv(real(normalise_vectors( ... 154 | ROInets.demean(envData(:,:,iTrial),2),2)), ... 155 | Regularize.path, ... 156 | Kfold, ... 157 | [], ... 158 | Regularize.adaptivePath); 159 | 160 | regPCorr(:,:,iTrial) = ROInets.convert_precision_to_pcorr(regPrecision(:,:,iTrial)); 161 | end%for 162 | mats.envPrecisionRegularized = regPrecision;% this is going to be scaled. Sorry. 163 | mats.envPartialCorrelationRegularized = regPCorr; 164 | mats.Regularization.mean = mean(rhoOpt,3); 165 | 166 | otherwise % not one of these methods 167 | error([mfilename ':UnrecognisedRegMethod'], ... 168 | 'Unrecognised regularization method: %s. \n', ... 169 | Regularize.method); 170 | end%if 171 | 172 | else 173 | mats.Regularization.mean = 0; 174 | end%if 175 | 176 | end%run_correlation_analysis 177 | 178 | function VV = normalise_vectors(V, dim) 179 | %NORMALISE_VECTORS normalises rows or columns of a matrix 180 | % 181 | % NORMV = GC_NORMALISE_VECTORS(V, DIM) normalises vectors along dimension 182 | % DIM of V. If DIM=1, this treats columns as vectors and if DIM=2, this 183 | % treats rows as vectors. 184 | % 185 | % NORMV = GC_NORMALISE_VECTORS(V) is the same as GC_NORMALISE_VECTORS(V, 1) 186 | % 187 | % Example: 188 | % If X = [3 3 3] 189 | % Then GC_NORMALISE_VECTORS(X,1) is [1 1 1] and GC_NORMALISE_VECTORS(X,2) 190 | % is [0.5774 0.5774 0.5774]. 191 | 192 | 193 | % Copyright 2013 Giles Colclough 194 | % This program is free software: you can redistribute it and/or modify 195 | % it under the terms of the GNU General Public License as published by 196 | % the Free Software Foundation, either version 3 of the License, or 197 | % (at your option) any later version. 198 | % 199 | % This program is distributed in the hope that it will be useful, 200 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 201 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 202 | % GNU General Public License for more details. 203 | % 204 | % You should have received a copy of the GNU General Public License 205 | % along with this program. If not, see . 206 | 207 | 208 | % $LastChangedBy: GilesColclough $ 209 | % $Revision: $ 210 | % $LastChangedDate: $ 211 | % Contact: giles.colclough 'at' eng.ox.ac.uk 212 | % Originally written on: GLNXA64 by Giles Colclough, 25-Nov-2013 11:49:41 213 | 214 | if nargin < 2 || ~exist('dim', 'var') || isempty(dim), 215 | dim = 1; 216 | end%if 217 | 218 | VV = bsxfun(@rdivide, V, sqrt(sum(V.^2, dim) ./ size(V, dim))); 219 | end%normalise_vectors 220 | % [EOF] 221 | -------------------------------------------------------------------------------- /+ROInets/save_vest.m: -------------------------------------------------------------------------------- 1 | function write_vest(x,filename) 2 | 3 | % write_vest(x,filename) 4 | % 5 | % writes x in vest format where size(x) is ntpts*nevs (NumPoints*NumWaves) 6 | 7 | fp=fopen(filename,'w'); 8 | 9 | dims = size(x); 10 | 11 | fprintf(fp,'! VEST-Waveform File\n'); 12 | fprintf(fp,'/NumWaves\t%i\n',dims(2)); 13 | fprintf(fp,'/NumPoints\t%i\n',dims(1)); 14 | fprintf(fp,'/Skip\n'); 15 | fprintf(fp,'\n/Matrix\n'); 16 | 17 | for t=1:dims(1), 18 | for e=1:dims(2), 19 | fprintf(fp,'%e\t',x(t,e)); 20 | end; 21 | fprintf(fp,'\n'); 22 | end; 23 | 24 | fclose(fp); 25 | -------------------------------------------------------------------------------- /+ROInets/scale_cols.m: -------------------------------------------------------------------------------- 1 | function y = scale_cols(x, s) 2 | % SCALE_COLS Scale each column of a matrix. 3 | % SCALE_COLS(x,s) returns matrix y, same size as x, such that 4 | % y(:,i) = x(:,i)*s(i) 5 | % It is more efficient than x*diag(s), but consumes a similar amount of memory. 6 | % Warning: It consumes a lot of memory when x is sparse. 7 | 8 | if isscalar(s), 9 | s = repmat(s, 1, ROInets.cols(x)); 10 | end%if 11 | 12 | if isrow(s), 13 | y = bsxfun(@times, x, s); 14 | else 15 | y = bsxfun(@times, x, s'); 16 | end%if 17 | 18 | 19 | % y = x.*repmat(s(:).', ROInets.rows(x), 1); 20 | %y = x.*(ones(rows(x),1)*s(:)'); 21 | -------------------------------------------------------------------------------- /+ROInets/scale_rows.m: -------------------------------------------------------------------------------- 1 | function y = scale_rows(x,s) 2 | % SCALE_ROWS Scale each row of a matrix. 3 | % SCALE_ROWS(x,s) returns matrix y, same size as x, such that 4 | % y(i,:) = s(i)*x(i,:) 5 | % It is more efficient than diag(s)*x. 6 | 7 | if isscalar(s), 8 | s = repmat(s, ROInets.rows(x), 1); 9 | end%if 10 | 11 | if iscolumn(s), 12 | y = bsxfun(@times, x, s); 13 | else 14 | y = bsxfun(@times, x, s'); 15 | end%if 16 | 17 | % y = repmat(s(:), 1, ROInets.cols(x)).*x; 18 | %y = (s(:)*ones(1,cols(x))).*x; 19 | -------------------------------------------------------------------------------- /+ROInets/scomponents.m: -------------------------------------------------------------------------------- 1 | function [sci, sizes] = scomponents(A) 2 | % SCOMPONENTS Compute the strongly connected components of a graph 3 | % 4 | % ci=scomponents(A) returns an index for the component number of every 5 | % vertex in the graph A. The total number of components is max(ci). 6 | % If the input is undirected, then this algorithm outputs just the 7 | % connected components. Otherwise, it output the strongly connected 8 | % components. 9 | % 10 | % The implementation is from Tarjan's 1972 paper: Depth-first search and 11 | % linear graph algorithms. In SIAM's Journal of Computing, 1972, 1, 12 | % pp.146-160. 13 | % 14 | % See also DMPERM 15 | % 16 | % Example: 17 | % load_gaimc_graph('cores_example'); % the graph A has three components 18 | % ci = scomponents(A) 19 | % ncomp = max(ci) % should be 3 20 | % R = sparse(1:size(A,1),ci,1,size(A,1),ncomp); % create a restriction matrix 21 | % CG = R'*A*R; % create the graph with each component 22 | % % collapsed into a single node. 23 | 24 | % David F. Gleich 25 | % Copyright, Stanford University, 2008-2009 26 | 27 | % History 28 | % 2008-04-11: Initial coding 29 | 30 | if isstruct(A), rp=A.rp; ci=A.ci; %ofs=A.offset; 31 | else [rp, ci]=sparse_to_csr(A); 32 | end 33 | 34 | n=length(rp)-1; sci=zeros(n,1); cn=1; 35 | root=zeros(n,1); dt=zeros(n,1); t=0; 36 | cs=zeros(n,1); css=0; % component stack 37 | rs=zeros(2*n,1); rss=0; % recursion stack holds two nums (v,ri) 38 | % start dfs at 1 39 | for sv=1:n 40 | v=sv; if root(v)>0, continue; end 41 | rss=rss+1; rs(2*rss-1)=v; rs(2*rss)=rp(v); % add v to the stack 42 | root(v)=v; sci(v)=-1; dt(v)=t; t=t+1; 43 | css=css+1; cs(css)=v; % add w to component stack 44 | while rss>0 45 | v=rs(2*rss-1); ri=rs(2*rss); rss=rss-1; % pop v from the stack 46 | while ridt(root(w)), root(v)=root(w); end 59 | end 60 | end 61 | if root(v)==v 62 | while css>0 63 | w=cs(css); css=css-1; sci(w)=cn; 64 | if w==v, break; end 65 | end 66 | cn=cn+1; 67 | end 68 | end 69 | end 70 | 71 | if nargout>1 72 | sizes=accumarray(sci,1,[max(sci) 1]); 73 | end 74 | end%scomponents 75 | 76 | function [rp, ci, ai, ncol]=sparse_to_csr(A,varargin) 77 | % SPARSE_TO_CSR Convert a sparse matrix into compressed row storage arrays 78 | % 79 | % [rp ci ai] = sparse_to_csr(A) returns the row pointer (rp), column index 80 | % (ci) and value index (ai) arrays of a compressed sparse representation of 81 | % the matrix A. 82 | % 83 | % [rp ci ai] = sparse_to_csr(i,j,v,n) returns a csr representation of the 84 | % index sets i,j,v with n rows. 85 | % 86 | % Example: 87 | % A=sparse(6,6); A(1,1)=5; A(1,5)=2; A(2,3)=-1; A(4,1)=1; A(5,6)=1; 88 | % [rp ci ai]=sparse_to_csr(A) 89 | % 90 | % See also CSR_TO_SPARSE, SPARSE 91 | 92 | % David F. Gleich 93 | % Copyright, Stanford University, 2008-2009 94 | 95 | % History 96 | % 2008-04-07: Initial version 97 | % 2008-04-24: Added triple array input 98 | % 2009-05-01: Added ncol output 99 | % 2009-05-15: Fixed triplet input 100 | 101 | narginchk(1, 5) 102 | retc = nargout>1; reta = nargout>2; 103 | 104 | if nargin>1 105 | if nargin>4, ncol = varargin{4}; end 106 | nzi = A; nzj = varargin{1}; 107 | if reta && length(varargin) > 2, nzv = varargin{2}; end 108 | if nargin<4, n=max(nzi); else n=varargin{3}; end 109 | nz = length(A); 110 | if length(nzi) ~= length(nzj), error('gaimc:invalidInput',... 111 | 'length of nzi (%i) not equal to length of nzj (%i)', nz, ... 112 | length(nzj)); 113 | end 114 | if reta && length(varargin) < 3, error('gaimc:invalidInput',... 115 | 'no value array passed for triplet input, see usage'); 116 | end 117 | if ~isscalar(n), error('gaimc:invalidInput',... 118 | ['the 4th input to sparse_to_csr with triple input was not ' ... 119 | 'a scalar']); 120 | end 121 | if nargin < 5, ncol = max(nzj); 122 | elseif ~isscalar(ncol), error('gaimc:invalidInput',... 123 | ['the 5th input to sparse_to_csr with triple input was not ' ... 124 | 'a scalar']); 125 | end 126 | else 127 | n = size(A,1); nz = nnz(A); ncol = size(A,2); 128 | retc = nargout>1; reta = nargout>2; 129 | if reta, [nzi, nzj, nzv] = find(A); 130 | else [nzi, nzj] = find(A); 131 | end 132 | end 133 | if retc, ci = zeros(nz,1); end 134 | if reta, ai = zeros(nz,1); end 135 | rp = zeros(n+1,1); 136 | for i=1:nz 137 | rp(nzi(i)+1)=rp(nzi(i)+1)+1; 138 | end 139 | rp=cumsum(rp); 140 | if ~retc && ~reta, rp=rp+1; return; end 141 | for i=1:nz 142 | if reta, ai(rp(nzi(i))+1)=nzv(i); end 143 | ci(rp(nzi(i))+1)=nzj(i); 144 | rp(nzi(i))=rp(nzi(i))+1; 145 | end 146 | for i=n:-1:1 147 | rp(i+1)=rp(i); 148 | end 149 | rp(1)=0; 150 | rp=rp+1; 151 | end%sparse_to_csr 152 | % [EOF] -------------------------------------------------------------------------------- /+ROInets/setdiff_pos_int.m: -------------------------------------------------------------------------------- 1 | function C = setdiff_pos_int(A, B) 2 | %SETDIFF_POS_INT Set difference of two sets of positive integers (much faster than built-in setdiff) 3 | % C = SETDIFF_POS_INT(A, B) 4 | % C = A \ B { things in A that are not in B } 5 | 6 | % Original by Kevin Murphy, modified by Leon Peshkin 7 | 8 | % $LastChangedBy: giles.colclough@gmail.com $ 9 | % $Revision: 214 $ 10 | % $LastChangedDate: 2014-07-24 12:40:42 +0100 (Thu, 24 Jul 2014) $ 11 | % Contact: giles.colclough@gmail.com 12 | % Originally written on: GLNXA64 by Giles Colclough, 14-Apr-2014 11:52:47 13 | 14 | if isempty(A) 15 | C = []; 16 | return; 17 | 18 | elseif isempty(B) 19 | C = A; 20 | return; 21 | 22 | else % both non-empty 23 | bits = zeros(1, max(max(A), max(B))); 24 | bits(A) = 1; 25 | bits(B) = 0; 26 | 27 | C = A(logical(bits(A))); 28 | end%if 29 | end%setdiff_pos_int 30 | % [EOF] 31 | -------------------------------------------------------------------------------- /+ROInets/symmetric_orthogonalise.m: -------------------------------------------------------------------------------- 1 | function [L, ignore, ignore2, W] = symmetric_orthogonalise(A, maintainMagnitudes) 2 | %SYMMETRIC_ORTHOGONALISE closest orthogonal matrix 3 | % 4 | % L = SYMMETRIC_ORTHOGONALISE(A) returns orthonormal matrix L which 5 | % is closest to A, as measured by the Frobenius norm of (L-A). 6 | % 7 | % The orthogonal matrix is constructed from a singular value decomposition 8 | % of A. 9 | % 10 | % L = SYMMETRIC_ORTHOGONALISE(A, KEEP_MAGNITUDES) returns the orthogonal 11 | % matrix L, whose columns have the same magnitude as the respective 12 | % columns of A, and which is closest to A, as measured by the Frobenius 13 | % norm of (L-A), if KEEP_MAGNITUDES is TRUE. 14 | % 15 | % The orthogonal matrix is constructed from a singular value decomposition 16 | % of A. 17 | % 18 | % [L, ~, ~, W] = SYMMETRIC_ORTHOGONALISE(...) also returns a weighting matrix 19 | % such that L = A * W; 20 | % 21 | % See also: ROINETS.HOUSEHOLDER_ORTHOGONALISE, ORTH, SVD. 22 | 23 | % References: Naidu, A. R. "Centrality of Lowdin Orthogonalizations", 24 | % arXiv 1105.3571v1, May 2011. 25 | % Available at: http://arxiv-web3.library.cornell.edu/pdf/1105.3571v1.pdf 26 | 27 | % Copyright 2013 OHBA 28 | % This program is free software: you can redirstribute it and/or modify 29 | % it under the terms of the GNU General Public License as published by 30 | % the Free Software Foundation, either version 3 of the License, or 31 | % (at your option) any later version. 32 | % 33 | % This program is distributed in the hope that it will be useful, 34 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 35 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 | % GNU General Public License for more details. 37 | % 38 | % You should have received a copy of the GNU General Public License 39 | % along with this program. If not, see . 40 | 41 | % $LastChangedBy: giles.colclough@gmail.com $ 42 | % $Revision: 239 $ 43 | % $LastChangedDate: 2014-08-15 14:58:49 +0100 (Fri, 15 Aug 2014) $ 44 | % Contact: giles.colclough 'at' magd.ox.ac.uk 45 | % Originally written on: GLNXA64 by Giles Colclough, 31-Oct-2013 13:30:05 46 | 47 | if nargin < 2 || ~exist('maintainMagnitudes', 'var'), 48 | maintainMagnitudes = false; 49 | end 50 | 51 | [ignore, ignore2] = deal([]); % to match up with other functions 52 | 53 | if maintainMagnitudes, 54 | D = diag(sqrt(diag(A' * A))); 55 | 56 | 57 | 58 | if nargout > 1, 59 | % call function again 60 | [Lnorm, ~, ~, W] = ROInets.symmetric_orthogonalise(A * D, false); 61 | 62 | % scale result 63 | L = Lnorm * D; 64 | W = D * W * D; 65 | 66 | else 67 | % call function again 68 | Lnorm = ROInets.symmetric_orthogonalise(A * D, false); 69 | 70 | % scale result 71 | L = Lnorm * D; 72 | end%if 73 | 74 | else 75 | [U, S, V] = svd(A, 'econ'); 76 | 77 | if ~isempty(S), 78 | % we need to check that we have sufficient rank 79 | S = diag(S); 80 | tol = max(size(A)) * S(1) * eps(class(A)); 81 | r = sum(S > tol); 82 | isFullRank = (r >= ROInets.cols(A)); 83 | 84 | if isFullRank, 85 | % polar factors of A 86 | L = U * conj(transpose(V)); 87 | 88 | if nargout > 1, 89 | W = V * diag(1.0 ./ S) * conj(transpose(V)); 90 | end%if 91 | else % not enough degrees of freedom 92 | error_message = ['The ROI time-course matrix is not full rank. \n', ... 93 | ' This prevents you from using an all-to-all ', ... 94 | 'orthogonalisation method. \n', ... 95 | ' Your data have rank %d, and you are looking ', ... 96 | 'at %d ROIs. \n', ... 97 | ' You could try reducing the number of ROIs, or ', ... 98 | 'using an alternative orthogonalisation method. \n']; 99 | error('ROInets:RankError',error_message,r,ROInets.cols(A)); 100 | end%if 101 | 102 | else 103 | L = []; 104 | W = []; 105 | end%if 106 | end%if 107 | end%symmetric_orthogonalise 108 | % [EOF] -------------------------------------------------------------------------------- /+ROInets/tikhonov_inverse.m: -------------------------------------------------------------------------------- 1 | function P = tikhonov_inverse(C, rho) 2 | %TIKHONOV_INVERSE inverse covariance using Tikhonov regularisation 3 | % 4 | % Regularises the estimate of the inverse covariance by adding rho*I 5 | % 6 | % P = TIKHONOV_INVERSE(C, RHO) returns the inverse covariance P estimated 7 | % from sample covariance matrix C, using regularisation parameter RHO. 8 | 9 | 10 | % Copyright 2016 OHBA 11 | % This program is free software: you can redistribute it and/or modify 12 | % it under the terms of the GNU General Public License as published by 13 | % the Free Software Foundation, either version 3 of the License, or 14 | % (at your option) any later version. 15 | % 16 | % This program is distributed in the hope that it will be useful, 17 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | % GNU General Public License for more details. 20 | % 21 | % You should have received a copy of the GNU General Public License 22 | % along with this program. If not, see . 23 | 24 | % $LastChangedBy: giles.colclough@gmail.com $ 25 | % $Revision: 214 $ 26 | % $LastChangedDate: 2014-07-24 12:40:42 +0100 (Thu, 24 Jul 2014) $ 27 | % Contact: giles.colclough@gmail.com 28 | % Originally written on: MACI64 by Giles Colclough, 14-Jul-2016 29 | 30 | if nargin < 2 || ~exist('rho', 'var') || isempty(rho), 31 | rho = 0.1; % Steve's default 32 | end 33 | 34 | nModes = ROInets.rows(C); 35 | P = GC_cholinv(C + rho * eye(nModes)); 36 | -------------------------------------------------------------------------------- /+ROInets/univariate_edge_test.m: -------------------------------------------------------------------------------- 1 | function [T, p, corrp, COPE] = univariate_edge_test(netmats, designMatrix, contrasts, standardise) 2 | %UNIVARIATE_EDGE_TEST permutation test for group-level stats on networks 3 | % 4 | % [T, P, CORRP] = UNIVARIATE_EDGE_TEST(NETMATS, DESIGN, CONTRAST) performs 5 | % univariate testing for significance on each edge of a network matrix. 6 | % Testing is performed using FSL's randomise, and uses 5000 permutations 7 | % of the group labels to perform nonparametric inference. 8 | % 9 | % Pass in NETMATS, which are symmetric network matrices with subjects in 10 | % the third dimension. The diagonals will be ignored. The DESIGN matrix 11 | % should have as many rows as subjects. The CONTRAST matrix should have 12 | % as many columns as the design matrix has columns. 13 | % 14 | % [T, P, CORRP] = UNIVARIATE_EDGE_TEST(..., STANDARDISE) demeans and 15 | % variance normalises the design matrix, if TRUE. 16 | % 17 | % [T, P, CORRP, COPE] = ... also returns the contrast of parameter 18 | % estimates from the regression. 19 | % 20 | % T provides single-edge T-stats; P the uncorrected p-Values; and CORRP the 21 | % FWE-corrected p-values. To use the weaker FDR correction, see 22 | % ROINETS.FALSE_DISCOVERY_RATE. 23 | % 24 | % This code demeans over subjects at the top level, so it is not good for 25 | % testing the whole-group mean effect. 26 | 27 | % Copyright 2015 OHBA, FMRIB 28 | % This program is free software: you can redistribute it and/or modify 29 | % it under the terms of the GNU General Public License as published by 30 | % the Free Software Foundation, either version 3 of the License, or 31 | % (at your option) any later version. 32 | % 33 | % This program is distributed in the hope that it will be useful, 34 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 35 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 | % GNU General Public License for more details. 37 | % 38 | % You should have received a copy of the GNU General Public License 39 | % along with this program. If not, see . 40 | 41 | 42 | % $LastChangedBy: GilesColclough $ 43 | % $Revision: 763 $ 44 | % $LastChangedDate: 2015-10-21 11:52:19 +0100 (Wed, 21 Oct 2015) $ 45 | % Contact: giles.colclough@gmail.com 46 | % Originally written on: MACI64 by Giles Colclough, 25-Sep-2014 15:20:18 47 | 48 | 49 | %% Input checking 50 | [nNodes, checkMe, nSessions] = size(netmats); 51 | assert(checkMe == nNodes, ... 52 | [mfilename ':NonsquareInput'], ... 53 | 'Input netmats must be square, with subjects in the third dimension. \n'); 54 | 55 | [checkMe, nEVs] = size(designMatrix); 56 | assert(checkMe == nSessions, ... 57 | [mfilename ':BadDesign'], ... 58 | 'Design matrix must have as many rows as subjects. \n'); 59 | 60 | assert(ROInets.cols(contrasts) == nEVs, ... 61 | [mfilename ':BadContrasts'], ... 62 | 'Contrasts must have as many columns as EVs in the design matrix. \n'); 63 | 64 | if nargin < 4 || ~exist('standardise', 'var'), 65 | standardise = false; 66 | else 67 | assert(islogical(standardise), ... 68 | [mfilename ':BadStandardise'], ... 69 | 'Standardise input must be a logical value. \n'); 70 | end%if 71 | 72 | edges = ROInets.get_edges(netmats); % note this assumes symmetry 73 | 74 | [Ttmp, ptmp, corrptmp, COPEtmp] = ROInets.perform_glm_with_randomise(edges, ... 75 | designMatrix, ... 76 | contrasts, ... 77 | standardise); 78 | 79 | % convert back to symmetric matrices 80 | T = ROInets.unvectorize(Ttmp); 81 | p = ROInets.unvectorize(1 - ptmp); 82 | corrp = ROInets.unvectorize(1 - corrptmp); 83 | COPE = ROInets.unvectorize(COPEtmp); 84 | 85 | end%univariate_edge_tests 86 | % [EOF] 87 | -------------------------------------------------------------------------------- /+ROInets/unvectorize.m: -------------------------------------------------------------------------------- 1 | function M = unvectorize(v) 2 | %UNVECTORIZE convert edges into square matrix 3 | % 4 | % M = UNVECTORIZE(E) converts edges E into square symmetric network matrix 5 | % M. Diagonal entries will be zero. 6 | 7 | if size(v,2)>1, 8 | for m = size(v,2):-1:1, 9 | M(:,:,m) = ROInets.unvectorize(v(:,m)); 10 | end%for 11 | return 12 | end%if 13 | 14 | nNodes = ROInets.num_nodes(length(v)); 15 | M = zeros(nNodes); 16 | triUpInd = triu(true(nNodes),1); 17 | 18 | M(triUpInd) = v(:); 19 | M = M + M'; 20 | end%unvectorize -------------------------------------------------------------------------------- /+ROInets/z_to_p_two_tailed.m: -------------------------------------------------------------------------------- 1 | function p = z_to_p_two_tailed(z) 2 | %Z_TO_P_TWO_TAILED convert standard z-value to p-value in two-tailed test 3 | 4 | p = 2 .* normcdf(-abs(z)); 5 | % [EOF] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ## OS generated files 3 | *._* 4 | .DS_Store* 5 | .Spotlight* 6 | .Trash* 7 | *ehthumbs.db 8 | *Thumbs.db 9 | *.fuse_hidden* 10 | 11 | ## Temporary files/folders 12 | *.swp 13 | *~ 14 | tmp* 15 | _tmp* 16 | _ 17 | 18 | ## Compiled files 19 | *.o 20 | *.o-* 21 | *.obj 22 | *.a 23 | *.class 24 | *.jar 25 | *.pyc 26 | 27 | ## Matlab files 28 | *.mexmac* 29 | *.mexa* 30 | 31 | ## Application-specific 32 | node_modules 33 | __pycache__ 34 | .ipynb_checkpoints 35 | .vscode 36 | .ipynb* 37 | 38 | ## Data files 39 | *.mat 40 | *.csv 41 | *.nii 42 | *.nii.gz 43 | 44 | *.gif 45 | *.png 46 | *.jpg 47 | *.jpeg 48 | *.tiff 49 | 50 | *.wav 51 | *.mp3 52 | *.avi 53 | *.mp4 54 | *.mov 55 | *.flv 56 | *.mkv 57 | 58 | *.ppt 59 | *.pptx 60 | *.doc 61 | *.docx 62 | *.pdf 63 | 64 | *.zip 65 | *.rar 66 | *.tar 67 | *.tar.gz 68 | *.tar.bz2 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) 3 | 4 | ## ROInets 5 | 6 | **A Matlab package for performing leakage-robust network inference between ROIs in MEG data** 7 | 8 | The methodology used in this pipeline is set out in 9 | 10 | > Colclough, G. L., Brookes, M., Smith, S. M. and Woolrich, M. W., "A symmetric multivariate leakage correction for MEG connectomes," NeuroImage 117, pp. 439-448 (2015) 11 | 12 | This package provides tools for network analysis between regions of interest 13 | in source-reconstructured MEG data, analysing amplitude correlations in 14 | band-limited power using a Gaussian Markov Random Field network model, after 15 | applying a symmetric multivariate orthogonalisation correction for source leakage. 16 | 17 | This package was originally developed by [@GilesColclough](https://github.com/GilesColclough), and is now maintained by the [OHBA Analysis](https://github.com/OHBA-analysis) group. 18 | 19 | ### What you need for it to run 20 | 21 | - FSL 22 | - FieldTrip 23 | - Matlab Stats + signal processing toolboxes - though you could probably change the code to make it work 24 | - QPAS mex files (optional) 25 | 26 | ### What you need to get started 27 | 28 | - Source-reconstructed resting-state MEG data (you can probably get it to work for task) 29 | - A set of ROIs or a spatial basis set, in the same space and resolution as the MEG data, saved as a nifti 30 | 31 | ### How to get started 32 | 33 | This package is distributed as part of [OSL](https://ohba-analysis.github.io/osl-docs/); this is how it should be installed on your machine. 34 | 35 | - The `+ROInets` folder is a Matlab package. Do not change the name of this folder, and do not add its contents on your path. All the functions inside it can be used with a "dot-syntax", by typing e.g. `ROInets.run_network_analysis`. 36 | - For a brief summary of what each function does, type `help ROInets.Contents`. The help text of each function should provide more information, and examples can be found in `+ROInets/+examples`. 37 | - The top-level function is `ROInets.run_individual_network_analysis`. View the helptext for this to view all the pipeline options. 38 | - If you often use this package, and would like to avoid typing the prefix `ROInets.*` all the time, check out [`import`](https://uk.mathworks.com/help/matlab/ref/import.html). 39 | 40 | ### What paper to cite 41 | 42 | > Colclough, G. L., Brookes, M., Smith, S. M. and Woolrich, M. W., "A symmetric multivariate leakage correction for MEG connectomes, "NeuroImage 117, pp. 439-448 (2015). 43 | 44 | ### Where to ask for help, or report bugs 45 | 46 | For technical support or any other issues, please either [open an issue](https://github.com/OHBA-analysis/MEG-ROI-nets/issues), or contact the OHBA Analysis Group by [email](mailto:mark.woolrich@ohba.ox.ac.uk). 47 | 48 | ### An overview of the pipeline 49 | 50 | - Select time region for analysis 51 | - Band-pass filter the data 52 | - Find a time-course for each ROI using PCA 53 | - Remove effects of leakage using an orthogonalisation process which finds the closest set of orthogonal vectors 54 | - Find down-sampled power envelopes 55 | - Perform network inference using partial correlation, L1 regularised using DP-glasso, optimised using cross-validation 56 | - Convert correlations to z-statistics, by scaling relative to an empirical null, generated to share the same temporal smoothness properties as the input data. 57 | 58 | ### License 59 | 60 | ``` 61 | Copyright 2015 OHBA 62 | This program is free software: you can redistribute it and/or modify 63 | it under the terms of the GNU General Public License as published by 64 | the Free Software Foundation, either version 3 of the License, or 65 | (at your option) any later version. 66 | 67 | This program is distributed in the hope that it will be useful, 68 | but WITHOUT ANY WARRANTY; without even the implied warranty of 69 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 70 | GNU General Public License for more details. 71 | 72 | You should have received a copy of the GNU General Public License 73 | along with this program. If not, see . 74 | ``` 75 | -------------------------------------------------------------------------------- /licenses.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 OHBA 2 | This package is free software: you can redistribute it and/or modify 3 | it under the terms of the GNU General Public License as published by 4 | the Free Software Foundation, either version 3 of the License, or 5 | (at your option) any later version. 6 | 7 | This package is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | GNU General Public License for more details. 11 | 12 | You should have received a copy of the GNU General Public License 13 | along with this package. If not, see . 14 | 15 | This software builds heavily on contributions by Mark Schmidt 16 | (L1precisionBCD) and Hao Wang (glasso_FTH), including significant code 17 | snippets. See references: 18 | http://www.di.ens.fr/~mschmidt/Software/L1precision.html 19 | http://statweb.stanford.edu/~tibs/glasso/index.html 20 | http://www.stat.sc.edu/~wang345/RESEARCH/Bglasso/bglasso.html 21 | Neither code package was released with a license, beyond author 22 | acknowledgment. 23 | 24 | Some small functions from Tom Minka's open-source LIGHTSPEED Matlab 25 | toolbox are copied across. If you don't have this toolbox, try it out. 26 | Also included is the logdet function from Dahua Lin at MIT, freely 27 | available. 28 | 29 | If you use Adrian Willis' QPAS package to offer speed increases in the 30 | regularization functions, he asks that you cite his work. --------------------------------------------------------------------------------