Skip to content

Multivariate Pattern Analysis

David López-García edited this page Nov 25, 2021 · 10 revisions

Overview

Multivariate Pattern Analyses, also known as decoding analyses, comprise a set of machine learning models that extract information patterns from multi-dimensional data. One of the most remarkable advantages of these multivariate over univariate techniques is its sensitivity in detecting subtle changes in the patterns of activations, considering information distributed across all sensors simultaneously.


mvpalab-mvpa Figure. 1. (a) Feature extraction process in simulated data. The feature vectors of each condition and time point consisted of a voltage array for all the scalp electrodes. (b) Cross-validated LSVM classifier. For each time point, an LSVM was trained and tested (stratified k-fold CV, k = 5). Chance level was calculated by permuting the labels.


To obtain the classification performance in a time-resolved way, the feature vectors are extracted as shown in Fig. 1. During the feature extraction step, feature vectors are defined as a selection/combination of variables of the original dataset. Typical multivariate analyses use the raw voltage of the signal as a feature for the classification, but other characteristics, such the power envelope of the signal, can also be used as features.

Before computing the multivariate pattern analysis, the MVPAlab Toolbox can execute several preprocessing procedures that may improve the final results in different ways (e.g. increasing accuracy, avoiding skewed results, data normalization, data smoothing, etc.). These procedures and their configuration parameters can be adjusted by the users to meet the required specific analysis conditions.

The classification procedure, for each participant, run as follows:

  • For each participant, time-point and trial, two feature vectors (one for each condition or class) are generated, consisting of the raw potential (or any other feature such the power envelope) measured in all electrodes.

  • To evaluate the model performance, the classification algorithms are trained and validated, resulting in a single performance value for each time-point and participant. The classification performance at the group level was calculated by averaging these values across participants. The chance level was calculated following the former analysis but using randomly permuted labels for each trial.


mvpalab-results Figure 2. Time-resolved MVPA results. (a) Decoding performance (f1-score) for different classification models at a group-level: support vector ma-chine vs. linear discriminant analysis. Single subject plots are represented in dashed and dotted lines. Significant clusters are highlighted using horizontal colored bars. Shaded areas represent the standard error of the mean. (b) Group-level decoding performance for different number of features when PCA is applied. (c) Group-level decoding performance as a function of the selected number of trials to average. (d) Group-level decoding performance when different power envelopes are extracted and employed as features instead of the raw voltage.


Demo scripts:

To compute a time-resolved Multivariate Pattern Analysis, a classification model is trained and cross-validated for each time point and participant individually, extracting different performance metrics according to the cfg structure. All of this process is coded in the function mvpalab_mvpa(cfg,fv), which computes the decoding analysis completely:


mvpa_demo.m

%% MVPAlab TOOLBOX - (mvpa_demo.m)
% -------------------------------------------------------------------------
% Brain, Mind and Behavioral Research Center - University of Granada.
% Contact: [email protected] 
% -------------------------------------------------------------------------

% Initialize project and run configuration file:
cfg = mvpalab_init();
run cfg_file;

% Load data, generate conditions and feature extraction:
[cfg,data,fv] = mvpalab_import(cfg);

% Compute MVPA analysis:
[result,cfg] = mvpalab_mvpa(cfg,fv);

% Compute permutation maps and run statistical analysis:
% [OPTIONAL]
[permaps,cfg] = mvpalab_permaps(cfg,fv);
stats = mvpalab_permtest(cfg,result,permaps);

% Save cfg file:
mvpalab_savecfg(cfg);

% Plot the results:
run mvpa_plot;

This mvpalab_mvpa() function returns an updated version of the configuration structure (cfg) and the result variable (result). Performance values are stored in data matrices [1 x time x subject] inside the result variable as shown in the following figure:


mvpalab-result Figure 3. Data structure of the result file. Performance values are stored in 1 x timepoint x subject matrices. Group-level performance values can be calculated computing the mean across the third dimension.


Here you can find a configuration template for the former analysis:

cfg_file.m

%% Advanced configuration file for MVPA analysis
% -------------------------------------------------------------------------
% Brain, Mind and Behavioral Research Center - University of Granada.
% Contact: [email protected]
% -------------------------------------------------------------------------

cfg.analysis = 'MVPA';
cfg.location = pwd;

% Condition indentifiers:
cfg.study.conditionIdentifier{1,1} = 'condition_a';
cfg.study.conditionIdentifier{1,2} = 'condition_b';

% Data paths:
cfg.study.dataPaths{1,1} = 'C:\Users\Cimcyc\Desktop\data\condition_a\';
cfg.study.dataPaths{1,2} = 'C:\Users\Cimcyc\Desktop\data\condition_b\';

% Data files:
cfg.study.dataFiles{1,1} = {'1.mat','2.mat','3.mat'};
cfg.study.dataFiles{1,2} = {'1.mat','2.mat','3.mat'};

%% FEATURE EXTRACTION:

cfg.feature = 'voltage';

% cfg.feature = 'voltage'  - Raw voltage as feature.
% cfg.feature = 'envelope' - Power evelope as feature.

cfg.powenv.method = 'analytic';
cfg.powenv.uplow  = 'upper';
cfg.powenv.length = 5;

% cfg.powenv.method = 'analytic' - Envelope using the analytic signal.
% cfg.powenv.method = 'peak'     - Peak envelopes.

% cfg.powenv.uplow = 'upper' - Select upper envelope.
% cfg.powenv.uplow = 'lower' - Select lower envelope.

%% TRIAL AVERAGE:

cfg.trialaver.flag     = true;
cfg.trialaver.ntrials  = 5;
cfg.trialaver.order    = 'rand';

% cfg.trialaver.order = 'rand' - Random order.
% cfg.trialaver.order = 'seq'  - Secuential order.

%% BALANCED DATASETS:

cfg.classsize.match = true;
cfg.classsize.matchkfold = true;

%% DIMENSION REDUCTION:

% cfg.dimred.method = 'none' - Diemnsion reduction disabled.
% cfg.dimred.method = 'pca'  - Principal Component Analysis.

cfg.dimred.method = 'none';
cfg.dimred.ncomp  = 0;

%% DATA NORMALIZATION:

% cfg.normdata = 0 - raw data
% cfg.normdata = 1 - z-score (across features)
% cfg.normdata = 2 - z-score (across time)
% cfg.normdata = 3 - z-score (across trials)
% cfg.normdata = 4 - std_nor (across trials)

cfg.normdata = 4; 

%% DATA SMOOTHING:

% cfg.smoothdata.method = 'none'   - Data smooth disabled.
% cfg.smoothdata.method = 'moving' - Moving average method.

cfg.smoothdata.method   = 'moving';
cfg.smoothdata.window   = 5;

%% ANALYSIS TIMING:

cfg.tm.tpstart   = -200;
cfg.tm.tpend     = 1500;
cfg.tm.tpsteps   = 3;

%% CLASSIFICATION ALGORITHM:

% cfg.classmodel.method = 'svm' - Support Vector Machine.
% cfg.classmodel.method = 'da'  - Linear Discriminant Analysis.

% cfg.classmodel.kernel = 'linear'     - Support Vector Machine.
% cfg.classmodel.kernel = 'gaussian'   - Support Vector Machine.
% cfg.classmodel.kernel = 'rbf'        - Support Vector Machine.
% cfg.classmodel.kernel = 'polynomial' - Support Vector Machine.

% cfg.classmodel.kernel = 'linear' - Discriminant Analysis.
% cfg.classmodel.kernel = 'quadratic' - Discriminant Analysis.

cfg.classmodel.method = 'svm';
cfg.classmodel.kernel = 'linear';

%% PERFORMANCE METRICS:

cfg.classmodel.roc       = false;
cfg.classmodel.auc       = false;
cfg.classmodel.confmat   = false;
cfg.classmodel.precision = false;
cfg.classmodel.recall    = false;
cfg.classmodel.f1score   = false;
cfg.classmodel.wvector   = false;

%% EXTRA CONFIGURATION:

cfg.classmodel.tempgen = false;
cfg.classmodel.extdiag = false;
cfg.classmodel.permlab = false;

% Enable parallel comp. if the Distrib_Computing_Toolbox is installed:

if license('test','Distrib_Computing_Toolbox')
    cfg.classmodel.parcomp = true;
else
    cfg.classmodel.parcomp = false;
end

%% CROSS-VALIDATIONN PROCEDURE:

% cfg.cv.method = 'kfold' - K-Fold cross-validation.
% cfg.cv.method = 'loo'   - Leave-one-out cross-validation.

cfg.cv.method  = 'kfold';
cfg.cv.nfolds  = 5;

%% PERMUTATION TEST

cfg.stats.flag   = true;
cfg.stats.nper   = 100;
cfg.stats.nperg  = 1e5;
cfg.stats.pgroup = 99.9;
cfg.stats.pclust = 99.9;
cfg.stats.shownulldis = 0;

And finally, the following scripts generate a graphic representation of the result:

%% Mean accuracy plot (no statistical significance)
% -------------------------------------------------------------------------
% Brain, Mind and Behavioral Research Center - University of Granada.
% Contact: [email protected]
% -------------------------------------------------------------------------

% Initialize and configure plots:
graph = mvpalab_plotinit();

%% Load results if needed: 
load results/time_resolved/acc/result.mat

% Axis limits:
graph.xlim = [-200 1500];
graph.ylim = [.3 .95];

% Axes labels and titles:
graph.xlabel = 'Time (ms)';
graph.ylabel = 'Classifier performance';
graph.title = 'Demo plot (no statistical significance)';

% Smooth results:
graph.smoothdata = 5; % (1 => no smoothing)

% Plot results:
figure;
hold on
mvpalab_plotdecoding(graph,cfg,result);
%% Mean accuracy plot (statistical significance)
% -------------------------------------------------------------------------
% Brain, Mind and Behavioral Research Center - University of Granada.
% Contact: [email protected]
% -------------------------------------------------------------------------

% Initialize and configure plots:
graph = mvpalab_plotinit();

% Load results and and statistics:
load results/time_resolved/acc/result.mat
load results/time_resolved/acc/stats.mat

% Axis limits:
graph.xlim = [-200 1500];
graph.ylim = [.3 .95];

% Axes labels and titles:
graph.xlabel = 'Time (ms)';
graph.ylabel = 'Classifier performance';
graph.title = 'Demo plot (no statistical significance)';

% Plot significant clusters (above and below chance):
graph.stats.above = true;
graph.stats.below = true;

% Significant indicator:
graph.sigh = .4;

% Title:
graph.title = 'Demo plot (statistical significance)';

% Plot results:
figure;
hold on
mvpalab_plotdecoding(graph,cfg,result,stats);
Clone this wiki locally