Build it in sklearn
numeric_pipe = Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
])
categorical_pipe = Pipeline([
("impute", SimpleImputer(strategy="most_frequent")),
("encode", OneHotEncoder(handle_unknown="ignore")),unseen city gives zeros
])
preprocessor = ColumnTransformer([
("num", numeric_pipe, ["age", "income", "score"]),
("cat", categorical_pipe, ["city", "gender", "plan"]),
])
full_pipeline = Pipeline([("preprocess", preprocessor),
("model", GradientBoostingClassifier())])
Sources
Original source
---
id: feature-engineering
title: Feature Engineering and Pipelines
code: FE
subtitle: Impute, scale, encode, select and ensemble inside one leak-free pipeline. Python from the lessons.
hue: 30
sources: phases/02-ml-fundamentals/08-feature-engineering, phases/02-ml-fundamentals/11-ensemble-methods, phases/02-ml-fundamentals/13-ml-pipelines, phases/02-ml-fundamentals/18-feature-selection
source_hash: 25a47477c7c00c8d
---
## One pipeline for mixed columns
type: architecture
from: phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md
```arch
node | raw | 1,1 | Training data | X_train only
node | num | 2,1 | numeric_pipe | median impute, scale
node | cat | 2,2 | categorical_pipe | most_frequent, one-hot
node | pre | 3,1 | ColumnTransformer | preprocess step
node | model | 4,1 | Model | GradientBoostingClassifier
node | pred | 4,2 | Prediction | transform, then predict
edge | raw | num | numeric cols
edge | raw | cat | categorical cols
edge | num | pre |
edge | cat | pre |
edge | pre | model | fit
edge | model | pred |
```
## Build it in sklearn
type: annotated-code
lang: python
from: phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md
```python
numeric_pipe = Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
])
categorical_pipe = Pipeline([
("impute", SimpleImputer(strategy="most_frequent")),
("encode", OneHotEncoder(handle_unknown="ignore")), # unseen city gives zeros
])
preprocessor = ColumnTransformer([
("num", numeric_pipe, ["age", "income", "score"]),
("cat", categorical_pipe, ["city", "gender", "plan"]),
])
full_pipeline = Pipeline([("preprocess", preprocessor),
("model", GradientBoostingClassifier())])
```
## What fit and predict do
type: flow
from: phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md, phases/02-ml-fundamentals/13-ml-pipelines/code/pipeline.py
1. **Split first**: `X_train, X_test = X[:800], X[800:]` before any fit.
2. **pipe.fit**: each transformer calls `fit_transform` on X_train.
3. **pipe.fit**: the last step calls `fit` on the transformed X_train.
4. **pipe.predict**: each transformer calls `transform`, not `fit_transform`.
5. **pipe.predict**: the model calls `predict` on the transformed X_test.
6. **Cross-validation**: each fold fits the preprocessor on its own training split.
## Transform and encode tasks
type: task-table
from: phases/02-ml-fundamentals/08-feature-engineering/code/features.py
| Task | Code |
|---|---|
| Scale to [0, 1] | `min_max_scale(values)` |
| Mean 0, std 1 | `standardize(values)` |
| Compress right skew | `log_transform(values)` uses log(v + 1) |
| Equal-width bins | `bin_values(values, n_bins=5)` |
| Squares and pairs | `polynomial_features(row, degree=2)` |
| Smoothed target mean | `target_encode(feature_values, target_values, smoothing=10)` |
| Flag holes first | `add_missing_indicator(values)` |
| Text weights | `tfidf(documents)` returns vectors, vocab |
## Feature selection tasks
type: task-table
from: phases/02-ml-fundamentals/18-feature-selection/code/feature_selection.py, phases/02-ml-fundamentals/18-feature-selection/docs/en.md
| Task | Code |
|---|---|
| Drop near-constant columns | `variance_threshold(X, threshold=0.01)` |
| Rank by MI | `mutual_information(X, y, n_bins=10)` |
| Prune by weight | `rfe(X, y, n_features_to_select=5)` |
| Zero weights with L1 | `l1_feature_selection(X, y, alpha=0.1)` |
| Split-gain importance | `tree_importance(X, y, n_trees=50)` |
| sklearn RFE | `RFE(LogisticRegression(), n_features_to_select=10)` |
| sklearn L1 | `SelectFromModel(Lasso(alpha=0.01))` |
| sklearn trees | `rf.feature_importances_` |
## Ensembles: use X when
type: command-table
from: phases/02-ml-fundamentals/11-ensemble-methods/docs/en.md
| Command | What it does |
|---|---|
| Bagging / Random Forest | Cuts variance. Noisy data, many features |
| AdaBoost | Cuts bias. Clean data. Hurt by outliers |
| Gradient Boosting | Cuts bias. Tabular data. Easy to overfit |
| XGBoost / LightGBM | Both. Start here for production tabular ML |
| Stacking | Both. Last 1-2% accuracy. Use CV meta-features |
| Soft voting | Average probabilities of diverse models |
## Boosting loops
type: annotated-code
lang: python
from: phases/02-ml-fundamentals/11-ensemble-methods/docs/en.md, phases/02-ml-fundamentals/11-ensemble-methods/code/ensembles.py
```python
weights = np.full(n, 1 / n) # AdaBoost starts uniform
err = np.clip(err, 1e-10, 1 - 1e-10) # weighted stump error
alpha = 0.5 * np.log((1 - err) / err) # low error, high vote
weights *= np.exp(-alpha * y * pred) # misses gain weight
weights /= weights.sum()
self.initial_pred = np.mean(y) # gradient boosting start
residuals = y - current_pred # squared error gradient
current_pred += self.lr * update # shrink each tree
```
## Defaults and numbers
type: defaults
from: phases/02-ml-fundamentals/08-feature-engineering/code/features.py, phases/02-ml-fundamentals/11-ensemble-methods/docs/en.md, phases/02-ml-fundamentals/11-ensemble-methods/code/ensembles.py
| Setting | Default | Note |
|---|---|---|
| Target encoding `smoothing` | 10 | Pulls rare categories to the global mean |
| `remove_correlated` threshold | 0.9 | Drops the second column of a pair |
| `AdaBoostScratch` `n_estimators` | 50 | Decision stumps |
| `GradientBoostingScratch` | 100 | learning_rate=0.1, max_depth=3 |
| `BaggingClassifier` | 20 | n_estimators=20, max_depth=5 |
| Learning rate range | 0.01 to 0.3 | Smaller needs more trees |
| Bootstrap unique rows | 63.2% | 36.8% out-of-bag for validation |
| Forest split features | `sqrt(n_features)` | `n_features / 3` for regression |
## Pipeline failures
type: troubleshooting
from: phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md, phases/02-ml-fundamentals/08-feature-engineering/docs/en.md, phases/02-ml-fundamentals/18-feature-selection/docs/en.md, phases/02-ml-fundamentals/11-ensemble-methods/docs/en.md
| Symptom | Cause | Fix |
|---|---|---|
| Test score too high | Scaler fit before the split | Use Pipeline with `cross_val_score` |
| Train and serve outputs differ | Transforms outside the pipeline | One Pipeline object for both |
| Crash on a new city | Encoder never saw the value | `OneHotEncoder(handle_unknown="ignore")` |
| Breaks on schema change | Hardcoded column names | Column name lists from config |
| Random ID ranks high | Tree importance cardinality bias | Check with permutation importance |
| Target encoding overfits | Encoded on test rows | Fit on train data only, smooth |
| Stacking leaks | Meta-features from fitted data | Generate them with cross-validation |
## Formulas
type: concepts
from: phases/02-ml-fundamentals/08-feature-engineering/docs/en.md, phases/02-ml-fundamentals/18-feature-selection/docs/en.md, phases/02-ml-fundamentals/11-ensemble-methods/docs/en.md
- **TF-IDF**: $\frac{\text{count in doc}}{\text{words in doc}} \cdot \log \frac{\text{docs}}{\text{docs with word}}$
- **Mutual information**: $I(X; Y) = \sum_x \sum_y p(x, y) \log \frac{p(x, y)}{p(x) p(y)}$, zero when independent.
- **L1 loss**: prediction error plus $\alpha \sum \lvert w_i \rvert$. Higher alpha zeros more weights.
- **Majority vote**: 21 classifiers at 60% give about 74%. 101 give 84%.
- **RFE cost**: $N - \text{target}$ trainings. 500 features to 10 is 490 runs.
- **MI bins**: $\sqrt{n}$ bins or Sturges' rule $1 + \log_2 n$.
## Questions
type: qa
from: phases/02-ml-fundamentals/08-feature-engineering/docs/en.md, phases/02-ml-fundamentals/13-ml-pipelines/docs/en.md, phases/02-ml-fundamentals/18-feature-selection/docs/en.md, phases/02-ml-fundamentals/11-ensemble-methods/docs/en.md
- **Why scale features?** K-Means, KNN and SVM use distance. Scaling makes all features count equally.
- **Why add a missing indicator?** The fact that data is missing can itself be informative.
- **Why MI and not correlation?** MI catches quadratic or periodic links. Correlation can read zero.
- **Why soft voting?** It uses confidence information. It is usually better than hard voting.
- **What makes a run reproducible?** Fixed seeds, pinned dependencies, versioned data and config files.
- **Which model first on tabular data?** LightGBM or XGBoost with default parameters.