MiddleCodeCommonNot answered yet
Find the step in this pandas pipeline that fits before the train/test split.
The pipeline below reports a suspiciously high score. One preprocessing step sees data it must never see.
Constraints:
- Keep the same imputation and scaling behaviour.
- Do not change the split ratio or the random seed.
- Both parts must end up transformed with the same fitted statistics.
import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
def prepare(df: pd.DataFrame, target: str):
X, y = df.drop(columns=[target]), df[target]
X = pd.DataFrame(SimpleImputer(strategy="median").fit_transform(X), columns=X.columns)
X = pd.DataFrame(StandardScaler().fit_transform(X), columns=X.columns)
return train_test_split(X, y, test_size=0.2, random_state=42)
Find and fix the error.
The scaler and the imputer are fitted on the full frame, so test statistics leak into training features and the score comes out optimistic. Split first, fit only on the training part, then transform both parts with those statistics.
- ✗Calling fit_transform on the test part instead of transform with the training statistics
- ✗Thinking a leak requires touching the target, when feature statistics leak on their own
- ✗Splitting first but re-fitting the scaler separately on each part
- →How does the sklearn helper Pipeline make this mistake structurally impossible?
- →Where does the same leak reappear under cross-validation rather than one split?
Both preprocessing steps call fit_transform on the full X, so the medians and the scaling statistics are computed together with the held-out rows. The split has to come first.
import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
def prepare(df: pd.DataFrame, target: str):
X, y = df.drop(columns=[target]), df[target]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
imputer = SimpleImputer(strategy="median").fit(X_train)
scaler = StandardScaler().fit(imputer.transform(X_train))
def apply(part):
return pd.DataFrame(scaler.transform(imputer.transform(part)), columns=X.columns, index=part.index)
return apply(X_train), apply(X_test), y_train, y_test
The rule is: fit on train only, transform on both parts. The same ordering is mandatory inside every cross-validation fold; sklearn.pipeline.Pipeline inside cross_val_score enforces it automatically.