% STD_READFILE - Read data file containing STUDY measures.
%
% Usage:
% >> [data param range1 range2] = std_readfile(filename, 'key', val);
%
% Inputs:
% filename - [string] read specific file, for instance 's1.daterp'
% containing ERP data for dataset "s1.set". It is also
% possible to provide only the "base" file name "s1" and
% function will load the appropriate file based on the
% selected input measure (measure input).
%
% Optional inputs:
% 'channels' - [cell or integer] channel labels - for instance
% { 'cz' 'pz' } of channels to load from the data file.
% 'components' - [integer] component index in the selected EEG dataset
% for which to read the data
% 'timelimits' - [min max] ERSP/ERP time (latency in ms) range of interest
% 'freqlimits' - [min max] ERSP/Spectrum frequency range (in Hz) of interest
% 'measure' - ['erp'|'spec'|'ersp'|'itc'|'timef'|'erspbase'|'erspboot'
% 'itcboot'|'erpim'] data measure to read. If a full file name
% is provided, the data measure is selected automatically.
% 'getparamsonly' - ['on'|'off'] only read file parameters not data.
% 'trialselect' - Cell of cells defining the field and the values
% of the field in the trialinfo structure to be used to pull out the trials.
% i.e. if values are string: {'field1',{val1_fromfield1 val2_fromfield1}, 'field2'...}
% if values are numeric: {'field1',[val1 val2], 'field2'...}
% 'designvar' - Structure of independent variables with fields 'label'
% and 'value'. Each independent variable should have these
% two fields so the function can use these values to pull
% out the trials. If empty, all the trials are read.
% 'singletrials' - { 'on','off' } Extract single trials. Default 'off'
% 'getparamonly' - { 'on','off'} Get only parameters. Default 'off'
% 'concatenate' - { 'on','off'} In case of ERP images this function
% set 'singletrials' to 'on'. Default 'off'
% 'dataindices' - obsolete input
%
% Outputs:
% data - the multi-channel or multi-component data. The size of this
% output depends on the number of dimension (for ERSP or ERP
% for instance). The last dimension is channels or components.
% params - structure containing parameters saved with the data file.
% range1 - time points (ERP, ERSP) or frequency points (spectrum)
% range2 - frequency points (ERSP, ITCs)
% events - Event read from the data structure
%
% Examples:
% % the examples below read all data channels for the selected files
% [ersp params times freqs] = std_readfiles('s1.datersp');
% [erp params times] = std_readfiles('s1.daterp');
% [erp params times] = std_readfiles('s1.daterp', timerange', [-100 500]);
% [erp params times] = std_readfiles('s1.daterp', timerange', [-100 500],'trialselect','load',[1 2],'type', {'Y'});
%
% Authors: Arnaud Delorme, SCCN, INC, UCSD, May 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.
% dimensions
% time x freqs x channel_comps x subjects_trials
function [measureData, parameters, measureRange1, measureRange2, events, trialInfo] = std_readfile(fileBaseName, varargin)
if nargin < 1
help std_readfile;
return;
end
if iscell(fileBaseName)
% this is when the data of a subject is split across sessions
% then we need to call std_readfile several times and combine
% the results
% ------------------------------------------------------------
if length(fileBaseName) > 1
for iFile = 1:length(fileBaseName)
[measureDataTmp{iFile}, parameters, measureRange1, measureRange2, eventsTmp{iFile}, trialInfoTmp{iFile}] = std_readfile(fileBaseName{iFile}, varargin{:});
end
% get the size of each session cond x group and sum 3rd dim (trials) for each cond and group
% ignore empty cells; these are always trials, even when processing subjects
measureData = cell(size(measureDataTmp{1})); % cond x group
sz = cell(size(measureDataTmp{1})); % cond x group
trialInfo = cell(size(measureDataTmp{1})); % cond x group
for iSess = 1:length(measureDataTmp) % scan session
szTmp = cellfun(@size, measureDataTmp{iSess}, 'uniformoutput', false);
for iCond = 1:length(sz(:))
if szTmp{iCond}(1) ~= 0
if isempty(sz{iCond})
sz{iCond} = szTmp{iCond};
else
sz{iCond}(2) = sz{iCond}(2)+szTmp{iCond}(2);
end
end
end
end
for iCond = 1:length(sz(:))
trialInfo{ iCond} = [];
if isempty(sz{iCond})
measureData{iCond} = [];
else
measureData{iCond} = zeros(sz{iCond});
end
end
% combine arrays
events = eventsTmp{1};
for iCond = 1:length(measureDataTmp{1}(:))
pointer = 1;
for iSess = 1:length(measureDataTmp)
if ~isempty(measureDataTmp{iSess}{iCond})
% when trials concatenate them
measureData{iCond}(:, pointer:pointer+size(measureDataTmp{iSess}{iCond},2)-1,:,:) = measureDataTmp{iSess}{iCond};
trialInfo{iCond} = [ trialInfo{iCond} trialInfoTmp{iSess}{iCond} ];
pointer = pointer + size(measureDataTmp{iSess}{iCond},2);
end
end
end
return
else
fileBaseName = fileBaseName{1};
end
end
limomeasures = {'itcbeta1' , 'itcbeta2' ,'itcr2r' ,'itcr2f' ,'itcr2p' ,...
'erpbeta1' , 'erpbeta2' ,'erpr2r' ,'erpr2f' ,'erpr2p' ,...
'erspbeta1', 'erspbeta2','erspr2r','erspr2f','erspr2p',...
'specbeta1', 'specbeta2','specr2r','specr2f','specr2p'};
opt = finputcheck(varargin, { 'components' 'integer' [] [];
'getparamonly' 'string' { 'on','off' } 'off';
'trialselect' 'cell' {} {};
'trialinfo' 'struct' {} struct([]);
'designvar' 'struct' [] struct([]);
'singletrials' 'string' { 'on','off' } 'off';
'concatenate' 'string' { 'on', 'off' } 'off'
'channels' 'cell' [] {}
'cache' 'struct' [] struct([]);
'function' { 'function_handle' 'integer' } [] [];
'measure' 'string' {limomeasures{:} 'erp' 'spec' 'timef' 'topo'} 'erp';
'timelimits', 'real', [], [];
'triallimits', 'real', [], [];
'freqlimits' 'real' [] [];
'dataindices' 'integer' [] [] }, 'std_readfile');
if ischar(opt), error(opt); end
if ~isempty(opt.triallimits), opt.freqlimits = opt.triallimits; end
if strcmpi(opt.concatenate, 'on'), opt.singletrials = 'on'; end
% get file extension
% ------------------
if ~isempty(opt.channels) || (~isempty(opt.dataindices) && opt.dataindices(1) < 0) , dataType = 'chan';
else dataType = 'comp';
end
[~, ~, currentFileExt] = fileparts(fileBaseName);
if length(currentFileExt) > 3 && (strcmpi(currentFileExt(2:4), 'dat') || strcmpi(currentFileExt(2:4), 'ica'))
opt.measure = currentFileExt(5:end);
if strcmpi(currentFileExt(2:4), 'dat'), dataType = 'chan';
else dataType = 'comp';
end
fileExt = '';
else
if strcmpi(dataType, 'chan'), fileExt = [ '.dat' opt.measure ];
else fileExt = [ '.ica' opt.measure ];
end
end
if nargin > 5 && strcmpi(opt.singletrials, 'on')
indFlag = true;
else indFlag = false;
end
% get fields to read
% ------------------
v6Flag = testv6([ fileBaseName fileExt ]);
v6Flag = 1;
if v6Flag || strcmpi(opt.measure, 'topo')
if ~isempty(opt.channels)
fileData = load('-mat', [ fileBaseName fileExt ], 'labels');
end
else
fileData = matfile([ fileBaseName fileExt ]);
end
% get channel or component indices
% --------------------------------
if ~isempty(opt.channels) && isnumeric(opt.channels)
opt.dataindices = opt.channels;
elseif ~isempty(opt.channels)
chan.chanlocs = struct('labels', fileData.labels);
opt.dataindices = std_chaninds(chan, opt.channels);
elseif ~isempty(opt.components)
opt.dataindices = opt.components;
else opt.dataindices = abs(opt.dataindices);
end
if v6Flag
for iChan = 1:length(opt.dataindices)
chanList{iChan} = [ dataType int2str(opt.dataindices(iChan)) fastif(strcmpi(opt.measure, 'topo'), '_grid', '') ];
end
warning('off', 'MATLAB:load:variableNotFound');
if ~isempty(opt.dataindices)
fileData = load('-mat', [ fileBaseName fileExt ], chanList{:}, 'trialinfo', 'times', 'freqs', 'parameters', 'events', 'chanlocsforinterp', 'chanall');
else fileData = load('-mat', [ fileBaseName fileExt ], 'trialinfo', 'times', 'freqs', 'parameters', 'events', 'chanlocsforinterp', 'chanall');
end
% read all channels ERPimage only
if length(opt.channels) > 2 && isfield(fileData, 'chanall')
chanlocsforinterp = fileData.chanlocsforinterp;
tmpX = eval( fileData.chanall );
for iChan = 1:size(tmpX,1)
fileData.(sprintf('chan%d', iChan)) = squeeze(tmpX(iChan,:,:));
end
end
if strcmpi(opt.measure, 'topo') && ~isfield(fileData, 'trialinfo'), error('Compatibilty issue. Recompute ICA topographic maps'); end
warning('on', 'MATLAB:load:variableNotFound');
end
% scan datasets
% -------------
if strcmpi(opt.getparamonly, 'on'), opt.dataindices = 1; end
measureRange1 = [];
measureRange2 = [];
measureData = [];
parameters = [];
events = {};
% get output for parameters and measure ranges
% --------------------------------------------
fileFields = fieldnames(fileData);
if ~isempty( strmatch('parameters', fileFields) )
parameters = removedup(fileData.parameters);
for index = 1:length(parameters), if iscell(parameters{index}), parameters{index} = { parameters{index} }; end; end
parameters = struct(parameters{:});
end
if ~isempty(strmatch('times', fileFields, 'exact')), measureRange1 = fileData.times; end
if ~isempty(strmatch('freqs', fileFields, 'exact')), measureRange2 = fileData.freqs; end
if isempty(measureRange1) && isfield(fileData, 'chan1'), measureRange1 = 1:size(fileData.chan1,1); end
% if the function is only called to get parameters
% ------------------------------------------------
if strcmpi(opt.measure, 'spec'), measureRange1 = measureRange2; opt.timelimits = opt.freqlimits; end
if ~isempty(opt.timelimits)
[measureRange1, indBegin1, indEnd1 ] = indicesselect(measureRange1, opt.timelimits);
else
indBegin1 = 1;
indEnd1 = length(measureRange1);
end
if ~isempty(opt.freqlimits)
[measureRange2, indBegin2, indEnd2 ] = indicesselect(measureRange2, opt.freqlimits);
else
indBegin2 = 1;
indEnd2 = length(measureRange2);
end
if strcmpi(opt.getparamonly, 'on')
return;
end
options = { opt.dataindices, opt.function, dataType, indBegin1, indEnd1, indBegin2, indEnd2 };
if isempty(opt.designvar)
[ measureData, trialInfo, events ] = getfiledata(fileData, NaN, v6Flag, options{:}); % read all data
measureData = { measureData };
trialInfo = { trialInfo };
events = { events };
else
[ measureData, trialInfo, events ] = globalgetfiledata(fileData, opt.designvar, options, {}, v6Flag);
end
% remove duplicates in the list of parameters
% -------------------------------------------
function cella = removedup(cella)
[tmp, indices] = unique_bc(cella(1:2:end));
if length(tmp) ~= length(cella)/2
%fprintf('Warning: duplicate ''key'', ''val'' parameter(s), keeping the last one(s)\n');
end
cella = cella(sort(union(indices*2-1, indices*2)));
% find indices for selection of measure
% -------------------------------------
function [measureRange, indBegin, indEnd] = indicesselect(measureRange, measureLimits)
indBegin = 1;
indEnd = length(measureRange);
if ~isempty(measureRange) && ~isempty(measureLimits) && (measureLimits(1) > measureRange(1) || measureLimits(end) < measureRange(end))
indBegin = min(find(measureRange >= measureLimits(1)));
indEnd = max(find(measureRange <= measureLimits(end)));
measureRange = measureRange(indBegin:indEnd);
end
% recursive function to load data
% -------------------------------
function [ measureData, trialInfo, eventVals ] = globalgetfiledata(fileData, designvar, options, trialselect, v6Flag)
if length(designvar) == 0
[ measureData, trialInfo, eventVals ] = getfiledata(fileData, trialselect, v6Flag, options{:});
measureData = { measureData };
trialInfo = { trialInfo };
eventVals = { eventVals };
else
% scan independent variable values
if isfield(designvar(1), 'vartype') && strcmpi('continuous', designvar(1).vartype)
if ~ischar(designvar(1).value), designvar(1).value = ''; end
trialselect = { trialselect{:} designvar(1).label designvar(1).value };
[ tmpMeasureData, tmpTrialInfo, tmpEvents ] = globalgetfiledata(fileData, designvar(2:end), options, trialselect, v6Flag);
measureData(1,:,:,:) = reshape(tmpMeasureData, [ 1 size(tmpMeasureData) ]);
trialInfo( 1,:,:,:) = reshape(tmpTrialInfo, [ 1 size(tmpTrialInfo ) ]);
eventVals( 1,:,:,:) = reshape(tmpEvents , [ 1 size(tmpEvents ) ]);
else
trialselectOri = trialselect;
for iField = 1:length(designvar(1).value)
trialselect = { trialselectOri{:} designvar(1).label designvar(1).value{iField} };
[ tmpMeasureData, tmpTrialInfo, tmpEvents ] = globalgetfiledata(fileData, designvar(2:end), options, trialselect, v6Flag);
measureData(iField,:,:,:) = reshape(tmpMeasureData, [ 1 size(tmpMeasureData) ]);
trialInfo( iField,:,:,:) = reshape(tmpTrialInfo , [ 1 size(tmpTrialInfo ) ]);
eventVals( iField,:,:,:) = reshape(tmpEvents , [ 1 size(tmpEvents ) ]);
end
end
end
% load data from structure or file
% --------------------------------
function [ fieldData, trialInfo, events ] = getfiledata(fileData, trialselect, v6Flag, chan, func, dataType, indBegin1, indEnd1, indBegin2, indEnd2)
persistent tmpcache;
persistent hashcode;
if length(chan) > 1
% error('This function can only read one channel at a time');
end
% get trial indices
fieldData = [];
trialInfo = [];
events = [];
subTrials = [];
trials = [];
if ~isempty(trialselect)
if isnumeric(trialselect) && isnan(trialselect(1))
trials = [1:length(fileData.trialinfo)]; % read all trials if NaN
else
[trials, events] = std_gettrialsind(fileData.trialinfo, trialselect{:});
if length(unique(diff(trials))) > 1 && ~v6Flag % speed up access when subsets of trials are selected
temptrials = [trials(1):trials(end)];
subTrials = trials-trials(1)+1;
trials = temptrials;
end
end
end
% get trial information
trialInfo = fileData.trialinfo(trials);
if ~isempty(subTrials), tmpTrialInfo = tmpTrialInfo(subTrials); end
% scan channels
for index = 1:length(chan)
allfields = fieldnames(fileData);
topoFlag = ~isempty(findstr(allfields{1}, '_grid'));
fieldToRead = [ dataType int2str(chan(index)) fastif(topoFlag, '_grid', '') ];
% find trials
if isempty(trials)
return;
% trials = size(fileData.(fieldToRead), ndims(fileData.(fieldToRead))); % not sure what this does
end
% load data
warning('off', 'MATLAB:MatFile:OlderFormat');
if ischar(fileData.(fieldToRead)) % special ERP-image
try
fileData.chanlocsforinterp; % isfield does not work because fileData is a MatFile
catch, error('Missing field in ERPimage STUDY file, try recomputing them');
end
chanlocsforinterp = fileData.chanlocsforinterp;
% caching for ERPimage only
if isequal(hashcode, fileData.(fieldToRead))
tmpFieldData = tmpcache;
else
tmpFieldData = eval( fileData.(fieldToRead) );
tmpcache = tmpFieldData;
hashcode = fileData.(fieldToRead);
end
tmpFieldData = tmpFieldData(indBegin1:indEnd1,trials);
if ~isempty(subTrials)
tmpFieldData = tmpFieldData(:, subTrials);
tmpTrialInfo = tmpTrialInfo(subTrials);
end
else
if topoFlag
tmpFieldData = fileData.(fieldToRead);
if isempty(trials), tmpFieldData = []; end
elseif ndims(fileData.(fieldToRead)) == 2
tmpFieldData = fileData.(fieldToRead)(indBegin1:indEnd1,trials);
if ~isempty(subTrials), tmpFieldData = tmpFieldData(:, subTrials); end
else
tmpFieldData = fileData.(fieldToRead)(indBegin2:indEnd2,indBegin1:indEnd1,trials); % frequencies first here
if ~isempty(subTrials), tmpFieldData = tmpFieldData(:, :, subTrials); end
end
end
if isfield(fileData, 'events') && ~isempty(fileData.events)
events = fileData.events(trials);
if ~isempty(subTrials), events = events(subTrials); end
end
warning('on', 'MATLAB:MatFile:OlderFormat');
% average single trials if necessary
if ~isempty(func)
tmpFieldData = func(tmpFieldData);
end
% store data
if index == 1 && length(chan) == 1
fieldData = tmpFieldData;
else
if index == 1
fieldData = zeros([ size(tmpFieldData) length(chan) ]);
end
if ndims(tmpFieldData) == 2
fieldData(:,:,index) = tmpFieldData;
else
fieldData(:,:,:,index) = tmpFieldData;
end
end
end
% see if a file is v6 of v7.3
function v6 = testv6(x)
fid=fopen(x);
if fid == -1
error('File %s not found - this could be because your STUDY contains data files with relative path, try changing your MATLAB path to the STUDY folder', x);
end
txt=char(fread(fid,20,'uchar')');
tmp = fclose(fid);
txt=[txt,char(0)];
txt=txt(1:find(txt==0,1,'first')-1);
if ~isempty(strfind(txt, 'MATLAB 5.0')), v6 = true; else v6 = false; end