Dataset Splitting
SkillDatabases & dataCreate reproducible train/validation/test splits with stratification, leakage prevention, and distribution validation. Covers random, stratified, grouped, and time-series split strategies.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Dataset Splitting skill
What this skill tells your AI
The instructions your AI receives, as published by mkurman/zorai in skills/dataset-splitting/SKILL.md and read by ahel’s review.
Overview
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.
When to Use
Use this skill when:
- Preparing data for supervised learning.
- Designing evaluation protocols for model comparison.
- Setting up cross-validation folds.
- Ensuring no data leakage between splits.
Do not use for:
- Unsupervised learning evaluation — different rules apply.
- Time-series forecasting with backtesting — use
dartsorprophetskills. - Data cleaning — use
dataset-cleaningbefore splitting.
Split Strategies
1. Standard Random Split (IID)
from 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
2. Stratified Split (Class Imbalance)
# 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
)
3. Group-Level Split (No Cross-Contamination)
from 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]
4. Time-Series Split (No Future Leakage)
# 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
Cross-Validation Setup
from 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)
Leakage Prevention Checklist
Before locking the split, verify:
- No ID leakage: same entity does not appear in train and test.
- No temporal leakage: all train timestamps precede all test timestamps.
- No target leakage: no feature derived from test data (including imputation, scaling, encoding).
- No group leakage: groups (patients, users, experiments) are fully in one split.
- Stratification preserved: target distribution similar across splits.
# 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!"
Locking the Split
Once created, save split assignments immutably:
# 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)
Quality Gate
A split is valid when:
- Every row belongs to exactly one split.
- No group, entity, or timestamp leakage exists.
- Target distribution is consistent across splits (within tolerance).
- Split artifacts (indices, assignments) are saved and versioned.
- All preprocessing decisions are made using only the training set.
Signals
- GitHub stars
- 324
- Forks
- 26
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
dataset-splitting- Source
- github.com/mkurman/zorai