Data leakage is one of the easiest ways to build a machine-learning model that appears impressive during experimentation and disappoints as soon as it encounters genuinely unseen data.
The dangerous part is that leakage does not always look like an obvious programming error. The code can run correctly, cross-validation can look excellent, and every metric can improve. The problem is methodological: information crossed a boundary that will not be crossed when the model is used in production.
Preventing leakage therefore starts before choosing an algorithm. You need to define what will be known at prediction time, which observations are truly independent, and what future deployment should look like.
The core question
For every feature and preprocessing step, ask: “Would this information be available, in exactly this form, at the moment the production model has to make the prediction?”
1. What data leakage actually means
Leakage occurs when model development benefits from information that would not legitimately be available for a new production prediction.
Leakage-safe information boundary (diagram)
Leakage can enter through several routes:
- Features containing post-outcome information.
- Imputation statistics calculated before the split.
- Scaling fitted on the complete dataset.
- Feature selection performed using all labels.
- Oversampling before cross-validation.
- Target encoding calculated across validation observations.
- Repeated measurements from one entity appearing in multiple folds.
- Duplicates appearing in both train and test data.
- Future observations used to predict earlier events.
- Repeated tuning against the final test set.
Leakage is different from ordinary overfitting
Overfitting means learning patterns that do not generalize well. Leakage means the experiment itself provided information that should not have been available.
The two can coexist, but leakage is particularly dangerous because even a simple model can achieve unrealistic scores when the validation design is contaminated.
2. Define the prediction boundary before splitting data
A correct split reflects the real prediction task. Before calling
train_test_split(), write down what the model is supposed to
do.
Define these five things
- What entity receives a prediction?
- At what moment is the prediction made?
- Which information exists at that moment?
- Which entities can appear repeatedly?
- What kind of unseen data will production present?
Example: customer churn
Suppose the model predicts on January 1 whether a customer will churn during the next 90 days.
Features available on January 1 may be legitimate:
- Account age.
- Current subscription plan.
- Support tickets created before January 1.
- Past usage.
Features created afterward are not:
- Cancellation date.
- Final account state.
- Refund issued after cancellation.
- Retention-team outcome.
Availability is not the same as existence
A field may exist today in a historical database while still being unavailable at the historical prediction time. Build features according to when they become known, not simply according to whether the final database contains them.
3. Preprocessing leakage: split before you fit
Many preprocessing operations learn statistics from data. Those statistics are model parameters in everything but name.
Common learned preprocessing
- Mean or median imputation.
- Standardization.
- Min-max scaling.
- Quantile transformations.
- PCA.
- Feature selection.
- Rare-category grouping.
- Target encoding.
- Vocabulary creation.
- Learned embeddings or representations.
Incorrect: transform before splitting
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(
X_scaled,
y,
test_size=0.2,
random_state=42
)
The scaler has already seen the mean and standard deviation of observations that later become the test set.
Correct: split first
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
The same principle applies to imputation:
from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
X_train_imp = imputer.fit_transform(X_train)
X_test_imp = imputer.transform(X_test)
Think in terms of fit and transform
Held-out data may usually pass through a transformation that was already fitted on training data. It should not normally participate in learning that transformation.
4. Target leakage and post-outcome features
Target leakage occurs when a feature directly or indirectly reveals the outcome.
Obvious examples
| Prediction | Leaky feature |
|---|---|
| Customer churn | Cancellation date |
| Loan default | Collections status recorded after default |
| Fraud detection | Final investigator decision |
| Hospital outcome | Treatment performed after diagnosis |
| Delivery delay | Actual delivery timestamp |
Indirect leakage is harder
A feature may not literally contain the target but can encode a downstream process that only occurs because the outcome has already happened.
Examples:
- A support queue assigned only after escalation.
- A workflow status generated after fraud confirmation.
- A billing adjustment normally issued after churn.
- A medical procedure ordered after the condition is diagnosed.
Audit feature timestamps
For important models, create a feature-availability table:
feature available_at_prediction
------------------------------------------------
account_age yes
current_plan yes
past_30d_usage yes
cancellation_reason no
refund_amount no
retention_result no
Feature provenance is often more useful for finding leakage than looking at model coefficients.
Extremely predictive features deserve investigation
A feature producing almost perfect separation may be genuinely excellent, but it should trigger questions:
- When is this field created?
- Does it contain target-derived information?
- Could it exist for a brand-new production observation?
- Did a join accidentally bring future data backward?
5. Random, stratified, grouped, or temporal split?
Validation-split decision tree (diagram)
Random split
A random split is appropriate when rows are sufficiently independent and future production observations come from roughly the same process.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42
)
Stratified split
Classification datasets with uncommon classes may benefit from preserving approximate class proportions.
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.2,
random_state=42,
stratify=y
)
Stratification helps class balance. It does not prevent entity or temporal leakage.
Grouped validation
Use groups when several rows belong to the same logical entity and the production task requires generalizing beyond that entity.
Examples:
- Many transactions from one customer.
- Repeated measurements from one patient.
- Several photographs of the same person.
- Many sensor readings from one machine.
- Multiple comments written by one author.
from sklearn.model_selection import GroupKFold
cv = GroupKFold(n_splits=5)
for train_idx, valid_idx in cv.split(X, y, groups=customer_id):
X_train = X.iloc[train_idx]
X_valid = X.iloc[valid_idx]
The important property is that one group does not appear in both the training and held-out portion of a fold.
Temporal validation
When production predicts future outcomes from past information, preserve chronological order instead of randomly mixing observations.
train = df[df["timestamp"] < "2026-01-01"]
test = df[df["timestamp"] >= "2026-01-01"]
For repeated temporal evaluation, use a time-aware splitting strategy.
6. Duplicate and entity leakage
A random splitter knows nothing about the semantic relationship between rows.
Exact duplicates
If identical observations appear in both train and test sets, a model may effectively be evaluated on records it has already seen.
duplicates = df.duplicated().sum()
print("Duplicate rows:", duplicates)
Investigate duplicates before splitting and decide whether they represent legitimate repeated events or accidental copies.
Near-duplicates
Images, text, product records, or documents may have slightly modified versions that remain essentially the same example.
Examples include:
- The same photograph resized several ways.
- Duplicated documents with minor formatting differences.
- The same product listing copied from different feeds.
- Repeated customer records with normalized spelling differences.
Entity memorization
Suppose you predict future purchases but randomly split transactions. The model may see earlier and later transactions from the same customer in both partitions.
That may be correct if production predicts another event for an existing known customer. It is misleading if the stated goal is generalization to completely new customers.
The split defines the question
“Can we predict another event for a known customer?” and “Can we generalize to a customer never seen before?” are different experiments and often require different validation splits.
7. Temporal leakage and future information
Time creates some of the most subtle leakage problems because a feature can be historically valid but still calculated using future observations.
Bad: rolling feature using future data
Imagine predicting tomorrow's demand while computing a centered moving average containing values from both before and after the prediction timestamp.
The feature looks mathematically reasonable, but production cannot know tomorrow's future neighbors.
Use past-only windows
df = df.sort_values("timestamp")
df["past_7d_average"] = (
df["value"]
.shift(1)
.rolling(window=7)
.mean()
)
The shift(1) makes the example explicitly exclude the current
outcome from its historical window.
TimeSeriesSplit
from sklearn.model_selection import TimeSeriesSplit
cv = TimeSeriesSplit(
n_splits=5
)
for train_idx, valid_idx in cv.split(X):
X_train = X.iloc[train_idx]
X_valid = X.iloc[valid_idx]
Time-aware splitting trains on earlier observations and evaluates on later observations instead of reversing the production direction.
Consider a gap
Some applications require separation between training and validation periods because labels mature slowly or features near the boundary share information.
cv = TimeSeriesSplit(
n_splits=5,
gap=7
)
Whether a gap is required depends on the meaning and spacing of the observations.
Random cross-validation can answer the wrong question
For a forecasting or future-event problem, random folds often estimate performance under random interpolation between historical observations, not genuine forward-looking production performance.
8. Feature selection, target encoding, and oversampling traps
Feature selection before cross-validation
Suppose you select the 20 features most correlated with the target using the complete dataset and then run cross-validation.
Validation labels already influenced which features were selected.
Feature selection must occur independently inside each training fold.
Target encoding
A target-encoded categorical feature may replace a category with the average label associated with that category.
Calculating those averages using the complete dataset leaks validation labels directly into features.
Use fold-aware or out-of-fold target encoding and ensure the validation observation's own label does not influence its encoded value.
Oversampling
Oversampling the minority class before splitting can create synthetic or repeated observations closely related to samples that later appear in validation.
The safe conceptual order is:
- Define the validation split.
- Take only the training portion.
- Fit the resampling strategy there.
- Train the model.
- Evaluate on untouched validation observations.
Dimensionality reduction
PCA and similar learned transformations also need the training boundary. Fit them inside each fold rather than once on the full feature matrix.
Global feature engineering
Aggregates can leak even when they do not directly use the target.
Example:
customer_total_orders =
count of every order the customer ever places
If the prediction occurs halfway through that customer's history, the final lifetime total includes future behavior.
The correct feature is closer to:
orders_before_prediction_time =
count of orders known before the prediction timestamp
9. Protect the final test set
A held-out test set estimates performance on data that did not influence model development.
That purpose disappears if you check the test result after every experiment.
Test-set contamination through decision making
Imagine this workflow:
- Train model A.
- Check test accuracy.
- Add a feature because test accuracy was disappointing.
- Check test accuracy again.
- Change the algorithm.
- Change the classification threshold.
- Repeat until the test score looks good.
The test set has become a validation set. Its feedback shaped the model.
Better workflow
- Set aside the final test set.
- Develop using training data plus cross-validation or a validation set.
- Select preprocessing, features, model family, and hyperparameters there.
- Freeze the decision process.
- Evaluate on the final test set.
Hyperparameter tuning belongs inside validation
from sklearn.model_selection import GridSearchCV
search = GridSearchCV(
estimator=pipeline,
param_grid=params,
cv=5,
scoring="roc_auc"
)
search.fit(X_train, y_train)
final_score = search.score(
X_test,
y_test
)
The test set is not passed into the grid search.
Nested cross-validation
When data is limited and you need an especially careful estimate while tuning many hyperparameters, nested cross-validation can separate model selection from performance estimation.
It is more computationally expensive, so it is not automatically required for every project.
10. Build leakage-safe validation pipelines
Leakage-safe model evaluation flow (diagram)
Use Pipeline for learned transformations
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
pipeline = Pipeline([
(
"imputer",
SimpleImputer(strategy="median")
),
(
"scaler",
StandardScaler()
),
(
"model",
LogisticRegression(max_iter=1000)
)
])
When the pipeline is evaluated through cross-validation, each training fold fits its own imputer and scaler.
Cross-validation
from sklearn.model_selection import cross_val_score
scores = cross_val_score(
pipeline,
X,
y,
cv=5,
scoring="roc_auc"
)
print(scores)
print(scores.mean())
For grouped observations, replace ordinary folds with the appropriate grouped splitter and supply the group labels.
from sklearn.model_selection import GroupKFold
from sklearn.model_selection import cross_val_score
cv = GroupKFold(n_splits=5)
scores = cross_val_score(
pipeline,
X,
y,
cv=cv,
groups=customer_id,
scoring="roc_auc"
)
For time-dependent data
from sklearn.model_selection import TimeSeriesSplit
cv = TimeSeriesSplit(
n_splits=5,
gap=7
)
scores = cross_val_score(
pipeline,
X,
y,
cv=cv,
scoring="neg_mean_absolute_error"
)
Audit the entire information path
Before trusting the score, review every stage:
- Raw data extraction.
- Joins.
- Label creation.
- Feature timestamps.
- Deduplication.
- Train-validation splitting.
- Imputation.
- Scaling.
- Encoding.
- Feature selection.
- Resampling.
- Hyperparameter tuning.
- Threshold selection.
- Final test evaluation.
Validate the deployment scenario, not the dataset format
The best split is the one that reproduces the information boundary the model will face after deployment. Rows in the same CSV file are not necessarily independent observations.
11. Copy/paste data-leakage checklist
Data leakage prevention checklist
Prediction definition
- Define exactly what the model predicts.
- Define the prediction timestamp.
- Define what information exists at prediction time.
- Define whether production predicts known or unseen entities.
- Define whether predictions concern future events.
- Document the intended production population.
Raw data
- Preserve raw input.
- Record data extraction timestamps.
- Inspect duplicate rows.
- Inspect near-duplicates where relevant.
- Identify repeated entities.
- Identify historical snapshots of the same entity.
- Review joins for accidental future information.
- Review label-generation logic.
Feature availability
- Record when every important feature becomes available.
- Remove post-outcome features.
- Remove target-derived features.
- Investigate suspiciously predictive fields.
- Check whether operational workflow states occur after the target.
- Check whether aggregate features contain future observations.
- Ensure rolling statistics use past-only windows.
- Verify external datasets were historically available at prediction time.
Initial splitting
- Choose the split before fitting preprocessing.
- Use random splitting only when observations are sufficiently independent.
- Use stratification when class balance requires it.
- Use grouped splitting when entities repeat.
- Use temporal splitting for future prediction.
- Consider a gap between train and validation when the domain requires it.
- Document the reason for the chosen validation design.
Duplicates and entities
- Prevent exact duplicates from crossing the validation boundary.
- Investigate near-duplicate content.
- Keep repeated measurements together when production requires entity-level generalization.
- Keep images of the same subject together where appropriate.
- Keep transactions from the same customer together where appropriate.
- Keep documents derived from the same original source together where appropriate.
- Check whether identifier-like features allow memorization.
Preprocessing
- Split before fitting imputers.
- Fit imputation statistics on training data only.
- Fit scalers on training data only.
- Fit PCA on training data only.
- Fit learned encoders on training data only.
- Fit vocabulary creation inside the training boundary.
- Fit rare-category grouping inside the training boundary.
- Transform validation and test data using already fitted training transformers.
Feature selection
- Do not select features using the full dataset.
- Keep supervised feature selection inside cross-validation.
- Keep dimensionality reduction inside cross-validation.
- Keep variance or frequency rules inside the correct training boundary when they depend on data.
- Investigate perfect or nearly perfect predictors.
Target encoding
- Do not calculate target means using validation labels.
- Use fold-aware or out-of-fold encoding.
- Prevent a row's own label from influencing its encoded feature where required.
- Handle unseen categories safely.
Resampling
- Do not oversample the full dataset before splitting.
- Apply oversampling only to training folds.
- Do not generate synthetic validation observations from training neighbors.
- Evaluate on untouched natural validation distributions.
- Keep resampling inside the evaluation pipeline when possible.
Temporal features
- Sort observations by time.
- Confirm timestamps reflect when information became available.
- Avoid future values in rolling statistics.
- Shift target-related aggregates when necessary.
- Avoid backward filling from future observations into past predictions.
- Avoid fitting global temporal statistics using future periods.
- Use chronological validation for future prediction.
- Consider concept and data drift.
Cross-validation
- Choose cross-validation that matches the deployment scenario.
- Use grouped folds for repeated entities when appropriate.
- Use time-aware folds for chronological prediction.
- Keep preprocessing inside each fold.
- Keep feature selection inside each fold.
- Keep resampling inside each fold.
- Tune hyperparameters using validation folds, not the final test set.
Final test set
- Set aside the final test set before model development.
- Do not inspect test results after every experiment.
- Do not choose features based on final test performance.
- Do not tune hyperparameters on final test results.
- Do not choose the classification threshold on the final test set.
- Do not choose preprocessing based on the final test set.
- Evaluate the frozen modeling approach on the final test set.
- Document any decision made after seeing final test performance.
Pipeline
- Put learned preprocessing in a Pipeline.
- Use ColumnTransformer for different feature types where appropriate.
- Place imputation inside the pipeline.
- Place scaling inside the pipeline.
- Place encoding inside the pipeline.
- Place feature selection inside the pipeline.
- Evaluate the complete pipeline with cross-validation.
- Persist the same fitted pipeline used for production prediction.
Validation quality
- Compare validation distributions with expected production data.
- Check performance by time period.
- Check performance by entity group.
- Check performance by important business segment.
- Check whether validation examples are unrealistically similar to training data.
- Compare validation and production feature availability.
- Investigate unexpectedly high validation scores.
Before deployment
- Reconstruct one prediction exactly as production will see it.
- Confirm every required feature exists at that moment.
- Confirm no feature is calculated from future information.
- Confirm preprocessing uses only fitted training parameters.
- Confirm the model does not require labels at inference time.
- Confirm the validation split matches the expected deployment population.
- Record known limitations and leakage risks.
12. FAQ
What is data leakage in machine learning?
Data leakage occurs when information unavailable during real production prediction influences training, preprocessing, model selection, or evaluation. The result is usually an overly optimistic validation score.
Should I split data before preprocessing?
Yes for preprocessing that learns from observations. Split first, then fit imputers, scalers, feature selectors, encoders, PCA, and similar transformations on training data only. Apply the fitted transformation to validation and test data.
When should I use GroupKFold?
Use grouped validation when multiple samples belong to the same logical entity and those groups should remain separate across training and validation. Common examples include repeated measurements from patients, transactions from customers, readings from devices, and several samples derived from one subject.
Why is random splitting dangerous for time-series data?
Random splitting can place later observations in training and earlier observations in validation. A model can therefore learn from a future state that would not exist when making the historical prediction being evaluated.
Does stratification prevent leakage?
No. Stratification helps preserve class proportions. It does not prevent the same customer, patient, device, document family, or future time period from leaking across the split.
Can I keep checking the test set while developing the model?
Repeated test-set feedback influences feature, model, threshold, and hyperparameter decisions. At that point the test set becomes part of model development. Use validation or cross-validation during iteration and preserve a separate final evaluation set.
Key terms (quick glossary)
- Data leakage
- Information unavailable under the intended production prediction conditions influencing model development or evaluation.
- Target leakage
- Leakage caused by a feature that directly or indirectly contains information about the outcome that would not be available when the prediction is made.
- Training set
- Data used to fit model parameters and learned preprocessing transformations.
- Validation set
- Held-out observations used during model development to compare configurations, features, hyperparameters, or thresholds.
- Test set
- Data reserved for final estimation of performance after the development process has been completed.
- Cross-validation
- A resampling procedure that repeatedly divides data into training and validation portions to estimate model performance and variability.
- GroupKFold
- A cross-validation strategy that keeps the same group from appearing in both training and validation portions of a fold.
- TimeSeriesSplit
- A cross-validation strategy for ordered data in which models train on earlier observations and evaluate on later observations.
- Preprocessing leakage
- Leakage introduced when statistics or transformations are learned using held-out observations.
- Entity leakage
- Leakage caused when related observations from the same underlying entity appear on both sides of a validation boundary.
- Pipeline
- A sequence of preprocessing transformers and an estimator that can be fitted and evaluated as one object, helping keep learned transformations within the appropriate training fold.
- Prediction boundary
- The point in time and information state defining exactly what the model is allowed to know when making a real prediction.
Worth reading
Recommended guides from the category.