[ENH] Add support for ieeg interpolation#12336
Conversation
| picks_bad = pick_channels(inst.info["ch_names"], bads_type, exclude=[]) | ||
|
|
||
| if method[ch_type] == "nan": | ||
| inst._data[picks_bad] = np.nan |
There was a problem hiding this comment.
This is the line with the bug as far as I can tell, this should only work on raw and have some odd and unexpected indexing behavior on epochs
There was a problem hiding this comment.
As opposed to here https://github.com/mne-tools/mne-python/pull/12336/files#diff-fcfbd5c9390aa74bdd3005e5ae1c786df72afc324d3532273dc5937ef9674787L130-R131 which does it correctly
|
Confirmed, this is incorrect on |
|
Oh odd, if you try the above code snippet on this branch, the data is correct but when you call Looks like an unrelated bug. |
|
Looks like everything works as expected. This fixes both the ieeg quiet non-interpolations and the newly discovered |
drammock
left a comment
There was a problem hiding this comment.
nice PR, thanks! A few questions/suggestions; I ran out of time and didn't thoroughly check the tests FWIW, so should get at least one other review (or another pass from me next week)
mne/channels/interpolation.py
Outdated
| inst.info._check_consistency() | ||
| bads_idx[picks] = [inst.ch_names[ch] in inst.info["bads"] for ch in picks] | ||
|
|
||
| if len(picks) < 3 or bads_idx.sum() == 0: |
There was a problem hiding this comment.
[question] IDK how much of an edge case this is or if it's a realistic worry, but should we warn if len(picks) < 3? I can imagine maybe being surprised that the single bad channel wasn't interpolated because I (inadvertantly?) excluded all but one of the good channels (leaving just 2 channels in picks)
There was a problem hiding this comment.
Yeah, I think maybe best just to kick it to the runtime error if there's a bad channel on a shaft with less than 3 contacts which is the edge case and I think an error is warranted there since that's not a good interpolation situation. So no need to silently return.
There was a problem hiding this comment.
I think a warning is better. If you have 3 bad channels and this edge-case represents only one of them, the interpolation will fail and raise unless you mark the edge-case bad channel as good, interpolate, and mark it again as bad. I find this more error prone than warning, interpolating the 2 other channels and ignoring the edge-case one, and finally removing only the 2 interpolated channels from .info["bads"].
There was a problem hiding this comment.
This won't raise an error if there are 3 bad channels on a shaft (electrode) only if there aren't three good ones. If there are only two good channels, I don't think there is a use case where you want to use those to interpolate any of the bad channels so I'd still be disposed to an error. Although if you had 3 contacts (usually 4 for DBS) but as in a DBS and you wanted to interpolate the middle one on some epochs that would make sense so maybe it should just be two required.
There was a problem hiding this comment.
I mixed up bad channels/good ones in my comment. I agree there is no use case for interpolating along a shaft if you lack the information to do so. But I would still warn, else if you have 2 shafts each with some bad channels, one with less than 3 good contacts remaining; the interpolation function will fail even for the shaft which has enough information to interpolate.
raw.ch_names = [f"Shaft1_{k} for k in range(5)] + [f"Shaft2_{k} for k in range(5)]
raw.info["bads"] = [f"Shaft1_{k} for k in range(4)] + ["Shaft2_0"]
raw.interpolate_bads(...) # warning for the bad contacts on Shaft1, interpolation for Shaft2_0
assert raw.info["bads"] == [f"Shaft1_{k} for k in range(4)]
Or am I misunderstanding something and/or a multi-shaft recording is not realistic? (I lack experience with intra-cranial recordings)
There was a problem hiding this comment.
The length of the shaft isn't checked if the channels aren't marked bad so I think if you do mark it and try and interpolate it you should get an error rather than unreliable data. You would then have to drop that channel without interpolation to continue without an error which I think is the safest way to go and wouldn't cause major issues in multi-subject pipelines.
There was a problem hiding this comment.
I think the other consideration if it's a warning is what do you do to the data in that channel? If it was supposed to be interpolated and you just warned and the data was left as is, that seems potentially misleading. If it only requires two other contacts, I think it's pretty safe that this is an unlikely edge case and the failure will be warranted so that the user can be notified to fix their data by dropping all the contacts on that electrode shaft.
|
Thanks for the review, some nice cleanups |
mne/channels/interpolation.py
Outdated
| inst.info._check_consistency() | ||
| bads_idx[picks] = [inst.ch_names[ch] in inst.info["bads"] for ch in picks] | ||
|
|
||
| if len(picks) < 3 or bads_idx.sum() == 0: |
There was a problem hiding this comment.
The length of the shaft isn't checked if the channels aren't marked bad so I think if you do mark it and try and interpolate it you should get an error rather than unreliable data. You would then have to drop that channel without interpolation to continue without an error which I think is the safest way to go and wouldn't cause major issues in multi-subject pipelines.
|
Good to go by me, no rush though |
mne/channels/interpolation.py
Outdated
| np.fill_diagonal(dist, np.inf) | ||
|
|
||
| picks_bad = list(np.where(bads_idx_pos)[0]) | ||
| while picks_bad: |
There was a problem hiding this comment.
can we avoid while loops and use a plain for? I find them usually unecessary and error prone.
There was a problem hiding this comment.
The while loop was used because you don't know if two bad channels are on the same shaft until you find the shaft. I can't think of an easy way around that with a for loop.
There was a problem hiding this comment.
I wonder if a helper function (_find_shafts or so) that returns a list-of-lists of channel indices would be generally useful? If so, it could be used here to pre-compute the channels into shaft groupings, then a for-loop over picks_bad would work.
There was a problem hiding this comment.
Sure, we could do that. I was just finding them for the bad contacts to save the computation but it's a very small amount
There was a problem hiding this comment.
Hmm, I wrote that but even if you know the shafts, you still have to iterate over picks_bad (list of bad indices) and you don't know whether two are on the same shaft or not so you have to use a while loop.
There was a problem hiding this comment.
Oh never mind you can just loop over shafts
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>


Fixes #12333.
It also looked like there was a bug where epochs would have the channel indices of their epochs set to
nanifnanwas the method which was also fixed.