Skip to content

Commit d3f752d

Browse files
authored
ENH - allow for slower, but more memory efficient appending (#2510)
1 parent 8e4c695 commit d3f752d

File tree

3 files changed

+85
-2
lines changed

3 files changed

+85
-2
lines changed

ft_appendfreq.m

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@
2525
% These mat files should contain only a single variable, corresponding with
2626
% the input/output structure.
2727
%
28+
% If you encounter difficulties with memory usage, you can use
29+
% cfg.memory = 'low' or 'high', whether to be memory or computationally efficient, respectively (default = 'high')
30+
%
2831
% See also FT_FREQANALYSIS, FT_DATATYPE_FREQ, FT_APPENDDATA, FT_APPENDTIMELOCK,
2932
% FT_APPENDSENS
3033

3134
% Copyright (C) 2011-2017, Robert Oostenveld
35+
% Copyright (C) 2018-, Jan-Mathijs Schoffelen and Robert Oostenveld
3236
%
3337
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
3438
% for the documentation and details.
@@ -77,6 +81,7 @@
7781
cfg.appenddim = ft_getopt(cfg, 'appenddim', []);
7882
cfg.tolerance = ft_getopt(cfg, 'tolerance', 1e-5); % this is passed to append_common, which passes it to ft_selectdata
7983
cfg.appendsens = ft_getopt(cfg, 'appendsens', 'no');
84+
cfg.memory = ft_getopt(cfg, 'memory', 'high');
8085

8186
hastime = isfield(varargin{1}, 'time');
8287
hasfreq = isfield(varargin{1}, 'freq');
@@ -120,7 +125,14 @@
120125
assert(~isempty(cfg.parameter), 'cfg.parameter should be specified');
121126

122127
% use a low-level function that is shared with the other ft_appendxxx functions
123-
freq = append_common(cfg, varargin{:});
128+
if strcmp(cfg.memory, 'high') || numel(varargin)<=2
129+
freq = append_common(cfg, varargin{:});
130+
elseif strcmp(cfg.memory, 'low')
131+
freq = varargin{1};
132+
for i=2:numel(varargin)
133+
freq = append_common(cfg, freq, varargin{i});
134+
end
135+
end
124136

125137
% do the general cleanup and bookkeeping at the end of the function
126138
ft_postamble debug

ft_appendtimelock.m

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,22 @@
1616
% are allowed to still be considered compatible (default = 1e-5)
1717
% cfg.keepsampleinfo = 'yes', 'no', 'ifmakessense' (default = 'ifmakessense')
1818
%
19+
% To facilitate data-handling and distributed computing you can use
20+
% cfg.inputfile = ...
21+
% cfg.outputfile = ...
22+
% If you specify one of these (or both) the input data will be read from a
23+
% *.mat file on disk and/or the output data will be written to a *.mat file.
24+
% These mat files should contain only a single variable, corresponding with
25+
% the input/output structure.
26+
%
27+
% If you encounter difficulties with memory usage, you can use
28+
% cfg.memory = 'low' or 'high', whether to be memory or computationally efficient, respectively (default = 'high')
29+
%
1930
% See also FT_TIMELOCKANALYSIS, FT_DATATYPE_TIMELOCK, FT_APPENDDATA, FT_APPENDFREQ,
2031
% FT_APPENDSOURCE, FT_APPENDSENS
2132

2233
% Copyright (C) 2011-2018, Robert Oostenveld
34+
% Copyright (C) 2019-, Jan-Mathijs Schoffelen and Robert Oostenveld
2335
%
2436
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
2537
% for the documentation and details.
@@ -63,6 +75,7 @@
6375
cfg.tolerance = ft_getopt(cfg, 'tolerance', 1e-5); % this is passed to append_common, which passes it to ft_selectdata
6476
cfg.appendsens = ft_getopt(cfg, 'appendsens', 'no');
6577
cfg.keepsampleinfo = ft_getopt(cfg, 'keepsampleinfo', 'no');
78+
cfg.memory = ft_getopt(cfg, 'memory', 'high');
6679

6780
try
6881
% although not 100% robust, this could make some users becoming aware of the issue of overlapping trials
@@ -114,7 +127,14 @@
114127
end
115128

116129
% use a low-level function that is shared with the other ft_appendxxx functions
117-
timelock = append_common(cfg, varargin{:});
130+
if strcmp(cfg.memory, 'high') || numel(varargin)<=2
131+
timelock = append_common(cfg, varargin{:});
132+
elseif strcmp(cfg.memory, 'low')
133+
timelock = varargin{1};
134+
for i=2:numel(varargin)
135+
timelock = append_common(cfg, timelock, varargin{i});
136+
end
137+
end
118138

119139
if isfield(timelock, 'avg') && ~isfield(timelock, 'trial')
120140
ft_warning('renaming the appended averages to "trial"');

test/test_ft_appendtimelock.m

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,55 @@
180180
assert(isfield(tlockapp, 'sampleinfo')); % here it should exist
181181
assert(~isfield(tlockapp, 'fsample'));
182182

183+
%% test with cfg.memory high/low
184+
tlock1 = [];
185+
tlock1.label = {'1';'2'};
186+
tlock1.time = 1:5;
187+
tlock1.dimord = 'rpt_chan_time';
188+
tlock1.trial = randn(10, 2, 5);
189+
190+
tlock2 = [];
191+
tlock2.label = {'1';'2'};
192+
tlock2.time = 1:5;
193+
tlock2.dimord = 'rpt_chan_time';
194+
tlock2.trial = randn(8, 2, 5);
195+
196+
tlock3 = [];
197+
tlock3.label = {'1';'2'};
198+
tlock3.time = 1:5;
199+
tlock3.dimord = 'rpt_chan_time';
200+
tlock3.trial = randn(7, 2, 5);
201+
202+
cfg = [];
203+
cfg.memory = 'high';
204+
thigh = ft_appendtimelock(cfg, tlock1, tlock2, tlock3);
205+
cfg.memory = 'low';
206+
tlow = ft_appendtimelock(cfg, tlock1, tlock2, tlock3);
207+
assert(isequal(thigh.trial, tlow.trial));
208+
209+
tlock1 = [];
210+
tlock1.label = {'1';'2'};
211+
tlock1.time = 1:5;
212+
tlock1.dimord = 'rpt_chan_time';
213+
tlock1.trial = randn(8, 2, 5);
214+
215+
tlock2 = [];
216+
tlock2.label = {'3';'4'};
217+
tlock2.time = 1:5;
218+
tlock2.dimord = 'rpt_chan_time';
219+
tlock2.trial = randn(8, 2, 5);
220+
221+
tlock3 = [];
222+
tlock3.label = {'5';'6'};
223+
tlock3.time = 1:5;
224+
tlock3.dimord = 'rpt_chan_time';
225+
tlock3.trial = randn(8, 2, 5);
226+
227+
cfg = [];
228+
cfg.memory = 'high';
229+
thigh = ft_appendtimelock(cfg, tlock1, tlock2, tlock3);
230+
cfg.memory = 'low';
231+
tlow = ft_appendtimelock(cfg, tlock1, tlock2, tlock3);
232+
assert(isequal(thigh.trial, tlow.trial));
233+
183234

0 commit comments

Comments
 (0)