├── +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 ri