Le feature engineering (ingénierie des variables) consiste à transformer les données brutes pour rendre l’information plus facile à exploiter par le modèle. Sur des données tabulaires, c’est souvent là que se joue l’essentiel de la performance. L’intérêt d’une transformation dépend du modèle : un modèle linéaire en profite beaucoup, un arbre de décision beaucoup moins, car il est insensible à toute transformation monotone (qui conserve l’ordre des valeurs).
Corriger l’asymétrie
Revenus, montants d’achat, nombres de visites : ces variables ont souvent une longue traîne à droite. Quelques très grandes valeurs écrasent les autres et tirent les coefficients d’un modèle linéaire.
import numpy as np
import pandas as pd
rng = np.random.default_rng(0)
revenu = pd.Series(rng.lognormal(mean=10, sigma=1, size=1000))
print(revenu.skew()) # environ 2.8 : forte asymétrie
print(np.log1p(revenu).skew()) # proche de 0 après transformation
np.log1p(x) calcule log(1 + x) et accepte les zéros. Dans un pipeline, on l’encapsule dans un FunctionTransformer(np.log1p). Pour une transformation apprise automatiquement :
PowerTransformer(method="yeo-johnson")(défaut) cherche la puissance qui rend la distribution la plus gaussienne possible, valeurs négatives comprises ;method="box-cox"exige des valeurs strictement positives ;QuantileTransformer(output_distribution="normal")force une distribution cible à partir des rangs, très robuste aux valeurs extrêmes.
Si c’est la cible de régression qui est asymétrique, utilisez TransformedTargetRegressor(regressor=..., func=np.log1p, inverse_func=np.expm1) : le modèle apprend sur le log, et les prédictions sont automatiquement ramenées à l’échelle d’origine.
Discrétiser
KBinsDiscretizer découpe une variable continue en intervalles :
from sklearn.preprocessing import KBinsDiscretizer
kb = KBinsDiscretizer(n_bins=4, encode="ordinal", strategy="quantile")
tranche = kb.fit_transform(revenu.to_frame()) # 0, 1, 2 ou 3 (quartiles)
print(kb.bin_edges_[0]) # bornes apprises sur les données
strategy="quantile" crée des tranches de même effectif, "uniform" des tranches de même largeur. Avec encode="onehot" (défaut), chaque tranche devient une colonne binaire, ce qui permet à un modèle linéaire d’apprendre un effet non linéaire par paliers. La discrétisation fait perdre de l’information fine : utilisez-la quand l’effet est réellement par seuils ou pour la lisibilité (tranches d’âge).
Interactions et polynômes
Un modèle linéaire additionne les effets. Si l’effet d’une variable dépend d’une autre (par exemple surface × quartier), il faut lui fournir le produit :
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_val_score
X = rng.uniform(-2, 2, size=(400, 2))
y = 3 * X[:, 0] * X[:, 1] + X[:, 0] + rng.normal(0, 0.3, 400) # interaction pure
lineaire = LinearRegression()
poly = make_pipeline(PolynomialFeatures(degree=2, include_bias=False), LinearRegression())
print(cross_val_score(lineaire, X, y, cv=5).mean()) # environ 0.05
print(cross_val_score(poly, X, y, cv=5).mean()) # environ 0.99
print(poly.fit(X, y)[0].get_feature_names_out(["a", "b"])) # ['a' 'b' 'a^2' 'a b' 'b^2']
Attention à l’explosion combinatoire : avec 50 features et degree=2, on obtient 1 325 colonnes. Utilisez interaction_only=True pour ne garder que les produits, combinez avec une régularisation (Ridge) et standardisez ensuite. SplineTransformer est une alternative souvent plus stable aux polynômes pour modéliser des courbes.
Exercices
Les solutions et les tests restent sur le serveur. Dans l'espace étudiant, ton code est exécuté et corrigé, et le tuteur donne des indices avant la solution.
Réduire l'asymétrie
Calculez l’asymétrie (
.skew()) de la Seriesmontantsdansskew_avant, puis appliqueznp.log1pet stockez la Series transformée dansmontants_loget son asymétrie dansskew_apres.import numpy as np import pandas as pd rng = np.random.default_rng(42) montants = pd.Series(rng.lognormal(mean=4, sigma=1.2, size=2000)) skew_avant = ... montants_log = ... skew_apres = ...Capturer une interaction
La cible dépend du produit des deux features. Construisez
poly, un pipelinePolynomialFeatures(degree=2, include_bias=False)+LinearRegression(), puis stockez les R² moyens de validation croisée (cv=5) d’uneLinearRegression()seule dansr2_linet depolydansr2_poly.import numpy as np from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.pipeline import make_pipeline from sklearn.model_selection import cross_val_score rng = np.random.default_rng(0) X = rng.uniform(-2, 2, size=(400, 2)) y = 3 * X[:, 0] * X[:, 1] + X[:, 0] + rng.normal(0, 0.3, 400) poly = ... r2_lin = ... r2_poly = ...Transformation et arbres
Appliquer
np.log1pà une feature change-t-il les prédictions d’un arbre de décision ?- Oui, fortement
- Non, ou presque pas : la transformation est monotone et l'arbre ne dépend que de l'ordre des valeurs
- Oui, l'arbre exige des données gaussiennes
- L'arbre refuse les valeurs transformées
Quiz de fin de leçon
Pourquoi préférer np.log1p à np.log ?
- Il est plus rapide
- Il gère les valeurs nulles (log(1 + 0) = 0)
- Il ne change pas les données
- Il standardise
Quel est le principal risque de PolynomialFeatures(degree=3) sur 40 features ?
- Aucun
- L'explosion du nombre de colonnes et le surapprentissage
- La perte des valeurs manquantes
- La suppression des interactions
Que fait TransformedTargetRegressor ?
- Transforme les features
- Entraîne le modèle sur une cible transformée et ramène les prédictions à l'échelle d'origine
- Sélectionne des features
- Discrétise la cible
La suite du cours
- Encoder les variables catégorielles15 min
- Traiter les valeurs manquantes15 min
- ColumnTransformer : des pipelines pour données hétérogènes20 min
- Créer des features métier et temporelles15 min
- Sélectionner les features utiles15 min