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

Bad channel future? #835

Open
larsoner opened this issue Jan 24, 2024 · 2 comments
Open

Bad channel future? #835

larsoner opened this issue Jan 24, 2024 · 2 comments

Comments

@larsoner
Copy link
Member

In https://mne.tools/mne-bids-pipeline/stable/settings/preprocessing/autobads.html it says:

Warning

This functionality will soon be removed from the pipeline, and will be integrated into MNE-BIDS.

This is from 3 years ago -- @hoechenberger is it still relevant? Any objection to adding bad channel detection for EEG in the meantime (e.g., mne-tools/mne-python#12384)?

@larsoner
Copy link
Member Author

Quoting from @sappelhoff here:

I would also suggest comparing against pyprep's: pyprep.NoisyChannels(raw).find_all_bads() ...
you can use pyprep.NoisyChannels.get_bads with as_dict=True to then get info on why channels were marked bad :-)

and @hoechenberger :

I have to say that find_all_Bads marks way too many channels as bad in my datasets. I usually only use a subset of the functions in pyprep (I can share which those are specifically in case you're interested)

I have never tried pyprep -- @hoechenberger do you think it would make sense to add an option to MNE-BIDS-Pipeline to use pyprep with a user-selected subset of "why"s (maybe suggesting users try your subset of functions as a starting point)?

@hoechenberger
Copy link
Member

hoechenberger commented Mar 1, 2024

This is from 3 years ago -- @hoechenberger is it still relevant?

I don't think so. We should leave it in the pipeline, even though I'd prefer users to run bad channel detection on the BIDS dataset before they run the pipeline. But there are situations where you might want to work with a dataset that was provided to you that doesn't have all problematic channels marked as bad, and then this feature comes in handy. WDYT?

Any objection to adding bad channel detection for EEG in the meantime

Not at all!

I have never tried pyprep -- @hoechenberger do you think it would make sense to add an option to MNE-BIDS-Pipeline to use pyprep with a user-selected subset of "why"s (maybe suggesting users try your subset of functions as a starting point)?

Yes. Users may provide a tuple of processing steps to run. FWIW here's what I currently use before converting to BIDS; I don't use the full set of pyprep features, but iterate 3 times (cc @sappelhoff); importantly, I mark break periods first so they'll get excluded from data quality assessment:

class BadChannels(TypedDict):
    Subject: str
    Run: int
    Bads: str  # "Ch1, Ch2, …"


def run_bads_detection(subject: str, subject_run_path: Path) -> BadChannels:
    # run number is appended at the end of the input filename ater the 2nd underscore,
    # or after a dash, or after one underscore
    print(f"Processing file: {subject_run_path.name}")
    if subject_run_path.stem.count("_") == 2:
        run = int(subject_run_path.stem.split("_")[2])
    elif "-" in subject_run_path.stem:
        run = int(subject_run_path.stem.split("-")[1])
    else:
        run = int(subject_run_path.stem.split("_")[1])

    # read input data
    raw = mne.io.read_raw(subject_run_path, preload=True, verbose=False)
    assert isinstance(raw, mne.io.BaseRaw)

    raw.set_montage("easycap-M1")

    annot_breaks = mne.preprocessing.annotate_break(
        raw=raw,
        min_break_duration=10,
        t_start_after_previous=2,
        t_stop_before_next=2,
        ignore=(
            "bad",
            "edge",
            "New Segment",
        ),
    )
    raw.set_annotations(raw.annotations + annot_breaks)

    all_bads: list[str] = []

    for _ in range(3):
        nc = pyprep.NoisyChannels(raw=raw, random_state=42)
        nc.find_bad_by_deviation()
        nc.find_bad_by_correlation()
        nc.find_bad_by_ransac()

        bads = nc.get_bads()
        all_bads.extend(bads)
        all_bads = sorted(all_bads)
        raw.info["bads"] = all_bads

    print(all_bads)
    return {"Subject": subject, "Run": run, "Bads": ", ".join(sorted(all_bads))}

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

No branches or pull requests

2 participants