Skip to content
  • Feature Engineering & Selection · Machine Learning

Feature Engineering & Selection

LearnViaNotes @learnvianotes· 3 min read

Feature engineering creates inputs that make the pattern easier to learn; feature selection removes inputs that add noise, cost or leakage. On tabular data, good features usually beat a fancier algorithm.

Engineering ideas

IdeaExample
Ratios & differencesprice_per_item = total / items, debt_to_income
Aggregates over historyPurchases in the last 7 / 30 days, average basket size per user
Date & time partsHour, weekday, month, is_weekend, days since signup
Cyclical encodingsin(2π·hour/24), cos(2π·hour/24) so 23:00 is next to 00:00
BinningAge bands — can help linear models capture non-linear effects
Interactions & polynomialsarea × quality, x² for linear models (PolynomialFeatures)
TextLength, keyword flags, TF-IDF (see Data Science)
Domain knowledgeBMI from height and weight; distance from coordinates
Python
import numpy as np
import pandas as pd

df = pd.DataFrame({
    "ts": pd.to_datetime(["2024-01-05 09:15", "2024-01-06 23:40", "2024-01-08 00:20"]),
    "total": [120.0, 80.0, 300.0],
    "items": [4, 1, 6],
})
df["hour"] = df["ts"].dt.hour
df["is_weekend"] = df["ts"].dt.dayofweek >= 5          # Monday = 0 … Sunday = 6
df["price_per_item"] = df["total"] / df["items"]
print(df[["hour", "is_weekend", "price_per_item"]].values.tolist())   # [[9, False, 30.0], [23, True, 80.0], [0, False, 50.0]]

angle = 2 * np.pi * df["hour"] / 24
pts = np.c_[np.sin(angle), np.cos(angle)]
print(abs(23 - 0), round(np.linalg.norm(pts[1] - pts[2]), 2))   # 23 0.26 ← raw gap vs cyclical distance

Selecting features

FamilyHow it worksscikit-learnTrade-off
FilterScore each feature against the target, independently of any modelVarianceThreshold, SelectKBest(f_classif / chi2 / mutual_info_classif)Fast; misses interactions
WrapperSearch subsets by repeatedly training a modelRFE, RFECV, SequentialFeatureSelectorModel-aware; expensive, can overfit the selection
EmbeddedSelection happens during trainingL1 (Lasso) coefficients, tree importances via SelectFromModelCheap; tied to one model's view
Python
import numpy as np
from sklearn.feature_selection import RFE, SelectFromModel, SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression

rng = np.random.default_rng(0)
X = rng.normal(size=(400, 10))                                    # 10 features …
y = (2 * X[:, 0] - 1.5 * X[:, 1] + X[:, 2] + rng.normal(0, 0.5, 400) > 0).astype(int)   # … only 0, 1, 2 matter

print(SelectKBest(f_classif, k=3).fit(X, y).get_support(indices=True))                     # [0 1 2] ← filter
print(RFE(LogisticRegression(), n_features_to_select=3).fit(X, y).get_support(indices=True))    # [0 1 2] ← wrapper
l1 = LogisticRegression(l1_ratio=1, solver="liblinear", C=0.05)                            # L1 penalty
print(SelectFromModel(l1).fit(X, y).get_support(indices=True))                            # [0 1 2] ← embedded

Why bother selecting?

  • Fewer irrelevant features → less overfitting, especially with few rows (see Curse of Dimensionality in the k-NN topic).

  • Faster training and prediction; fewer data pipelines to maintain in production.

  • Easier explanations.

  • Removing features that are available in training but not at prediction time prevents leakage.

Filter vs wrapper vs embedded feature selection?

How would you encode the hour of day?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report