Add EEGBCI dataset and tasks#1177
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a full EEGBCI (PhysioNet EEG Motor Movement/Imagery) pipeline to PyHealth, including a dataset loader, task implementations, a pattern-discovery example/reporting utilities, API documentation registration, and comprehensive core tests.
Changes:
- Introduces
EEGBCIDatasetwith local EDF discovery, optional MNE download, and metadata CSV generation. - Adds EEGBCI motor execution/imagery task logic, channel selection, normalization, bandpower feature extraction, and a pattern-discovery task.
- Adds an end-to-end pattern discovery example + README, updates API docs, and adds extensive tests.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/core/test_eegbci.py | Adds unit tests covering dataset metadata, download path, label mapping, signal helpers, features, and example integration. |
| pyhealth/tasks/eegbci.py | Implements EEGBCI label/run helpers, channel selection, normalization, bandpower features, and two EEGBCI tasks. |
| pyhealth/tasks/init.py | Exposes the new EEGBCI tasks from the tasks package. |
| pyhealth/datasets/eegbci.py | Implements EEGBCI metadata preparation and dataset wiring to BaseDataset. |
| pyhealth/datasets/configs/eegbci.yaml | Registers the records table schema for EEGBCI metadata CSV. |
| pyhealth/datasets/init.py | Exposes EEGBCIDataset from the datasets package. |
| examples/eeg/eegbci/eegbci_pattern_discovery.py | Adds the pattern discovery CLI, CSV export, and markdown report rendering. |
| examples/eeg/eegbci/README.md | Documents running the EEGBCI pattern discovery pipeline and output semantics. |
| docs/api/datasets/pyhealth.datasets.EEGBCIDataset.rst | Adds dataset API doc entry for EEGBCI. |
| docs/api/tasks/pyhealth.tasks.eegbci.rst | Adds task API doc entry for EEGBCI tasks. |
| docs/api/tasks.rst | Registers the EEGBCI tasks page in the tasks index. |
| docs/api/datasets.rst | Registers the EEGBCI dataset page in the datasets index. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self.root = root | ||
| self.subjects = list(subjects) if subjects is not None else [1, 2, 3] | ||
| self.runs = list(runs) if runs is not None else list(range(3, 15)) | ||
| self.download = download |
There was a problem hiding this comment.
Addressed in 712619a. EEGBCIDataset now normalizes subjects and runs to sorted unique integers before building selection_key, dataset_name, or metadata, with focused regression coverage for duplicate/reordered selections.
| def _find_local_edf(self, subject: int, run: int) -> Path | None: | ||
| root = Path(self.root) | ||
| pattern = f"S{subject:03d}R{run:02d}.edf" | ||
| matches = sorted(root.rglob(pattern)) | ||
| return matches[0] if matches else None |
There was a problem hiding this comment.
Addressed in 712619a. _find_local_edf() now checks the canonical MNE EEGBCI path first and only falls back to recursive lookup for nonstandard cache layouts.
| root = Path(self.root) | ||
| csv_path = root / "eegbci-pyhealth.csv" | ||
| if csv_path.exists() and self._metadata_matches_request(csv_path): | ||
| return |
There was a problem hiding this comment.
Addressed in 712619a. Metadata is now written to a selection-specific CSV filename derived from selection_key, and the loaded dataset config points at that file so separate selections do not overwrite each other.
| def parse_int_list(value: str) -> list[int]: | ||
| items: list[int] = [] | ||
| for part in value.split(","): | ||
| if "-" in part: | ||
| start, end = part.split("-", 1) | ||
| items.extend(range(int(start), int(end) + 1)) | ||
| else: | ||
| items.append(int(part)) | ||
| return items |
There was a problem hiding this comment.
Addressed in 712619a. parse_int_list() now strips each comma-separated part, rejects empty entries, and raises a clear ValueError for descending ranges like 5-3.
| "subjects": parse_int_list(args.subjects), | ||
| "runs": parse_int_list(args.runs), | ||
| "max_windows": args.max_windows, | ||
| "baseline_row_count": len(all_rows), |
There was a problem hiding this comment.
Addressed in 712619a. The report now computes baseline_row_count from rows with task_label == "rest", so “Baseline source rows” reflects the rest windows used for baseline construction instead of all windows.
Contributor: Vihaan Agrawal
Contribution Type: Dataset + Task + Example + Documentation
Description:
Adds support for the PhysioNet EEG Motor Movement/Imagery (EEGBCI) dataset as a PyHealth EEG pipeline.
EEGBCIDataset, dataset configuration, local EDF discovery, optional MNE-based download, and metadata generation for selected subjects and runs.Data Access:
EEGBCI files are loaded from local EDF files when available, or fetched through MNE with
download=True.Files to Review:
pyhealth/datasets/eegbci.pypyhealth/datasets/configs/eegbci.yamlpyhealth/tasks/eegbci.pyexamples/eeg/eegbci/eegbci_pattern_discovery.pyexamples/eeg/eegbci/README.mddocs/api/datasets/pyhealth.datasets.EEGBCIDataset.rstdocs/api/tasks/pyhealth.tasks.eegbci.rsttests/core/test_eegbci.pyTesting:
make testall