[422372]: / functions / sigprocfunc / reref.m

Download this file

321 lines (298 with data), 12.7 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
% REREF - convert common reference EEG data to some other common reference
% or to average reference
% Usage:
% >> Dataout = reref(data); % convert all channels to average reference
% >> [Dataout Chanlocs] = reref(data, refchan, 'key', 'val');
% % convert data to new reference with options
% Inputs:
% data - 2-D or 3-D data matrix (chans,frames*epochs)
% refchan - reference channel number(s). There are three possibilities:
% 1) [] - compute average reference
% 2) [X]: re-reference to channel X
% 2) [X Y Z ...]: re-reference to the average of channel X Y Z ...
%
% Optional inputs:
% 'huber' - ['float'] If not NaN, use the Huber average reference
% instead of the standard average reference. See EEGLAB
% tutorial section 5.b for more information. This
% threshold is given in microvolt.
% 'exclude' - [integer array] channel indices to exclude from re-referencing
% (e.g., event marker channels, etc.)
% 'keepref' - ['on'|'off'] keep reference channel in output (only usable
% when there are several references).
% 'elocs' - Current data electrode location structure (e.g., EEG.chanlocs).
% 'refloc' - Reference channel location single element structure or cell array
% {'label' theta radius} containing the name and polar coordinates
% of the current channel. Including this entry means that
% this channel will be included (reconstructed) in the
% output.
%
% Outputs:
% Dataout - Input data converted to the new reference
% Chanlocs - Updated channel locations structure
%
% Notes: 1) The average reference calculation implements two methods
% (see www.egi.com/Technotes/AverageReference.pdf)
% V'i = (Vi-Vref) - sum(Vi-Vref)/number_of_electrodes
%
% Authors: Arnaud Delorme, SCCN/INC/UCSD, La Jolla, 2009-
% previous version: Arnaud Delorme & Scott Makeig, 1999-2002
% Deprecated inputs:
% These inputs are still accepted but not processed. The function returns
% accurate results irrespective of the entry of these options.
% 'refstate ' - ['common'|'averef'|[indices]] Current reference condition,
% ('averef') = average reference; ('common' or 0) = common
% reference. [indices] designate the current reference channel
% or channels if present in the data {default: 'common'}
% 'method' - ['standard'|'withref'] Do not ('standard') or do ('withref')
% include reference channel data in output {def: 'standard'}.
% Note: Option 'withref' not possible when multiple ref channel
% indices are given as argument to 'refstate' (below).
%
% ICA inputs:
% These inputs are still accepted but not the ICA conversion is now
% performed from within POP_REREF
% 'icaweights' - ICA weight matrix. Note: If this is ICA weights*sphere,
% then the 'icasphere' input below should be [] or identity.
% 'icasphere' - ICA sphere matrix (if any)
% 'icachansind' - Indices of the channels used in ICA decomposition
%
% Outputs:
% Wout - ICA weight matrix (former icaweights*icasphere)
% converted to new data reference
% Sout - ICA sphere matrix converted to an identity matrix
% ICAinds - New indices of channels used in ICA decomposition
% meandata - (1,dataframes) means removed from each data point
%
% 2) In conversion of the weight matrix to a new reference
% where WS = Wts*Sph and ica_act = WS*data, then
% data = inv(WS)*ica_act;
% If R*data are the re-referenced data,
% R*data= R*inv(WS)*ica_act;
% And Wout = inv(R*inv(WS));
% Now, Sout = eye(length(ICAinds));
% The re-referenced ICA component maps are now the
% columns of inv(Wout), and the icasphere matrix, Sout,
% is an identity matrix. Note: INV -> PINV when
% PCA dimension reduction is used during ICA decomposition.
% Copyright (C) 1999 Scott Makeig, SCCN/INC/UCSD, scott@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.
% 12/16/99 Corrected denomiator on the suggestion of Ian Nimmo-Smith, Cambridge UK
% 01-25-02 reformated help & license -ad
function [data, Elocs, morechans, W, S, icachansind, meandata] = reref(data, ref, varargin)
if nargin<1
help reref
return
end
if nargin < 2
ref = [];
end
% check inputs
% ------------
g = finputcheck(varargin, { 'icaweight' 'real' [] [];
'icaweights' 'real' [] [];
'icasphere' 'real' [] [];
'icachansind' 'integer' [] [];
'interpchan' {''} [] [];
'refica' {''} [] [];
'huber' 'float' [] NaN;
'method' 'string' { 'standard','withref' } 'standard';
'refstate', {'string', 'integer'}, {{'common', 'averef'}, [1 size(data,1)]}, 'common';
'exclude', 'integer', [1 size(data,1)], [];
'refloc' { 'cell','struct' } { [] [] } {};
'keepref' 'string' {'on','off' } 'off';
'elocs' {'integer','struct'} [] [] });
if ischar(g), error(g); end
if ~isempty(g.icaweight)
g.icaweights = g.icaweight;
end
if ~isempty(g.icaweights)
if isempty(g.icachansind)
g.icachansind = 1:size(g.icaweights,2);
disp('Warning: reref() output has changed slightly since EEGLAB 5.02');
disp(' the 4th output argument is the indices of channels used for ICA instead');
disp(' of the mean reference value (which is now output argument 5)');
end
end
if ischar(ref), ref = { ref }; end
if iscell(ref), ref = eeg_chaninds(g.elocs, ref); end
if ~isempty(ref)
if ref > size(data,1)
error('reference channel index out of range');
end
end
[dim1, dim2, dim3] = size(data);
data = reshape(data, dim1, dim2*dim3);
% single reference not present in the data
% add it as blank data channel at the end
% ----------------------------------------
if ~isempty(g.refloc) == 1
if ~isempty(g.elocs)
if iscell(g.refloc)
data(end+1,:) = 0;
g.elocs(end+1).labels = g.refloc{1};
g.elocs(end ).theta = g.refloc{2};
g.elocs(end ).radius = g.refloc{3};
else
data(end+length(g.refloc),:) = 0;
for iLocs = 1:length(g.refloc)
g.elocs(end+1).labels = g.refloc(iLocs).labels;
fieldloc = fieldnames(g.elocs);
for ind = 1:length(fieldloc)
g.elocs(end) = setfield(g.elocs(end), fieldloc{ind}, getfield(g.refloc(iLocs), fieldloc{ind}));
end
end
end
end
[dim1 dim2 dim3] = size(data);
end
% exclude some channels
% ---------------------
chansin = setdiff_bc([1:dim1], g.exclude);
nchansin = length(chansin);
% return mean data
% ----------------
if nargout > 4
meandata = sum(data(chansin,2))/nchansin;
end
% generate rereferencing matrix
% -----------------------------
if ~isnan(g.huber) && isempty(ref)
chansout=chansin;
[data(chansout,:),~,w] = huber_mean(data(chansout,:), g.huber);
if ~all(w == 1)
if ~isempty(g.elocs)
fprintf('List of channels not weighted 1 by the huber average reference:\n')
for iChan = 1:length(chansout)
if w(iChan) < 1
fprintf('Channel %s: %1.2f\n', g.elocs(iChan).labels, w(iChan));
end
end
else
fprintf('Some channels not weighted 1 by the huber average reference\n')
end
else
fprintf('All channels weighted 1 by the huber average reference (not outlier detected; same as average reference)\n')
end
else
if 0 % alternate code - should work exactly the same
if isempty(ref)
ref=chansin; % average reference
end % if
chansout=chansin;
data(chansout,:)=data(chansout,:)-ones(nchansin,1)*mean(data(ref,:),1);
else
if ~isempty(ref) % not average reference
refmatrix = eye(nchansin); % begin with identity matrix
tmpref = ref;
for index = length(g.exclude):-1:1
tmpref(find(g.exclude(index) < tmpref)) = tmpref(find(g.exclude(index) < tmpref))-1;
end
for index = 1:length(tmpref)
refmatrix(:,tmpref(index)) = refmatrix(:,tmpref(index))-1/length(tmpref);
end
else % compute average reference
refmatrix = eye(nchansin)-ones(nchansin)*1/nchansin;
end
chansout = chansin;
data(chansout,:) = refmatrix*data(chansin,:);
end
end
% change reference in elocs structure
% -----------------------------------
if ~isempty(g.elocs)
if isempty(ref)
for ind = chansin
g.elocs(ind).ref = 'average';
end
else
reftxt = { g.elocs(ref).labels };
if length(reftxt) == 1, reftxt = reftxt{1};
else
reftxt = cellfun(@(x)([x ' ']), reftxt, 'uniformoutput', false);
reftxt = [ reftxt{:} ];
end
for ind = chansin
g.elocs(ind).ref = reftxt;
end
end
end
% remove reference
% ----------------
morechans = [];
if strcmpi(g.keepref, 'off')
data(ref,:) = [];
if ~isempty(g.elocs)
morechans = g.elocs(ref);
g.elocs(ref) = [];
end
end
data = reshape(data, size(data,1), dim2, dim3);
% treat optional ica parameters
% -----------------------------
W = []; S = []; icachansind = [];
if ~isempty(g.icaweights)
disp('Warning: This function does not process ICA array anymore, use the pop_reref function instead');
end
Elocs = g.elocs;
% Huber Mean Estimator
%
% m = huber_mean(x, delta, tol, max_iter)
%
% x : Input data vector.
% delta : Threshold parameter (tuning constant).
% tol : Tolerance for convergence (default: 1e-6).
% max_iter: Maximum number of iterations (default: 100).
%
% Returns the robust Huber mean of x.
function [x,m,w] = huber_mean(x, delta, tol, max_iter)
if nargin < 2 || isempty(delta)
delta = 25.0; % Default threshold if not provided in microvolts
end
if nargin < 3 || isempty(tol)
tol = 1e-6;
end
if nargin < 4 || isempty(max_iter)
max_iter = 100;
end
% Initial estimate using the arithmetic mean
m = mean(x);
for iter = 1:max_iter
r = mean(bsxfun(@minus, x, m),2); % Residuals
w = ones(size(x,1),1); % Default weights
% Update weights for large residuals
idx = abs(r) > delta;
w(idx) = delta ./ abs(r(idx));
% Compute the weighted mean
m_new = sum(bsxfun(@times, w, x))/ sum(w);
% Check convergence
if abs(m_new - m) < tol
break;
end
m = m_new;
end
x = bsxfun(@minus, x, m);