clean_channels: raise on degenerate channel locations instead of returning a meaningless result

Open Beginner friendly
#539 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
73/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
data

Research direction

Start by locating clean_channels and its existing checks for missing locations and for removing more than 75 percent of channels. Check how it handles unit-normalized locations and whether their numerical rank is tested. Add coverage for collinear and identical locations alongside the existing error cases; done means these degenerate inputs raise a ValueError or produce a warning instead of silently removing channels.

Written by the indexing model from the issue text.

Description

With all channels at one point, or on a line, clean_channels raises nothing and removes a large part of the channels.

import numpy as np
from eegprep import clean_channels, eeg_checkset, eeg_emptyset
LOCS = {"Fp1": (-32, 77, 19), "Fp2": (32, 77, 19), "F3": (-52, 35, 57), "F4": (52, 35, 57), "C3": (-69, -29, 69),
        "C4": (69, -29, 69), "P3": (-54, -92, 44), "P4": (54, -92, 44), "O1": (-31, -116, -7), "O2": (31, -116, -7),
        "F7": (-72, 32, 5), "F8": (72, 32, 5), "T3": (-83, -21, -4), "T4": (83, -21, -4), "T5": (-73, -74, -8),
        "T6": (73, -74, -8), "Fz": (0, 37, 84), "Cz": (0, -34, 105), "Pz": (0, -98, 66)}
names = list(LOCS); xyz = np.array([LOCS[n] for n in names], dtype=float)
rng = np.random.default_rng(0); srate, t = 250.0, 250 * 120
src = rng.standard_normal((5, t)); pos = rng.standard_normal((5, 3)); pos = 80 * pos / np.linalg.norm(pos, axis=1, keepdims=True)
data = np.exp(-np.linalg.norm(xyz[:, None, :] - pos[None], axis=2) / 60.0) @ src + 0.2 * rng.standard_normal((len(names), t))
def run(positions):
    eeg = eeg_emptyset()
    eeg.update(data=data.copy(), nbchan=len(names), pnts=t, trials=1, srate=srate, xmin=0.0, xmax=(t - 1) / srate,
               chanlocs=[{"labels": n, "X": float(p[0]), "Y": float(p[1]), "Z": float(p[2])} for n, p in zip(names, positions)])
    return len(names) - int(clean_channels(eeg_checkset(eeg))["nbchan"])
print("true geometry:", run(xyz))                                                # 0
print("collinear:", run([(10 + 5 * i, 0, 0) for i in range(len(names))]))       # 6
print("identical:", run([(10, 20, 30)] * len(names)))                            # 6

Expected: a ValueError (or at least a warning) when the unit-normalised locations do not span three dimensions
(numerical rank below 3), as the existing errors for missing locations and for more than 75 percent removed channels do.

Checked on eegprep 0.5.0.

Dominant language
Python
Stars
38
Forks
7
Avg merge
1d 5h
Merged PRs (30d)
58

Getting set up

  • Ships a Dockerfile or Docker Compose file
  • No pull request template
  • No contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from sccn/eegprep

All issues in sccn/eegprep

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.