Home/Machine Learning/Feature Engineering & Data Quality
Back
๐Ÿ”ง

ML Engineer

Feature Engineering & Data Quality

Feature engineering is where domain knowledge becomes model performance โ€” in industry, the difference between a 70% and 90% AUC model is almost always the features, not the algorithm. MLE interviews at Amazon, Meta, and Google test whether you understand the full data pipeline: identifying and handling missing values correctly, encoding categoricals without leakage, detecting distribution shift before it tanks your model in production, and building feature stores that serve features consistently between training and serving. This is the most practical, highest-signal topic for senior MLE roles.

Basic statistics (mean, variance, distributions)scikit-learn basicsSQL/pandas proficiencyUnderstanding of train/val/test splitsFeature EngineeringMissing DataEncodingData LeakageClass ImbalanceDistribution ShiftFeature Selection

Core Theory

  • 1Missing data taxonomy (MCAR/MAR/MNAR): Missing Completely At Random (MCAR) means missingness is independent of both observed and unobserved data โ€” a sensor randomly drops packets. Safe to drop rows or impute with mean/median. Missing At Random (MAR) means missingness depends on observed data but not on the missing value itself โ€” older users less likely to fill in income, but age is recorded. Imputation using other observed features (KNN, MICE) is valid. Missing Not At Random (MNAR) means missingness depends on the missing value itself โ€” high-earners skip the income field. Simple imputation introduces bias; must model the missingness mechanism itself or use domain-specific rules. Always test: does the missingness pattern correlate with the target? If yes, add a binary indicator feature for missingness.
  • 2Imputation strategies: Mean/median imputation is fast but reduces variance and distorts correlations โ€” never use for MNAR. KNN imputation finds the K nearest complete observations and averages their values; works well for MAR with moderate missingness (<30%), expensive at O(nยฒ). MICE (Multiple Imputation by Chained Equations) runs m separate imputed datasets, trains models on each, and pools results โ€” the gold standard for MAR. Indicator variables for missingness: always add a binary column `feature_is_missing` alongside imputed values so the model can learn that missingness itself is informative. For MNAR tree-based models, the 'missing' category itself often becomes a strong predictor; XGBoost/LightGBM handle NaN natively by learning the optimal direction for missing values.
  • 3Categorical encoding: One-hot encoding creates k binary columns for k categories โ€” fine for low-cardinality (<20 levels) nominals, but explodes with high-cardinality features. Target encoding replaces each category with the mean target value (for regression: mean(y | category); for classification: P(y=1 | category)). Without regularization, target encoding leaks the target โ€” small categories will overfit perfectly. Regularization: (1) add Gaussian noise to the category mean, (2) blend with global mean via n_smoothing: encoded_value = (n ร— category_mean + k ร— global_mean) / (n + k), (3) leave-one-out encoding (exclude the current row when computing the category mean). Ordinal encoding preserves order (cold < warm < hot) and is appropriate for ordinal features. Entity embeddings (as in the Rossmann competition) learn dense low-dimensional representations of categories via a neural network embedding layer โ€” outperforms one-hot for very high-cardinality features (user IDs, product IDs).
  • 4Feature scaling: StandardScaler (z-score normalization: x' = (x - ฮผ) / ฯƒ) centers features at mean 0 with unit variance โ€” required for distance-based algorithms (KNN, SVM, k-means), gradient descent-based models (linear/logistic regression, neural nets), and PCA. MinMaxScaler (x' = (x - min) / (max - min)) maps to [0,1] โ€” useful when you need bounded outputs, but very sensitive to outliers (one outlier at x=1000 compresses all other values). RobustScaler uses median and IQR (x' = (x - median) / IQR) โ€” best choice when outliers are present. Tree-based models (decision trees, random forests, gradient boosting) are completely scale-invariant โ€” scaling provides no benefit. Never fit the scaler on the full dataset before the train/test split; fit only on train, transform both train and test.
  • 5Feature interactions and polynomial features: Linear models cannot capture interactions between features โ€” if CTR depends on (ad_format ร— user_segment), a linear model treating them separately will miss this. Polynomial features: PolynomialFeatures(degree=2) adds all products x_i ร— x_j and squares x_iยฒ โ€” for p features, generates O(pยฒ) interaction terms. Impractical beyond p~100 features. Manual interactions: domain-driven multiplication of semantically meaningful pairs (price_per_sqft = price / sqft). For tree-based models, interactions are captured automatically via tree splits โ€” polynomial features add no benefit. For neural networks, feature interactions are learned implicitly in deeper layers.
  • 6Date/time features: Raw timestamps are useless to most models. Extract: hour_of_day (0-23), day_of_week (0-6), month (1-12), is_weekend (bool), is_holiday (bool), days_since_last_purchase, days_until_event. Cyclical encoding for periodic features: sin/cos encoding prevents the model from treating hour 23 and hour 0 as far apart. hour_sin = sin(2ฯ€ ร— hour / 24), hour_cos = cos(2ฯ€ ร— hour / 24). Lag features for time series: lag_1 = value at t-1, lag_7 = value at t-7 (same day last week), rolling_mean_7d = 7-day moving average. Be careful: lag features for time series require special cross-validation (time-based splits, not random splits) to avoid leakage.
  • 7Data leakage โ€” the #1 source of production model failures: Target leakage occurs when features are derived from or correlated with the target in a way that wouldn't be available at prediction time. Example: 'days_in_hospital' as a feature for predicting hospital readmission โ€” you don't know this when the patient is admitted. Train-test contamination occurs when preprocessing is fit on the full dataset before splitting: if you compute the global mean for imputation on all data, the test set's information leaks into training statistics. Prevention: wrap all preprocessing in a scikit-learn Pipeline, fit Pipeline.fit() only on training data. Time-based contamination: using future data to compute features for the past (e.g., rolling average that looks forward in time). Feature store contamination: if the feature store serves stale features or uses different computation logic for training vs serving, predictions will be systematically wrong (training-serving skew).
  • 8Distribution shift detection and handling: Covariate shift occurs when P(X) changes but P(y|X) stays the same โ€” input distribution drifts, but the relationship between features and target remains stable. Example: user age distribution shifts as the app grows. Detection: monitor feature statistics (mean, std, percentiles) over time; compute KL divergence or Population Stability Index (PSI > 0.2 indicates significant shift). PSI = ฮฃ (Actual% - Expected%) ร— ln(Actual%/Expected%). Concept drift occurs when P(y|X) changes โ€” the relationship between features and target changes. Example: fraud patterns evolve as fraudsters adapt. Detection: monitor model performance (AUC, accuracy) on labeled data with short lags. Label shift occurs when P(y) changes โ€” class priors shift. Handling: retrain on recent data, apply importance weighting (weight training samples by P_new(X) / P_old(X)), use concept drift detectors (ADWIN, DDM, KSWIN).
  • 9Feature selection methods: Filter methods use statistical tests independent of any model. Mutual Information measures non-linear dependence between feature and target: MI(X;Y) = ฮฃ P(x,y) log[P(x,y)/(P(x)P(y))]. Chi-squared test for categorical features vs categorical target. Pearson correlation for linear dependence. Wrapper methods use model performance to select features. Recursive Feature Elimination (RFE) trains a model, removes the least important feature, and repeats โ€” exact but expensive at O(p) model trainings. Embedded methods perform selection during model training. LASSO (L1 regularization) shrinks unimportant feature coefficients to exactly zero โ€” performs feature selection implicitly. Tree feature importance measures the average gain in purity (Gini or entropy) from splits on each feature โ€” but biased toward high-cardinality features. SHAP-based importance is unbiased and more reliable. Permutation importance: shuffle each feature column and measure drop in validation performance โ€” model-agnostic and unbiased.
  • 10Class imbalance handling: Most real-world classification problems are imbalanced (fraud: 0.1%, churn: 5%, click: 1%). Never evaluate on accuracy alone โ€” a model predicting all negatives achieves 99.9% accuracy on 0.1% fraud data. Use AUC-ROC, Precision-Recall AUC, or F1. Oversampling: SMOTE (Synthetic Minority Over-sampling Technique) creates synthetic minority samples by interpolating between real minority samples in feature space โ€” better than simple replication. ADASYN (Adaptive Synthetic Sampling) generates more samples in harder-to-learn regions. Undersampling: random undersampling of the majority class (information loss); Tomek Links removes majority samples adjacent to minority samples (cleans decision boundary). Class weights: set class_weight='balanced' in sklearn or scale_pos_weight in XGBoost โ€” equivalent to reweighting the loss function without modifying the data. Threshold tuning: the default 0.5 decision threshold is wrong for imbalanced classes; find the threshold that maximizes F1 or satisfies your precision/recall requirement on the validation set.
  • 11Feature stores in production: A feature store is a centralized repository for computing, storing, and serving ML features consistently. The core problem it solves: training-serving skew โ€” if you compute features differently in the training pipeline (batch, using all historical data) vs the serving pipeline (real-time, per-request), model performance degrades. Components: offline store (Hive, Redshift, BigQuery) for training data retrieval, online store (Redis, DynamoDB, Cassandra) for low-latency serving (<10ms), feature registry (metadata: feature name, owner, computation logic, freshness SLA), feature pipelines (Spark/Flink jobs that compute features from raw events). Point-in-time correctness: when generating training data for a model that will predict at time T, all features must be computed using only data available before time T โ€” no future leakage. Examples: Feast (open-source), Tecton (enterprise), Hopsworks, Vertex AI Feature Store. At Amazon, the ML platform computes ~trillions of feature values per day for ranking models.

Key Formulas

01Target encoding with smoothing: ฮผ^c=ncyห‰c+kyห‰globalnc+k\hat{\mu}_c = \frac{n_c \bar{y}_c + k \bar{y}_{global}}{n_c + k} where kk controls regularization strength
02StandardScaler: xโ€ฒ=xโˆ’ฮผฯƒx' = \frac{x - \mu}{\sigma}, RobustScaler: xโ€ฒ=xโˆ’medianIQRx' = \frac{x - \text{median}}{\text{IQR}}
03Cyclical time encoding: xsinโก=sinโก(2ฯ€xT)x_{\sin} = \sin\left(\frac{2\pi x}{T}\right), xcosโก=cosโก(2ฯ€xT)x_{\cos} = \cos\left(\frac{2\pi x}{T}\right)
04Population Stability Index: PSI=โˆ‘i=1k(Aiโˆ’Ei)lnโก(AiEi)\text{PSI} = \sum_{i=1}^{k} (A_i - E_i) \ln\left(\frac{A_i}{E_i}\right) (PSI > 0.2 signals severe shift)
05SMOTE interpolation: xnew=xi+ฮป(xneighborโˆ’xi)x_{\text{new}} = x_i + \lambda (x_{\text{neighbor}} - x_i), ฮปโˆผUniform(0,1)\lambda \sim \text{Uniform}(0, 1)
06Mutual Information: MI(X;Y)=โˆ‘x,yP(x,y)logโกP(x,y)P(x)P(y)\text{MI}(X;Y) = \sum_{x,y} P(x,y) \log \frac{P(x,y)}{P(x)P(y)}
07MICE: iterates P(XjโˆฃXโˆ’j,y)P(X_j | X_{-j}, y) to impute column jj from all other columns; repeats until convergence
08Importance weighting for covariate shift: w(x)=Pnew(x)Pold(x)w(x) = \frac{P_{\text{new}}(x)}{P_{\text{old}}(x)}, estimated by training a classifier to distinguish old vs new data
python
1import numpy as np
2import pandas as pd
3from sklearn.pipeline import Pipeline
4from sklearn.compose import ColumnTransformer
5from sklearn.preprocessing import StandardScaler, OneHotEncoder, RobustScaler
6from sklearn.impute import SimpleImputer, KNNImputer
7from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
8from sklearn.feature_selection import mutual_info_classif
9from sklearn.model_selection import train_test_split, cross_val_score
10from sklearn.metrics import roc_auc_score, classification_report
11from imblearn.over_sampling import SMOTE
12from imblearn.pipeline import Pipeline as ImbPipeline
13import warnings
14warnings.filterwarnings('ignore')
15
16np.random.seed(42)
17
18# โ”€โ”€โ”€ 1. Simulate a realistic e-commerce churn dataset โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
19n = 10_000
20df = pd.DataFrame({
21 'age': np.random.normal(35, 12, n).clip(18, 80),
22 'days_since_purchase': np.random.exponential(30, n),
23 'n_purchases': np.random.poisson(5, n),
24 'avg_order_value': np.random.lognormal(4, 0.8, n), # heavy-tailed
25 'device_type': np.random.choice(['mobile', 'desktop', 'tablet'], n, p=[0.6, 0.3, 0.1]),
26 'country': np.random.choice(['US', 'UK', 'DE', 'FR', 'Other'], n, p=[0.5, 0.2, 0.1, 0.1, 0.1]),
27 'signup_month': np.random.randint(1, 13, n),
28 'last_activity_ts': pd.date_range('2023-01-01', periods=n, freq='1H'),
29})
30
31# Introduce missing values (MAR pattern: older users missing income)
32missing_mask_age = np.random.rand(n) < 0.15
33df.loc[missing_mask_age, 'age'] = np.nan
34missing_mask_aov = np.random.rand(n) < 0.10
35df.loc[missing_mask_aov, 'avg_order_value'] = np.nan
36
37# Binary target: churn (imbalanced: 8% positive rate)
38df['churned'] = (
39 (df['days_since_purchase'].fillna(99) > 60) &
40 (df['n_purchases'].fillna(0) < 3) &
41 (np.random.rand(n) < 0.3)
42).astype(int)
43print(f"Churn rate: {df['churned'].mean():.2%}") # ~8%
44
45# โ”€โ”€โ”€ 2. Feature engineering โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
46def engineer_features(df: pd.DataFrame) -> pd.DataFrame:
47 df = df.copy()
48
49 # Missingness indicators (before imputation)
50 df['age_missing'] = df['age'].isna().astype(int)
51 df['avg_order_value_missing'] = df['avg_order_value'].isna().astype(int)
52
53 # Date/time features
54 df['signup_month_sin'] = np.sin(2 * np.pi * df['signup_month'] / 12)
55 df['signup_month_cos'] = np.cos(2 * np.pi * df['signup_month'] / 12)
56
57 # Interaction: recency ร— frequency (RFM-style)
58 df['rfm_score'] = df['days_since_purchase'] / (df['n_purchases'] + 1)
59
60 # Log transform heavy-tailed feature
61 df['log_avg_order_value'] = np.log1p(df['avg_order_value'])
62
63 # Bin age into ordinal groups (after flagging missingness)
64 df['age_group'] = pd.cut(
65 df['age'].fillna(df['age'].median()),
66 bins=[0, 25, 35, 50, 100],
67 labels=['young', 'adult', 'middle', 'senior']
68 ).astype(str)
69
70 return df
71
72df = engineer_features(df)
73
74# โ”€โ”€โ”€ 3. Target encoding with smoothing (MUST be fit only on train) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
75def target_encode(train: pd.DataFrame, val: pd.DataFrame,
76 col: str, target: str, k: int = 10) -> tuple:
77 """
78 Smoothed target encoding โ€” prevents overfitting on small categories.
79 k controls the strength of regularization (higher k โ†’ more shrinkage).
80 """
81 global_mean = train[target].mean()
82 stats = train.groupby(col)[target].agg(['mean', 'count'])
83 stats['encoded'] = (stats['count'] * stats['mean'] + k * global_mean) / (stats['count'] + k)
84 mapping = stats['encoded'].to_dict()
85
86 train_encoded = train[col].map(mapping).fillna(global_mean)
87 val_encoded = val[col].map(mapping).fillna(global_mean)
88 return train_encoded, val_encoded
89
90# โ”€โ”€โ”€ 4. Train/test split โ€” BEFORE any fit on preprocessing โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
91feature_cols = [
92 'age', 'days_since_purchase', 'n_purchases', 'log_avg_order_value',
93 'device_type', 'country', 'age_missing', 'avg_order_value_missing',
94 'signup_month_sin', 'signup_month_cos', 'rfm_score', 'age_group',
95]
96X = df[feature_cols]
97y = df['churned']
98
99X_train, X_test, y_train, y_test = train_test_split(
100 X, y, test_size=0.2, random_state=42, stratify=y
101)
102
103# Apply target encoding (fit only on train)
104X_train = X_train.copy()
105X_test = X_test.copy()
106X_train['country_te'], X_test['country_te'] = target_encode(
107 X_train.assign(churned=y_train), X_test, 'country', 'churned'
108)
109
110# โ”€โ”€โ”€ 5. Sklearn Pipeline (prevents leakage from scaler/imputer) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
111numeric_features = ['age', 'days_since_purchase', 'n_purchases',
112 'log_avg_order_value', 'rfm_score', 'country_te',
113 'signup_month_sin', 'signup_month_cos',
114 'age_missing', 'avg_order_value_missing']
115categorical_features = ['device_type', 'age_group']
116
117numeric_transformer = Pipeline([
118 ('imputer', SimpleImputer(strategy='median')), # fit only on train
119 ('scaler', RobustScaler()), # fit only on train
120])
121categorical_transformer = Pipeline([
122 ('imputer', SimpleImputer(strategy='most_frequent')),
123 ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False)),
124])
125preprocessor = ColumnTransformer([
126 ('num', numeric_transformer, numeric_features),
127 ('cat', categorical_transformer, categorical_features),
128])
129
130# โ”€โ”€โ”€ 6. Handle class imbalance with SMOTE inside pipeline โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
131# ImbPipeline from imbalanced-learn applies SMOTE only during fit, not predict
132pipeline = ImbPipeline([
133 ('preprocessor', preprocessor),
134 ('smote', SMOTE(random_state=42, k_neighbors=5)),
135 ('clf', GradientBoostingClassifier(n_estimators=200, learning_rate=0.05,
136 max_depth=4, random_state=42)),
137])
138
139pipeline.fit(X_train, y_train)
140y_pred_proba = pipeline.predict_proba(X_test)[:, 1]
141auc = roc_auc_score(y_test, y_pred_proba)
142print(f"\nTest AUC-ROC: {auc:.4f}")
143
144# Tune threshold for F1 (default 0.5 is wrong for imbalanced data)
145from sklearn.metrics import f1_score
146thresholds = np.linspace(0.1, 0.9, 81)
147f1_scores = [f1_score(y_test, y_pred_proba >= t) for t in thresholds]
148best_thresh = thresholds[np.argmax(f1_scores)]
149print(f"Optimal threshold: {best_thresh:.2f} (default 0.5 F1: {f1_score(y_test, y_pred_proba >= 0.5):.3f})")
150print(f"Best threshold F1: {max(f1_scores):.3f}")
151
152# โ”€โ”€โ”€ 7. Feature selection via mutual information โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
153X_train_transformed = preprocessor.fit_transform(X_train, y_train)
154mi_scores = mutual_info_classif(X_train_transformed, y_train, random_state=42)
155top_idx = np.argsort(mi_scores)[::-1][:5]
156print(f"\nTop features by Mutual Information:")
157feature_names = (numeric_features +
158 list(preprocessor.named_transformers_['cat']
159 .named_steps['onehot'].get_feature_names_out(categorical_features)))
160for i in top_idx:
161 print(f" {feature_names[i]:35s} MI = {mi_scores[i]:.4f}")
162
163# โ”€โ”€โ”€ 8. Distribution shift detection (PSI) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
164def compute_psi(expected: np.ndarray, actual: np.ndarray, buckets: int = 10) -> float:
165 """Population Stability Index. PSI > 0.2 indicates severe shift."""
166 breakpoints = np.percentile(expected, np.linspace(0, 100, buckets + 1))
167 breakpoints[0] = -np.inf
168 breakpoints[-1] = np.inf
169
170 expected_counts = np.histogram(expected, bins=breakpoints)[0] / len(expected)
171 actual_counts = np.histogram(actual, bins=breakpoints)[0] / len(actual)
172
173 # Avoid log(0)
174 expected_counts = np.clip(expected_counts, 1e-6, None)
175 actual_counts = np.clip(actual_counts, 1e-6, None)
176
177 psi = np.sum((actual_counts - expected_counts) * np.log(actual_counts / expected_counts))
178 return psi
179
180# Simulate production drift: users get older on average
181production_age = np.random.normal(42, 12, 5000).clip(18, 80) # drifted distribution
182train_age = df.loc[X_train.index, 'age'].dropna().values
183
184psi = compute_psi(train_age, production_age)
185print(f"\nAge PSI (train vs production): {psi:.3f} ({'DRIFT DETECTED' if psi > 0.2 else 'stable'})")

This pipeline covers the full feature engineering lifecycle: (1) missingness indicators added before imputation so models can learn that missingness is informative; (2) cyclical encoding for month features; (3) RFM interaction feature; (4) smoothed target encoding fit only on train to prevent leakage; (5) scikit-learn Pipeline ensures scalers/imputers are fit only on train data; (6) SMOTE applied inside the pipeline (only during training, never on test); (7) threshold tuning for imbalanced classes; (8) PSI computation for production distribution shift monitoring.

Worked Interview Problems

3 problems

Problem

A data scientist at Meta built a churn model with 0.97 AUC in cross-validation but only 0.61 AUC in production. The features include: user_id, days_active_last_30, n_posts_last_7d, support_tickets_filed, account_deleted_date, and plan_type. Identify the leakage and fix it.

Solution

1

Identify the leaky features: 'account_deleted_date' is directly derived from whether the user churned (deleted their account). If a user has a non-null account_deleted_date, they definitely churned โ€” this is pure target leakage. The model is essentially memorizing the target.

2

Check 'support_tickets_filed': If this count includes tickets filed after the churn event, it's also leaky. A user who churned might file a ticket during the exit process. Need to verify: is this the count as of the prediction date, or the total count ever?

3

Check cross-validation procedure: If StandardScaler or any other preprocessing was fit on the full dataset before cross-validation, there's train-test contamination. Fix: wrap everything in a Pipeline so preprocessing is re-fit on each CV fold's training data only.

4

Check the temporal split: For churn, you must use time-based splitting. If data is split randomly, you might train on users who churned in March and predict users who churned in January โ€” the model can learn temporal patterns that are actually future information.

5

Fix: (1) Drop 'account_deleted_date'. (2) Recompute 'support_tickets_filed' as of the prediction date only. (3) Wrap all preprocessing in Pipeline. (4) Use TimeSeriesSplit for cross-validation. (5) Re-run experiment and expect AUC to drop from 0.97 to a realistic 0.70-0.80.

Answer

Root cause: 'account_deleted_date' directly encodes the target (a non-null value means churned = True). Secondary leakage: support tickets counted after churn event. Tertiary: preprocessing fit on full dataset before CV. Fix: remove leaky features, use Pipeline for preprocessing, and TimeSeriesSplit for evaluation. The realistic AUC should drop substantially โ€” a 0.97 AUC on churn data is always a red flag.

Common Mistakes

!

Fitting the scaler/imputer on the full dataset (including test set) before the train/test split. This is the most common leakage bug in take-home assignments. Fix: always use a Pipeline where all transformers are fit only in Pipeline.fit(), then applied in Pipeline.transform().

!

Using target encoding without regularization on small categories. A category with 3 observations will have a perfect mean target that overfits completely. Always use smoothed target encoding or leave-one-out encoding, and validate that small-category encodings are reasonable.

!

Applying SMOTE or any oversampling before the train/test split. SMOTE generates synthetic samples by interpolating between real minority samples โ€” if you oversample before splitting, synthetic samples derived from test-set minority samples will appear in training, leaking test information.

!

Using accuracy as the evaluation metric for imbalanced classification (99.9% accuracy by always predicting the majority class). Use AUC-ROC for ranking quality, Precision-Recall AUC for absolute class imbalance (very rare positive class), and F1 for a single-threshold summary. Always report both metrics.

!

Forgetting to apply the same feature engineering logic to serving data. Training pipeline computes 'log1p(avg_order_value)' but the serving pipeline forgets the log transform โ€” this creates training-serving skew. The solution is a feature store or ensuring the same code path generates features for both training and serving.

Only works in the Electron app

<webview> is an Electron-only tag. Run npm run electron:dev to use this.

25:00Focus
0

25 min focus ยท 5 min break ยท long break every 4 sessions

The Ultimate Grind