Skip to content

Commit 1541ef8

Browse files
committed
Minor additions and improvements.
1 parent b5235c2 commit 1541ef8

File tree

10 files changed

+62
-24
lines changed

10 files changed

+62
-24
lines changed

RELEASE NOTES.TXT

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,27 @@ If you are making extensive use of the underlying EEGLAB tools, please consider
9999

100100

101101

102+
=== Changes since 1.1-beta ===
103+
104+
Major New Features
105+
106+
Minor New Features
107+
- set_insert_markers: now supports negative upper bounds in the absoluterange segment specification
108+
- window_func: now supports some more alternative names for window functions and one or another func
109+
- hlp_diskcache: now has a tad less overhead
110+
- ParadigmMTCSP: now supports LogTransform option, also a bit more efficient
111+
- flt_ica: now allows to retain channel labels when TransformData is set
112+
113+
Major Changes
114+
115+
Minor Changes
116+
- partitioning of continuous data during a parameter search will now cache the data set rather than
117+
reload it every time
118+
119+
Serious Fixes
120+
121+
Minor Fixes
122+
- flt_ica, robust_sphere option: was calling an obsolete function
102123

103124
=== Changes since 1.01 ===
104125

@@ -252,7 +273,7 @@ Minor New Features
252273
listing data sets to process (using rdir() rather than dir()), such as /data/mystudy/**/*.set
253274
- hlp_serialize: now serializes gpuArray data (as double arrays)
254275

255-
Major Changes:
276+
Major Changes
256277
- arg_tovals: now by default sets the arg_direct flag to false rather than true, meaning that some
257278
existing functions taking outputs generated via arg_tovals will likely run the slow path unless the
258279
flag is set to true explicitly (e.g., via arg_setdirect)
@@ -262,7 +283,7 @@ Major Changes:
262283
- some methods are now marked as “experimental” and not shown in the GUI by default (can be
263284
toggled at startup or in the bcilab_config.m)
264285

265-
Minor Changes:
286+
Minor Changes
266287
- utl_fileinfo: now returns the version identifier string for the given file if one is present as the first
267288
argument instead of the code hash (otherwise returns the hash as before); the 5th output
268289
always contains the code hash
@@ -300,13 +321,14 @@ Minor Changes:
300321
- bci_batchtrain: was previously running locally by default; now the default matches the current global setting
301322
- fit_eeg_distribution: now by default fits an ensemble of truncated distributions and returns a quantile thereof
302323
- flt_clean_windows: updated defaults in accordance with fit_eeg_distribution
324+
- ParadigmDALERP: now uses a FIR filter by default instead of IIR
303325

304-
Serious Fixes:
326+
Serious Fixes
305327
- ml_calcloss: fixed calculation of FP and FN (Dan Roberts)
306328
- utl_crossval: calculated mean AUC loss incorrectly, usually pessimistically
307329
(per-fold AUC’s were correct)
308330

309-
Minor Fixes:
331+
Minor Fixes
310332
- arg_subswitch: fixed an error message
311333
- arg_subtoggle: now properly supports ‘off’ / ‘on’ strings in the defaults
312334
- env_showmenu: now highlights the menu when it’s already open

code/dataset_editing/set_insert_markers.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
arg_subswitch({'segmentspec','SegmentSpec','segment'},'absoluterange', ...
130130
{'absoluterange',{ ...
131131
arg({'lo','BeginOffset'},0,[],'Lower bound of insertion interval. Events will be inserted beginning from this time point, in seconds.'), ...
132-
arg({'hi','EndOffset'},Inf,[],'Upper bound of insertion interval. Events will be inserted beginning from this time point, in seconds.')}, ...
132+
arg({'hi','EndOffset'},Inf,[],'Upper bound of insertion interval. Events will be inserted up to this time point, in seconds. If this is negative, it counts from the end of the recording.')}, ...
133133
'relativerange',{ ...
134134
arg({'event','EventType'},'event1',[],'Reference event type. New events will be inserted in a range around each event of this type.'), ...
135135
arg({'lo','BeginOffset'},-0.5,[],'Lower bound relative to event. This is the lower boundary of insertion intervals relative to the reference events. In seconds.'), ...
@@ -179,6 +179,9 @@
179179
opts.segmentspec.lo = signal.xmin*signal.srate; end
180180
if opts.segmentspec.hi == Inf
181181
opts.segmentspec.hi = signal.xmax*signal.srate; end
182+
if opts.segmentspec.hi < 0
183+
opts.segmentspec.hi = signal.xmax*signal.srate - opts.segmentspec.hi; end
184+
182185
% inject using absolute latencies
183186
if isempty(opts.event)
184187
error('an event type must be specified'); end

code/filters/flt_ica.m

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@
346346
}, 'ICA variant. AMICA is the highest quality (but slowest, except if run on a cluster), Infomax is second-highest quality, FastICA is fastest (but can fail to converge and gives poorer results), KernelICA is experimental.'), ...
347347
arg_sub({'data_cleaning','DataCleaning','CleaningLevel','clean'},{}, @flt_clean_settings,'Optional data cleaning prior to running an ICA. The computed ICA solution will be applied to the original uncleaned data.'), ...
348348
arg({'do_transform','TransformData','transform'},false,[],'Transform the data rather than annotate. By default, ICA decompositions are added as annotations to the data set.'),...
349+
arg({'retain_labels','RetainLabels'},true,[],'Retain labels when transforming. If this is false the channel labels will be replaced by 1:k for k components, if TransformData is checked.'),...
349350
arg({'clear_after_trans','ClearAfterTransform'},true,[],'Clear .icaweights after transform. This is so that later functions do not attempt to transform the already transformed data.'),...
350351
arg({'do_calcact','CalculateActivation'},false,[],'Calculate component activations. If true, the .icaact field will be populated.'),...
351352
arg({'cleaned_data','OutputCleanedData'},false,[],'Emit cleaned data. Whether the cleaned data, instead of the original data should be output (note: this is not applicable for online use, since most cleaning filters cannot be run online).'),...
@@ -916,7 +917,7 @@
916917
pre.icasphere = inv(sqrtm(cov(pre.data')));
917918
pre.icaweights = eye(size(pre.data,1));
918919
case 'robust_sphere'
919-
pre.icasphere = inv(sqrtm(hlp_diskcache('icaweights',@cov_robust,pre.data')));
920+
pre.icasphere = inv(sqrtm(hlp_diskcache('icaweights',@cov_blockgeom,pre.data')));
920921
pre.icaweights = eye(size(pre.data,1));
921922
otherwise
922923
% let pop_runica handle all the rest
@@ -989,7 +990,11 @@
989990
if isfield(signal.etc,'amica') && size(signal.etc.amica.W,3) > 1
990991
warn_once('Note: The signal will only be transformed according to the 1st amica model.'); end
991992
signal.data = (signal.icaweights*signal.icasphere)*signal.data(signal.icachansind,:);
992-
signal.chanlocs = struct('labels',cellfun(@num2str,num2cell(1:length(signal.icachansind),1),'UniformOutput',false));
993+
if retain_labels && nnz(signal.icachansind) == size(signal.data,1)
994+
signal.chanlocs = signal.chanlocs(signal.icachansind);
995+
else
996+
signal.chanlocs = struct('labels',cellfun(@num2str,num2cell(1:length(signal.icachansind),1),'UniformOutput',false));
997+
end
993998
signal.nbchan = size(signal.data,1);
994999
if clear_after_trans
9951000
signal.icaweights = [];

code/helpers/hlp_diskcache.m

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@
150150

151151
archive_version = 1.0; % version of the archive format
152152
persistent settings;
153+
persistent have_translatepath;
154+
if isempty(have_translatepath)
155+
have_translatepath = exist('env_translatepath','file'); end
153156

154157
% parse options
155158
if iscell(options) || isstruct(options)
@@ -180,7 +183,7 @@
180183
end
181184

182185
% make path platform-specific
183-
if ~exist('env_translatepath','file')
186+
if ~have_translatepath
184187
options.folder = strrep(strrep(options.folder,'\',filesep),'/',filesep);
185188
else
186189
options.folder = env_translatepath(options.folder);
@@ -384,11 +387,9 @@
384387
if isfield(x,'tracking') && isfield(x.tracking,'expression')
385388
x = trim_expression(x.tracking.expression);
386389
elseif iscell(x)
387-
for i=1:length(x)
388-
x{i} = trim_expression(x{i}); end
390+
x = cellfun(@trim_expression,x,'UniformOutput',false);
389391
elseif isfield(x,{'head','parts'})
390-
for i=1:length(x.parts)
391-
x.parts{i} = trim_expression(x.parts{i}); end
392+
x.parts = trim_expression(x.parts);
392393
end
393394

394395

code/misc/window_func.m

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,21 @@
4141
% write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
4242
% USA
4343

44-
4544
p = (0:(m-1))/(m-1);
4645
switch name
4746
case 'bartlett'
4847
w = 1 - abs(((0:(m-1)) - (m-1)/2)/((m-1)/2));
49-
case 'barthann'
48+
case {'barthann','barthannwin'}
5049
w = 0.62 - 0.48*abs(p-0.5) - 0.38*cos(2*pi*p);
5150
case 'blackman'
5251
w = 0.42-0.5*cos(2*pi*p) + 0.08*cos(4*pi*p);
5352
case 'blackmanharris'
5453
w = 0.35875 - 0.48829*cos(2*pi*p) + 0.14128*cos(4*pi*p) - 0.01168*cos(6*pi*p);
55-
case 'flattop'
54+
case {'bohman','bohmanwin'}
55+
w = (1-abs(p*2-1)).*cos(pi*abs(p*2-1)) + (1/pi)*sin(pi*abs(p*2-1));
56+
case {'flattop','flattopwin'}
5657
w = 0.2157 - 0.4163*cos(2*pi*p) + 0.2783*cos(4*pi*p) - 0.0837*cos(6*pi*p) + 0.0060*cos(8*pi*p);
57-
case 'gauss'
58+
case {'gauss','gausswin'}
5859
if nargin < 3
5960
param = 2.5; end
6061
w = exp(-0.5*(param*2*(p-0.5)).^2);
@@ -68,9 +69,9 @@
6869
w = besseli(0,param*sqrt(1-(2*p-1).^2))/besseli(0,param);
6970
case 'lanczos'
7071
w = sin(pi*(2*p-1))./(pi*(2*p-1)); w(isnan(w)) = 1;
71-
case 'nuttall'
72+
case {'nuttall','nuttallwin'}
7273
w = 0.3635819 - 0.4891775*cos(2*pi*p) + 0.1365995*cos(4*pi*p) - 0.0106411*cos(6*pi*p);
73-
case 'rect'
74+
case {'rect','rectwin'}
7475
w = ones(1,m);
7576
case 'triang'
7677
w = 1 - abs(((0:(m-1)) - (m-1)/2)/((m+1)/2));

code/paradigms/ParadigmMTCSP.m

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
arg({'patterns','PatternPairs'},uint32(3),[],'CSP patterns per frequency (times two).'), ...
3838
arg({'whycsp','SkipCSP','WhyCSP'},false,[],'Classify cross-spectrum directly. This results in much higher-dimensional features, but can be approached with appropriately regularized classifiers (see ml_trainproximal).'), ...
3939
arg({'normalize_spectrum','NormalizeSpectrum'},false,[],'Normalize the spectrum. Recommended if using sophisticated regularized classifiers.'), ...
40+
arg({'logtransform','LogTransform'},false,[],'Log-transform output. Log-transformed spectra are more likely to be separable by a linear classifier.'), ...
4041
arg({'vectorize_features','VectorizeFeatures'},true,[],'Vectorize the features. For compatibility with basic classifiers.'));
4142

4243
if args.signal.nbchan == 1
@@ -45,6 +46,7 @@
4546
error('Multi-taper CSP prefers to work on at least as many channels as you request output patterns. Please reduce the number of pattern pairs.'); end
4647
if isempty(args.timewnds)
4748
args.timewnds = struct(); end
49+
[C,dummy,T] = size(args.signal.data); %#ok<NASGU,ASGLU>
4850
if args.whycsp
4951
% shortcut
5052
for w = size(args.timewnds,1):-1:1
@@ -62,7 +64,7 @@
6264
mean_covar{w}(~isfinite(mean_covar{w})) = 0; weighted_covar{w}(~isfinite(weighted_covar{w})) = 0;
6365
% calculate spatial filters for each frequency
6466
for f=size(mean_covar{w},1):-1:1
65-
[V,D] = eig(squeeze(weighted_covar{w}(f,:,:)),squeeze(mean_covar{w,1}(f,:,:))); %#ok<NASGU>
67+
[V,D] = eig(reshape(weighted_covar{w}(f,:,:),C,C),reshape(mean_covar{w,1}(f,:,:),C,C)); %#ok<NASGU>
6668
P = inv(V);
6769
% retain k best filters/patterns at both ends of the eigenvalue spectrum
6870
filters(w,f,:,:) = real(V(:,[1:args.patterns end-args.patterns+1:end]));
@@ -80,14 +82,14 @@
8082
end
8183
% solve a CSP instance for each frequency
8284
for f=size(covar{w,1},1):-1:1
83-
[V,D] = eig(squeeze(covar{w,1}(f,:,:)),squeeze(covar{w,1}(f,:,:)+covar{w,2}(f,:,:))); %#ok<NASGU>
85+
[V,D] = eig(reshape(covar{w,1}(f,:,:),C,C),reshape(covar{w,1}(f,:,:),C,C)+reshape(covar{w,2}(f,:,:),C,C)); %#ok<NASGU>
8486
P = inv(V);
8587
filters(w,f,:,:) = real(V(:,[1:args.patterns end-args.patterns+1:end]));
8688
patterns(w,f,:,:) = real(P([1:args.patterns end-args.patterns+1:end],:))';
8789
end
8890
end
8991
end
90-
model = struct('filters',{filters},'patterns',{patterns},'time_args',{time_args},'spec_args',{args.spectral_estimation}, 'covar',{covar}, 'mean_covar',{mean_covar}, 'weighted_covar',{weighted_covar}, 'chanlocs',{args.signal.chanlocs},'vectorize_features',{args.vectorize_features},'whycsp',{args.whycsp},'normalize_spectrum',{args.normalize_spectrum});
92+
model = struct('filters',{filters},'patterns',{patterns},'time_args',{time_args},'spec_args',{args.spectral_estimation}, 'covar',{covar}, 'mean_covar',{mean_covar}, 'weighted_covar',{weighted_covar}, 'chanlocs',{args.signal.chanlocs},'vectorize_features',{args.vectorize_features},'whycsp',{args.whycsp},'normalize_spectrum',{args.normalize_spectrum},'logtransform',{args.logtransform});
9193
end
9294
global tracking; %#ok<TLEV>
9395
tracking.inspection.signal = args.signal;
@@ -121,6 +123,8 @@
121123
freqs = freqs(1):(freqs(2)-freqs(1))/(nfreqs-1):freqs(2);
122124
features = bsxfun(@times,features,max(1,1./freqs'));
123125
end
126+
if featuremodel.logtransform
127+
features = log(features); end
124128
end
125129
end
126130
% apply minimal conditioning to features

code/utils/utl_calc_crossspec.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
arg({'bandwidth','TimeBandwidth'},5,[],'Spectral smoothing. Controls the bias vs. variance of the spectral estimation. Reasonable values are 1 to 3 (1 being fairly noisy, and 3 being fairly smooth but 5x slower)'), ...
4848
arg({'tapers','Tapers'},[],[],'Number of tapers. Should be an integer smaller than 2*TimeBandwith; default 2*TimeBandwidth-1'), ...
4949
arg({'padding','Padding'},0,[],'FFT padding factor. Controls the oversampling of the spectrum; 0 is the next largest power of two, 1 is 2x as much, etc.'), ...
50-
arg({'subsample_spectrum','SubsampleSpectrum'},1,[],'Sub-sample the spectrum. This is according to the TimeBandwidth parameter.'), ...
50+
arg({'subsample_spectrum','SubsampleSpectrum'},1,[],'Sub-sample the spectrum. This is the sub-sampling factor.'), ...
5151
arg({'robust_estimation','RobustEstimation'},false,[],'Robust cross-spectral estimation. Whether cross-spectral matrices should be aggregated across trials in a robust manner.'), ...
5252
arg({'sum_weights','SumWeights'}, [],[],'Weights for weighted averaging. If passed, the weighted average cross-spectrum will be returned as a second output.'), ...
5353
arg({'filtered_subsampling','FilteredSubsampling'},false,[],'Use a filter prior to sub-sampling. Slower but yields a better spectral estimate.'), ...
@@ -98,7 +98,7 @@
9898
for t = 1:T
9999
cs = abs(CrossSpecMatc(signal.data(:,:,t)',signal.pnts/signal.srate,struct('tapers',[2*args.bandwidth args.tapers],'pad',args.padding,'Fs',signal.srate,'fpass',args.freqwnd)));
100100
% filter and sub-sample
101-
if args.subsample_spectrum > 1 && args. filtered_subsampling
101+
if args.subsample_spectrum > 1 && args.filtered_subsampling
102102
cs = filter(wnd,1,cs,1); end
103103
cs = cs(1:args.subsample_spectrum:end,:,:);
104104
if isempty(meanspec)

code/utils/utl_partition_bundle.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
if isempty(inds)
2121
% compute index set size (from first stream)
22-
res = exp_eval(set_partition(bundle.streams{1},[],varargin{:}));
22+
res = exp_eval_optimized(set_partition(bundle.streams{1},[],varargin{:}));
2323
else
2424
% partition the streams (symbolically)
2525
for s=1:length(bundle.streams)

dependencies/fast_decode.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
function bytes = fast_decode(str)
2+
% decode a string into a uint8 vector
23
bytes = uint8(str)-uint8(32);
34
bytes = (bytes(1:end/2) + bitshift(bytes((end/2+1):end),uint8(4)))';

dependencies/fast_encode.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
function str = fast_encode(bytes)
2+
% encode a uint8 vector into a string
23
bytes = uint8(32) + [bitand(bytes,uint8(15)) bitshift(bitand(bytes,uint8(15*16)),-4)];
34
str = char(bytes(:)');

0 commit comments

Comments
 (0)