Repository navigation
ENH: Allow top-level splitters in CrossSubjectEvaluation - #1207
bruAristimunha merged 2 commits into
Conversation
|
Follow-up on the current head |
|
Self-review hardening on the current head: I made the splitter index contract explicit and executable. The parallel evaluator slices folds with |
|
Protocol-safety hardening on the current head: explicit top-level splitters are now validated before any fold reaches the evaluator. Only 2-way |
|
Updated exact-head verification on The 3-way calibration case still goes through |
6a9ab07 to
aa8c877
Compare
|
Review-history cleanup only: I squashed the branch to a single commit before maintainer review. The Git tree is unchanged ( |
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
bruAristimunha
left a comment
There was a problem hiding this comment.
Thanks @lindicaphxag-tech. I checked the design against #1088: this is option 1 (splitter instance, additive, default unchanged) with the cross-subject-only scope, and the mutual exclusion against cv_class/cv_kwargs/n_splits/groups/non-default cs_mode removes the two-protocol ambiguity. Positional-index contract and one-subject-per-test-fold invariant match the existing .iloc/per-subject result-row semantics. splitter=None is preserved by the early return, and the _preview_splits refactor is equivalent for 2- and 3-tuple outputs. CI is green on aa8c877.
Two points I want to settle before approving, both about new global behaviour outside the splitter path:
_validate_test_fold_metadatanow raises on an empty test fold for every evaluation type. I'm inclined to keep it as a hard error; speak up if you see a legitimate custom-CV case that emits empty folds._preview_splitsnow rejects 4+-tuple splitters instead of silently binning the middle elements intocal. Also fine by me, but it's an API change for third-party splitters and should be listed under API changes inwhats_new.rst, not only as an enhancement.
Optional simplification: the five if kwargs.get(...) is not None: conflicts.append(...) blocks in evaluations.py could be one comprehension.
|
Both review points are addressed on current head |
f9cb054 to
50b6d1d
Compare
Closes #1088.
CrossSubjectEvaluationcurrently hardcodes its top-levelCrossSubjectSplitter, so a metadata-aware cross-subject protocol cannot reuse MOABB's caching, scoring, joblib parallelism, result database, and fold metadata handling without reimplementing the evaluation engine.This PR adds an optional
splitter=injection point for a scikit-learnBaseCrossValidatorfollowing MOABB's existingsplit(y, metadata)contract.Design constraints:
Default
CrossSubjectEvaluationbehavior is unchanged whensplitter=None.An explicit top-level splitter is mutually exclusive with the existing top-level protocol controls (
cv_class,cv_kwargs,n_splits,groups, and non-defaultcs_mode) so two protocol definitions cannot silently conflict.Splitter-declared
metadata_columnsare carried into result rows, and per-foldget_metadata()output goes through the existing evaluation task path.Train/calibration/test indices are consumed by the same fold engine as the built-in cross-subject protocols. Explicit splitters must yield exactly 2 or 3 slices with unique, in-range, one-dimensional integer positions; train/calibration/test slices must be pairwise disjoint, so malformed custom protocols cannot silently leak trials.
The positional contract is explicit because the evaluator slices metadata with
iloc; custom splitters must not rely on DataFrame index labels.Each explicit test fold must resolve to exactly one subject. MOABB stores one subject identity per result row, so accepting a multi-subject test fold would silently misattribute the scientific unit being evaluated.
Splitter metadata is deep-copied at fold materialization time, so a custom splitter that reuses/mutates nested metadata objects cannot retroactively change earlier folds' recorded provenance.
Validation:
4a01cb02independently ranuv run --no-sync python -m pytest moabb/tests/test_evaluations.py -q -k cross_subject_top_level_splitteron Python 3.11 → 14 passed, including the end-to-end fake-EEG evaluation and all malformed-fold contract checks (run #37115116998).Scope boundary with grouped cross-subject CV
The explicit
splitter=hook in this PR intentionally follows the single-target use case from #1104: a metadata-aware top-level splitter may exclude part of the held-out target subject (for example, test only one target session while dropping the target's other sessions), so each fold still has one target subject and one subject identity in the result rows.This is distinct from the standard
cv_class/ grouped-CV path. Multi-subject test folds produced there are a valid separate use case; #1211 handles their result provenance by fitting once and scoring/storing each held-out subject separately. In other words, #1207 does not redefine grouped CV as single-subject-only; it keeps the new transfer/exclusion hook narrow to the issue that motivated it.