Skip to content

Commit

Permalink
included Table with features per epoch and per channel
Browse files Browse the repository at this point in the history
  • Loading branch information
otoolej committed Aug 26, 2020
1 parent 867849b commit c521173
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 19 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ in the git logs.
### Changed
### Removed
### Fixed
### Added


## [0.4.4] - 26-08-2020
### Changed
- Returns Table of feature values (per epoch and per channel) from function
`generate_all_features` if input argument `return_feat_epoch=true`
### Removed
### Fixed
- Bug fix for `connectivity_features`; was: if channel labels (cell of strings) are empty
then would assume (incorrectly) only 2 channels and would use index [1,2] for left,right
hemisphere; now: if no channel labels throw warning
Expand All @@ -15,8 +24,6 @@ in the git logs.
warning)?




## [0.4.3] - 2020-04-22
### Changed
- changed behaviour for `IBI_burst_prc` and `IBI_burst_number` features: now removing any
Expand Down
6 changes: 3 additions & 3 deletions connectivity_features/channel_hemisphere_pairs.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
% John M. O' Toole, University College Cork
% Started: 20-04-2016
%
% last update: Time-stamp: <2019-11-06 09:22:54 (otoolej)>
% last update: Time-stamp: <2020-08-26 17:43:45 (otoolej)>
%-------------------------------------------------------------------------------
function ipairs=channel_hemisphere_pairs(channel_labels)
function ipairs=channel_hemisphere_pairs(channel_labels, DBverbose)
if(nargin < 2 || isempty(DBverbose)), DBverbose = false; end

DBverbose=0;

N=length(channel_labels);
[ileft,iright]=channel_hemispheres(channel_labels);
Expand Down
14 changes: 9 additions & 5 deletions connectivity_features/connectivity_features.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
% John M. O' Toole, University College Cork
% Started: 13-04-2016
%
% last update: Time-stamp: <2020-08-17 16:49:56 (otoolej)>
% last update: Time-stamp: <2020-08-26 18:00:41 (otoolej)>
%-------------------------------------------------------------------------------
function featx = connectivity_features(x, Fs, feat_name, params_st, ch_labels)
if(nargin<2), error('need 2 input arguments'); end
Expand All @@ -50,12 +50,12 @@


DBplot = 0;
featx = NaN;


[N_channels, N] = size(x);
if(N_channels<2)
featx = NaN;
warning('requires at least 2 channels');
warning('REQUIRED: at least 2 channels for connectivity functions');
return;
end

Expand All @@ -81,10 +81,14 @@
[ileft, iright] = channel_hemispheres(ch_labels);
ipairs = channel_hemisphere_pairs(ch_labels);
else
warning('REQUIRED: channel names for coherence function.');
featx = NaN;
warning('REQUIRED: channel names for connectivity functions.');
return;
end
if(isempty(ipairs))
warning('no channel pairs (left / right) for connectivity functions.');
return;
end




Expand Down
72 changes: 63 additions & 9 deletions generate_all_features.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
%
% Outputs:
% feat_st - structure containing features
% feats_per_epoch - cell of features x channel x epoch x frequency_band
% feats_per_epoch - table of features per epoch; in long format, with columns:
% [time - channel - freq. band - value - feature name]
% start_time_sec: start time of the epoch (seconds)
% channel: name of channel (string)
% freq_band: number of frequency band (string, e.g. FB1)
% feature_value: value of the feature (real number, scalar)
% feature: name of the feature (string)
%
%
% Example:
Expand All @@ -32,10 +38,10 @@
% John M. O' Toole, University College Cork
% Started: 07-04-2016
%
% last update: Time-stamp: <2020-04-23 16:24:02 (otoolej)>
% last update: Time-stamp: <2020-08-17 18:08:33 (otoolej)>
%-------------------------------------------------------------------------------
function [feat_st,feats_per_epochs]=generate_all_features(fname,channel_names,feat_set, ...
return_feat_epoch)
function [feat_st, feats_all_epochs_tb] = generate_all_features(fname, channel_names, feat_set, ...
return_feat_epoch)
if(nargin<2 || isempty(channel_names)), channel_names=[]; end
if(nargin<3 || isempty(feat_set)), feat_set=[]; end
if(nargin<4 || isempty(return_feat_epoch)), return_feat_epoch = false; end
Expand Down Expand Up @@ -102,6 +108,7 @@
end

N_feats=length(feat_set);
feats_all_epochs_tb = [];

% A) iterate over features
for n=1:N_feats
Expand All @@ -117,9 +124,9 @@
if( any(strcmp({'amplitude','spectral','rEEG','FD'},feat_group)) )

% B) iterate over channels
feats_channel=[]; x_epochs=[];
feats_channel=[]; x_epochs=[]; feats_tbl = [];
for c=1:N_channels
x_epochs=overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP);
[x_epochs, epoch_start_times] = overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP);
N_epochs=size(x_epochs,1);

% C) iterate over epochs
Expand Down Expand Up @@ -147,14 +154,35 @@
end
% if want to return feature estimated over all epochs:
if(return_feat_epoch)
feats_per_epochs{n}(c,:,:)=feats_epochs;
% feats_per_epochs{n}(c,:,:)=feats_epochs;

% create table with features and start time of epoch:
fb_names = arrayfun(@(x) ['FB' num2str(x)], 1:size(feats_epochs, 2), 'un', false);
tb = array2table([epoch_start_times' feats_epochs], ...
'VariableNames', ['start_time_sec', fb_names]);
% add channel:
tb.channel(:) = string(ch_labels{c});

% convert from wide to long format for frequency bands:
tb = stack(tb, fb_names, 'newDataVariableName', {'feature_value'}, ...
'IndexVariableName', {'freq_band'});

feats_tbl = [feats_tbl; tb];
end

% median over all epochs
feats_channel(c,:)=nanmedian(feats_epochs, 1);
end
% and median over all channels:
feat_st.(char(feat_set{n}))=nanmedian(feats_channel, 1);


if(return_feat_epoch)
% add feature name and combine:
feats_tbl.feature(:) = string(feat_set{n});
feats_all_epochs_tb = [feats_all_epochs_tb; feats_tbl];
end


%---------------------------------------------------------------------
% CONNECTIVITY FEATURES
Expand All @@ -164,7 +192,12 @@

x_epochs=[];
for c=1:N_channels
x_epochs(c,:,:)=overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP);
if(c == N_channels)
[x_epochs(c,:,:), epoch_start_times] = ...
overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP);
else
x_epochs(c,:,:) = overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP);
end
end
N_epochs=size(x_epochs,2);

Expand All @@ -186,6 +219,25 @@
% median over all epochs
feat_st.(char(feat_set{n}))=nanmedian(feats_epochs, 1);

% if want to return feature estimated over all epochs:
if(return_feat_epoch)
% create table with features and start time of epoch:
fb_names = arrayfun(@(x) ['FB' num2str(x)], 1:size(feats_epochs, 2), 'un', false);
tb = array2table([epoch_start_times' feats_epochs], ...
'VariableNames', ['start_time_sec', fb_names]);
% add channel:
tb.channel(:) = NaN;

% convert from wide to long format for frequency bands:
tb = stack(tb, fb_names, 'newDataVariableName', {'feature_value'}, ...
'IndexVariableName', {'freq_band'});

% add feature name and combine:
tb.feature(:) = string(feat_set{n});
feats_all_epochs_tb = [feats_all_epochs_tb; tb];
end



%---------------------------------------------------------------------
% inter-burst interval features
Expand All @@ -208,7 +260,7 @@



function [x_epochs] = overlap_epochs(x, Fs, L_window, overlap, window_type)
function [x_epochs, start_times] = overlap_epochs(x, Fs, L_window, overlap, window_type)
%---------------------------------------------------------------------
% overlapping epochs in one matrix
%---------------------------------------------------------------------
Expand All @@ -232,11 +284,13 @@


x_epochs = NaN(N_epochs, L_epoch);
start_times = NaN(1, N_epochs);
for k = 1:N_epochs
nf = nw + (k - 1) * L_hop;
% zero-pad if outside x:
nf = nf(ismember(nf, ix)) + 1;
i_nf = 1:length(nf);
start_times(k) = (min(nf) - 1) / Fs;

x_epochs(k, i_nf) = x(nf) .* win_epoch(i_nf).';
end

0 comments on commit c521173

Please sign in to comment.