diff --git a/eeglab.m b/eeglab.m index 3355d9a72..394370b37 100644 --- a/eeglab.m +++ b/eeglab.m @@ -1404,6 +1404,10 @@ function updatemenu() index = 1; indexmenu = 1; MAX_SET = max(length( ALLEEG ), length(EEGMENU)-1); +if MAX_SET > 200 + disp('Updating menu, allowing selection of the first 200 datasets only to speed up display...') + MAX_SET = 200; +end tmp = warning; warning off; diff --git a/eeglablicense.txt b/eeglablicense.txt index 06027f641..9a9a95265 100644 --- a/eeglablicense.txt +++ b/eeglablicense.txt @@ -1,6 +1,6 @@ This is the license for the core for the eeglab.m function and the source code in the "functions" folder. EEGLAB plugins (in the -"plugins" folder) may be released under different licenses. +"plugins" folder) may be released under different licenses. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/functions/adminfunc/eeg_checkset.m b/functions/adminfunc/eeg_checkset.m index f2511ec56..abfedbbf0 100644 --- a/functions/adminfunc/eeg_checkset.m +++ b/functions/adminfunc/eeg_checkset.m @@ -189,6 +189,11 @@ % checking multiple datasets % -------------------------- if length(EEG) > 1 + + if length(EEG) > 5000 + disp('Too many datasets, abording check') + return; + end if nargin > 1 switch varargin{1} diff --git a/functions/guifunc/inputdlg2.m b/functions/guifunc/inputdlg2.m index 88cc0ae6f..d3945bdd7 100644 --- a/functions/guifunc/inputdlg2.m +++ b/functions/guifunc/inputdlg2.m @@ -48,16 +48,32 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF % THE POSSIBILITY OF SUCH DAMAGE. -function [result] = inputdlg2(Prompt,Title,LineNo,DefAns,funcname); +function [result] = inputdlg2(Prompt,Title,LineNo,DefAns,funcname) +if nargin < 2 + help inputdlg2; + return; +end +if nargin < 3 + LineNo = 1; +end if nargin < 4 + DefAns = {}; +end +if nargin < 2 help inputdlg2; return; -end; +end if nargin < 5 funcname = ''; end +if ~iscell(Prompt), Prompt = { Prompt }; end +if isempty(DefAns) + DefAns = cell(1,length(Prompt)); + DefAns(:) = { '' }; +end + if length(Prompt) ~= length(DefAns) error('inputdlg2: prompt and default answer cell array must have the same size'); end diff --git a/functions/guifunc/questdlg2.m b/functions/guifunc/questdlg2.m index e542f763f..571ad0f59 100644 --- a/functions/guifunc/questdlg2.m +++ b/functions/guifunc/questdlg2.m @@ -37,7 +37,7 @@ % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF % THE POSSIBILITY OF SUCH DAMAGE. -function [result] = questdlg2(Prompt,Title,varargin); +function [result] = questdlg2(Prompt,Title,varargin) result = ''; if nargin < 2 @@ -47,7 +47,6 @@ if isempty(varargin) varargin = { 'Yes' 'No' 'Cancel' 'Yes' }; end -result = varargin{end}; if Prompt(end) == 10, Prompt(end) = []; end if Prompt(end) == 10, Prompt(end) = []; end if Prompt(end) == 10, Prompt(end) = []; end @@ -74,9 +73,17 @@ end listui{end+1} = {}; +width = 80; geometry = { geometry{:} 1 ones(1,length(varargin)-1) }; for index = 1:length(varargin)-1 % ignoring default val - listui = {listui{:} { 'width',80,'align','center','Style', 'pushbutton', 'string', varargin{index}, 'callback', ['set(gcbf, ''userdata'', ''' varargin{index} ''');'] } }; + if index == 1 + if length(varargin{index}) > 15 + width = 160; + elseif length(varargin{index}) > 10 + width = 120; + end + end + listui = {listui{:} { 'width',width,'align','center','Style', 'pushbutton', 'string', varargin{index}, 'callback', ['set(gcbf, ''userdata'', ''' varargin{index} ''');'] } }; if strcmp(varargin{index}, varargin{end}) listui{end}{end+1} = 'fontweight'; listui{end}{end+1} = 'bold'; diff --git a/functions/miscfunc/arrow.m b/functions/miscfunc/arrow.m old mode 100755 new mode 100644 diff --git a/functions/miscfunc/corrimage.m b/functions/miscfunc/corrimage.m old mode 100755 new mode 100644 diff --git a/functions/miscfunc/erpregout.m b/functions/miscfunc/erpregout.m old mode 100755 new mode 100644 diff --git a/functions/miscfunc/erpregoutfunc.m b/functions/miscfunc/erpregoutfunc.m old mode 100755 new mode 100644 diff --git a/functions/popfunc/pop_chanedit.m b/functions/popfunc/pop_chanedit.m index fa5532e6c..2a80f7153 100644 --- a/functions/popfunc/pop_chanedit.m +++ b/functions/popfunc/pop_chanedit.m @@ -194,6 +194,7 @@ end % Apply to all datasets and resave if necessary + if isempty(com), return; end eeglab_options for iDat = 1:length(chans) EEG = chans(iDat); diff --git a/functions/popfunc/pop_envtopo.m b/functions/popfunc/pop_envtopo.m old mode 100755 new mode 100644 diff --git a/functions/popfunc/pop_rejcont.m b/functions/popfunc/pop_rejcont.m old mode 100755 new mode 100644 diff --git a/functions/sigprocfunc/envtopo.m b/functions/sigprocfunc/envtopo.m old mode 100755 new mode 100644 diff --git a/functions/studyfunc/pop_limo.m b/functions/studyfunc/pop_limo.m index 99393387e..dca3cd522 100644 --- a/functions/studyfunc/pop_limo.m +++ b/functions/studyfunc/pop_limo.m @@ -132,7 +132,7 @@ ' set(findobj(gcbf, ''tag'', ''options''), ''string'', ''''''freqlim'''', [1 25]'');' ... 'end;' ]; cb_listfactors = [ 'pop_listfactors(STUDY, ''gui'', ''on'', ' ... - '''level'', ''one'',' ... + '''level'', ''both'',' ... '''splitreg'' , fastif(get(findobj(gcbf, ''tag'', ''splitreg'' ), ''value''), ''on'', ''off''),' ... '''interaction'', fastif(get(findobj(gcbf, ''tag'', ''interaction''), ''value''), ''on'', ''off''));' ]; uilist = { ... diff --git a/functions/studyfunc/pop_listfactors.m b/functions/studyfunc/pop_listfactors.m index f403f5ef3..9570381df 100644 --- a/functions/studyfunc/pop_listfactors.m +++ b/functions/studyfunc/pop_listfactors.m @@ -57,14 +57,19 @@ g = finputcheck(varargin, { 'gui' 'string' { 'on' 'off' } 'on'; 'splitreg' 'string' { 'on','off' } 'off'; 'contrast' 'string' { 'on','off' } 'off'; + 'constant' 'string' { 'on','off' } 'on'; 'level' 'string' { 'one','two','both'} 'both'; + 'vartype' 'string' { 'categorical','continuous','both'} 'both'; 'interaction' 'string' { 'on','off' } 'off' }); if ischar(g) error(g); end if isfield(des,'design') + STUDY = des; des = des.design; +else + STUDY = []; end allFactors = {}; % (strings) still used to find unique values @@ -106,6 +111,24 @@ allFactorsStruct = allFactorsStruct(inds); end +% second level +if ~isempty(STUDY) + [indvar, indvarvals] = std_getindvar(STUDY, 'datinfo'); + % remove non numerical values + for iVar = length(indvar):-1:1 + allFactorsStruct(end+1).label = indvar{iVar}; + allFactorsStruct(end ).level = 'two'; + if ~isnumeric(indvarvals{iVar}{1}) + allFactorsStruct(end).vartype = 'categorical'; + allFactorsStruct(end).value = indvarvals{iVar}; % multiple values + allFactorsStruct(end).description = [ allFactorsStruct(end).label ' - categorical (several values)']; + else + allFactorsStruct(end).vartype = 'continuous'; + allFactorsStruct(end).description = [ allFactorsStruct(end).label ' - continuous']; + end + end +end + % filter first or second level if ~strcmpi(g.level, 'both') allFactorsStruct = filterlevel(allFactorsStruct, g.level); @@ -128,20 +151,20 @@ end % generate categorical labels - allLabels1 = getlabels(des1); - allLabels2 = getlabels(des2); - + allLabels1 = getlabels_l1(des1); + allLabels2 = getlabels_l2(des1, des2); + listui = {}; if ~isempty(des2) - listui{2,1} = { 'Style', 'text', 'string' '2nd-level variables' 'fontweight' 'bold' }; - listui{2,2} = { 'Style', 'text', 'string' '(inter participant)' }; + listui{2,1} = { 'Style', 'text', 'string' '2nd-level available statistics' 'fontweight' 'bold' }; + listui{2,2} = { 'Style', 'text', 'string' '(inter subject)' }; for index = 1:length(allLabels2) listui{2,index+2} = { 'Style', 'text', 'string' allLabels2{index} }; end end if ~isempty(des1) listui{1,1} = { 'Style', 'text', 'string' '1st-level variables' 'fontweight' 'bold' }; - listui{1,2} = { 'Style', 'text', 'string' '(intra participant)' }; + listui{1,2} = { 'Style', 'text', 'string' '(intra subject)' }; for index = 1:length(allLabels1) listui{1,index+2} = { 'Style', 'text', 'string' allLabels1{index} }; end @@ -195,7 +218,7 @@ waitfor( fig, 'userdata'); end - try, + try result = get(fig, 'userdata'); close(fig); drawnow; @@ -203,6 +226,18 @@ end +% add the constant level one +if ~strcmpi(g.level, 'two') && strcmpi(g.constant, 'on') + allFactorsStruct(end+1).label = 'constant'; + allFactorsStruct(end ).level = 'one'; + allFactorsStruct(end ).vartype = 'categorical'; + allFactorsStruct(end ).description = 'Constant'; +end +if ~strcmpi(g.vartype, 'both') + inds = strmatch(g.vartype, {allFactorsStruct.vartype}); + allFactorsStruct = allFactorsStruct(inds); +end + % convert nested values to linear sequence function res = getstrval(vals) @@ -227,7 +262,10 @@ tmpFactor = sprintf('%s (continuous)', cellVal{iItem}); elseif isnumeric(cellVal{iItem+1}) tmpFactor = sprintf('%s = %d', cellVal{iItem}, cellVal{iItem+1}); - else tmpFactor = sprintf('%s = %s', cellVal{iItem}, cellVal{iItem+1}); + elseif iscell(cellVal{iItem+1}) + tmpFactor = sprintf('%s', cellVal{iItem}); + else + tmpFactor = sprintf('%s = %s', cellVal{iItem}, cellVal{iItem+1}); end if iItem == 1 str = tmpFactor; @@ -245,8 +283,10 @@ end allFactorsStruct(rmInd) = []; -% get labels for design -function allLabels = getlabels(des) +% ------- +% level 1 +% ------- +function allLabels = getlabels_l1(des) allLabels = {}; count = 1; if isempty(des), return; end @@ -264,4 +304,46 @@ end count = count+1; end +% add constant allLabels{count} = [ int2str(count) '. Constant' ]; + +% ------- +% level 2 +% ------- +function allLabels = getlabels_l2(des1, des2) +allLabels = {}; +count = 1; +if isempty(des2), return; end +allLabels{count} = '* One-sample t-test on 1st level var.'; count = count+1; +if ~isempty(des1.categorical) + allLabels{count} = '* Paire/unpaired t-test on 1st level var.'; count = count+1; +end +if any(cellfun(@length, des1.categorical) > 2) + allLabels{count} = '* ANOVA on 1st level var.'; count = count+1; +end +if length(des1.categorical) > 1 && length(des1.continuous) > 1 + allLabels{count} = '* ANCOVA on 1st level var.'; count = count+1; +end +for iCat = 1:length(des2.categorical) + for iVal = 1:length(des2.categorical{iCat}) + if ~isempty(des2.continuous) + allLabels{count} = [ '* t-test/ANOVA/ANCOVA using ' formatcond(des2.categorical{iCat}{iVal}) ]; + else + allLabels{count} = [ '* t-test/ANOVA using ' formatcond(des2.categorical{iCat}{iVal}) ]; + end + count = count+1; + end +end +for iCont = 1:length(des2.continuous) + if ~isempty(des2.continuous) + allLabels{count} = [ '* Regression/ANCOVA using ' des2.continuous{iCont}{1} ]; + else + allLabels{count} = [ '* Regression using ' formatcond(des2.continuous{iCont}) ]; + end + count = count+1; +end +% allLabels{count} = '* Use STUDY "group" for subjects'' groups'; count = count+1; +allLabels{count} = '* Regression/ANOVA/ANCOVA with text file'; count = count+1; +allLabels{count} = ' (text file must have 1 row per subject)'; count = count+1; + + diff --git a/functions/studyfunc/std_limo.m b/functions/studyfunc/std_limo.m index c17576e2c..fbc622cee 100644 --- a/functions/studyfunc/std_limo.m +++ b/functions/studyfunc/std_limo.m @@ -300,7 +300,7 @@ uniqueSessions = unique(allSessions); % by default we create a design matrix with all condition -factors = pop_listfactors(STUDY.design(opt.design), 'gui', 'off', 'level', 'one'); +factors = pop_listfactors(STUDY.design(opt.design), 'gui', 'off', 'level', 'one', 'constant', 'off'); for iSubj = 1:nb_subjects for iSess = 1:length(uniqueSessions) @@ -548,7 +548,7 @@ model.defaults.method = opt.method; % default is OLS - to be updated to 'WLS' once validated model.defaults.Level = 1; % 1st level analysis model.defaults.type_of_analysis = 'Mass-univariate'; % option can be multivariate (work in progress) - +model.defaults.labels = pop_listfactors(STUDY, 'gui', 'off', 'level', 'one', 'splitreg', opt.splitreg, 'interaction', opt.interaction); if ~exist('limocontrast','var') [LIMO_files, procstatus] = limo_batch('model specification',model,[],STUDY); @@ -580,7 +580,7 @@ fprintf('std_limo, computing additional between sessions contrasts for subject %s\n',uniqueSubjects{s}) sess_name = allSessions(sess_index); pairs = nchoosek(1:length(sess_index),2); % do all session pairs - parfor p=1:size(pairs,1) + for p=1:size(pairs,1) strpair = [cell2mat(sess_name(pairs(p,1))) cell2mat(sess_name(pairs(p,2)))]; strpair(isspace(strpair)) = []; % remove spaces filesout{p} = limo_contrast_sessions(cell2mat(LIMO_files.mat(sess_index(pairs(p,1)))), ... diff --git a/functions/studyfunc/std_limodesign.m b/functions/studyfunc/std_limodesign.m index 5f4ca9848..aed626223 100644 --- a/functions/studyfunc/std_limodesign.m +++ b/functions/studyfunc/std_limodesign.m @@ -94,6 +94,7 @@ % ----------------------------------------- alloptions = {}; limodesign.categorical = {}; +alloptionsinter = {}; if ~isempty(catVar) for iVar = 1:length(catVarLabel) indVals = find(cellfun(@(x)strcmpi(x, catVarLabel{iVar} ), { factors.label })); diff --git a/functions/studyfunc/std_lm_seteegfields.m b/functions/studyfunc/std_lm_seteegfields.m index ce84d9eda..80ef19a36 100644 --- a/functions/studyfunc/std_lm_seteegfields.m +++ b/functions/studyfunc/std_lm_seteegfields.m @@ -97,6 +97,10 @@ % -------------- path_tmp = rel2fullpath(STUDY.filepath,STUDY.datasetinfo(index).filepath); name = fullfile(path_tmp, STUDY.datasetinfo(index).subject); +if ~isempty(STUDY.datasetinfo(index).session) + name2 = name; + name = fullfile(path_tmp, [ STUDY.datasetinfo(index).subject sprintf('_ses-%2.2d', STUDY.datasetinfo(index).session) ]); +end %% Channels: update EEG.set file % ----------------------------- @@ -109,7 +113,7 @@ % DATERP if strcmp(opt.erp,'on') ext = [ prefix 'erp' ]; - EEG.etc.datafiles.(ext) = getfilename(name, [ '.' ext ]); + EEG.etc.datafiles.(ext) = getfilename(name, name2, [ '.' ext ]); data = load('-mat',EEG.etc.datafiles.(ext)); EEG.etc.timeerp = data.times; end @@ -117,7 +121,7 @@ % DATSPEC if strcmp(opt.spec,'on') ext = [ prefix 'spec' ]; - EEG.etc.datafiles.(ext) = getfilename(name, [ '.' ext ]); + EEG.etc.datafiles.(ext) = getfilename(name, name2, [ '.' ext ]); data = load('-mat',EEG.etc.datafiles.(ext)); EEG.etc.freqspec = data.freqs; end @@ -125,7 +129,7 @@ % DAT TIMEF if strcmp(opt.timef,'on') ext = [ prefix 'timef' ]; - EEG.etc.datafiles.(ext) = getfilename(name, [ '.' ext ]); + EEG.etc.datafiles.(ext) = getfilename(name, name2, [ '.' ext ]); EEG.etc.datafiles.datersp = EEG.etc.datafiles.(ext); data = load('-mat',EEG.etc.datafiles.(ext),'times','freqs'); EEG.etc.timeersp = data.times; @@ -159,12 +163,16 @@ end end -function name = getfilename(name, ext) +function name = getfilename(name, name2, ext) if ~exist([name ext],'file') - tmp = dir([name '*' ext ]); - name = fullfile(tmp(1).folder,tmp(1).name); - warning('couldn''t find a direct match between .set and .daterp\n loading %s',name) + if ~exist([name2 ext],'file') + tmp = dir([name2 '*' ext ]); + name = fullfile(tmp(1).folder,tmp(1).name); + warning('couldn''t find a direct match between .set and .daterp, loading %s',name) + else + name = [name2 ext]; + end else name = [name ext]; end diff --git a/functions/studyfunc/std_rmdat.m b/functions/studyfunc/std_rmdat.m index 425448d55..8456d3686 100644 --- a/functions/studyfunc/std_rmdat.m +++ b/functions/studyfunc/std_rmdat.m @@ -1,7 +1,7 @@ % std_rmdat() - remvoe datasets from STUDY % % Usage: -% >> STUDY = std_rmdat(STUDY, ALLEEG, 'key', val); +% >> [STUDY,ALLEEG] = std_rmdat(STUDY, ALLEEG, 'key', val); % % Inputs: % STUDY - EEGLAB STUDY set @@ -20,6 +20,12 @@ % 'rmvarvalues' - {'string' range} remove datasets having variable value % in the selected range. May also be {'string' 'value'} % for non-numerical variables. +% 'keepvarvalues' - {'string' range} keep datasets having variable value +% in the selected range. May also be {'string' 'value'} +% for non-numerical variables. +% 'subjectind' - [integer array] keep only specific subject indices in +% STUDY.subject. To remove or keep specificy subjects, +% use 'rmvarvalues' and 'keepvarvalues' % 'checkeventtype' - [cell|array|string] check event type are present. % 'numeventrange' - [min max] range for number of event of type above. % Default is [1 Inf]. @@ -71,8 +77,10 @@ 'sraterange' 'float' {} [0 Inf]; ... 'trialrange' 'float' {} [1 Inf]; ... 'checkeventtype' '' {} []; ... - 'numeventrange' 'integer' {} 1; ... - 'rmvarvalues' 'cell' {} {} }, 'std_rmdat'); + 'numeventrange' 'integer' {} 1; ... + 'subjectind' 'integer' {} []; ... + 'rmvarvalues' 'cell' {} {}; ... + 'keepvarvalues' 'cell' {} {} }, 'std_rmdat'); if isstr(g) error(g); end @@ -92,7 +100,17 @@ rmDats = g.chanrange(1) > allChans | allChans > g.chanrange(2) | rmDats; rmDats = g.trialrange(1) > allTrials | allTrials > g.trialrange(2) | rmDats; +% check subject indices +% --------------------- +if ~isempty(g.subjectind) + if ~isempty(g.keepvarvalues) + error('Cannot use ''subjectind'' and ''keepvarvalues'' at the same time') + end + g.keepvarvalues = { 'subject' STUDY.subject(g.subjectind) }; +end + % check variable name values +% -------------------------- if ~isempty(g.rmvarvalues) varName = g.rmvarvalues{1}; varValues = g.rmvarvalues{2}; @@ -100,6 +118,33 @@ if ischar(varValues) allValues = cellfun(@num2str, allValues, 'uniformoutput', false); rmDats = rmDats | cellfun(@(x)isequal(x, varValues), allValues); + elseif iscell(varValues) + for iCell = 1:length(varValues) + allValues = cellfun(@num2str, allValues, 'uniformoutput', false); + rmDats = rmDats | cellfun(@(x)isequal(x, varValues{iCell}), allValues); + end + elseif length(varValues) ~= 2 + error('When providing nmumerical input for variable selection, there must be 2 values - min and max'); + else + allValues = cellfun(@(x)fastif(ischar(x), str2double(x), x), allValues); + rmVals = varValues(1) <= allValues & allValues <= varValues(2); % to deal with NaNs + rmDats = rmDats | rmVals; + end +end +if ~isempty(g.keepvarvalues) + varName = g.keepvarvalues{1}; + varValues = g.keepvarvalues{2}; + allValues = { STUDY.datasetinfo.(varName) }; + if ischar(varValues) + allValues = cellfun(@num2str, allValues, 'uniformoutput', false); + rmDats = rmDats | ~cellfun(@(x)isequal(x, varValues), allValues); + elseif iscell(varValues) + keepDats = zeros(1, length(allPnts)); + for iCell = 1:length(varValues) + allValues = cellfun(@num2str, allValues, 'uniformoutput', false); + keepDats = keepDats | cellfun(@(x)isequal(x, varValues{iCell}), allValues); + end + rmDats = rmDats | ~keepDats; elseif length(varValues) ~= 2 error('When providing nmumerical input for variable selection, there must be 2 values - min and max'); else @@ -107,9 +152,10 @@ keepVals = varValues(1) <= allValues & allValues <= varValues(2); % to deal with NaNs rmDats = rmDats | ~keepVals; end -end +end % check event type present +% -------------------------- if ~isempty(g.checkeventtype) if ischar(g.checkeventtype) g.checkeventtype = { g.checkeventtype }; end rmDatEvents = zeros(1, length(ALLEEG)); diff --git a/functions/supportfiles/eeglab_run_this_one_on_osx_or_linux b/functions/supportfiles/eeglab_run_this_one_on_osx_or_linux old mode 100755 new mode 100644 diff --git a/functions/supportfiles/eeglab_run_this_one_on_windows.bat b/functions/supportfiles/eeglab_run_this_one_on_windows.bat old mode 100755 new mode 100644 diff --git a/functions/supportfiles/ica_linux b/functions/supportfiles/ica_linux old mode 100755 new mode 100644 diff --git a/sample_data/eeglab_data_epochs_ica.fdt b/sample_data/eeglab_data_epochs_ica.fdt old mode 100755 new mode 100644 diff --git a/sample_data/eeglab_data_epochs_ica.set b/sample_data/eeglab_data_epochs_ica.set old mode 100755 new mode 100644 diff --git a/sample_data/test_data/BIDS_test/sub-S1/ses-01/eeg/sub-S1_ses-01_task-Experiment_run-01_eeg.edf b/sample_data/test_data/BIDS_test/sub-S1/ses-01/eeg/sub-S1_ses-01_task-Experiment_run-01_eeg.edf old mode 100755 new mode 100644 diff --git a/sample_data/test_data/test.cnt b/sample_data/test_data/test.cnt old mode 100755 new mode 100644 diff --git a/sample_data/test_data/test.edf b/sample_data/test_data/test.edf old mode 100755 new mode 100644 diff --git a/sample_data/test_data/testant.avr b/sample_data/test_data/testant.avr old mode 100755 new mode 100644 diff --git a/sample_data/test_data/testmusemonitor.csv b/sample_data/test_data/testmusemonitor.csv old mode 100755 new mode 100644