This page shows the chapter notebook with its saved results. Constructed examples do not measure competition gains.
Chapter 12: The GBM Trio
Try the decision first
How much does it matter whether a category is fed as ordered integer codes, as native categorical splits or as one-hot columns?
Write your expectation before opening the worked results. Use the chapter activity to compare supported inputs. To change the calculation itself, run the notebook on GitHub.
Your answers stay on this page until you download them. They are not sent to a server.
Worked notebook and saved results
How much does it matter whether a category is fed as ordered integer codes, as native categorical splits or as one-hot columns?
The chapter warns that integer codes impose an order the category does not have, and that native categorical support has its own requirements. Whether the choice is worth a deadline hour depends on how many levels the column has.
Constructed data with random level effects and a stand-in learner. The gap between native splits and one-hot columns is a few thousandths of AUC up to 64 levels and about 0.01 at 128 levels, and it changed size across seed bases, so no general ranking between them is claimed.
The experiment
A constructed binary task with four numeric features and one category whose levels have random effects and arbitrary integer codes. One scikit-learn histogram gradient boosting configuration (100 rounds, learning rate 0.1, 15 leaves) is fitted on 3,000 rows and scored by AUC on 6,000 new rows, with the category as ordered integer codes, as native categorical splits, as one-hot columns, and left out. Five independent datasets per setting; the control is the number of levels.
Calculation code
import numpy as np
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.metrics import roc_auc_score
from kaggle_companion.activities._common import clean
SEED = 12
REPLICATES = 5 # independent constructed datasets; every estimate is their mean
N_TRAIN, N_NEW = 3000, 6000
NUMERIC = 4 # numeric feature columns before the category column
def generate(levels, seed):
"""Four numeric features plus one category with `levels` levels. Each level has a random effect on the log odds,
and the integer code given to a level is arbitrary, as it is for a label-encoded column."""
rng = np.random.default_rng(seed)
effect = rng.normal(0.0, 1.0, levels)
code = rng.permutation(levels)
def rows(n):
level = rng.integers(0, levels, n)
x = rng.normal(size=(n, NUMERIC))
logit = 0.9 * x[:, 0] - 0.6 * x[:, 1] + 0.5 * x[:, 2] * x[:, 3] + effect[level]
y = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
return x, level, y
return rows(N_TRAIN), rows(N_NEW), code
def learner(**extra):
"""One fixed configuration for every representation: the comparison changes the input, not the budget."""
return HistGradientBoostingClassifier(max_iter=100, learning_rate=0.1, max_leaf_nodes=15, random_state=0, **extra)
def run(levels):
scores = {"none": [], "ordinal": [], "native": [], "onehot": []}
for rep in range(REPLICATES):
(x, lv, y), (x_new, lv_new, y_new), code = generate(levels, SEED * 100 + rep)
ordinal, ordinal_new = np.column_stack([x, code[lv]]), np.column_stack([x_new, code[lv_new]])
onehot, onehot_new = np.column_stack([x, np.eye(levels)[lv]]), np.column_stack([x_new, np.eye(levels)[lv_new]])
fits = {"none": (learner(), x, x_new), # no category column at all
"ordinal": (learner(), ordinal, ordinal_new), # codes treated as ordered numbers
"native": (learner(categorical_features=[NUMERIC]), ordinal, ordinal_new), # native categorical splits
"onehot": (learner(), onehot, onehot_new)} # one 0/1 column per level
for name, (model, a, b) in fits.items():
scores[name].append(roc_auc_score(y_new, model.fit(a, y).predict_proba(b)[:, 1]))
return clean({
"levels": levels, "columns": {"none": NUMERIC, "ordinal": NUMERIC + 1, "native": NUMERIC + 1, "onehot": NUMERIC + levels},
"auc": {k: float(np.mean(v)) for k, v in scores.items()}, "per_replicate": scores,
"rows_per_level": N_TRAIN / levels, "replicates": REPLICATES, "train_rows": N_TRAIN, "new_rows": N_NEW,
})
Measure it across the control
The control is levels in the category. Each value below runs the whole experiment from the generator up.
Calculation code
import pandas as pd
from kaggle_companion.activities.ch12 import explain
VALUES = [8, 32, 64, 128]
results = {value: run(value) for value in VALUES}
table = pd.DataFrame({str(v): explain(results[v], v)['metrics'] for v in VALUES})
print(table.to_string())8 32 64 128 Native splits 0.792 0.788 0.784 0.782 Ordered codes 0.791 0.779 0.765 0.751 One-hot 0.791 0.788 0.787 0.769 No category 0.715 0.715 0.710 0.707 One-hot columns 12 36 68 132
Look at it
The figure shows the default setting, levels in the category = 128.
Calculation code
import matplotlib.pyplot as plt
from kaggle_companion.activities.ch12 import draw, NCOLS, HEIGHT
DEFAULT = 128
fig, axes = plt.subplots(1, NCOLS, figsize=(6.6 if NCOLS == 1 else 10.4, HEIGHT), layout='constrained')
draw(axes, results[DEFAULT], DEFAULT)
plt.show()What the result says
With 128 levels (about 23 training rows each), native categorical splits score 0.782 AUC. Ordered integer codes score 0.751, so 0.782 - 0.751 = 0.031. One-hot columns score 0.769 with 132 columns instead of 5. Leaving the category out scores 0.707. The choice of representation is worth a comparison at this size.
- Ordered codes behind native: 0.782 - 0.751 = 0.031 AUC.
- One-hot behind native: 0.782 - 0.769 = 0.013 AUC.
- Value of the category: 0.782 - 0.707 = 0.075 AUC.
- Columns fed to the trees: 5 (codes, native) against 132 (one-hot).
Apply this to your competition
- Fix rounds, leaves and folds first, then change only the category representation, so a difference can be attributed to it.
- Compare ordered codes, native categorical splits and one-hot on the same rows; the gap grows with the number of levels.
- Keep the cheapest representation that is within noise of the best, and record columns and fit time next to the score.
- Native handling still needs consistent category meaning at inference and an unknown-level policy; it does not replace the outer assessment.
Book location: Chapter 12, Feature Types and Framework Fit.
Apply a lesson from this chapter
Select a lesson whose conditions match your project. State how you would test it and what would make you reject it.
Predict a continuous destination through cluster probabilities
Conditions
- taxi destinations concentrate in recurring geographic regions
Action
- Proposed conditional application: Cluster training destinations with mean shift, predict probabilities over cluster centers, and output the probability-weighted center mean under distance loss.
Check
- Proposed check: Fit centers on training-only trips, compare direct regression, and inspect implausible weighted means and unseen destination regions.
Limits
- A convex mean can fall off-road or between modes; centers may miss novel destinations.
Keep a simple model when stacking adds negligible value and audit ID use
Conditions
- a tabular stack gains little while identifiers encode accidental ordering
Action
- Proposed adaptation of author advice: Compare a simpler model and no-ID version on target-like held-outs before retaining extra complexity.
Check
- Proposed verification: Check chronological/new-data errors and final inference cost.
Limits
- Similar source scores do not prove statistical equivalence; ID risks depend on how it was generated.
- The single no-ID test is not a confirmed matched ablation of Model A; leaderboard similarity cannot establish future-data robustness or statistical equivalence.
Re-test domain-standard models when relational features change
Conditions
- click prediction includes rich relational feature structure
Action
- Proposed adaptation of source observation: Compare boosting and factorization baselines using the same engineered features rather than copying a previous contest winner.
Check
- Proposed verification: Evaluate held-out log loss or the specified CTR metric and feature-group ablations.
Limits
- No numerical comparator or validation protocol is given.
Compare imbalanced classifiers inside a common pipeline
Conditions
- imbalanced numerical entity features need classifier comparison
Action
- Proposed adaptation of source observation: Compare candidate classifiers with the same resampling/scaling recipe and ROC AUC; fit resampling within training folds as a proposed safeguard.
Check
- Proposed verification: Evaluate untouched validation data and inspect the effect of resampling separately from model choice.
Limits
- Source split details are missing; SMOTE before scaling was their order, and no isolated resampling gain is shown.
Retain partially missing features when the learner supports missing values
Conditions
- useful tabular columns include NaNs and the learner handles them
Action
- Proposed adaptation of author advice: Compare native missing-value handling with dropping or imputing columns and keep useful partial information.
Check
- Proposed verification: Evaluate missingness patterns across folds and compare predictions when missingness shifts.
Limits
- Support for NaNs does not prove unbiased behavior or universal superiority of native handling.
Compare tree learners before packaging a clinical demo
Conditions
- tabular patient data supports tree-based risk prediction
Action
- Proposed conditional application: Compare Random Forest and Gradient Boosting on ROC AUC before choosing the model for an interactive demonstration.
Check
- Proposed check: Compare tree baselines on patient-independent untouched data, measure calibration and subgroup errors, and separate demonstration behavior from clinical evaluation.
Limits
- Interactive demo does not establish clinical utility; source omits split construction and RF comparison scores.
Compare nonlinear baselines after explicit linear transformations
Conditions
- vehicle prices have nonlinear structured predictors
Action
- Proposed adaptation of source observation: Compare transformed linear baselines with trees using the competition RMSE on original price units.
Check
- Proposed verification: Use identical folds and invert target transforms before evaluating original-unit RMSE.
Limits
- No isolated transformation gain is supplied.
Test tuned CatBoost models after exploratory features
Conditions
- credit-scoring tabular data supports categorical tree models
Action
- Proposed recipe: compare several CatBoost configurations tuned with Optuna.
Check
- Proposed check: Build an untuned CatBoost benchmark, tune within inner folds under fixed trial budget, and assess credit/time-grouped outer holdouts.
Limits
- Sparse source lacks reproducible features, split and trial budget; win does not isolate tuning benefit.
Retest a simple model after domain preprocessing
Conditions
- A strong preprocessing pipeline exists and training or inference efficiency matters.
Action
- Proposed conditional application: Apply the selected preprocessing to a simple HGB model.
- Proposed conditional application: Compare that alternative with the available stronger models.
Check
- Proposed check: Compare HGBand ensemble on the same untouched measurements and hardware, measuring preprocessing, training and submission generation separately.
Limits
- 45secondrunhardware is unknown; outperforming second-best does not prove equality with the winning blend.