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

  1. Drop insignificant variables (with a stated reason).
  2. Encode categoricals.
  3. Separate X and y.
  4. Split.
  5. 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_dummies silently 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.