Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Create reproducible train/validation/test splits with stratification, leakage prevention, and distribution validation. Covers random, stratified, grouped, and time-series split strategies.
.claude/skills/mkurman-dataset-splitting/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-14 | ✗→✓ | ▲ Improved | 3% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 38% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 56% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 21% | 0% |
Train/validation/test splits are the single most important guardrail against overfitting and data leakage. A bad split invalidates everything downstream. Split once, lock the split, and never let test data influence any decision.
Use this skill when:
Do not use for:
darts or prophet skills.dataset-cleaning before splitting.pythonfrom sklearn.model_selection import train_test_split # Single split (fixed seed, stratified) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=y, random_state=42 # NEVER change this casually ) # Train / validation / test (two-step) X_temp, X_test, y_temp, y_test = train_test_split( X, y, test_size=0.15, stratify=y, random_state=42 ) X_train, X_val, y_train, y_val = train_test_split( X_temp, y_temp, test_size=0.1765, stratify=y_temp, random_state=42 ) # Result: 70% train, 15% val, 15% test
python# Stratify on target AND protected attributes X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, stratify=df[['target', 'gender', 'region']].apply(tuple, axis=1), random_state=42 )
pythonfrom sklearn.model_selection import GroupShuffleSplit # When rows from the same group MUST stay together # Example: multiple samples per patient gss = GroupShuffleSplit(n_splits=1, test_size=0.2, random_state=42) train_idx, test_idx = next(gss.split(X, y, groups=df['patient_id'])) X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
python# Chronological split — NEVER shuffle time data df = df.sort_values('timestamp') split_idx = int(len(df) * 0.8) train = df.iloc[:split_idx] test = df.iloc[split_idx:] # For multiple backtest windows: from sklearn.model_selection import TimeSeriesSplit tscv = TimeSeriesSplit(n_splits=5) for train_idx, test_idx in tscv.split(X): X_train, X_test = X.iloc[train_idx], X.iloc[test_idx] # Each fold uses older data for training, newer for testing
pythonfrom sklearn.model_selection import StratifiedKFold, RepeatedStratifiedKFold # Standard 5-fold cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) # Repeated for small datasets cv = RepeatedStratifiedKFold(n_splits=5, n_repeats=3, random_state=42)
Before locking the split, verify:
python# Split distribution validation for split_name, split_df in [('train', train_df), ('val', val_df), ('test', test_df)]: print(f"{split_name}: {split_df['target'].value_counts(normalize=True).to_dict()}") # Group integrity check train_groups = set(train_df['group_id']) test_groups = set(test_df['group_id']) assert len(train_groups & test_groups) == 0, "Group leakage detected!"
Once created, save split assignments immutably:
python# Add split column and save df['split'] = 'train' df.loc[val_idx, 'split'] = 'val' df.loc[test_idx, 'split'] = 'test' # Save with version df.to_parquet('dataset_v1.0.0_with_splits.parquet', index=False) # Save split indices for reproducibility np.savez('split_indices_v1.0.0.npz', train=train_idx, val=val_idx, test=test_idx)
A split is valid when:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-14 | fail→pass | 14,851 | 7,908 | -47% | 1 | 1 | 0% | 2,672 | 2,744 | +3% | 0 | 0 | — |
case-01 | fail→fail | 20,537 | 17,710 | -14% | 1 | 1 | 0% | 3,998 | 5,181 | +30% | 0 | 0 | — |
case-02 | pass→pass | 12,003 | 8,114 | -32% | 1 | 1 | 0% | 2,572 | 3,107 | +21% | 0 | 0 | — |
case-03 | fail→pass | 10,385 | 7,789 | -25% | 1 | 1 | 0% | 1,989 | 2,754 | +38% | 0 | 0 | — |
case-04 | pass→pass | 13,561 | 10,081 | -26% | 1 | 1 | 0% | 2,542 | 3,286 | +29% | 0 | 0 | — |
case-05 | pass→pass | 11,372 | 7,112 | -37% | 1 | 1 | 0% | 2,115 | 2,642 | +25% | 0 | 0 | — |
case-06 | pass→pass | 11,216 | 11,272 | +0% | 1 | 1 | 0% | 2,396 | 3,676 | +53% | 0 | 0 | — |
case-07 | pass→pass | 13,532 | 10,785 | -20% | 1 | 1 | 0% | 2,452 | 3,347 | +37% | 0 | 0 | — |
case-08 | fail→pass | 13,700 | 13,856 | +1% | 1 | 1 | 0% | 2,727 | 4,251 | +56% | 0 | 0 | — |
case-09 | pass→pass | 7,186 | 6,396 | -11% | 1 | 1 | 0% | 1,508 | 2,705 | +79% | 0 | 0 | — |
case-10 | pass→pass | 8,452 | 8,222 | -3% | 1 | 1 | 0% | 1,608 | 3,066 | +91% | 0 | 0 | — |
case-11 | pass→pass | 3,777 | 4,139 | +10% | 1 | 1 | 0% | 703 | 2,041 | +190% | 0 | 0 | — |
case-12 | pass→pass | 6,223 | 3,327 | -47% | 1 | 1 | 0% | 1,250 | 2,004 | +60% | 0 | 0 | — |
case-13 | pass→pass | 10,478 | 6,784 | -35% | 1 | 1 | 0% | 2,184 | 2,692 | +23% | 0 | 0 | — |
case-15 | pass→pass | 12,815 | 10,227 | -20% | 1 | 1 | 0% | 2,613 | 3,515 | +35% | 0 | 0 | — |
case-16 | pass→pass | 5,262 | 3,678 | -30% | 1 | 1 | 0% | 1,016 | 1,932 | +90% | 0 | 0 | — |
case-17 | fail→fail | 9,004 | 11,129 | +24% | 1 | 1 | 0% | 1,879 | 3,721 | +98% | 0 | 0 | — |
case-18 | pass→pass | 10,136 | 11,616 | +15% | 1 | 1 | 0% | 1,986 | 3,426 | +73% | 0 | 0 | — |
case-19 | pass→pass | 8,378 | 8,289 | -1% | 1 | 1 | 0% | 1,651 | 3,074 | +86% | 0 | 0 | — |
case-20 | fail→fail | 14,594 | 14,212 | -3% | 1 | 1 | 0% | 2,432 | 3,419 | +41% | 0 | 0 | — |
case-21 | pass→pass | 18,800 | 16,561 | -12% | 1 | 1 | 0% | 3,085 | 4,286 | +39% | 0 | 0 | — |
case-22 | fail→pass | 18,482 | 13,773 | -25% | 1 | 1 | 0% | 3,591 | 3,980 | +11% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.