- Runs feature extraction once, then variance-aware ComBat, returns
both states as a single long-format DataFrame with columns
subject_id / site / feature / feature_value / harmonization_state.
- Reuses the same _MIN_VAR_THRESHOLD split as run_pipeline so
diagnostics reflect production exactly.
- 2 new tests: long-format shape + post-ComBat site-gap < pre-ComBat
(regression pin for the 5.0 → 0.0015 reduction story).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(mri): log run params, metrics, and parquet artifact to MLflow
Addresses I1, I2, M1 from code review:
- Docstring step-2 now calls out 6-connectivity, iterations=1, and the
thin-feature erosion caveat (suggesting 26-connectivity for production).
- New paragraph documents the all-False WARNING behaviour.
- logger.warning emitted when cleaned.any() is False, reporting volume
min/max and effective threshold so silent feature-zeroing is visible
in production logs.
- .astype(bool) moved onto the binary_opening line; return is just cleaned.
- New regression test: constant-valued volume produces empty mask + WARNING.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace pre-screen-before-filter logic with principled filter→ICA→epoch→validate
order so that FIR-spread NaN is caught at epoch validation time, not missed after
surviving epochs are extracted from contaminated filtered data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat(eeg): flatten 3D epochs into deterministic 2D feat_<ch>_<band|stat> table
Implements ICA-based EOG artifact removal using measure="correlation",
threshold=0.9 to reliably flag components on small (4-channel) fixtures
where the default z-score threshold is algebraically unreachable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(eeg): guard bandpass_filter against inverted l_freq/h_freq; document picks=all