Splitting¶
Deterministic 10-fold splits with patient-level grouping. Folds are 1-indexed,
and the default split mapping is derived from the fold count rather than fixed:
train=1..n-2, val=n-1, test=n.
Engine¶
engine
¶
Universal splitting engine.
Dispatches to StratifiedGroupKFold (patient-aware), StratifiedKFold, or reads predefined splits based on the dataset config.
split_dataset
¶
split_dataset(df: DataFrame, labels: Series, config: DatasetConfig, n_folds: int = 10, random_state: int = 42) -> SplitResult
Universal splitting pipeline.
Decision logic: 1. If config.has_predefined_splits: read fold assignments from config 2. Else if config.patient_id_column is set: StratifiedGroupKFold 3. Else: StratifiedKFold
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
DataFrame
|
Metadata DataFrame (from splitter.load_metadata) |
required |
labels
|
Series
|
Stratification labels (from splitter.get_stratification_labels) |
required |
config
|
DatasetConfig
|
The dataset's DatasetConfig |
required |
n_folds
|
int
|
Number of folds (default 10) |
10
|
random_state
|
int
|
Random seed for determinism |
42
|
Returns:
| Type | Description |
|---|---|
SplitResult
|
SplitResult with fold assignments (1-indexed folds) |
Source code in ecgbench/splitting/engine.py
Base classes¶
base
¶
Abstract base class for dataset splitters and the SplitResult dataclass.
SplitResult
dataclass
¶
SplitResult(folds: dict[int, DataFrame], default_train_folds: list[int], default_val_folds: list[int], default_test_folds: list[int], stratify_column: str, group_column: str | None, split_metadata: dict = dict())
Output of any splitting operation.
get_fold
¶
Get a single fold by number (1-indexed).
Source code in ecgbench/splitting/base.py
get_kfold_split
¶
Get train/val/test for a custom k-fold rotation.
All folds except val_fold and test_fold become train.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
val_fold
|
int
|
Fold number to use as validation. |
required |
test_fold
|
int
|
Fold number to use as test. |
required |
Returns:
| Type | Description |
|---|---|
tuple[DataFrame, DataFrame, DataFrame]
|
Tuple of (train_df, val_df, test_df). |
Source code in ecgbench/splitting/base.py
DatasetSplitter
¶
Bases: ABC
Abstract base for dataset-specific splitting logic.
Subclass this when a dataset has unusual structure that can't be handled by config alone (e.g., PTB-XL's SCP code -> superclass mapping). For simple datasets, use GenericSplitter which reads everything from config.
load_metadata
abstractmethod
¶
load_metadata(data_path: Path, config: DatasetConfig) -> DataFrame
Load the dataset's metadata CSV and normalise it.
Must return a DataFrame containing at minimum
- config.record_id_column
- config.signal_path_columns values
- config.label_column (or derived stratification column)
- config.patient_id_column (if applicable)
Source code in ecgbench/splitting/base.py
get_stratification_labels
abstractmethod
¶
get_stratification_labels(df: DataFrame, config: DatasetConfig) -> Series
Return a Series of categorical labels for stratification.
Must be aligned with df's index.
Registry¶
Splitters register themselves with @register("<config-slug>"). A strategy
module must also be imported in ecgbench/splitting/strategies/__init__.py,
otherwise the decorator never runs and lookup silently falls back to
GenericSplitter.
registry
¶
Splitter registry with automatic GenericSplitter fallback.
register
¶
Decorator to register a splitter class for a dataset slug.
get_splitter
¶
get_splitter(dataset_slug: str) -> DatasetSplitter
Get the splitter for a dataset. Falls back to GenericSplitter.
Source code in ecgbench/splitting/registry.py
Export¶
Fold CSVs carry minimal columns only — record ID, patient ID, signal paths,
fold, default_split, plus is_valid/quality_issues in original/. Full
metadata stays in the dataset's own CSV.
export
¶
Fold CSV export for both original/ and clean/ versions.
Exported CSVs contain only the minimum columns needed to identify records in the original dataset: record ID, patient ID (if available), signal file paths, fold number, and default split assignment. Users join these back to the original dataset CSV to get full metadata (age, sex, labels, etc.).
export_splits
¶
export_splits(split_result: SplitResult, validation_result: ValidationResult, output_dir: Path, config: DatasetConfig) -> dict
Export fold CSVs in both original/ and clean/ versions.
Exported CSVs contain only identification columns (record ID, patient ID, signal file paths) plus fold/split assignment. Full metadata stays in the original dataset CSV — users join on record_id when they need it.
Creates
output_dir/ original/ folds.csv train/fold_1.csv ... fold_N.csv val/fold_M.csv test/fold_K.csv clean/ folds.csv train/fold_1.csv ... fold_N.csv val/fold_M.csv test/fold_K.csv validation_report.json
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
split_result
|
SplitResult
|
SplitResult from split_dataset() |
required |
validation_result
|
ValidationResult
|
ValidationResult from validate_dataset() |
required |
output_dir
|
Path
|
Root output directory |
required |
config
|
DatasetConfig
|
DatasetConfig |
required |
Returns:
| Type | Description |
|---|---|
dict
|
dict with statistics: counts per split, per version |
Source code in ecgbench/splitting/export.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | |