Skip to content

Conversation

@sappelhoff
Copy link
Member

@sappelhoff sappelhoff commented Jul 12, 2021

closes #9548

Work in progress

To do

@sappelhoff sappelhoff added the ENH label Jul 12, 2021
@sappelhoff sappelhoff mentioned this pull request Jul 12, 2021
2 tasks
Copy link
Member Author

@sappelhoff sappelhoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CircleCI is failing, see screenshot below - if somebody immediately knows why, a hint would be appreciated :-)

image

@sappelhoff
Copy link
Member Author

sappelhoff commented Jul 13, 2021

I think I found a bug along the way, see this docstr of find_bads_ecg:

start : int | float | None
First sample to include. If float, data will be interpreted as
time in seconds. If None, data will be used from the first sample.
stop : int | float | None
Last sample to not include. If float, data will be interpreted as
time in seconds. If None, data will be used to the last sample.

start and stop are then passed to _make_ecg just here:

if idx_ecg is None:
ecg, times = _make_ecg(inst, start, stop,
reject_by_annotation=reject_by_annotation)
else:

Then in _make_ecg, weird things happen: If the instance is Epochs, .crop is used, interpreting start and stop as seconds (even if they might have been int, so sample indices, not seconds) ... if it's Raw, these are passed to start and stop of raw.get_data, regardless of whether they are int (sample indices, that would be fine) or float (which would raise an error, because raw.get_data start and stop cannot deal with floats)

if isinstance(inst, BaseRaw):
reject_by_annotation = 'omit' if reject_by_annotation else None
ecg, times = inst.get_data(picks, start, stop, reject_by_annotation,
True)
elif isinstance(inst, BaseEpochs):
ecg = np.hstack(inst.copy().crop(start, stop).get_data())
times = inst.times

In particular, the error raised would be:

TypeError: slice indices must be integers or None or have an index method

Just try it yourself by modifying our ICA tutorial, add a tstart=2 to these lines:

# find which ICs match the ECG pattern
ecg_indices, ecg_scores = ica.find_bads_ecg(raw, method='correlation',
threshold='auto')

That should work. But now modify it to tstart=2.3 (or any other float), which raises an error although according to the docstr it shouldn't.

@sappelhoff sappelhoff added the FIX label Jul 13, 2021
@sappelhoff sappelhoff changed the title [WIP] Add tmin/tmax parameters to get_data methods [MRG] ENH, FIX: Add tmin/tmax parameters to get_data methods, fix bug in find_bads_ecg Jul 15, 2021
Copy link
Member Author

@sappelhoff sappelhoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I think I am done here (ready for review/merge). I have one failure that I can't get to the bottom to: mne/tests/test_epochs.py::test_own_data FAILED [ 50%]

mne/tests/test_epochs.py:517: in test_own_data
    assert epochs._data.flags['OWNDATA']
E   assert False

One more point: I am using the following short snippet two-and-a-half times:

        _validate_type(tmin, types=('numeric', None), item_name='tmin',
                       type_name="float, None")
        _validate_type(tmax, types=('numeric', None), item_name='tmax',
                       type_name='float, None')

        # handle tmin/tmax as start and stop indices into data array
        n_times = self.times.size
        start = 0 if tmin is None else self.time_as_index(tmin)[0]
        stop = n_times if tmax is None else self.time_as_index(tmax)[0]

        # truncate start/stop to the open interval [0, n_times]
        start = min(max(0, start), n_times)
        stop = min(max(0, stop), n_times)

Should I turn that into a private function in mne.utils and import + use it? Or is it reasonably short / repeated few enough times to just ignore "DRY"? :-)

@larsoner
Copy link
Member

I would make it a private method in the TimeMixin in base.py, DRY and scoped to the same level as time_as_index (which it uses) which is nice

@sappelhoff
Copy link
Member Author

I would make it a private method in the TimeMixin in base.py, DRY and scoped to the same level as time_as_index (which it uses) which is nice

agreed, thanks for the suggestion, this is much better!

@larsoner
Copy link
Member

Thanks @sappelhoff !

@larsoner larsoner merged commit 08b4b0e into mne-tools:main Jul 16, 2021
@hoechenberger
Copy link
Member

Amazing, thanks, @sappelhoff!

@sappelhoff sappelhoff deleted the enh/get_data/tmin_tmax branch July 16, 2021 18:57
larsoner added a commit to alexrockhill/mne-python that referenced this pull request Jul 19, 2021
* upstream/main:
  [MRG, ENH] Find aseg labels near montage (mne-tools#9545)
  Add label to colorbar in GAT plot [skip actions] (mne-tools#9582)
  [ENH, MRG] Encapsulate warp elec image in function (mne-tools#9544)
  [DOC, MRG] Add "info" to `docdict` (mne-tools#9574)
  [MRG] Add `units` parameter to get_data for Evoked (mne-tools#9578)
  [MRG, ENH] Get annotation description from snirf stim name (mne-tools#9575)
  [MRG] ENH, FIX: Add tmin/tmax parameters to get_data methods, fix bug in find_bads_ecg (mne-tools#9556)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ENH: [raw|epochs|evoked].get_data() specify tmin, tmax in seconds to get that segment

4 participants