├── r2z.m ├── searchlightSEL.m ├── searchlightROI.m ├── README.md ├── temporalSNR.m ├── searchlightRFX.m ├── searchlightONE.m └── searchlight.m /r2z.m: -------------------------------------------------------------------------------- 1 | function z = r2z(r) 2 | 3 | % Z = R2Z(R) takes as input a single value or a correlation R and 4 | % applies a Fisher transformation. The resulting z-values are ouput in 5 | % matrix Z. 6 | % 7 | % Written by Juan Manuel Contreras (juan.manuel.contreras.87@gmail.com) on 8 | % April 27, 2012. 9 | 10 | matrixDims = length(size(r)); 11 | 12 | if matrixDims == 1 13 | 14 | z = fisher(r) 15 | 16 | elseif matrixDims == 2 17 | 18 | [a b] = size(r); 19 | z = nan(a, b); 20 | for ai = 1:a 21 | for bi = 1:b 22 | z(ai, bi) = fisher(r(ai, bi)); 23 | end 24 | end 25 | 26 | elseif matrixDims == 3 27 | 28 | [a b c] = size(r); 29 | z = nan(a, b, c); 30 | for ai = 1:a 31 | for bi = 1:b 32 | for ci = 1:c 33 | z(ai, bi, ci) = fisher(r(ai, bi, ci)); 34 | end 35 | end 36 | end 37 | 38 | else 39 | 40 | error('Cannot process %.f-dimensional matrices.', matrixDims) 41 | 42 | end 43 | 44 | end 45 | 46 | function z = fisher(r) 47 | 48 | z = 0.5 * log((1 + r) / (1 - r)) 49 | 50 | end 51 | -------------------------------------------------------------------------------- /searchlightSEL.m: -------------------------------------------------------------------------------- 1 | function searchlightSEL(whichsearchlight, whichsubs, d) 2 | 3 | switch whichsearchlight 4 | 5 | % Subject-level searchlight 6 | case 'subject' 7 | for iSub = whichsubs(1):whichsubs(end) 8 | d.iSub = iSub; 9 | d.subName = d.subs(iSub).name; 10 | searchlightONE(d) 11 | end 12 | 13 | % Random-effects analysis of individual searchlights 14 | case 'rfx' 15 | searchlightRFX(d) 16 | 17 | % Subject-level searchlights followed by a random-effects analysis 18 | case 'subject_rfx' 19 | searchlightSEL('subject', whichsubs, d) 20 | searchlightSEL('rfx', whichsubs, d) 21 | 22 | % Region-of-interest analysis 23 | case 'roi' 24 | mask = spm_get_mat(d.maskFile); 25 | nROI = max(unique(mask)); 26 | results = nan(d.nSubs, nROI); 27 | for iSub = whichsubs(1):whichsubs(end) 28 | d.iSub = iSub; 29 | d.subName = d.subs(iSub).name; 30 | results = searchlightROI(d, results); 31 | end 32 | save([d.resultsDir '\ROI_' d.analysis], 'results', 'd') 33 | 34 | % Don't know what to do 35 | otherwise 36 | error('Input subject, rfx, subject_rfx, or roi.') 37 | 38 | end 39 | -------------------------------------------------------------------------------- /searchlightROI.m: -------------------------------------------------------------------------------- 1 | function results = searchlightROI(d, results) 2 | 3 | %%%%%%%%%%%%%%%% 4 | % DECLARATIONS % 5 | %%%%%%%%%%%%%%%% 6 | 7 | % Identify files with voxel statistics 8 | statsDir = fullfile(d.subsDir, d.subName, d.glmDir); 9 | files = dir([statsDir '\' d.fileType '*.img']); 10 | files = strcat(statsDir, '\', cat(1,files(d.fileInds).name)); 11 | 12 | % Load brain data 13 | pats = spm_get_mat(files); % Voxel statistics 14 | mask = spm_get_mat(d.maskFile); % Logical index of brain position 15 | 16 | % Count the number of unique ROIs in the brain mask 17 | nROI = max(unique(mask)); 18 | 19 | %%%%%%%%%%%%%%%% 20 | % COMPUTATIONS % 21 | %%%%%%%%%%%%%%%% 22 | 23 | % For every ROI... 24 | for iROI = 1:nROI 25 | 26 | % ...determine its size 27 | sizeROI = sum(sum(sum(mask == iROI))); 28 | 29 | % ...extract the multivoxel patterns for every condition 30 | iROIpats = nan(sizeROI, d.nConds); 31 | for iCond = 1:d.nConds 32 | iROIpatsTemp = pats(:, :, :, iCond); 33 | iROIpats(:, iCond) = iROIpatsTemp(mask == iROI); 34 | end 35 | 36 | % ...compute correlation differences and store results 37 | r = fisher(corr(iROIpats, 'type', d.rType)); 38 | within = mean(r(logical(d.within))); 39 | between = mean(r(logical(d.between))); 40 | results(d.iSub, iROI) = within - between; 41 | 42 | end 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fmri 2 | ==== 3 | **Repository of MATLAB scripts that I wrote as a graduate student (2011-2013) to perform [multivoxel pattern analysis (MVPA)](http://www.ncbi.nlm.nih.gov/pubmed/16899397) of [functional magnetic resonance imaging (fMRI)](http://en.wikipedia.org/wiki/Functional_magnetic_resonance_imaging) data. MVPA is a novel data mining method in fMRI research to analyze distributed patterns of brain activity, which the human brain uses to represent information ([Mahmoudi, Takerkart, Regragui, Boussaoud, & Brovelli, 2012](http://www.hindawi.com/journals/cmmm/2012/961257/)). Variations of some of the scripts in this repository were used for two scientific publications ([Contreras, Banaji, & Mitchell, 2013](http://www.plosone.org/article/info%3Adoi%2F10.1371%2Fjournal.pone.0069684); [Contreras, Schirmer, Banaji, & Mitchell, 2013](http://www.wjh.harvard.edu/~jmcontre/ContrerasSchirmerBanajiMitchell2013.pdf)).** 4 | 5 | - **searchlight.m** uses parameter estimates from SPM8 images to perform two types of MVPA: Whole-brain searchlights ([Kriegeskorte, Goebel, & Bandettini, 2006](http://www.ncbi.nlm.nih.gov/pubmed/16537458)) and region-of-interest (ROI) analyses. It ouputs the resulting statistics as .mat files and brain images. It serves as a wrapper to the following four scripts. 6 | - **searchlightSEL.m** selects which MVPA to execute: individual- and/or group-level searchlight, or ROI analysis 7 | - **searchlightONE.m** computes individual-level searchlights 8 | - **searchlightRFX.m** uses searchlight results produced by **searchlightONE.m** to perform a group-level analysis 9 | - **searchlightROI.m** executes an ROI analysis given a brain mask denoting one or more ROIs 10 | 11 | - **r2z.m** applies a [Fisher transformation](http://en.wikipedia.org/wiki/Fisher_transformation) to transform correlation coefficients to *z*-statistics. 12 | 13 | - **temporalSNR.m** computes temporal signal-to-noise ratio, number of experimental timepoints, or effect size in an fMRI experiment given two of these variables and a *p*-value ([Murphy, Bodzurka, & Bandettini, 2007](http://www.ncbi.nlm.nih.gov/pubmed/17126038)). 14 | -------------------------------------------------------------------------------- /temporalSNR.m: -------------------------------------------------------------------------------- 1 | function [tnsr tp e] = temporalSNR(which, tp, e, p, tsnr) 2 | 3 | % [TSNR TP E] = TEMPORALSNR(WHICH, TP, E, P, TSNR) computes temporal signal- 4 | % to-noise ratio (TSNR), number of experimental timepoints (TP), or effect 5 | % size (E; percentage of baseline activity by which experimental activity 6 | % differs) given two of these variables and a p-value (P). The variable to 7 | % be computed is specified by WHICH, which can be 'tsnr', 'tp', or 'e'. The 8 | % corresponding variable is left undefined. 9 | % 10 | % The equations that perform these computations are derived from theory and 11 | % experimental simulations (Murphy, Bodzurka, & Bandettini, 2007). These 12 | % simulations indicate that a TSNR of .4 is the minimum value to detect 13 | % effects between conditions in functional magnetic resonance imaging data 14 | % reliably (e.g., Anzellotti, Mahon, Schwarzbach, & Caramazza, 2011; 15 | % Simmons, Reddish, Bellgowan, & Martin, 2009). 16 | % 17 | % Anzellotti, S., Mahon, B. Z., Schwarzbach, J., & Caramazza, A. (2011). 18 | % Differential activity for animals and manipulable objects in the 19 | % anterior temporal lobes. Journal of Cognitive Neuroscience, 23(8), 20 | % 2059-2067. 21 | % Simmons, W. K., Reddish, M., Bellgowan, P. S. F., & Martin, A. (2010). 22 | % The selectivity and functional connectivity of the anterior temporal 23 | % lobes. Cerebral Cortex, 20, 813–825. 24 | % Murphy, K., Bodzurka, J., & Bandettini, P. A. (2007). How long to scan? 25 | % The relationship between fMRI temporal signal to noise ratio and 26 | % necessary scan duration. NeuroImage, 34, 565-574. 27 | % 28 | % Written by Juan Manuel Contreras (juan.manuel.contreras.87@gmail.com) on 29 | % November 24, 2011. 30 | 31 | % Declare the old standard coefficient 32 | gold = 1.5 * (1 + exp(1) ^ log10(p / 2)); 33 | 34 | % Temporal signal-to-noise ratio 35 | if strcmp(which, 'tsnr') 36 | tnsr = gold * sqrt(8 / tp) * (1 / e) * erfcinv(p); 37 | 38 | % Experimental timepoints 39 | elseif strcmp(which, 'tp') 40 | tp = 8 * (gold * (erfcinv(p) / (tsnr * e))) ^ 2; 41 | 42 | % Effect size 43 | elseif strcmp(which, 'e') 44 | e = (gold * erfcinv(p)) / (tsnr * sqrt(tp / 8)); 45 | 46 | % Don't know what to do 47 | else 48 | error('The value in WHICH specifies an unknown command.') 49 | 50 | end 51 | -------------------------------------------------------------------------------- /searchlightRFX.m: -------------------------------------------------------------------------------- 1 | function searchlightRFX(d) 2 | 3 | %%%%%%%%%%%%%%%% 4 | % DECLARATIONS % 5 | %%%%%%%%%%%%%%%% 6 | 7 | nSubs = length(d.subs); % Number of subjects 8 | files = dir([d.resultsDir '\s*.mat']); % Searchlight statistics 9 | mask = spm_get_mat(d.maskFile); % Logical index of brain position 10 | brainSize = size(mask); % Dimensions of brain mask 11 | 12 | %%%%%%%%%%%%%%%%%%%%%%%%% 13 | % LOAD VOXEL STATISTICS % 14 | %%%%%%%%%%%%%%%%%%%%%%%%% 15 | 16 | % Initialize result matrices 17 | mats = nan([brainSize nSubs]); % Searchlight statistics 18 | pRFX = mats(:, :, :, 1); % Random-effects p-values 19 | tRFX = mats(:, :, :, 1); % Random-effects t-values 20 | 21 | % Load voxel statistics from searchlight analysis 22 | for iSub = 1:nSubs 23 | load([d.resultsDir '\' files(iSub).name], 'srchlghtCorr') 24 | mats(:, :, :, iSub) = eval('srchlghtCorr'); 25 | end 26 | 27 | %%%%%%%%%%%%%%%%%%% 28 | % COMPUTE T-TESTS % 29 | %%%%%%%%%%%%%%%%%%% 30 | 31 | % Declare waitbar to report progress and associated variables 32 | h = waitbar(0,['RFX of ' d.analysis ' in progress...']); 33 | elapsed = 0; nVox = brainSize(1) * brainSize(2) * brainSize(3); 34 | 35 | % For every voxel in the brain... 36 | for x = 1:brainSize(1) 37 | for y = 1:brainSize(2) 38 | for z = 1:brainSize(3) 39 | 40 | % ...if it has numeric values and is within the brain mask 41 | if any(~isnan(mats(x, y, z, :))) && mask(x, y, z) > 0 42 | 43 | % ...compute a one-sample right-tailed t-test 44 | [~, p, ~, stats] = ttest(mats(x, y, z, :), 0, .05, 'right'); 45 | 46 | % ...store t- and p-values (if the correlation is positive) 47 | if stats.tstat > 0 48 | tRFX(x,y,z) = stats.tstat; 49 | pRFX(x,y,z) = p; 50 | end 51 | 52 | end 53 | 54 | % Update the progress bar 55 | waitbar(elapsed / nVox, h); elapsed = elapsed + 1; 56 | 57 | end 58 | end 59 | end 60 | 61 | % Remove the progress bar 62 | delete(h) 63 | 64 | %%%%%%%%%%%%%%%%%% 65 | % OUTPUT RESULTS % 66 | %%%%%%%%%%%%%%%%%% 67 | 68 | % Save results 69 | save([d.resultsDir '\RFX_' d.analysis], 'mats', 'tRFX', 'pRFX') 70 | 71 | % Write t- and p-value images 72 | maskVol = spm_vol(d.maskFile); 73 | maskVol.dt = [spm_type('float64') spm_platform('bigend')]; 74 | maskVol.fname = [saveName '_Tval.img']; 75 | spm_write_vol(maskVol, tRFX); 76 | maskVol.fname = [saveName '_Pval.img']; 77 | spm_write_vol(maskVol, 1 - pRFX); 78 | -------------------------------------------------------------------------------- /searchlightONE.m: -------------------------------------------------------------------------------- 1 | function searchlightONE(d) 2 | 3 | %%%%%%%%%%%%%%%% 4 | % DECLARATIONS % 5 | %%%%%%%%%%%%%%%% 6 | 7 | % Identify files with voxel statistics 8 | statsDir = fullfile(d.subsDir, d.subName, d.glmDir); 9 | files = dir([statsDir '\' d.fileType '*.img']); 10 | files = strcat(statsDir, '\', cat(1, files(d.fileInds).name)); 11 | 12 | % Load brain data 13 | pats = spm_get_mat(files); % Voxel statistics 14 | mask = spm_get_mat(d.maskFile); % Logical index of brain position 15 | 16 | % Initialize result matrices 17 | brainSize = size(mask); % Dimensions of brain mask 18 | srchlghtWith = nan(brainSize); % Within-condition correlations 19 | srchlghtBetw = nan(brainSize); % Between-condition correlations 20 | srchlghtSize = nan(brainSize); % Number of voxels in searchlight sphere 21 | 22 | % Declare waitbar to report progress and associated variables 23 | h = waitbar(0, ['srchlght of ' d.subName ' with r = ' ... 24 | num2str(d.rSphere) ' in progress...']); 25 | elapsed = 0; nVox = brainSize(1) * brainSize(2) * brainSize(3); tic 26 | 27 | %%%%%%%%%%%%%%%%%%%%%%% 28 | % COMPUTE SEARCHLIGHT % 29 | %%%%%%%%%%%%%%%%%%%%%%% 30 | 31 | % For every voxel in the brain... 32 | for x = 1:brainSize(1) 33 | for y = 1:brainSize(2) 34 | for z = 1:brainSize(3) 35 | 36 | % ...if it has numeric values and is within the brain mask 37 | if any(~d.outsideBrain(pats(x, y, z, :))) && mask(x, y, z) > 0 38 | 39 | % ...build a sphere around it 40 | sphere = makeSphere([x y z], d.rSphere, brainSize); 41 | 42 | % ...extract the multivoxel pattern for every sphere element 43 | iPat = nan(length(sphere), d.nConds); 44 | for v = 1:length(sphere) 45 | iPat(v, :) = reshape(pats(sphere(v, 1), ... 46 | sphere(v, 2), sphere(v, 3), :), 1, d.nConds); 47 | end 48 | 49 | % ...remove voxels outside the brain mask, if any 50 | iPat = iPat(sum(iPat ~= 0, 2) == d.nConds, :); 51 | 52 | % ...compute correlations 53 | r = fisher(corr(iPat, 'type', d.rType)); 54 | 55 | % ...store the results 56 | srchlghtWith(x, y, z) = mean(r(logical(d.within))); 57 | srchlghtBetw(x, y, z) = mean(r(logical(d.between))); 58 | srchlghtSize(x, y, z) = size(iPat, 1); 59 | 60 | end 61 | 62 | % Update the progress bar 63 | waitbar(elapsed / nVox, h); elapsed = elapsed + 1; 64 | 65 | end 66 | end 67 | end 68 | 69 | % Compute correlation differences 70 | srchlghtCorr = srchlghtWith - srchlghtBetw; 71 | 72 | % Remove the progress bar and report completion 73 | delete(h) 74 | fprintf(['\n' d.subName ' searchlight completed in %.2f min.\n'], toc / 60) 75 | 76 | %%%%%%%%%%%%%%%%%% 77 | % OUTPUT RESULTS % 78 | %%%%%%%%%%%%%%%%%% 79 | 80 | % Save results 81 | save([d.resultsDir '\' d.subName '_' d.analysis], 'srchlghtCorr', ... 82 | 'srchlghtWith', 'srchlghtBetw', 'srchlghtSize', 'd') 83 | 84 | % Write the correlation image 85 | maskVol = spm_vol(d.maskFile); 86 | maskVol.dt = [spm_type('float64') spm_platform('bigend')]; 87 | maskVol.fname = [saveName '_Corr.img']; 88 | spm_write_vol(maskVol, srchlghtCorr); 89 | -------------------------------------------------------------------------------- /searchlight.m: -------------------------------------------------------------------------------- 1 | function searchlight(statmap, analysis, mask, whichsrchlght, whichsubs) 2 | 3 | % SEARCHLIGHT(STATMAP, ANALYSIS, MASK, WHICHSRCHLGHT, WHICHSUBS) scans 4 | % imaged brain volumes at each voxel within MASK. The searchlight is a 5 | % spherical cube, the contents of which are analyzed via differences in 6 | % Fisher-transformed correlations across conditions of interest. The 7 | % difference value computed at each location is assigned to the center voxel 8 | % of the searchlight. The resulting z-map shows how well the multivariate 9 | % signal in the local spherical neighborhood differentiates between 10 | % conditions of interest. 11 | % 12 | % SEARCHLIGHT assumes a home directory that contains a "searchlight" 13 | % directory, which contains a "results" directory and a "masks" directory. 14 | % In turn, "results" is assumed to contain one directory for each set of 15 | % general linear model (GLM) maps that is analyzed. STATMAP declares the 16 | % name of this directory within "results". The directory specified by 17 | % STATMAP will be made to contain one directory for each radius-mask 18 | % analysis, declared by ANALYSIS, which can take the form ['r' radius mask] 19 | % (e.g., "r3wholebrain"). 20 | % 21 | % Though this searchlight analysis is executed at the level of individual 22 | % subjects (WHICHSEARCHLIGHT = 'subject'), a sample of participants can be 23 | % analyzed as a group in a univariate random-effects analysis 24 | % (WHICHSEARCHLIGHT = 'rfx'). At each voxel, a t-statistic is computed 25 | % comparing the average z-score across participants against 0. The resulting 26 | % t-map shows how well the multivariate signal in the local spherical 27 | % neighborhood of the sample differentiates between conditions of interest. 28 | % 29 | % Finally, a non-searchlight multivariate analysis can be performed for one 30 | % or many regions-of-interest (ROI) at the level of individual subjects 31 | % (WHICHSEARCHLIGHT = 'roi'). This analysis correlates the voxels within an 32 | % ROI instead of creating spherical searchlights at each voxel. 33 | % 34 | % SEARCHLIGHT is a wrapper script that can analyze multiple subjects 35 | % sequentially (those declared by WHICHSUBS, a vector that specifies which 36 | % subjects will be analyzed) should be edited from analysis to analysis. In 37 | % turn, it calls, SEARCHLIGHTONE.m, SEARCHLIGHTRFX.m, or SEARCHLIGHTROI.m. 38 | % These three scripts are robust across analyses and should not be modified 39 | % in their original form, except in cases that build on their functionality. 40 | % 41 | % Written by Juan Manuel Contreras (juan.manuel.contreras.87@gmail.com) on 42 | % April 27, 2012. 43 | 44 | %%%%%%%%%%%%%%%% 45 | % DECLARATIONS % 46 | %%%%%%%%%%%%%%%% 47 | 48 | % Directories 49 | homeDir = 'I:\JuanManuel\FaceCategorization'; 50 | d.subsDir = fullfile(homeDir, 'SUBJECTS'); 51 | d.glmDir = ['RESULTS\GLM_' statmap]; 52 | d.masksDir = fullfile(homeDir, 'SEARCHLIGHT\MASKS'); 53 | d.resultsDir = fullfile(homeDir, 'SEARCHLIGHT\RESULTS', statmap, analysis); 54 | 55 | % Load subject files and brain mask 56 | d.subs = dir(fullfile(d.subsDir, 's*')); 57 | d.maskFile = fullfile(d.masksDir, mask); 58 | 59 | % Searchlight parameters 60 | d.statmap = statmap; 61 | d.analysis = analysis; 62 | d.nSubs = length(d.subs); 63 | d.nConds = 4; 64 | 65 | % Correlation sphere size and statistic type 66 | d.rSphere = str2double(d.analysis(2)); 67 | d.rType = 'spearman'; % pearson, kendall, or spearman 68 | 69 | % Indices for correlation matrices 70 | d.within = [0 1 0 0 71 | 0 0 0 0 72 | 0 0 0 1 73 | 0 0 0 0]; 74 | d.between = [0 0 0 1 75 | 0 0 1 0 76 | 0 0 0 0 77 | 0 0 0 0]; 78 | 79 | % Determine which SPM8 files to load 80 | d.fileType = 'spmT'; % spmT or beta 81 | d.nDeriv = 2; 82 | if strcmp(d.fileType, 'spmT') 83 | d.fileInds = 1:d.nConds; 84 | d.outsideBrain = str2func('iszero'); 85 | elseif strcmp(d.fileType, 'beta') 86 | d.fileInds = 1:d.nDeriv + 1:d.nConds * (d.nDeriv + 1); 87 | d.outsideBrain = str2func('isnan'); 88 | end 89 | 90 | %%%%%%%%%%%%%%%% 91 | % COMPUTATIONS % 92 | %%%%%%%%%%%%%%%% 93 | 94 | searchlightSEL(whichsrchlght, whichsubs, d) 95 | --------------------------------------------------------------------------------