Skip to content

Commit d97fcc0

Browse files
cbrnragramfort
authored andcommitted
[MRG] Log number of rejected samples in raw.get_data (#5824)
* Log number of rejected samples in raw.get_data * Better names and f-strings * Add single test * Increase verbosity to get the log message * Add verbose parameter to raw.get_data * Remove f-strings because of Python 3.5 :-(
1 parent 92e8bec commit d97fcc0

File tree

2 files changed

+59
-16
lines changed

2 files changed

+59
-16
lines changed

mne/io/base.py

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -933,8 +933,9 @@ def __setitem__(self, item, value):
933933
# set the data
934934
self._data[sel, start:stop] = value
935935

936+
@verbose
936937
def get_data(self, picks=None, start=0, stop=None,
937-
reject_by_annotation=None, return_times=False):
938+
reject_by_annotation=None, return_times=False, verbose=None):
938939
"""Get data in the given range.
939940
940941
Parameters
@@ -953,6 +954,10 @@ def get_data(self, picks=None, start=0, stop=None,
953954
'bad' are omitted. If 'NaN', the bad samples are filled with NaNs.
954955
return_times : bool
955956
Whether to return times as well. Defaults to False.
957+
verbose : bool, str, int, or None
958+
If not None, override default verbose level (see
959+
:func:`mne.verbose` and :ref:`Logging documentation <tut_logging>`
960+
for more). Defaults to self.verbose.
956961
957962
Returns
958963
-------
@@ -985,29 +990,43 @@ def get_data(self, picks=None, start=0, stop=None,
985990
if return_times:
986991
return data, times
987992
return data
988-
989-
used = np.ones(stop - start, bool)
993+
n_samples = stop - start # total number of samples
994+
used = np.ones(n_samples, bool)
990995
for onset, end in zip(onsets, ends):
991996
if onset >= end:
992997
continue
993998
used[onset - start: end - start] = False
994999
used = np.concatenate([[False], used, [False]])
9951000
starts = np.where(~used[:-1] & used[1:])[0] + start
9961001
stops = np.where(used[:-1] & ~used[1:])[0] + start
997-
if reject_by_annotation == 'omit':
998-
999-
data = np.zeros((len(picks), (stops - starts).sum()))
1000-
times = np.zeros(data.shape[1])
1001-
idx = 0
1002-
for start, stop in zip(starts, stops): # get the data
1003-
if start == stop:
1004-
continue
1005-
end = idx + stop - start
1006-
data[:, idx:end], times[idx:end] = self[picks, start:stop]
1007-
idx = end
1002+
n_kept = (stops - starts).sum() # kept samples
1003+
n_rejected = n_samples - n_kept # rejected samples
1004+
if n_rejected > 0:
1005+
if reject_by_annotation == 'omit':
1006+
msg = ("Omitting {} of {} ({:.2%}) samples, retaining {}"
1007+
" ({:.2%}) samples.")
1008+
logger.info(msg.format(n_rejected, n_samples,
1009+
n_rejected / n_samples,
1010+
n_kept, n_kept / n_samples))
1011+
data = np.zeros((len(picks), n_kept))
1012+
times = np.zeros(data.shape[1])
1013+
idx = 0
1014+
for start, stop in zip(starts, stops): # get the data
1015+
if start == stop:
1016+
continue
1017+
end = idx + stop - start
1018+
data[:, idx:end], times[idx:end] = self[picks, start:stop]
1019+
idx = end
1020+
else:
1021+
msg = ("Setting {} of {} ({:.2%}) samples to NaN, retaining {}"
1022+
" ({:.2%}) samples.")
1023+
logger.info(msg.format(n_rejected, n_samples,
1024+
n_rejected / n_samples,
1025+
n_kept, n_kept / n_samples))
1026+
data, times = self[picks, start:stop]
1027+
data[:, ~used[1:-1]] = np.nan
10081028
else:
10091029
data, times = self[picks, start:stop]
1010-
data[:, ~used[1:-1]] = np.nan
10111030

10121031
if return_times:
10131032
return data, times

mne/io/tests/test_raw.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from mne.annotations import _handle_meas_date
1818
from mne.datasets import testing
1919
from mne.io import read_raw_fif, RawArray, BaseRaw
20-
from mne.utils import _TempDir
20+
from mne.utils import _TempDir, catch_logging
2121
from mne.io.meas_info import _get_valid_units
2222

2323

@@ -257,3 +257,27 @@ def test_meas_date_orig_time():
257257
assert raw.annotations.orig_time is None
258258
assert raw.annotations.onset[0] == 0.5
259259
assert raw.annotations.duration[0] == 0.2
260+
261+
262+
def test_get_data_reject():
263+
"""Test if reject_by_annotation is working correctly."""
264+
fs = 256
265+
ch_names = ["C3", "Cz", "C4"]
266+
info = create_info(ch_names, sfreq=fs)
267+
raw = RawArray(np.zeros((len(ch_names), 10 * fs)), info)
268+
raw.set_annotations(Annotations(onset=[2, 4], duration=[3, 2],
269+
description="bad"))
270+
271+
with catch_logging() as log:
272+
data = raw.get_data(reject_by_annotation="omit", verbose=True)
273+
msg = ('Omitting 1024 of 2560 (40.00%) samples, retaining 1536' +
274+
' (60.00%) samples.')
275+
assert log.getvalue().strip() == msg
276+
assert data.shape == (len(ch_names), 1536)
277+
with catch_logging() as log:
278+
data = raw.get_data(reject_by_annotation="nan", verbose=True)
279+
msg = ('Setting 1024 of 2560 (40.00%) samples to NaN, retaining 1536' +
280+
' (60.00%) samples.')
281+
assert log.getvalue().strip() == msg
282+
assert data.shape == (len(ch_names), 2560) # shape doesn't change
283+
assert np.isnan(data).sum() == 3072 # but NaNs are introduced instead

0 commit comments

Comments
 (0)