From aba66fe8007d4d2c91d60cb71c4943b071374cd8 Mon Sep 17 00:00:00 2001 From: Bernhard <90285552+4gwe@users.noreply.github.com> Date: Mon, 30 Sep 2024 07:39:26 +0200 Subject: [PATCH 1/7] made pspm_expand_epochs and added to pspm_remove --- src/pspm_expand_epochs.m | 147 +++++++++++++++++++++++++++++++++++++++ src/pspm_remove_epochs.m | 27 ++++++- 2 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 src/pspm_expand_epochs.m diff --git a/src/pspm_expand_epochs.m b/src/pspm_expand_epochs.m new file mode 100644 index 000000000..dff0a1d37 --- /dev/null +++ b/src/pspm_expand_epochs.m @@ -0,0 +1,147 @@ +function [sts, ep_exp] = pspm_expand_epochs(epoches, expansion, options) + +% fn passt nicht zu missing epochs +% [sts, channel_index] = pspm_expand_epochs( {data_fn, channel}, expansion , options) +% [sts, output_file] = pspm_expand_epochs( missing_epochs_fn, expansion , options) +% [sts, expanded_epochs] = pspm_expand_epochs( missing_epochs, expansion , options) % +% options.mode = 'datafile' +% 'missing_ep_file' +% 'missing_ep' + +global settings +if isempty(settings) + pspm_init; +end + +sts = -1; + +if nargin < 3 + warning('ID:invalid_input', 'Not enough input arguments'); + return; +end + +switch options.mode + % missing epochs + case 'missing_ep' + % Directly expand the given epochs + [sts, ep_exp] = expand(epoches, expansion); + return; + + % missing epoches file + case 'missing_ep_file' + % Load missing epochs from file + data = load(epoches); % Load the file without specifying a variable + + % Assuming the file contains a variable called 'epochs', access it + % Right name? + if isfield(data, 'epochs') + missing_epochs = data.epochs; + else + error('File does not contain variable ''epochs''.'); + return; + end + + if isempty(missing_epochs) + error('Failed to load missing epochs from file.'); + return; + end + + % Expand the loaded epochs + [sts, ep_exp] = expand(missing_epochs, expansion); + if sts == -1 + error('Failed to expand epochs.'); + end + + % Save expanded missing epoch to a new file with 'e' prefix + [pathstr, name, ext] = fileparts(epoches); + output_file = fullfile(pathstr, ['e' name ext]); + save(output_file, 'ep_exp'); % should i save it as epoch??? + disp(['Expanded epochs saved to: ', output_file]); + + sts = 0; + return; + + + case 'datafile' % rename!! + + % Load channel data + datafile = epoches{1}; + channel = epoches{2}; + [lsts, ~, data] = pspm_load_data(datafile, channel); + if lsts == -1 + error('Failed to load data from file.'); + end + + channel_data = data{1}; + sr = channel_data.header.sr; + + % Find NaN indices + nan_indices = isnan(channel_data.data); + + % Convert NaN indices to epochs + nan_epochs = pspm_logical2epochs(nan_indices, sr); + + % Expand the epochs + [sts, ep_exp] = expand(nan_epochs, expansion); + if sts == -1 + error('Failed to expand epochs.'); + end + + % Convert expanded epochs back to logical indices + expanded_indices = pspm_epochs2logical(ep_exp, numel(channel_data.data), sr); + + % Set data to NaN at expanded indices + channel_data.data(expanded_indices) = NaN; + + % Save the data back to the file + [sts, out] = pspm_write_channel(datafile, {channel_data}, 'replace'); % add to options 'newfile'? + varargout{2} = 23 % channel_data.data; + return; + + + otherwise + error('Unknown mode in options.'); + return; +end + +end + + +function [sts, ep_exp] = expand(ep, expansion) +% Helper function to expand epochs by the specified pre and post times +% and merge overlapping epochs. +% Also ensures that no epoch starts before time 0. + +% Initialize status +sts = -1; + +% Check if epochs matrix and expansion vector are valid +if isempty(ep) || numel(expansion) ~= 2 + error('Invalid input to expand function.'); + return; +end + +% Expand epochs +pre = expansion(1); +post = expansion(2); +expanded_epochs_temp = [ep(:,1) - pre, ep(:,2) + post]; + +% +% Ensure that the start of any epoch is not negative +expanded_epochs_temp(expanded_epochs_temp(:,1) < 0, 1) = 0; % or <1??? + +% Merge overlapping epochs +ep_exp = expanded_epochs_temp(1, :); % Start with the first epoch +for i = 2:size(expanded_epochs_temp, 1) + % If the current epoch overlaps with the previous, merge them + if ep_exp(end, 2) >= expanded_epochs_temp(i, 1) + ep_exp(end, 2) = max(ep_exp(end, 2), expanded_epochs_temp(i, 2)); + else + % Otherwise, add the current epoch as a new row + ep_exp = [ep_exp; expanded_epochs_temp(i, :)]; + end +end + +% Success +sts = 0; +end diff --git a/src/pspm_remove_epochs.m b/src/pspm_remove_epochs.m index 3236a1a47..491dfdf7d 100644 --- a/src/pspm_remove_epochs.m +++ b/src/pspm_remove_epochs.m @@ -12,14 +12,17 @@ % be in seconds. This parameter is passed to pspm_get_timing(). % * timeunits: timeunits of the epochfile. % ┌───options -% └─.channel_action: +% └─.channel_action: [pre, post] % ['add'/'replace'] Defines whether new channels should be added or % corresponding channels should be replaced. The default value is 'add'. +% .expand_epochs: +% Expand epochs by 0.5 seconds before and after % ● Output % * channel_index: index of channel containing the processed data % ● History % Introduced in PsPM 4.0 % Written in 2016 by Tobias Moser (University of Zurich) +% Maintained in 2024 by Bernhard Agoué von Raußendorf global settings if isempty(settings) @@ -36,7 +39,7 @@ elseif nargin < 4 options = struct(); end -options = pspm_options(options, 'remove_epochs'); +options = pspm_options(options, 'remove_epochs'); % change!!! [0,0] if options.invalid return end @@ -51,6 +54,26 @@ return; end + + +expansion = options.expand_epochs; + +if numel(expansion) ~= 2 + error('Expansion must be a 2-element vector [pre, post].'); +end +% Expand the epochs + +[sts, ep_exp] = pspm_expand_epochs(ep, expansion, 'missing_ep'); +if psts == -1 + return; +end + + + + + + + n_ep = size(ep, 1); n_data = numel(data); for i_data = 1:n_data From 0285c103c47fc9d1b93458d12d9f6008fe3b94f5 Mon Sep 17 00:00:00 2001 From: Bernhard <90285552+4gwe@users.noreply.github.com> Date: Sun, 6 Oct 2024 23:55:25 +0200 Subject: [PATCH 2/7] pspm_expand_epochs varagin structure --- src/pspm_expand_epochs.m | 188 +++++++++++++++++++++++---------------- src/pspm_remove_epochs.m | 9 +- 2 files changed, 117 insertions(+), 80 deletions(-) diff --git a/src/pspm_expand_epochs.m b/src/pspm_expand_epochs.m index dff0a1d37..b9ffa4a26 100644 --- a/src/pspm_expand_epochs.m +++ b/src/pspm_expand_epochs.m @@ -1,9 +1,9 @@ -function [sts, ep_exp] = pspm_expand_epochs(epoches, expansion, options) +function [sts, ep_exp] = pspm_expand_epochs(varargin) % fn passt nicht zu missing epochs -% [sts, channel_index] = pspm_expand_epochs( {data_fn, channel}, expansion , options) % [sts, output_file] = pspm_expand_epochs( missing_epochs_fn, expansion , options) -% [sts, expanded_epochs] = pspm_expand_epochs( missing_epochs, expansion , options) % +% [sts, expanded_epochs] = pspm_expand_epochs( missing_epochs, expansion , options) +% [sts, channel_index] = pspm_expand_epochs( filename, channel, expansion , options) % options.mode = 'datafile' % 'missing_ep_file' % 'missing_ep' @@ -20,100 +20,125 @@ return; end -switch options.mode - % missing epochs - case 'missing_ep' - % Directly expand the given epochs - [sts, ep_exp] = expand(epoches, expansion); - return; - % missing epoches file - case 'missing_ep_file' - % Load missing epochs from file - data = load(epoches); % Load the file without specifying a variable - - % Assuming the file contains a variable called 'epochs', access it - % Right name? - if isfield(data, 'epochs') - missing_epochs = data.epochs; - else - error('File does not contain variable ''epochs''.'); - return; - end - if isempty(missing_epochs) - error('Failed to load missing epochs from file.'); - return; - end - % Expand the loaded epochs - [sts, ep_exp] = expand(missing_epochs, expansion); - if sts == -1 - error('Failed to expand epochs.'); - end +if nargin == 3 + + expansion = varargin{2}; + options = varargin{3}; - % Save expanded missing epoch to a new file with 'e' prefix - [pathstr, name, ext] = fileparts(epoches); - output_file = fullfile(pathstr, ['e' name ext]); - save(output_file, 'ep_exp'); % should i save it as epoch??? - disp(['Expanded epochs saved to: ', output_file]); + switch options.mode + % missing epochs + case 'missing_ep' + % Directly expand the given epochs + epochs = varargin{1}; + [ests, ep_exp] = expand(epochs, expansion); - sts = 0; - return; + if ests == -1 + error("Failed to expand epochs."); + return; + end + + sts = 1; + return; + % missing epoches file + case 'missing_ep_file' + % Load missing epochs from file + filename = varargin{1}; + + [lsts, epochs] = pspm_get_timing('file', filename); + + + if lsts == -1 + error("Epoch could not be loaded"); + return; + end + + % Expand the loaded epochs + [ests, ep_exp] = expand(epochs.epochs, expansion); + if ests == -1 + error('Failed to expand epochs.'); + return; + end + + % Save expanded missing epoch to a new file with 'e' prefix + [pathstr, name, ext] = fileparts(filename); + output_file = fullfile(pathstr, ['e' name ext]); + save(output_file, 'ep_exp'); % should i save it as epoch??? + disp(['Expanded epochs saved to: ', output_file]); + + sts = 1; + return; + end +end - case 'datafile' % rename!! +if nargin == 4 %&& options.mode == 'datafile' % rename!! + + % Load channel data + filename = varargin{1}; + channel = varargin{2}; + expansion = varargin{3}; + [lsts, ~, data] = pspm_load_data(filename, channel); + if lsts == -1 + error('Failed to load data from file.'); + return; + end - % Load channel data - datafile = epoches{1}; - channel = epoches{2}; - [lsts, ~, data] = pspm_load_data(datafile, channel); - if lsts == -1 - error('Failed to load data from file.'); - end + channel_data = data{1}; + sr = channel_data.header.sr; - channel_data = data{1}; - sr = channel_data.header.sr; + % Find NaN indices + nan_indices = isnan(channel_data.data); - % Find NaN indices - nan_indices = isnan(channel_data.data); + % Convert NaN indices to epochs + nan_epochs = pspm_logical2epochs(nan_indices, sr); - % Convert NaN indices to epochs - nan_epochs = pspm_logical2epochs(nan_indices, sr); + % Expand the epochs + [ests, ep_exp] = expand(nan_epochs, expansion); + if ests == -1 + error('Failed to expand epochs.'); + end - % Expand the epochs - [sts, ep_exp] = expand(nan_epochs, expansion); - if sts == -1 - error('Failed to expand epochs.'); - end + % Convert expanded epochs back to logical indices + expanded_indices = pspm_epochs2logical(ep_exp, numel(channel_data.data), sr); - % Convert expanded epochs back to logical indices - expanded_indices = pspm_epochs2logical(ep_exp, numel(channel_data.data), sr); + % Set data to NaN at expanded indices + channel_data.data(logical(expanded_indices)) = NaN; - % Set data to NaN at expanded indices - channel_data.data(expanded_indices) = NaN; + % Save the data back to the file + opt = struct(); + [wsts, ~] = pspm_write_channel(filename, {channel_data}, 'replace',opt); % add to options 'newfile'? + % channel_data.data; - % Save the data back to the file - [sts, out] = pspm_write_channel(datafile, {channel_data}, 'replace'); % add to options 'newfile'? - varargout{2} = 23 % channel_data.data; - return; + if wsts == -1 + error('Failed to write the new channel.'); + return + end + sts = 1; + return; - otherwise - error('Unknown mode in options.'); - return; + +else + error('Unknown mode in options.'); + return; end + end -function [sts, ep_exp] = expand(ep, expansion) + +function [ests, ep_exp] = expand(ep, expansion) % Helper function to expand epochs by the specified pre and post times % and merge overlapping epochs. % Also ensures that no epoch starts before time 0. -% Initialize status -sts = -1; +% Initialize status of expand +ests = -1; +ep_exp = []; % Check if epochs matrix and expansion vector are valid if isempty(ep) || numel(expansion) ~= 2 @@ -126,9 +151,23 @@ post = expansion(2); expanded_epochs_temp = [ep(:,1) - pre, ep(:,2) + post]; -% +% % Ensure that the start of any epoch is not negative -expanded_epochs_temp(expanded_epochs_temp(:,1) < 0, 1) = 0; % or <1??? + +[ksts, expanded_epochs_temp] = pspm_get_timing('epochs',expanded_epochs_temp , 'seconds') ; +if ksts == -1 + error('Offsets must be larger than onsets') + return +end + +% If there is only one epoch, no need for merging +if size(expanded_epochs_temp, 1) == 1 + ep_exp = expanded_epochs_temp; + ests = 1; + return; +end + + % Merge overlapping epochs ep_exp = expanded_epochs_temp(1, :); % Start with the first epoch @@ -142,6 +181,5 @@ end end -% Success -sts = 0; +ests = 1; end diff --git a/src/pspm_remove_epochs.m b/src/pspm_remove_epochs.m index 491dfdf7d..7b725542c 100644 --- a/src/pspm_remove_epochs.m +++ b/src/pspm_remove_epochs.m @@ -12,11 +12,10 @@ % be in seconds. This parameter is passed to pspm_get_timing(). % * timeunits: timeunits of the epochfile. % ┌───options -% └─.channel_action: [pre, post] -% ['add'/'replace'] Defines whether new channels should be added or -% corresponding channels should be replaced. The default value is 'add'. -% .expand_epochs: -% Expand epochs by 0.5 seconds before and after +% └─.channel_action: ['add'/'replace'] Defines whether new channels should be added or +% corresponding channels should be replaced. The default value is 'add'. +% └─.expand_epochs: [pre, post] +% % ● Output % * channel_index: index of channel containing the processed data % ● History From a4b6010eaf467c998c1a6b528fdc44cdc565627e Mon Sep 17 00:00:00 2001 From: Bernhard <90285552+4gwe@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:46:22 +0100 Subject: [PATCH 3/7] changed error handeling --- src/pspm_expand_epochs.m | 111 ++++++++++++++++++++++++++------------- src/pspm_remove_epochs.m | 8 +-- 2 files changed, 79 insertions(+), 40 deletions(-) diff --git a/src/pspm_expand_epochs.m b/src/pspm_expand_epochs.m index b9ffa4a26..e2571a145 100644 --- a/src/pspm_expand_epochs.m +++ b/src/pspm_expand_epochs.m @@ -1,12 +1,20 @@ function [sts, ep_exp] = pspm_expand_epochs(varargin) - -% fn passt nicht zu missing epochs -% [sts, output_file] = pspm_expand_epochs( missing_epochs_fn, expansion , options) -% [sts, expanded_epochs] = pspm_expand_epochs( missing_epochs, expansion , options) -% [sts, channel_index] = pspm_expand_epochs( filename, channel, expansion , options) +% ● Description +% +% ● Format +% fn passt nicht zu missing epochs +% [sts, output_file] = pspm_expand_epochs( missing_epochs_fn, expansion , options) +% [sts, expanded_epochs] = pspm_expand_epochs( missing_epochs, expansion , options) +% [sts, channel_index] = pspm_expand_epochs( filename, channel, expansion , options) +% ● Arguments % options.mode = 'datafile' % 'missing_ep_file' -% 'missing_ep' +% 'missing_epochs' +% expansion = [pre , post] or [pre ; post] +% +% ● History +% Written in 2024 by Bernhard Agoué von Raußendorf + global settings if isempty(settings) @@ -14,13 +22,28 @@ end sts = -1; +ep_exp = []; -if nargin < 3 - warning('ID:invalid_input', 'Not enough input arguments'); +if nargin < 3 || nargin > 4 + warning('ID:invalid_input', 'Not enough or to many input arguments'); return; end +% Checks if expantion vector is the valid +if nargin == 3 + if numel(varargin{2}) ~= 2 || ~isnumeric(varargin{2}) + warning('Invalid input to expand vector musst be 1x2 or 2x1.'); + return; + end +end +if nargin == 4 + if numel(varargin{3}) ~= 2 || ~isnumeric(varargin{3}) + warning('Invalid input to expand vector musst be 1x2 or 2x1.'); + return; + end +end + if nargin == 3 @@ -30,43 +53,63 @@ switch options.mode % missing epochs - case 'missing_ep' + case 'missing_epochs' % Directly expand the given epochs + epochs = varargin{1}; + + [gsts, epochs] = pspm_get_timing('epochs',epochs,'seconds'); + if gsts < 1 + warning('Invaled epochs.') + return + end + + % Checks if epoch is empty bc pspm_get_timing returns sts = 1 + if isempty(epochs) + warning('Invaled epochs.') + return + end + + + [ests, ep_exp] = expand(epochs, expansion); - if ests == -1 - error("Failed to expand epochs."); + if ests < 1 + warning('Failed to expand epochs.'); return; end sts = 1; return; - % missing epoches file + case 'missing_ep_file' % Load missing epochs from file filename = varargin{1}; [lsts, epochs] = pspm_get_timing('file', filename); - + if isempty(epochs) + warning('Invaled epochs.') + return + end - if lsts == -1 - error("Epoch could not be loaded"); + if lsts < 1 + warning('Epoch could not be loaded'); return; - end + end + % Expand the loaded epochs - [ests, ep_exp] = expand(epochs.epochs, expansion); - if ests == -1 - error('Failed to expand epochs.'); + [ests, epochs] = expand(epochs.epochs, expansion); + if ests < 1 + warning('Failed to expand epochs.'); return; end % Save expanded missing epoch to a new file with 'e' prefix [pathstr, name, ext] = fileparts(filename); output_file = fullfile(pathstr, ['e' name ext]); - save(output_file, 'ep_exp'); % should i save it as epoch??? + save(output_file, 'epochs'); disp(['Expanded epochs saved to: ', output_file]); sts = 1; @@ -81,8 +124,8 @@ channel = varargin{2}; expansion = varargin{3}; [lsts, ~, data] = pspm_load_data(filename, channel); - if lsts == -1 - error('Failed to load data from file.'); + if lsts < 1 + warning('Failed to load data from file.'); return; end @@ -97,7 +140,7 @@ % Expand the epochs [ests, ep_exp] = expand(nan_epochs, expansion); - if ests == -1 + if ests < 1 error('Failed to expand epochs.'); end @@ -112,8 +155,8 @@ [wsts, ~] = pspm_write_channel(filename, {channel_data}, 'replace',opt); % add to options 'newfile'? % channel_data.data; - if wsts == -1 - error('Failed to write the new channel.'); + if wsts < 1 + warning('Failed to write the new channel.'); return end @@ -122,7 +165,7 @@ else - error('Unknown mode in options.'); + warning('Unknown mode in options.'); return; end @@ -140,23 +183,19 @@ ests = -1; ep_exp = []; -% Check if epochs matrix and expansion vector are valid -if isempty(ep) || numel(expansion) ~= 2 - error('Invalid input to expand function.'); - return; -end + % Expand epochs pre = expansion(1); post = expansion(2); expanded_epochs_temp = [ep(:,1) - pre, ep(:,2) + post]; -% -% Ensure that the start of any epoch is not negative +% Ensure that the start of epoch is not neg. but 0 [ksts, expanded_epochs_temp] = pspm_get_timing('epochs',expanded_epochs_temp , 'seconds') ; -if ksts == -1 - error('Offsets must be larger than onsets') + +if ksts < 1 + warning('Offsets must be larger than onsets') return end @@ -182,4 +221,4 @@ end ests = 1; -end +end \ No newline at end of file diff --git a/src/pspm_remove_epochs.m b/src/pspm_remove_epochs.m index 7b725542c..4a87fa4ca 100644 --- a/src/pspm_remove_epochs.m +++ b/src/pspm_remove_epochs.m @@ -44,12 +44,12 @@ end [lsts, ~, data, filestruct] = pspm_load_data(datafile, channel); pos_of_channels = filestruct.posofchannels; -if lsts == -1 +if lsts < 1 return; end [lsts, ep] = pspm_get_timing('epochs', epochfile, 'seconds'); -if lsts == -1 +if lsts < 1 return; end @@ -63,7 +63,7 @@ % Expand the epochs [sts, ep_exp] = pspm_expand_epochs(ep, expansion, 'missing_ep'); -if psts == -1 +if psts < 1 return; end @@ -100,4 +100,4 @@ % save data to file [sts, out] = pspm_write_channel(datafile, data, options.channel_action, ... struct('channel', pos_of_channels)); -outchanel = out.channel; +outchanel = out.channel; \ No newline at end of file From 311361535d448c24431cad0c7ec5b1a4ea1d1ac0 Mon Sep 17 00:00:00 2001 From: Dominik Bach Date: Thu, 14 Nov 2024 09:58:20 +0100 Subject: [PATCH 4/7] streamline pspm_expand_epochs --- src/pspm_expand_epochs.m | 280 +++++++++++++++------------------------ src/pspm_options.m | 4 + 2 files changed, 112 insertions(+), 172 deletions(-) diff --git a/src/pspm_expand_epochs.m b/src/pspm_expand_epochs.m index e2571a145..ca1a3bc96 100644 --- a/src/pspm_expand_epochs.m +++ b/src/pspm_expand_epochs.m @@ -1,131 +1,104 @@ -function [sts, ep_exp] = pspm_expand_epochs(varargin) +function [sts, out] = pspm_expand_epochs(varargin) % ● Description -% +% pspm_expand_epochs expands epochs in time, and merges overlapping epochs. +% This is useful in processing missing data epochs. The function can take +% a missing epochs file and creates a new file with the original name +% prepended with 'e', a matrix of missing epochs, or a PsPM data file with +% missing data in a given channel. % ● Format -% fn passt nicht zu missing epochs -% [sts, output_file] = pspm_expand_epochs( missing_epochs_fn, expansion , options) -% [sts, expanded_epochs] = pspm_expand_epochs( missing_epochs, expansion , options) -% [sts, channel_index] = pspm_expand_epochs( filename, channel, expansion , options) +% [sts, output_file] = pspm_expand_epochs(epochs_fn, expansion, options) +% [sts, expanded_epochs] = pspm_expand_epochs(epochs, expansion, options) +% [sts, channel_index] = pspm_expand_epochs(data_fn, channel, expansion , options) % ● Arguments -% options.mode = 'datafile' -% 'missing_ep_file' -% 'missing_epochs' -% expansion = [pre , post] or [pre ; post] -% +% * epochs_fn: An epochs file as defined in pspm_get_timing. +% * epochs: A 2-column matrix with epochs onsets and offsets in seconds. +% * data_fn: A PsPM data file. +% * channel: Channel identifier accepted by pspm_load_channel. +% * expansion: A 2-element vector with positive numbers [pre, post] +% ┌────────────options: +% ├─────────.overwrite: Define if already existing files should be +% │ overwritten. Default ist 0. (Only used if input +% │ is epochs file.) +% └────.channel_action: Channel action, add / replace existing data +% data (default: add) +% ● Output +% * channel_index: index of channel containing the processed data % ● History +% Introduced in PsPM 7.0 % Written in 2024 by Bernhard Agoué von Raußendorf - +% Initialise +% ------------------------------------------------------------------------- global settings if isempty(settings) pspm_init; end sts = -1; -ep_exp = []; +out = []; -if nargin < 3 || nargin > 4 - warning('ID:invalid_input', 'Not enough or to many input arguments'); +% Input checks and parsing +% ------------------------------------------------------------------------ +if nargin < 2 + warning('ID:invalid_input', 'Not enough input arguments'); return; end - -% Checks if expantion vector is the valid -if nargin == 3 - if numel(varargin{2}) ~= 2 || ~isnumeric(varargin{2}) - warning('Invalid input to expand vector musst be 1x2 or 2x1.'); - return; +% parse first input and determine mode +if isnumeric(varargin{1}) + mode = 'epochs'; + [gsts, epochs] = pspm_get_timing('epochs', varargin{1}, 'seconds'); + if gsts < 1 + return end -end -if nargin == 4 - if numel(varargin{3}) ~= 2 || ~isnumeric(varargin{3}) - warning('Invalid input to expand vector musst be 1x2 or 2x1.'); - return; +elseif ischar(varargin{1}) + fn = varargin{1}; + warning off + [gsts, epochs] = pspm_get_timing('epochs', fn, 'seconds'); + warning on + if gsts == 1 + mode = 'epochfile'; + else + fprint('Assuming input is a PsPM data file ...\n'); + mode = 'datafile'; end +else + warning('ID:invalid_input', 'First argument must be a file name or epoch matrix.'); end - - -if nargin == 3 - +% parse remaining arguments +if ismember(mode, {'epochs', 'epochfile'}) expansion = varargin{2}; - options = varargin{3}; - - switch options.mode - % missing epochs - case 'missing_epochs' - % Directly expand the given epochs - - epochs = varargin{1}; - - [gsts, epochs] = pspm_get_timing('epochs',epochs,'seconds'); - if gsts < 1 - warning('Invaled epochs.') - return - end - - % Checks if epoch is empty bc pspm_get_timing returns sts = 1 - if isempty(epochs) - warning('Invaled epochs.') - return - end - - - - [ests, ep_exp] = expand(epochs, expansion); - - if ests < 1 - warning('Failed to expand epochs.'); - return; - end - - sts = 1; - return; - - - case 'missing_ep_file' - % Load missing epochs from file - filename = varargin{1}; - - [lsts, epochs] = pspm_get_timing('file', filename); - if isempty(epochs) - warning('Invaled epochs.') - return - end - - if lsts < 1 - warning('Epoch could not be loaded'); - return; - end + k = 2; +else + channel = varargin{2}; + expansion = varargin{3}; + k = 3; +end - - % Expand the loaded epochs - [ests, epochs] = expand(epochs.epochs, expansion); - if ests < 1 - warning('Failed to expand epochs.'); - return; - end +if nargin > k + options = varargin{3}; +else + options = struct(); +end - % Save expanded missing epoch to a new file with 'e' prefix - [pathstr, name, ext] = fileparts(filename); - output_file = fullfile(pathstr, ['e' name ext]); - save(output_file, 'epochs'); - disp(['Expanded epochs saved to: ', output_file]); +% finalise options structure +options = pspm_options(options, 'expand_epochs'); - sts = 1; - return; - end +% check if expansion vector is valid +if ~isnumeric(expansion) || numel(expansion) ~= 2 + warning('Invalid input to expand vector musst be 1x2 or 2x1.'); + return; end -if nargin == 4 %&& options.mode == 'datafile' % rename!! - - % Load channel data - filename = varargin{1}; - channel = varargin{2}; - expansion = varargin{3}; - [lsts, ~, data] = pspm_load_data(filename, channel); +% work on input +% ------------------------------------------------------------------------- + +% construct epochs from data file +if strcmpi(mode, 'datafile') + + [lsts, ~, data] = pspm_load_data(fn, channel); if lsts < 1 - warning('Failed to load data from file.'); return; end @@ -136,89 +109,52 @@ nan_indices = isnan(channel_data.data); % Convert NaN indices to epochs - nan_epochs = pspm_logical2epochs(nan_indices, sr); - - % Expand the epochs - [ests, ep_exp] = expand(nan_epochs, expansion); - if ests < 1 - error('Failed to expand epochs.'); - end - - % Convert expanded epochs back to logical indices - expanded_indices = pspm_epochs2logical(ep_exp, numel(channel_data.data), sr); - - % Set data to NaN at expanded indices - channel_data.data(logical(expanded_indices)) = NaN; + epochs = pspm_logical2epochs(nan_indices, sr); - % Save the data back to the file - opt = struct(); - [wsts, ~] = pspm_write_channel(filename, {channel_data}, 'replace',opt); % add to options 'newfile'? - % channel_data.data; - - if wsts < 1 - warning('Failed to write the new channel.'); - return - end - - sts = 1; - return; - - -else - warning('Unknown mode in options.'); - return; end - -end - - - -function [ests, ep_exp] = expand(ep, expansion) -% Helper function to expand epochs by the specified pre and post times -% and merge overlapping epochs. -% Also ensures that no epoch starts before time 0. - -% Initialize status of expand -ests = -1; -ep_exp = []; - - - -% Expand epochs +% expand epochs pre = expansion(1); post = expansion(2); -expanded_epochs_temp = [ep(:,1) - pre, ep(:,2) + post]; - - -% Ensure that the start of epoch is not neg. but 0 -[ksts, expanded_epochs_temp] = pspm_get_timing('epochs',expanded_epochs_temp , 'seconds') ; - +expanded_epochs_temp = [epochs(:,1) - pre, epochs(:,2) + post]; +% remove negative values and merge overlapping epochs +[ksts, expanded_epochs] = pspm_get_timing('missing',expanded_epochs_temp , 'seconds') ; if ksts < 1 - warning('Offsets must be larger than onsets') return end -% If there is only one epoch, no need for merging -if size(expanded_epochs_temp, 1) == 1 - ep_exp = expanded_epochs_temp; - ests = 1; - return; -end +% generate output +switch mode + case 'epochs' + out = expanded_epochs; + + case 'epochfile' + % save expanded epochs to a new file with 'e' prefix + [pathstr, name, ext] = fileparts(fn); + output_file = fullfile(pathstr, ['e', name, ext]); + overwrite_final = pspm_overwrite(output_file, options.overwrite); + if overwrite_final == 1 + save(output_file, 'epochs'); + fprintf(['Expanded epochs saved to file: ', output_file, '\n']); + end + + case 'datafile' + % Convert expanded epochs back to logical indices + expanded_indices = pspm_epochs2logical(expanded_epochs, numel(channel_data.data), sr); + % Set data to NaN at expanded indices + channel_data.data(logical(expanded_indices)) = NaN; -% Merge overlapping epochs -ep_exp = expanded_epochs_temp(1, :); % Start with the first epoch -for i = 2:size(expanded_epochs_temp, 1) - % If the current epoch overlaps with the previous, merge them - if ep_exp(end, 2) >= expanded_epochs_temp(i, 1) - ep_exp(end, 2) = max(ep_exp(end, 2), expanded_epochs_temp(i, 2)); - else - % Otherwise, add the current epoch as a new row - ep_exp = [ep_exp; expanded_epochs_temp(i, :)]; + % Save the data + [wsts, out] = pspm_write_channel(fn, {channel_data}, options.channel_action, struct('channel', channel)); + if wsts < 1 + return end + out = out.channel; end -ests = 1; -end \ No newline at end of file +sts = 1; + + + diff --git a/src/pspm_options.m b/src/pspm_options.m index b97dff4ab..ec95ca1e4 100644 --- a/src/pspm_options.m +++ b/src/pspm_options.m @@ -496,6 +496,10 @@ % 2.48 pspm_combine_markerchannels options = autofill(options, 'marker_chan_num', 'marker', '*Int*Char' ); options = autofill_channel_action(options); + case 'expand_epochs' + % 2.49 pspm_expand_epochs + options = autofill(options, 'overwrite', 2, [0,1] ); + options = autofill_channel_action(options); end return From 50cced7d3e8a626d31c60e186828b06a075213f8 Mon Sep 17 00:00:00 2001 From: Bernhard <90285552+4gwe@users.noreply.github.com> Date: Sun, 17 Nov 2024 15:32:42 +0100 Subject: [PATCH 5/7] added new pspm_expand_epochs to pspm_remove_epochs --- src/pspm_remove_epochs.m | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/pspm_remove_epochs.m b/src/pspm_remove_epochs.m index 4a87fa4ca..5dae6e748 100644 --- a/src/pspm_remove_epochs.m +++ b/src/pspm_remove_epochs.m @@ -12,8 +12,8 @@ % be in seconds. This parameter is passed to pspm_get_timing(). % * timeunits: timeunits of the epochfile. % ┌───options -% └─.channel_action: ['add'/'replace'] Defines whether new channels should be added or -% corresponding channels should be replaced. The default value is 'add'. +% ├─.channel_action: ['add'/'replace'] Defines whether new channels should be added or +% │ corresponding channels should be replaced. The default value is 'add'. % └─.expand_epochs: [pre, post] % % ● Output @@ -38,7 +38,7 @@ elseif nargin < 4 options = struct(); end -options = pspm_options(options, 'remove_epochs'); % change!!! [0,0] +options = pspm_options(options, 'remove_epochs'); if options.invalid return end @@ -54,19 +54,25 @@ end +% add check for options.expand_epochs!!! +% was ist mit commer werten?? -expansion = options.expand_epochs; +% was ist mit mehren daten?? channels?? +% event and this function?? +% float epochts? -if numel(expansion) ~= 2 - error('Expansion must be a 2-element vector [pre, post].'); -end -% Expand the epochs +% options-> overwrite?? +% options -> load expansion?? -[sts, ep_exp] = pspm_expand_epochs(ep, expansion, 'missing_ep'); -if psts < 1 - return; -end +if numel(options.expand_epochs) ~= 2 || ~isnumeric(options.expand_epochs) + warning('Expansion must be a 2-element vector [pre, post].'); +else + [psts, ep] = pspm_expand_epochs(ep, options.expand_epochs); + if psts < 1 + return; + end +end @@ -98,6 +104,7 @@ end end % save data to file + [sts, out] = pspm_write_channel(datafile, data, options.channel_action, ... struct('channel', pos_of_channels)); -outchanel = out.channel; \ No newline at end of file +outchanel = out.channel; % out is channel_nr \ No newline at end of file From fd8dce6213f227238b7d1563487fcb6914e8046c Mon Sep 17 00:00:00 2001 From: Bernhard <90285552+4gwe@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:37:06 +0100 Subject: [PATCH 6/7] pspm_remove_epochs with pspm_expand_epochs --- src/pspm_remove_epochs.m | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/pspm_remove_epochs.m b/src/pspm_remove_epochs.m index 5dae6e748..a43ce022f 100644 --- a/src/pspm_remove_epochs.m +++ b/src/pspm_remove_epochs.m @@ -54,23 +54,15 @@ end -% add check for options.expand_epochs!!! -% was ist mit commer werten?? -% was ist mit mehren daten?? channels?? -% event and this function?? -% float epochts? - -% options-> overwrite?? -% options -> load expansion?? - - -if numel(options.expand_epochs) ~= 2 || ~isnumeric(options.expand_epochs) - warning('Expansion must be a 2-element vector [pre, post].'); -else - [psts, ep] = pspm_expand_epochs(ep, options.expand_epochs); - if psts < 1 - return; +if isfield(options,'expand_epochs') + if numel(options.expand_epochs) ~= 2 || ~isnumeric(options.expand_epochs) + warning('Expansion must be a 2-element vector [pre, post].'); + else + [psts, ep] = pspm_expand_epochs(ep, options.expand_epochs); + if psts < 1 + return; + end end end @@ -78,7 +70,6 @@ - n_ep = size(ep, 1); n_data = numel(data); for i_data = 1:n_data @@ -107,4 +98,4 @@ [sts, out] = pspm_write_channel(datafile, data, options.channel_action, ... struct('channel', pos_of_channels)); -outchanel = out.channel; % out is channel_nr \ No newline at end of file +outchanel = out.channel; \ No newline at end of file From 59c2d8a20c5a22b246b06a1ce49b377c7705a0f8 Mon Sep 17 00:00:00 2001 From: Bernhard <90285552+4gwe@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:39:47 +0100 Subject: [PATCH 7/7] added comment --- src/pspm_remove_epochs.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pspm_remove_epochs.m b/src/pspm_remove_epochs.m index a43ce022f..3714b8ae7 100644 --- a/src/pspm_remove_epochs.m +++ b/src/pspm_remove_epochs.m @@ -54,7 +54,7 @@ end - +% expands the epoch if options.expand_epochs exists if isfield(options,'expand_epochs') if numel(options.expand_epochs) ~= 2 || ~isnumeric(options.expand_epochs) warning('Expansion must be a 2-element vector [pre, post].');