Winning Kaggle the Reproducible Way

Interactive chapter

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.

Open the chapter activity

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()
Saved figure from this chapter calculation

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.

Apply this to your competition

Book location: Chapter 12, Feature Types and Framework Fit.

Technical corrections

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas

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.

Find source links and value grades in the lesson atlas