💻 Développement

dev-feature-engineering-guide

Techniques de feature engineering pour améliorer les modèles ML.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-feature-engineering-guide --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-feature-engineering-guide -Launch"

🚀 Déjà installé ?

claude "/dev-feature-engineering-guide"

Ou tapez /dev-feature-engineering-guide dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

feature engineeringfeaturestransformation de donnéesencodingnormalisationfeature selectionfeature store

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-feature-engineering-guide ~/.claude/skills/

Payload du plugin : skills/dev-feature-engineering-guide · source éditable : dev-skills/feature-engineering-guide

📖 Manuel

Feature Engineering Guide

Workflow en 8 étapes

1. Exploration et audit des données

import pandas as pd
import numpy as np

df.info()                              # types, nulls par colonne
df.describe(include="all")            # stats descriptives
df.isnull().mean().sort_values()      # % manquants
df.select_dtypes("number").skew()     # skewness
df.duplicated().sum()                 # doublons

# Corrélations
corr = df.select_dtypes("number").corr("spearman")
# Outliers par IQR
Q1, Q3 = df["col"].quantile([0.25, 0.75])
iqr = Q3 - Q1
outliers = df[(df["col"] < Q1 - 1.5*iqr) | (df["col"] > Q3 + 1.5*iqr)]

Critère de décision : Si une colonne dépasse 60 % de valeurs manquantes → supprimer (sauf raison métier forte). Entre 5 % et 60 % → imputer.


2. Nettoyage et imputation

from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.pipeline import Pipeline

# Imputation simple (train-only fit, jamais sur le test !)
imp_median = SimpleImputer(strategy="median")
X_train["col"] = imp_median.fit_transform(X_train[["col"]])
X_test["col"]  = imp_median.transform(X_test[["col"]])

# Imputation avancée (MICE via IterativeImputer)
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
imp_mice = IterativeImputer(max_iter=10, random_state=0)

# Winsorisation (capping à 1er/99e percentile)
from scipy.stats.mstats import winsorize
df["col"] = winsorize(df["col"], limits=[0.01, 0.01])

Choix d'imputation :

Mécanisme de manqueMéthode recommandée
MCAR (aléatoire)Médiane / mode
MAR (conditionnel)KNN, MICE
MNAR (non-aléatoire)Indicateur binaire + imputation

3. Encoding catégoriel

import pandas as pd
from sklearn.preprocessing import OrdinalEncoder, TargetEncoder
from sklearn.preprocessing import OneHotEncoder

# One-Hot (cardinalité < 15, modèles linéaires)
pd.get_dummies(df, columns=["ville"], drop_first=True)

# Ordinal (catégories avec ordre naturel)
oe = OrdinalEncoder(categories=[["bas","moyen","haut"]])

# Target encoding (haute cardinalité, ex : code postal)
# ⚠ Toujours avec cross-validation pour éviter le leakage
te = TargetEncoder(smooth="auto", cv=5)

# Fréquence encoding (cardinalité très élevée, sans leakage)
freq = df["col"].value_counts(normalize=True)
df["col_freq"] = df["col"].map(freq)

Règle cardinale : Un encoder appris (TargetEncoder, scaler) doit être .fit() uniquement sur X_train.


4. Transformations numériques

from sklearn.preprocessing import StandardScaler, RobustScaler, MinMaxScaler
import numpy as np

# Log pour distributions skewed (skew > 1)
df["col_log"] = np.log1p(df["col"])          # log1p gère les 0

# Box-Cox / Yeo-Johnson (automatique)
from sklearn.preprocessing import PowerTransformer
pt = PowerTransformer(method="yeo-johnson")  # gère les valeurs négatives

# Binning (discrétisation en quantiles)
df["col_bin"] = pd.qcut(df["col"], q=10, labels=False)

# Interactions polynomiales (modèles linéaires)
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)

Quel scaler choisir ?


5. Features temporelles

df["dt"] = pd.to_datetime(df["date"])
df["heure"]    = df["dt"].dt.hour
df["dow"]      = df["dt"].dt.dayofweek
df["mois"]     = df["dt"].dt.month
df["is_weekend"] = df["dow"].isin([5, 6]).astype(int)

# Encodage cyclique (évite la discontinuité 23h→0h)
df["heure_sin"] = np.sin(2 * np.pi * df["heure"] / 24)
df["heure_cos"] = np.cos(2 * np.pi * df["heure"] / 24)

# Lag features (séries temporelles)
df["lag_1"]  = df["valeur"].shift(1)
df["lag_7"]  = df["valeur"].shift(7)

# Rolling stats
df["roll_mean_7"] = df["valeur"].rolling(7).mean()
df["roll_std_7"]  = df["valeur"].rolling(7).std()

6. Features textuelles

from sklearn.feature_extraction.text import TfidfVectorizer

# TF-IDF (modèles classiques)
tfidf = TfidfVectorizer(max_features=5000, ngram_range=(1, 2))
X_tfidf = tfidf.fit_transform(train_texts)

# Sentence embeddings (BERT, modèles modernes)
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("all-MiniLM-L6-v2")
embeddings = model.encode(texts, batch_size=64)

# Features statistiques textuelles (signal faible mais rapide)
df["nb_mots"]     = df["texte"].str.split().str.len()
df["nb_chars"]    = df["texte"].str.len()
df["ratio_upper"] = df["texte"].apply(lambda x: sum(c.isupper() for c in x) / max(len(x), 1))

7. Feature selection

from sklearn.feature_selection import mutual_info_classif, RFE
from sklearn.ensemble import RandomForestClassifier
import shap

# Supprimer les features quasi-constantes
from sklearn.feature_selection import VarianceThreshold
vt = VarianceThreshold(threshold=0.01)

# Supprimer les features très corrélées (> 0.95)
corr_matrix = df.corr().abs()
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))
to_drop = [col for col in upper.columns if any(upper[col] > 0.95)]

# Mutual information (non-linéaire)
mi = mutual_info_classif(X_train, y_train)
top_features = pd.Series(mi, index=X_train.columns).nlargest(30)

# SHAP values (interprétable + sélection)
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
shap.summary_plot(shap_values, X_test)

8. Pipeline et Feature Store

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer

num_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])
cat_pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("encoder", OneHotEncoder(handle_unknown="ignore")),
])

preprocessor = ColumnTransformer([
    ("num", num_pipe, num_cols),
    ("cat", cat_pipe, cat_cols),
])

full_pipeline = Pipeline([
    ("preprocessor", preprocessor),
    ("model", RandomForestClassifier()),
])
full_pipeline.fit(X_train, y_train)

Feature store : Pour les équipes multi-projets, utiliser Feast (open-source) ou Hopsworks. Distinguer :


Anti-patterns et pièges

PiègeSymptômeCorrection
Data leakageScore parfait en validation, effondrement en prodFit les transformers uniquement sur train
Target encoding sans CVOverfitting cachéUtiliser TargetEncoder(cv=5) ou leave-one-out
Normaliser des arbresPerte de temps, pas d'impactSupprimer scalers pour XGBoost/LightGBM
Trop de featuresCurse of dimensionality, lenteurSélectionner < 50–100 features significatives
Log sur valeurs négativesNaN silencieuxUtiliser log1p ou Yeo-Johnson
Rolling sans groupbyFuite entre séries d'entités différentesToujours df.groupby("entity_id")["val"].rolling(n)
One-hot sur cardinalité > 50Explosion de dimensionsTarget encoding ou embeddings

Bonnes pratiques 2026