Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix prefilter management for EDF/BDF #12441

Merged
merged 8 commits into from
Apr 16, 2024
Merged

Conversation

rcmdnk
Copy link
Contributor

@rcmdnk rcmdnk commented Feb 14, 2024

What does this implement/fix?

For EDF, BDF, and GDF formats, prefilter information, including highpass and lowpass filters, is stored in the info structure.
Arrays for highpass/lowpass are generated by the _parse_prefilter_string function, which omits adding a value if no filter information is found.

These arrays are constructed using all channels except the last one, which is presumed to be an Annotation or TAL channel.

I think it should use only selected channels.

Furthermore, it is occasionally necessary to access highpass/lowpass values in _raw_extras for corresponding channel information. To address this, I suggest the following modifications:

  • The method to create prefiltering values is now adjusted to include only selected channels, using [sel].
  • The _parse_prefilter_string function has been improved to insert an empty string "" when no information is available. It has also been enhanced to parse strings like "DC".

I have reviewed filter string formats for
Biosemi EEG ECG EMG BSPM NEURO amplifiers systems, EDF specification, EDF+ specification.

In _get_info, where highpass/lowpass values are assigned to info, the current code exhibits bugs:

  • The condition all(highpass) is always true since highpass and lowpass are arrays of non-empty strings. The correct approach to check if all values are the same should be like np.all(highpass == highpass[0]).
  • The assignment info["highpass"] = float(np.max(highpass)) fails because highpass contains strings. However, due to the always-true nature of all(highpass), this code is never executed.

This PR addresses and fixes these issues as well. With these changes, some EDF files will trigger warnings about varying highpass and lowpass filter settings across channels, which, due to a bug, were previously not displayed. These warnings are essential for ensuring data consistency and integrity.

Copy link
Member

@larsoner larsoner left a comment

Choose a reason for hiding this comment

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

Thanks for the explanation, sounds reasonable and well thought out to me!

CIs fail because now more warnings need to be caught, see:

https://github.com/mne-tools/mne-python/actions/runs/7901365815/job/21564802456?pr=12441#step:17:4232

Locally if you make sure you have mne-testing-data and pytest>=8 installed you can do pytest mne/io/edf for example, then you should be able to replicate the failures rather than wait for CIs to complain

pass
elif highpass[0] == "DC":
info["highpass"] = 0.0
if "highpass" in edf_info and len(edf_info["highpass"]):
Copy link
Member

Choose a reason for hiding this comment

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

More compact equivalent if you want it

Suggested change
if "highpass" in edf_info and len(edf_info["highpass"]):
if len(edf_info.get("highpass", [])):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the tips.
I updated these codes for GDF with _set_prefilter function
and similar code was implemented there.

@rcmdnk
Copy link
Contributor Author

rcmdnk commented Feb 14, 2024

@larsoner
Thanks for the reply.
Yes, it seems several tasks need to be updated which I did not checked locally.
I will update with local tests for these jobs.

@rcmdnk rcmdnk force-pushed the fix_prefilter branch 2 times, most recently from 79131c2 to bced368 Compare February 15, 2024 07:14
@rcmdnk rcmdnk marked this pull request as ready for review February 15, 2024 09:58
and len(out["highpass"])
and out["highpass"][0] == "0.0"
):
out["highpass"][0] = "10.0"
Copy link
Contributor Author

@rcmdnk rcmdnk Feb 15, 2024

Choose a reason for hiding this comment

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

String "0.000" for lowpass was converted into float 0 in the previous code.
(it does not pass if lowpass[0] in ("NaN", "0", "0.0"):)
In this case, both lowpass and highpass is set to 0, and
if info["highpass"] > info["lowpass"]:
is False.

I think this is a kind of special test for highpass > lowpass case.

With the new code, "0.000" for lowpass is skipped to be set as info["lowpass"] and it remains as default (sfreq/2).
Therefore, lowpass=256, highpass=0 are correctly obtained.

To make highpass > lowpass case, _hp_lp_mod function was added to force to set highpass > lowpass.

Related PR: #8584

Copy link
Member

@larsoner larsoner left a comment

Choose a reason for hiding this comment

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

Looks reasonable to me. @cbrnr looks like you've worked on this stuff a bit, can you look?

@cbrnr
Copy link
Contributor

cbrnr commented Feb 16, 2024

Yes, I can take a look early next week!

Fix prefilter management for GDF (highpass/lowpass are float instead of list of str)
Fix tests which throw warnings of "Channels contain different..."
…highpass/lowpass of ndarray of str to _get_info. gdf returns highpass/lowpass of ndarray of np.float32 to _get_info)
… in _raw_extras

Select channels of sel but stim chanenl to set highpass/lowpass in info
@larsoner
Copy link
Member

@cbrnr you happy with this now? If so we could get it into 1.7 I think

@cbrnr
Copy link
Contributor

cbrnr commented Apr 15, 2024

It's been some time since I commented on this PR. Could you or @rcmdnk summarize the public (user-facing) changes? Are there any? "Fix pre-filtering management" doesn't really tell me a lot about the changes (and this more detailed information should go into the changelog entry as well). Thanks!

@larsoner larsoner added this to the 1.8 milestone Apr 15, 2024
@rcmdnk
Copy link
Contributor Author

rcmdnk commented Apr 15, 2024

@cbrnr
The problem is that
the current implementation inaccurately handles the highpass and lowpass prefilter information in the Raw.info.

Bugs:

  • All prefilter information other than empty channels are stored in _raw_extras
    • no selection by selected channels, empty channels are ignored.
  • all(highpass) seems to be used to check uniformity of highpass values across channels.
    • However, it fails to trigger the warning for differing highpass filters because it incorrectly checks for all True values instead of uniformity.
  • info["highpass"] = float(np.max(highpass)) does not work because highpass is an array of strings. (but this does not occurs due to the previous all check.

Changes Introduced in This PR:

  • The info['highpass'] and info['lowpass'] will now correctly reflect the maximum and minimum prefilter values from the selected channels, respectively.
  • Remain all channel values in _raw_extras
    • Put empty string "" if no the information is empty.
    • Select values for selected channels in _get_info to make highpass/lowpass of Raw.info
  • Warn if selected channels have different prefiltering values.

@cbrnr cbrnr merged commit 73661d1 into mne-tools:main Apr 16, 2024
30 checks passed
@cbrnr
Copy link
Contributor

cbrnr commented Apr 16, 2024

In it goes – the changelog entry is OK, people can always refer to this thread for more details. Thanks @rcmdnk!

larsoner added a commit to larsoner/mne-python that referenced this pull request Apr 16, 2024
* upstream/main:
  fix prefilter management for EDF/BDF (mne-tools#12441)
  [pre-commit.ci] pre-commit autoupdate (mne-tools#12541)
  ENH: Allow removal of (up to two) bad marker coils in read_raw_kit() (mne-tools#12394)
  Implement `picks` argument to `Raw.plot()` (mne-tools#12467)
  Add Meggie under Related Software documentation (mne-tools#12540)
@rcmdnk rcmdnk deleted the fix_prefilter branch April 25, 2024 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants