Reference · Preparation & leakage
Preparation and Leakage
Load when data Preparation, split, leakage, treatment.
Part of the Data Analysis skill · loaded on demand from SKILL.md
Loaded: prep-and-leakage.md — encoding, splitting, and post-split treatment.
Covers spine section 6. Read the resolved fork file (fork-supervised.md or fork-timeseries.md) before this one — the fork determines the split strategy, and getting the order wrong here is the most common way to invalidate every metric downstream.
Order of operations — this is the whole point of the section
- Drop insignificant variables (with a stated reason).
- Encode categoricals.
- Separate X and y.
- Split.
- Then treat missing values and outliers, learning any parameter from train only.
Steps 4 and 5 are in that order for a reason, and reversing them is the single most common invalidating mistake in this kind of work.
Dropping variables
Drop only with a stated reason, in a markdown cell. Identifiers with no predictive value are the clear case. A weak bivariate relationship is not sufficient grounds on its own — tree-based models down-weight uninformative features through the splits they choose, and feature importance later will show which ones mattered. Pre-removing them on a hunch discards evidence.
If nothing is dropped beyond the identifier, say so explicitly rather than staying silent. A rubric line reading "dropping insignificant variables with comments" is satisfied by a reasoned decision not to drop, and silence reads as an omission.
Encoding
pd.get_dummies(..., drop_first=True) is the default. Two cases need a decision rather than a default:
Ordinal categoricals (Low/Medium/High, ratings, size bands). One-hot encoding discards the ordering. An ordinal integer map preserves it. Neither is automatically correct — one-hot lets a tree find non-monotonic structure, ordinal gives it a single clean split point and one column instead of several. Ask, present both sides in one sentence each, don't decide silently. Whichever is chosen, record it in the session-state marker, because it changes how feature importance reads later: profile_completed_Medium topping the importance list means something different from profile_completed doing so.
High-cardinality categoricals. One-hot on a 50-level column adds 49 columns and can break the Adjusted R² guard (see fork-supervised.md). Name the problem and offer grouping rare levels into "Other" before encoding — noting that target or frequency encoding learns from the data and therefore must be fit post-split like any other learned parameter.
Note that get_dummies on the full frame before splitting is comparatively benign — it learns no target-dependent parameter. Treat that as the single exception, not a pattern to extend. Every other transformation follows the split-first rule.
The split
Standard: train_test_split(X, y, test_size=0.30, random_state=<fixed>, stratify=y) for classification. stratify=y preserves class balance across both sets and matters more as the target gets more imbalanced.
Do not use this for time series — see fork-timeseries.md.
Use GroupShuffleSplit or GroupKFold instead if the group-leakage check in overview-and-eda.md found a repeating entity ID.
Confirm shapes after splitting. Batchable with the split itself.
Missing values — treat here, not earlier
Named during .info(), treated now.
Present the options and their costs in a sentence each, then ask:
- Drop rows — clean, but discards data, and biases the result if missingness correlates with the target.
- Impute (median/mode) — retains rows, compresses variance, and the median must be computed on train and applied to test. Computing it on the full frame leaks test information into training.
- Defer — legitimate when the model tolerates it, but say what happens downstream.
get_dummiessilently drops NaN rows from that one-hot block without raising, which is how the LOS reference notebook lost rows without noticing.
Don't default silently in either direction. Name the choice and the reason in a markdown cell — this is separately graded on most rubrics and, more importantly, it's the decision a reviewer will ask about first.
Outliers — treat here, not earlier
Named during .describe() and univariate analysis, treated now.
Whether to treat depends on the model:
- Tree-based models (Decision Tree, Random Forest, gradient boosting) split on order, not distance. They're largely robust to outliers, and leaving them untreated is defensible.
- Distance- or scale-sensitive models (linear and regularised regression, KNN, SVM) are affected. RMSE also carries outlier weight regardless of model, so if outliers are deferred, say that when reporting RMSE.
If capping: use IQR bounds (Q1 - 1.5*IQR, Q3 + 1.5*IQR), computed on train, applied to both sets. Order matters inside the code as well — compute the test set's capped values from the original train column before overwriting train, or the bounds shift underneath. Write it so that ordering is visible, and verify with a .max() check on both sets afterwards.
Preprocessing leakage — the general rule
Anything that learns a parameter from data must be fit on train only and applied to test: imputation values, scalers, target or frequency encoding, outlier caps, feature selection thresholds, PCA components used as features.
The tell is whether a number computed from the data gets reused. A median, a mean, a standard deviation, a quantile, a category-level target rate — all learned. A row-wise transformation that uses no aggregate (log, ratio between two columns on the same row) is not, and can safely precede the split.