Skip to content

Commit 4b05979

Browse files
committed
Added robust re-referencing.
1 parent 56d7ca9 commit 4b05979

File tree

5 files changed

+134
-12
lines changed

5 files changed

+134
-12
lines changed

code/filters/flt_clean_windows.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
% output) to 0.3 (very lax cleaning of only coarse artifacts). Default: 0.15.
2424
%
2525
%
26+
%
2627
% The following are detail parameters that usually do not have to be tuned. If you can't get
2728
% the function to do what you want, you might consider adapting these to your data.
2829
%
@@ -56,6 +57,7 @@
5657
% is quite slow. (default: true)
5758
%
5859
%
60+
%
5961
% The following are legacy parameters to enable previous behaviors.
6062
%
6163
% FlaggedQuantile : upper quantile of the per-channel windows that should be flagged for potential
@@ -185,7 +187,7 @@
185187
% previous method (2011): based on quantiles
186188
removed_windows = find(sum(1-retain_mask) > min_badchans);
187189
elseif ~isempty(ignored_chans)
188-
% oldest method (2010)
190+
% oldest method (2010): attempt at being robust against broken channels
189191
removed_windows = find(sum(retain_mask) <= ignored_chans);
190192
else
191193
error('BCILAB:flt_clean_windows:legacy_options','By setting the flag_quantile parameter you switch to legacy methods; these require that you also set the min_badchans (former default: 0.5) or ignored_chans (former default: 0.3) parameter.');

code/filters/flt_reref.m

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
function signal = flt_reref(varargin)
22
% Re-references the data to a new (set of) channel(s) or the average of all channels.
3-
% Signal = flt_reref(Signal, ReferenceChannels, ExcludeChannels, KeepReference)
3+
% Signal = flt_reref(Signal, ReferenceChannels, ExcludeChannels, KeepReference, ReferenceType, HuberCutoff, HuberIterations)
44
%
55
% Re-referencing is a spatial filter in which the (instantaneous) mean signal of some "reference"
66
% channels is subtracted from the signal of each channel. This allows to dampen externally-induced
@@ -12,6 +12,9 @@
1212
% re-referencing, since their spatial filters are adaptively optimized and effectively incorporate
1313
% re-referencing to the degree that it is necessary.
1414
%
15+
% This function also supports robust re-referencing, where artifacts in few channels will not affect
16+
% the outcome disproportionally.
17+
%
1518
% In:
1619
% Signal : Epoched or continuous data set.
1720
%
@@ -22,6 +25,19 @@
2225
% (default: [])
2326
%
2427
% KeepReference : Whether to keep the reference channel (default: false)
28+
% If true and multiple channels were selected, a new channel named CAR is
29+
% appended.
30+
%
31+
% ReferenceType : Type of referencing to apply, can be one of the following:
32+
% * 'mean': subtract average of reference channels (default)
33+
% * 'median': subtract median of reference channels
34+
% * 'huber': subtract robust mean (under the Huber loss) of reference channels;
35+
%
36+
% HuberCutoff : cutoff/transition parameter for huber loss; if [], this is set
37+
% to one (robust) standard deviation of the signal
38+
%
39+
% HuberIterations : number of iterations to compute the Huber fit; a larger number can tolerate
40+
% larger outliers (default: 100)
2541
%
2642
% Out:
2743
% Signal : Re-referenced data set.
@@ -45,18 +61,52 @@
4561
% Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
4662
% 2010-03-28
4763

48-
% flt_reref_version<1.0> -- for the cache
64+
% flt_reref_version<1.01> -- for the cache
4965

5066
if ~exp_beginfun('filter') return; end
5167

5268
declare_properties('name',{'Rereferencing','ref'}, 'independent_channels',false,'independent_trials',true);
5369

5470
arg_define(varargin,...
5571
arg_norep({'signal','Signal'}), ...
56-
arg({'chn','ReferenceChannels'}, [], [], 'Cell array of reference channels. The signal data is be referenced to these, defaults to average reference if empty.','type','cellstr','shape','row'),...
57-
arg({'exclude','ExcludeChannels'}, [], [], 'Cell array of channels to exclude.','type','cellstr','shape','row'),...
58-
arg({'keepref','KeepReference'}, false, [], 'Keep the reference channel.'));
72+
arg({'ref_chn','ReferenceChannels','chn'}, [], [], 'Cell array of reference channels. The signal data is be referenced to these, defaults to average reference if empty.','type','cellstr','shape','row'),...
73+
arg({'exclude_chn','ExcludeChannels','exclude'}, [], [], 'Cell array of channels to exclude.','type','cellstr','shape','row'),...
74+
arg({'keepref','KeepReference'}, false, [], 'Keep the reference channel.'), ...
75+
arg({'ref_type','ReferenceType'}, 'mean', {'mean','median','huber'}, 'Type of reference. The traditional average reference operation uses the mean. If this is set to median, the median of the reference channels will be removed, and if set to huber the robust mean under the Huber loss will be removed (slower than median but closer to the mean).'), ...
76+
arg({'huber_cut','HuberCutoff'}, [], [], 'Cutoff for huber function. If left empty this is set to one (robust) standard deviation of the signal.','guru',true), ...
77+
arg({'huber_iters','HuberIterations'}, 100, [], 'Iterations for huber fitting. A larger number yields tolerance to larger outliers.','guru',true));
78+
79+
if ~isempty(ref_chn)
80+
ref_chns = set_chanid(signal,ref_chn);
81+
else
82+
ref_chns = 1:size(signal.data,1);
83+
end
84+
85+
if ~isempty(exclude_chn)
86+
ref_chns = setdiff(ref_chns,set_chanid(signal,exclude_chn)); end
87+
88+
switch ref_type
89+
case 'mean'
90+
ref_signal = mean(signal.data(ref_chns,:));
91+
case 'median'
92+
ref_signal = median(signal.data(ref_chns,:));
93+
case 'huber'
94+
if isempty(huber_cut)
95+
huber_cut = median(mad(signal.data,1,2))*1.4826; end
96+
ref_signal = robust_mean(signal.data(ref_chns,:)/huber_cut,1,huber_iters)*huber_cut;
97+
otherwise
98+
error('Unsupported reference type.');
99+
end
100+
101+
signal.data = bsxfun(@minus,signal.data,ref_signal);
59102

60-
signal = pop_reref(signal,set_chanid(signal,chn),'exclude',fastif(isempty(exclude),[],set_chanid(signal,exclude)),'keepref',fastif(keepref,'on','off')); %#ok<*NODEF>
103+
if ~keepref && ~isempty(ref_chn)
104+
signal = pop_select(signal,'nochannel',ref_chns); end
105+
if keepref && length(ref_chn)>1
106+
signal.data(end+1,:) = ref_signal;
107+
signal.nbchan = size(signal.data,1);
108+
signal.chanlocs(end+1).labels = 'CAR';
109+
signal.chanlocs(end).type = 'REF';
110+
end
61111

62-
exp_endfun;
112+
exp_endfun('append_online',{'huber_cut',huber_cut});

code/helpers/hlp_collect_datasets.m

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@
4949
% 'conditions',@(EEG) ~isempty(EEG.icawinv) && mean(cellfun('isempty',{EEG.chanlocs.X})) < 0.5, ...
5050
% 'collect',@(EEG,path){path,EEG.icawinv,EEG.chanlocs,EEG.icachansind})
5151
%
52-
% Dependencies:
53-
% env_translatepath.
54-
%
5552
% Laura Froelich, Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
5653
% 2010-09-10
5754

@@ -75,7 +72,7 @@
7572
opts = varargin{1}; % opts is already a struct: fast path
7673
else
7774
opts = hlp_varargin2struct(varargin, 'pattern',regexptranslate('wildcard','*.set'), 'nopattern',{}, 'checkset',1, 'nowarnings',1 ,'nodialogs',1, ...
78-
'maxsize',5*2^20, 'maxtime',Inf, 'maxnumber',Inf, 'conditions',[], 'collect',@(EEG,path) path, ....
75+
'maxsize',5*2^20, 'maxtime',Inf, 'maxnumber',Inf, 'conditions',{}, 'collect',@(EEG,path) path, ....
7976
'fileconditions',@(path,name,ext) exist([path filesep name '.fdt'],'file') || exist([path filesep name '.dat'],'file'));
8077
if ~iscell(opts.conditions)
8178
opts.conditions = {opts.conditions}; end

code/misc/robust_fit.m

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
function x = robust_fit(A,y,rho,iters)
2+
% Perform a robust linear regression using the Huber loss function.
3+
% x = robust_fit(A,y,rho,iters)
4+
%
5+
% Input:
6+
% A : design matrix
7+
% y : target variable
8+
% rho : augmented Lagrangian variable (default: 1)
9+
% iters : number of iterations to perform (default: 1000)
10+
%
11+
% Output:
12+
% x : solution for x
13+
%
14+
% Notes:
15+
% solves the following problem via ADMM for x:
16+
% minimize 1/2*sum(huber(A*x - y))
17+
%
18+
% Based on the ADMM Matlab codes also found at:
19+
% http://www.stanford.edu/~boyd/papers/distr_opt_stat_learning_admm.html
20+
%
21+
% Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
22+
% 2013-03-04
23+
24+
if ~exist('rho','var')
25+
rho = 1; end
26+
if ~exist('iters','var')
27+
iters = 1000; end
28+
29+
Aty = A'*y;
30+
L = sparse(chol(A'*A,'lower')); U = L';
31+
z = zeros(size(y)); u = z;
32+
for k = 1:iters
33+
x = U \ (L \ (Aty + A'*(z - u)));
34+
d = A*x - y + u;
35+
z = rho/(1+rho)*d + 1/(1+rho)*max(0,(1-(1+1/rho)./abs(d))).*d;
36+
u = d - z;
37+
end

code/misc/robust_mean.m

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
function X = robust_mean(Y,rho,iters)
2+
% Perform a robust mean under the Huber loss function.
3+
% x = robust_mean(Y,rho,iters)
4+
%
5+
% Input:
6+
% Y : MxN matrix over which to average (columnwise)
7+
% rho : augmented Lagrangian variable (default: 1)
8+
% iters : number of iterations to perform (default: 1000)
9+
%
10+
% Output:
11+
% x : 1xN vector that is the roust mean of Y
12+
%
13+
% Based on the ADMM Matlab codes also found at:
14+
% http://www.stanford.edu/~boyd/papers/distr_opt_stat_learning_admm.html
15+
%
16+
% Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
17+
% 2013-09-26
18+
19+
if ~exist('rho','var')
20+
rho = 1; end
21+
if ~exist('iters','var')
22+
iters = 1000; end
23+
24+
m = size(Y,1);
25+
if m==1
26+
X = Y;
27+
else
28+
mu = sum(Y)/m;
29+
Z = zeros(size(Y)); U = Z;
30+
for k = 1:iters
31+
X = mu + sum(Z - U)/m;
32+
D = bsxfun(@minus,X,Y - U);
33+
Z = (rho/(1+rho) + (1/(1+rho))*max(0,(1-(1+1/rho)./abs(D)))).*D;
34+
U = D - Z;
35+
end
36+
end

0 commit comments

Comments
 (0)