EDA & Feature Engineering
Interrogate a dataset before modeling — distributions, missing values, outliers, target balance — then craft features that make models smarter.
Models are only as good as the data you feed them, and most real-world accuracy gains come from understanding and reshaping that data — not from fancier algorithms. This lesson covers the two crafts that dominate working data science: exploratory data analysis (EDA), where you interrogate the data before modeling, and feature engineering, where you turn raw columns into signals a model can actually use.
What EDA is looking for
EDA isn't aimless plotting — it's a checklist of questions, each of which changes what you do next:
- What's the target, and is it balanced? A 95/5 class split changes your metrics and your baseline (next lesson digs into this).
- What type is each feature? Numeric, categorical, ordinal, datetime, free text — each needs different preprocessing.
- How much is missing, and where? A column that's 80% empty is probably a drop; a column missing 2% is an impute.
- What do the distributions look like? Skewed features (like incomes or fares) may benefit from a log transform or binning.
- Any outliers? Are they data-entry errors or real (and important) rare events?
- Which features relate to the target? Group means, correlations, and simple plots reveal which columns carry signal.
Let's run a compact EDA on a synthetic passenger dataset — a small ship manifest with survival labels, in the spirit of the famous Titanic problem:
Five minutes of EDA already wrote our modeling plan: the target is
imbalanced (roughly one third survived), age needs imputation, fare is
heavily right-skewed (compare its mean to its median), and sex and
pclass clearly carry signal. On larger datasets you'd add histograms
(df["fare"].hist()), correlation matrices (df.corr(numeric_only=True)),
and count plots per category — same questions, more pictures.
Handling missing values
Two families of fixes:
- Drop. Remove rows (
df.dropna()) when very few are affected, or drop a whole column when most of it is missing — there's little left to learn from. Always drop rows whose target is missing. - Impute. Fill numeric gaps with the median (robust to outliers) or mean;
fill categorical gaps with the most frequent value. In scikit-learn that's
SimpleImputer, which you met inside pipelines — and the pipeline is exactly where imputation belongs, so its statistics come from training folds only.
A third, underrated option: add a boolean age_missing indicator column.
Sometimes the fact that a value is missing is itself predictive.
Encoding categorical features
Models compute with numbers, so categories must be encoded:
- One-hot encoding (
OneHotEncoder, orpd.get_dummiesfor quick exploration) creates one 0/1 column per category. Right choice for nominal categories with no order: port, city, color. - Ordinal encoding (
OrdinalEncoder) maps categories to integers. Only right when the order is real — small, medium, large — because models will treat the numbers as ordered and evenly spaced.
Beware one-hot exploding on high-cardinality columns (thousands of zip
codes); grouping rare categories into an "other" bucket is a simple,
effective fix.
Creating new features
This is where domain thinking beats algorithms. Classic moves:
- Boolean flags —
is_alonefrom a family count. - Ratios and combinations — fare per family member; total family size from siblings plus parents.
- Binning — turn numeric
ageinto categories like child / teen / adult, which can capture non-linear effects and tame outliers. - Datetime parts — from a timestamp, extract hour, weekday, month; "purchases spike on weekends" is invisible to a raw timestamp.
- Text extraction — pull a title like "Mr" or "Dr" out of a name string.
After creating features, always sanity-check them against the target the way
we did with groupby — a new feature that doesn't separate the target at all
is probably not pulling its weight.
Scaling, one more time
Recap from the KNN lesson: distance- and gradient-based models (KNN, SVMs,
linear models with regularization, neural networks) need features on
comparable scales — StandardScaler or MinMaxScaler inside your pipeline.
Tree-based models split one feature at a time and don't care about scale.
When in doubt, scale: it never hurts, and forgetting it can quietly cripple
half your model zoo.
Feature engineering can leak too
Any transformation whose parameters are computed from data — imputation statistics, scaling means, bin edges chosen from quantiles, target-based encodings — must be fit on the training split only. Even EDA can leak in a subtle way: if you choose features by studying the full dataset, the test set has quietly influenced your decisions. Do exploratory work on the training split, and let pipelines handle the mechanics.
Check your understanding
Q1.A column is missing 85% of its values. What's usually the best first move?
Q2.Why is one-hot encoding preferred over ordinal encoding for a "port of embarkation" column?
Q3.The mean fare is 45 but the median is 18. What does this tell you?
Q4.Which of these is an example of data leakage during feature engineering?
Exercise: Engineer and evaluate two new features
Regenerate the synthetic passenger dataset and engineer two new features:
is_child (1 when age is below 12) and family_bucket (bin the family
count into alone, small for 1–2, and large for 3+ using pd.cut).
Print the survival rate for each group of both features, plus the group
sizes. Do the new features separate the target — and are any groups too
small to trust?
Next up: before celebrating any model's score, you need something to compare it against — baselines and benchmarks.