diff --git a/README.md b/README.md index bed72c5..4bbdb96 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,134 @@ + # TBT + [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1241518.svg)](https://doi.org/10.5281/zenodo.1241518) This EEGLAB plugin allows for the automatic rejection and interpolation of channels on an epoch-by-epoch basis. -It also comes with an additional method for rejecting epochs - max-minus-min threshold. +It also comes with an additional method for rejecting epochs - max-minus-min threshold [(see here for more info)](./doc/Using_eegmaxmin.md). ## Downloading -- You can download TBT through EEGLAB's data-processing extension GUI (_File > Manage EEGLAB Extensions > Data-Processing Extensions_). -- Or as a `.zip` file from EEGLABS's servers [(TBT v1.5)](http://sccn.ucsd.edu/eeglab/plugins/TBT1.5.zip). -- Or as a `.zip` file from the GitHub page [(TBT v1.5)](https://github.com/mattansb/TBT/releases). +- You can download TBT through EEGLAB's data-processing extension GUI (*File > Manage EEGLAB Extensions > Data-Processing Extensions*). +- Or as a `.zip` file from EEGLABS's servers [(TBT v1.5)](http://sccn.ucsd.edu/eeglab/plugins/TBT1.5.zip). +- Or as a `.zip` file from the GitHub page [(TBT)](https://github.com/mattansb/TBT/releases). -## Using `pop_TBT` -### List of included functions -- `eegplugin_TBT` - EEGLAB plugin function. -- `tbt_bcr` - epoch-by-epoch channel interpolation, based on any rejection method used by EEGLAB, or a manual cell-array listing which channels to interpolate in which epochs. -- `pop_TBT` - call from EEGLAB menu of `tbt_bcr`. If no parameters are specified, pops a GUI window +## List of included functions -### TBT GUI +- `eegplugin_TBT` - EEGLAB plugin function. +- `tbt_bcr` - epoch-by-epoch channel interpolation, based on any rejection method used by EEGLAB, or a manual cell-array listing which channels to interpolate in which epochs. +- `tbt_bool2cell` - converts a boolean channel-by-trial matrix to a tbt ready cell-list (see bellow). +- `tbt_cell2bool` - converts a tbt ready cell-list a boolean channel-by-trial matrix (see bellow). +- `pop_TBT` - call from EEGLAB menu of `tbt_bcr`. If no parameters are specified, pops a GUI window. -![pop_TBT](doc/TBT_eg.png) +## TBT GUI -### Scripting -```matlab +Use the menu *Tools > Epoch by Epoch Rejection/Interpolation*, or type into the command line: + +``` matlab [EEG, com, badlist] = pop_TBT(EEG); % pop-up interactive window mode +``` + +![](doc/TBT_eg.png) + +You will be asked to select a rejection method, and set its parameters, and also to set the following: +1. **The maximum percent of bad trials per channel.** If a channel is bad on more then this percent of the trials, the channel will be removed across the whole data-set. +2. **The maximum number of bad channels per trial.** If a trial has more than this number of bad channels, the trial will be removed. +3. Whether to plot the marked channels and trials before rejecting and interpolating the marked channels. + +![](doc/tbt_plot.png) + +## Scripting + +Scripting takes to general following form: + +``` matlab +% Use some rejection method: +EEG = pop_eegmaxmin(EEG); + +% Send the 'rejE' matrix to pop_TBT: +my_bads = EEG.reject.rejmaxminE; +EEG = pop_TBT(EEG,my_bads,0.3,10); + +% to get more info, type 'help pop_TBT' in the command line. +``` + +Scripting gives two major additional not available in the gui: + +1. Supplying a cell-list for rejection +2. interpolating additional missing channels (thus making TBT an optional last step in pre-processing) + +### Supplying a cell-list -EEG = pop_TBT(EEG,bads,badsegs,badchans,plot_bads); -% EEG - input dataset -% bads - a boolean 2-d matrix (channels * trials) specifying "bad -% channels" for each epoch. OR -% a x-by-2 cell list, with the first column specifying the -% epochs, and the second column specifying the bad channels -% in those epochs. e.g.: {1,{'E12'};[13 28],{'E22'}} will -% remove E12 from the 1st epoch, and E22 from epochs 13 and -% 28. -% badsegs - Number of max bad channels per epoch. If an epoch has -% more than this number of bad channels, the epoch is -% removed. -% badchans - Proportion (e.g., 0.3) of max bad epochs per channel. If -% a channel was found to be bad on more than this percent -% of trials, it is removed. -% plot_bads - [0|1] plot before executing. When plotting, will also ask -% to confirm. If no plotting, will execute immediately. +A cell list can be manually created to mark bad channels in specific trials. For example, if we wish to remove E12 and E45 from the 1st epoch, and E22 from epochs 13 and 28, we would create a cell list to be used this list as input for `pop_TBT`: +``` matlab +my_bads = {... + 1,{'E12','E45'};... + [13 28],{'E22'};... + } + +EEG = pop_TBT(EEG,my_bads,0.3,10); + ``` -## Using `pop_eegmaxmin` -### List of included functions +This method can also be combined with other `rejE` methods using the `tbt_cell2bool` function: -- `eegmaxmin` - new rejection method, based on max-min amplitude differences (available only for channel data, not IC activation). -- `pop_eegmaxmin` - call from EEGLAB menu of `eegmaxmin`. If no parameters are specified, pops a GUI window: +``` matlab +% Specify cell-list +my_bads = {... + 1,{'E12','E45'};... + [13 28],{'E22'};... + } -### Max-Min Threshold GUI +% transforms the cell-list into a 'rejE'-like matrix. +my_bads = tbt_cell2bool(my_bads,EEG); -![pop_eegmaxmin](doc/maxmin_eg.png) +% combine with automatic method: +EEG = pop_eegmaxmin(EEG); -### Scripting -```matlab -[EEG, com] = pop_eegmaxmin(EEG); % pop-up interactive window mode -EEG = pop_eegmaxmin(EEG,chanRange,timeRange,minmaxThresh,winSize,stepSize); -% EEG - input dataset. -% chanRange - [1:EEG.nbchan] indecies for channels. -% timeRange - [1:EEG.xmax*1000] range for inspection in ms. -% minmaxThresh- Threshold for the difference between max and min. -% winSize - size of moving window (in ms). -% stepSize - step size for moving window (in ms). -% maW - moving average window size [default 0]. +my_bads = my_bads | EEG.reject.rejmaxminE; +EEG = pop_TBT(EEG,my_bads,0.3,10); + ``` +The `tbt_bool2cell` function is the reverse of `tbt_cell2bool`, converting a boolean `rejE` matrix to a tbt-ready cell-list. For example: + +```Matlab + +EEG = pop_eegmaxmin(EEG); + +tbt_bool2cell(EEG.reject.rejmaxminE, EEG) + +>> ans = +>> +>> 70ª2 cell array +>> +>> [ 2] {'E64' 'E90'} +>> [ 3] {'E63' 'E64' 'E68' 'E90' 'E99'} +>> [ 5] {'E68' 'E73'} +>> ... + +``` + +### Intepolating all missing channels + +By default, trial-by-trial interpolation interpolates *only* the channels that are marked on a single-trial basis. i.e., channels marked as bad across the whole data-set will not be re-added by interpolation. If you wish to add them back (or any other channel that may have been removed in any previous processing step), channel locations can be added to `pop_tbt`: + +``` matlab +% To add back all channels from the input EEG data-set: +EEG = pop_eegmaxmin(EEG); + +my_bads = EEG.reject.rejmaxminE; + +EEG = pop_TBT(EEG,my_bads,0.3,10,[],EEG.chanlocs); % or any other chanloc struct + +``` + +This makes TBT an ideal 'last step' in preprocessing - providing a clean data-set with all missing channels interpolated. + +Author +------ + +- **Mattan S. Ben-Shachar** \[aut, cre\] \ diff --git a/doc/Using_eegmaxmin.md b/doc/Using_eegmaxmin.md new file mode 100644 index 0000000..decbc4b --- /dev/null +++ b/doc/Using_eegmaxmin.md @@ -0,0 +1,25 @@ + + +eegmaxmin +========= + +Using `pop_eegmaxmin` +--------------------- + +### List of included functions + +- `eegmaxmin` - new rejection method, based on max-min amplitude differences (available only for channel data, not IC activation). +- `pop_eegmaxmin` - call from EEGLAB menu of `eegmaxmin`. If no parameters are specified, pops a GUI window: + +### Max-Min Threshold GUI + +![pop\_eegmaxmin](maxmin_eg.png) + +### Scripting + +``` matlab +[EEG, com] = pop_eegmaxmin(EEG); % pop-up interactive window mode + +EEG = pop_eegmaxmin(EEG,chanRange,timeRange,minmaxThresh,winSize,stepSize,maW); +% to get more info, type 'help pop_eegmaxmin' in the command line. +``` diff --git a/doc/tbt_plot.png b/doc/tbt_plot.png new file mode 100644 index 0000000..7272c71 Binary files /dev/null and b/doc/tbt_plot.png differ diff --git a/tbt/pop_TBT.m b/tbt/pop_TBT.m index 4fc2fc7..716e535 100644 --- a/tbt/pop_TBT.m +++ b/tbt/pop_TBT.m @@ -1,10 +1,10 @@ % pop_TBT() - Rejects and iterpolates channels on a epoch by epoch basis. % % Usage: -% >> EEG = pop_TBT(EEG,bads,badsegs,badchans,plot_bads); +% >> EEG = pop_TBT(EEG,bads,badsegs,badchans,plot_bads,chanlocs); % % only interpolate channels according to `bads`: -% >> EEG = pop_TBT(EEG,bads,EEG.nbchan,1,plot_bads); +% >> EEG = pop_TBT(EEG,bads,EEG.nbchan,1,plot_bads,chanlocs); % % pop-up interative window mode: % >> [EEG, com, badlist] = pop_TBT(EEG); @@ -41,6 +41,12 @@ % of trials, it is removed. % plot_bads - [0|1] plot before executing. When plotting, will also ask % to confirm. If no plotting, will execute immediately. +% chanlocs - [optional] a chanlocs struct (e.g., EEG.chanlocs). If +% provided, missing channels will be interpolated according +% to this struct, and not the input EEG. NOTE that if not +% provided, channel that have been rejected across the +% dataset (according to the badchans critirion) will not be +% interpolated back in. % % Outputs: % EEG - output dataset. Note that due to the fucntion of @@ -64,7 +70,7 @@ % along with this program; if not, write to the Free Software % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -function [EEG, com, badlist] = pop_TBT(EEG,bads,badsegs,badchans,plot_bads) +function [EEG, com, badlist] = pop_TBT(EEG,bads,badsegs,badchans,plot_bads,chanlocs) com = ''; @@ -157,12 +163,14 @@ eval([comrej]); eval([comtbt]); end -elseif nargin < 5 - [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,1); - badlist.nbadchan = nbadchan; - badlist.nbadtrial = nbadtrial; else - [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,plot_bads); + if ~exist('plot_bads','var'), plot_bads = 1; end + if exist('chanlocs','var') + [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,plot_bads,chanlocs); + else + [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,plot_bads); + end + badlist.nbadchan = nbadchan; badlist.nbadtrial = nbadtrial; end @@ -184,8 +192,3 @@ function get_help(x,y) end - - - - - diff --git a/tbt/tbt_bcr.m b/tbt/tbt_bcr.m index 825ffa1..1fda8ba 100644 --- a/tbt/tbt_bcr.m +++ b/tbt/tbt_bcr.m @@ -1,7 +1,7 @@ % tbt_bcr() - Rejects and iterpolates channels on a epoch by epoch basis. % % Usage: -% >> [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,plot_bads); +% >> [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,plot_bads,chanlocs); % % Inputs: % EEG - input dataset @@ -16,6 +16,12 @@ % of trials, it is removed. % plot_bads - [0|1] plot before executing. When plotting, will also ask % to confirm. If no plotting, will execute immediately. +% chanlocs - [optional] a chanlocs struct (e.g., EEG.chanlocs). If +% provided, missing channels will be interpolated according +% to this struct, and not the input EEG. NOTE that if not +% provided, channel that have been rejected across the +% dataset (according to the badchans critirion) will not be +% interpolated back in. % % Outputs: % EEG - output dataset @@ -40,19 +46,12 @@ % Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -function [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,plot_bads) +function [EEG, nbadchan, nbadtrial] = tbt_bcr(EEG,bads,badsegs,badchans,plot_bads,chanlocs) %% convert bads from cell to array if iscell(bads) fprintf('pop_TBT(): Converting cell-array.') - bads_array = zeros([size(EEG.data,1) size(EEG.data,3)]); - for r = 1:size(bads,1) - % identift channels - chan_i = cellfun(@(x) any(strcmpi(x,bads{r,2})),{EEG.chanlocs.labels}); - epoch_i = bads{r,1}; - bads_array(chan_i,epoch_i) = 1; - end - bads = bads_array; + bads = tbt_cell2bool(bads,EEG); fprintf('.. done.\n') end @@ -104,7 +103,7 @@ if plot_bads==0 -%% Remove bad channels and trials + %% Remove bad channels and trials % Remove bad channels fprintf('\n') if ~isempty(bChan_lab) % if any bad channels @@ -126,15 +125,15 @@ %% Interpolate bad channels (trial by trial) EEG_old = EEG; % need the old channlocs an events for later - - tbt = {[],{}}; % make empty list - for tr = 1:size(bads,2) % each trial - if any(bads(:,tr)) % if has any bad channels - tbt{end+1,1} = tr; % list trial number - tbt{end,2} = {EEG.chanlocs(bads(:,tr)==1).labels}; % list bad channels in current trial - end + if ~exist('chanlocs','var') + interp_all = true; + chanlocs = EEG_old.chanlocs; + else + interp_all = false; end - tbt = tbt(2:end,:); % remove first empty row + + % convert bool array to n-by-2 cell-list + tbt = tbt_bool2cell(bads,EEG); if size(tbt,1)~=0 fprintf('pop_TBT(): Splitting data') @@ -160,8 +159,8 @@ for t = 1:length(NEWEEG) if ~mod(t,5), fprintf('.'); end % Interpolate: - evalc('NEWEEG(t) = pop_interp(NEWEEG(t), EEG_old.chanlocs, ''spherical'');'); - % NEWEEG(t) = pop_interp(NEWEEG(t), EEG_old.chanlocs, 'spherical'); + evalc('NEWEEG(t) = pop_interp(NEWEEG(t), chanlocs, ''spherical'');'); + % NEWEEG(t) = pop_interp(NEWEEG(t), chanlocs, 'spherical'); clear missing end @@ -190,6 +189,10 @@ EEG.urevent = EEG_old.urevent; fprintf([repmat('\b',[1 28]) '... done.\n']) + elseif interp_all + fprintf('pop_TBT(): Interpolating missing channels') + evalc('EEG = pop_interp(EEG, chanlocs, ''spherical'');'); + fprintf('.. done.\n') end EEG = eeg_checkset(EEG); diff --git a/tbt/tbt_bool2cell.m b/tbt/tbt_bool2cell.m new file mode 100644 index 0000000..87844ae --- /dev/null +++ b/tbt/tbt_bool2cell.m @@ -0,0 +1,12 @@ +function tbt = tbt_bool2cell(bads,EEG) + +tbt = {[],{}}; % make empty list +for tr = 1:size(bads,2) % each trial + if any(bads(:,tr)) % if has any bad channels + tbt{end+1,1} = tr; % list trial number + tbt{end,2} = {EEG.chanlocs(bads(:,tr)==1).labels}; % list bad channels in current trial + end +end +tbt = tbt(2:end,:); % remove first empty row + +end \ No newline at end of file diff --git a/tbt/tbt_cell2bool.m b/tbt/tbt_cell2bool.m new file mode 100644 index 0000000..a09c20f --- /dev/null +++ b/tbt/tbt_cell2bool.m @@ -0,0 +1,12 @@ +function bads_array = tbt_cell2bool(bads,EEG) + +bads_array = zeros([size(EEG.data,1) size(EEG.data,3)]); + +for r = 1:size(bads,1) + % identift channels + chan_i = cellfun(@(x) any(strcmpi(x,bads{r,2})),{EEG.chanlocs.labels}); + epoch_i = bads{r,1}; + bads_array(chan_i,epoch_i) = 1; +end + +end \ No newline at end of file