% std_getindvar - get independent variables of a STUDY
%
% Usage:
% [indvar indvarvals] = std_getindvar(STUDY);
% [indvar indvarvals] = std_getindvar(STUDY, mode, scandesign);
%
% Input:
% STUDY - EEGLAB STUDY structure
% mode - ['datinfo'|'trialinfo'|'both'] get independent variables
% linked to STUDY.datasetinfo, STUDY.datasetinfo.trialinfo or
% both. Default is 'both'.
% scandesign - [0|1] scan STUDY design for additional combinations of
% independent variable values. Default is 0.
%
% Output:
% indvar - [cell array] cell array of independent variable names
% indvarvals - [cell array] cell array of independent variable values
%
% Authors: A. Delorme, CERCO/CNRS and SCCN/UCSD, 2010-
% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, 2010, arno@sccn.ucsd.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.
function [ factor, factorvals, subjects, paired ] = std_getindvar(STUDY, mode, scandesign)
if nargin < 1
help std_getindvar;
return;
end
factor = {};
factorvals = {};
paired = {};
subjects = {};
if nargin < 2, mode = 'both'; end
if nargin < 3, scandesign = 0; end
countfact = 1;
setinfo = STUDY.datasetinfo;
if strcmpi(mode, 'datinfo') || strcmpi(mode, 'both')
% get trial info
ff = fieldnames(setinfo);
ff = setdiff_bc(ff, { 'filepath' 'filename' 'subject' 'index' 'ncomps' 'comps' 'trialinfo' });
for index = 1:length(ff)
% check we have the same data type
allSetinfoVals = { setinfo.(ff{index}) };
if any(cellfun(@ischar, allSetinfoVals))
if ~all(cellfun(@ischar, allSetinfoVals))
allSetinfoVals = cellfun(@num2str, allSetinfoVals, 'uniformoutput', false);
end
end
if ischar(allSetinfoVals{1})
tmpvals = unique_bc(allSetinfoVals);
if length(tmpvals) > 1
factor{ countfact} = ff{index};
factorvals{countfact} = tmpvals;
% get subject for each factor value
intersectSubject = { setinfo(:).subject };
for c = 1:length(tmpvals)
datind = strmatch(tmpvals{c}, allSetinfoVals, 'exact');
subjects{ countfact}{c} = unique_bc( { setinfo(datind).subject } );
intersectSubject = intersect(intersectSubject, subjects{ countfact}{c});
end
numValues = cellfun(@length, subjects{ countfact});
if length(intersectSubject) == numValues(1) && length(unique(numValues)) == 1
paired{countfact} = 'on'; % trial data always paired
else
paired{countfact} = 'off';
end
countfact = countfact + 1;
end
else
tmpvals = unique_bc([ allSetinfoVals{:} ] );
if length(tmpvals) > 1
factor{ countfact} = ff{index};
factorvals{countfact} = mattocell(tmpvals);
% get subject for each factor value
intersectSubject = { setinfo(:).subject };
for c = 1:length(tmpvals)
eval( [ 'datind = find(tmpvals(c) == [ setinfo.' ff{index} ']);' ] );
subjects{ countfact}{c} = unique_bc( { setinfo(datind).subject } );
intersectSubject = intersect(intersectSubject, subjects{ countfact}{c});
end
numValues = cellfun(@length, subjects{ countfact});
if length(intersectSubject) == numValues(1) && length(unique(numValues)) == 1
paired{countfact} = 'on'; % trial data always paired
else
paired{countfact} = 'off';
end
countfact = countfact + 1;
end
end
end
end
% add ind. variables for trials
% -----------------------------
if strcmpi(mode, 'trialinfo') || strcmpi(mode, 'both')
% add trial info
if isfield(setinfo, 'trialinfo')
ff = fieldnames(setinfo(1).trialinfo);
for index = 1:length(ff)
% check if any of the datasets are using string for event type
allFieldsPresent = cellfun(@(x)(isfield(x, ff{index})), { setinfo.trialinfo });
allFirstVal = cellfun(@(x)(getfield(x, ff{index})), { setinfo(allFieldsPresent).trialinfo }, 'uniformoutput', false);
if any(cellfun(@isstr, allFirstVal))
alltmpvals = {};
for ind = 1:length(setinfo)
if isfield(setinfo(ind).trialinfo, ff{index})
eval( [ 'tmpTrialVals = { setinfo(ind).trialinfo.' ff{index} ' };' ] );
if isnumeric(tmpTrialVals{1})
% convert to string if necessary
tmpTrialVals = cellfun(@num2str, tmpTrialVals, 'uniformoutput', false);
end
tmpvals = unique_bc(tmpTrialVals);
else tmpvals = {};
end
if isempty(alltmpvals)
alltmpvals = tmpvals;
else
alltmpvals = { alltmpvals{:} tmpvals{:} };
end
end
alltmpvals = unique_bc(alltmpvals);
if length(alltmpvals) > 1
factor{ countfact} = ff{index};
factorvals{countfact} = alltmpvals;
subjects{ countfact} = {};
paired{countfact} = 'on'; % trial data always paired
countfact = countfact + 1;
end
else
alltmpvals = [];
for ind = 1:length(setinfo)
if isfield(setinfo(ind).trialinfo, ff{index}) && ~iscell( setinfo(ind).trialinfo(1).(ff{index}))
eval( [ 'tmpvals = unique_bc([ setinfo(ind).trialinfo.' ff{index} ' ]);' ] );
else tmpvals = [];
end
alltmpvals = [ alltmpvals tmpvals ];
end
alltmpvals = unique_bc(alltmpvals);
if length(alltmpvals) > 1
factor{ countfact} = ff{index};
factorvals{countfact} = mattocell(alltmpvals);
subjects{ countfact} = {};
paired{countfact} = 'off'; % pairing is irrelevant for continuous var
countfact = countfact + 1;
end
end
end
end
end
% scan existing design for additional combinations
% ------------------------------------------------
if scandesign
for desind = 1:length(STUDY.design)
for iVar = 1:length(STUDY.design(desind).variable)
if strcmpi(STUDY.design(desind).variable(iVar).vartype, 'categorical')
pos1 = strmatch(STUDY.design(desind).variable(iVar).label, factor, 'exact');
if ~isempty(pos1), add1 = mysetdiff(STUDY.design(desind).variable(iVar).value, factorvals{pos1}); else add1 = []; end
for addInd = 1:length(add1)
duplicate = 0;
for iVarVal = 1:length(factorvals{pos1})
if isequal(factorvals{pos1}{iVarVal}, add1{addInd})
duplicate = 1;
end
end
if ~duplicate
factorvals{pos1} = { factorvals{pos1}{:} add1{addInd} };
end
end
end
end
end
end
function cellout = mysetdiff(cell1, cell2);
if ischar(cell2{1})
indcell = cellfun(@iscell, cell1);
else indcell = cellfun(@(x)(length(x)>1), cell1);
end
cellout = cell1(indcell);
% if ischar(cell1{1}) && ischar(cell2{1})
% cellout = setdiff_bc(cell1, cell2);
% elseif ~ischar(cell1{1}) && ~ischar(cell2{1})
% cellout = mattocell(setdiff( [ cell1{:} ], [ cell2{:} ]));
% elseif ischar(cell1{1}) && ~ischar(cell2{1})
% cellout = setdiff_bc(cell1, cellfun(@(x)(num2str(x)),cell2, 'uniformoutput', false));
% elseif ~ischar(cell1{1}) && ischar(cell2{1})
% cellout = setdiff_bc(cellfun(@(x)(num2str(x)),cell1, 'uniformoutput', false), cell2);
% end