{
"cells": [
{
"cell_type": "markdown",
"id": "intro-md",
"metadata": {},
"source": [
"# Benchmark: AutoCarver vs. optbinning vs. KBinsDiscretizer\n",
"\n",
"This notebook runs the three binning libraries side-by-side on two public datasets:\n",
"\n",
"1. **German Credit** — binary classification, mixed numeric / categorical features, 1,000 rows.\n",
"2. **California Housing** — regression, all-numeric features, 20,640 rows.\n",
"\n",
"For each library and dataset, we report:\n",
"\n",
"- **`fit` and `transform` wall-clock** (seconds)\n",
"- **Downstream-model score** — AUC for binary, R² for regression — using a linear model (logistic regression / ridge) on the one-hot-encoded bin output\n",
"- **`train` → `test` score drop** as a coarse proxy for drift sensitivity\n",
"\n",
"All three libraries see the same `train + dev` data and are evaluated on the same held-out `test`. AutoCarver uses the dev sample for its built-in robustness veto; optbinning and KBinsDiscretizer don't have a dev-set concept and so treat the union of train + dev as one pooled training set — which is the comparison practitioners actually run.\n",
"\n",
"**This is not an IV / Tschuprow's T leaderboard.** Those metrics structurally favour the library whose objective they are. The downstream-model score is the metric a real scorecard team would use to pick a binner.\n",
"\n",
"Numbers come from a single run on a single machine with a fixed seed; treat them as illustrative, not as authoritative benchmark figures. Re-run on your own data before drawing conclusions."
]
},
{
"cell_type": "markdown",
"id": "setup-md",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "imports",
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"import warnings\n",
"\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"from sklearn.datasets import fetch_california_housing, fetch_openml\n",
"from sklearn.linear_model import LogisticRegression, Ridge\n",
"from sklearn.metrics import r2_score, roc_auc_score\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.preprocessing import KBinsDiscretizer\n",
"\n",
"from AutoCarver import BinaryCarver, ContinuousCarver, Features\n",
"from AutoCarver.discretizers.utils.base_discretizer import ProcessingConfig\n",
"\n",
"try:\n",
" from optbinning import ContinuousOptimalBinning, OptimalBinning\n",
"\n",
" HAS_OPTBINNING = True\n",
"except ImportError:\n",
" HAS_OPTBINNING = False\n",
" print('optbinning is not installed \\u2014 its rows will be skipped.')\n",
"\n",
"SEED = 42\n",
"warnings.filterwarnings('ignore')\n",
"plt.rcParams['figure.figsize'] = (10, 3.5)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "helpers",
"metadata": {},
"outputs": [],
"source": [
"def one_hot(df):\n",
" \"\"\"Treat every bin label as a categorical level and one-hot encode it.\n",
"\n",
" Lets a linear downstream model consume any of the three libraries' outputs\n",
" uniformly, without us computing WoE per bin.\n",
" \"\"\"\n",
" return pd.get_dummies(df.astype(str), drop_first=True).astype(float)\n",
"\n",
"\n",
"def fit_eval_binary(X_train, X_test, y_train, y_test):\n",
" Xtr = one_hot(X_train)\n",
" Xte = one_hot(X_test).reindex(columns=Xtr.columns, fill_value=0.0)\n",
" model = LogisticRegression(max_iter=1000, random_state=SEED).fit(Xtr, y_train)\n",
" return {\n",
" 'train_auc': roc_auc_score(y_train, model.predict_proba(Xtr)[:, 1]),\n",
" 'test_auc': roc_auc_score(y_test, model.predict_proba(Xte)[:, 1]),\n",
" }\n",
"\n",
"\n",
"def fit_eval_regression(X_train, X_test, y_train, y_test):\n",
" Xtr = one_hot(X_train)\n",
" Xte = one_hot(X_test).reindex(columns=Xtr.columns, fill_value=0.0)\n",
" model = Ridge(random_state=SEED).fit(Xtr, y_train)\n",
" return {\n",
" 'train_r2': r2_score(y_train, model.predict(Xtr)),\n",
" 'test_r2': r2_score(y_test, model.predict(Xte)),\n",
" }\n",
"\n",
"\n",
"def plot_bars(results_df, score_cols, title):\n",
" fig, axes = plt.subplots(1, len(score_cols), figsize=(4 * len(score_cols), 3.5))\n",
" if len(score_cols) == 1:\n",
" axes = [axes]\n",
" for ax, col in zip(axes, score_cols):\n",
" results_df.plot.bar(x='library', y=col, ax=ax, legend=False, color='#4C72B0')\n",
" ax.set_title(col)\n",
" ax.set_xlabel('')\n",
" ax.tick_params(axis='x', rotation=0)\n",
" fig.suptitle(title)\n",
" fig.tight_layout()\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "binners",
"metadata": {},
"outputs": [],
"source": [
"from AutoCarver.combinations.binary import CramervCombinations\n",
"\n",
"MAX_N_MOD = 5\n",
"MIN_FREQ = 0.05\n",
"\n",
"def bin_with_autocarver(X_train, y_train, X_dev, y_dev, X_test, categoricals, quantitatives, kind):\n",
" Carver = BinaryCarver if kind == 'binary' else ContinuousCarver\n",
" features = Features(categoricals=categoricals, numericals=quantitatives)\n",
" config = ProcessingConfig(verbose=False, dropna=False, ordinal_encoding=False) # showing statistics\n",
" combination_evaluator = CramervCombinations() if kind == 'binary' else None\n",
" carver = Carver(features=features, min_freq=MIN_FREQ, max_n_mod=MAX_N_MOD, config=config,combination_evaluator=combination_evaluator)\n",
"\n",
" t0 = time.perf_counter()\n",
" X_tr = carver.fit_transform(X_train.copy(), y_train, X_dev=X_dev.copy(), y_dev=y_dev)\n",
" fit_t = time.perf_counter() - t0\n",
"\n",
" X_dv = carver.transform(X_dev.copy())\n",
" t1 = time.perf_counter()\n",
" X_te = carver.transform(X_test.copy())\n",
" transform_t = time.perf_counter() - t1\n",
" return pd.concat([X_tr, X_dv]), X_te, fit_t, transform_t, carver\n",
"\n",
"\n",
"def bin_with_optbinning(X_train, y_train, X_dev, y_dev, X_test, categoricals, quantitatives, kind):\n",
" Cls = OptimalBinning if kind == 'binary' else ContinuousOptimalBinning\n",
" X_all = pd.concat([X_train, X_dev])\n",
" y_all = pd.concat([y_train, y_dev])\n",
" binners = {}\n",
" train_binned = pd.DataFrame(index=X_all.index)\n",
" test_binned = pd.DataFrame(index=X_test.index)\n",
"\n",
" t0 = time.perf_counter()\n",
" for col in X_all.columns:\n",
" dtype = 'categorical' if col in categoricals else 'numerical'\n",
" binner = Cls(name=col, dtype=dtype, min_prebin_size=MIN_FREQ/2, max_n_bins=MAX_N_MOD)\n",
" binner.fit(X_all[col].to_numpy(), y_all.to_numpy())\n",
" binners[col] = binner\n",
" train_binned[col] = binner.transform(X_all[col].to_numpy(), metric='bins')\n",
" fit_t = time.perf_counter() - t0\n",
"\n",
" t1 = time.perf_counter()\n",
" for col, b in binners.items():\n",
" test_binned[col] = b.transform(X_test[col].to_numpy(), metric='bins')\n",
" transform_t = time.perf_counter() - t1\n",
" return train_binned, test_binned, fit_t, transform_t, binners\n",
"\n",
"\n",
"def bin_with_kbins(X_train, X_dev, X_test, categoricals, quantitatives, n_bins=5):\n",
" X_all = pd.concat([X_train, X_dev])\n",
" num_train = X_all[quantitatives].apply(lambda c: c.fillna(c.median()))\n",
" num_test = X_test[quantitatives].apply(lambda c: c.fillna(c.median()))\n",
" kbd = KBinsDiscretizer(n_bins=n_bins, encode='ordinal', strategy='quantile')\n",
"\n",
" t0 = time.perf_counter()\n",
" binned_num_train = pd.DataFrame(\n",
" kbd.fit_transform(num_train), columns=quantitatives, index=X_all.index\n",
" )\n",
" fit_t = time.perf_counter() - t0\n",
"\n",
" t1 = time.perf_counter()\n",
" binned_num_test = pd.DataFrame(\n",
" kbd.transform(num_test), columns=quantitatives, index=X_test.index\n",
" )\n",
" transform_t = time.perf_counter() - t1\n",
"\n",
" # KBins has no opinion on categoricals — pass them through as labels\n",
" train = pd.concat([binned_num_train, X_all[categoricals].astype(str)], axis=1)\n",
" test = pd.concat([binned_num_test, X_test[categoricals].astype(str)], axis=1)\n",
" return train, test, fit_t, transform_t, kbd"
]
},
{
"cell_type": "markdown",
"id": "binary-md",
"metadata": {},
"source": [
"## Binary classification — German Credit\n",
"\n",
"20 features (numeric + categorical), 1,000 rows, target = `class == 'bad'`. Train / dev / test split = 60 / 20 / 20 %."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "381a7051",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"train=600, dev=200, test=200\n",
"categoricals=13, numericals=7\n",
"bad rate (train)=0.300, (test)=0.300\n"
]
}
],
"source": [
"credit = fetch_openml(data_id=31, as_frame=True)\n",
"df = credit.frame.copy()\n",
"\n",
"y_binary = (df['class'] == 'bad').astype(int)\n",
"X_binary = df.drop(columns=['class'])\n",
"\n",
"X_train, X_rest, y_train, y_rest = train_test_split(\n",
" X_binary, y_binary, test_size=0.4, random_state=SEED, stratify=y_binary,\n",
")\n",
"X_dev, X_test, y_dev, y_test = train_test_split(\n",
" X_rest, y_rest, test_size=0.5, random_state=SEED, stratify=y_rest,\n",
")\n",
"\n",
"categoricals = [c for c in X_binary.columns if X_binary[c].dtype == object or isinstance(X_binary[c].dtype, pd.CategoricalDtype)]\n",
"quantitatives = [c for c in X_binary.columns if c not in categoricals]\n",
"\n",
"print(f'train={len(X_train)}, dev={len(X_dev)}, test={len(X_test)}')\n",
"print(f'categoricals={len(categoricals)}, numericals={len(quantitatives)}')\n",
"print(f'bad rate (train)={y_train.mean():.3f}, (test)={y_test.mean():.3f}')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "run-binary",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"WARNING: No robust combination for Categorical('own_telephone'). Consider increasing the size of X_dev or dropping the feature (X not representative of X_dev for this feature).\n",
"WARNING: No robust combination for Categorical('foreign_worker'). Consider increasing the size of X_dev or dropping the feature (X not representative of X_dev for this feature).\n",
"WARNING: No robust combination for Numerical('residence_since'). Consider increasing the size of X_dev or dropping the feature (X not representative of X_dev for this feature).\n",
"WARNING: No robust combination for Numerical('existing_credits'). Consider increasing the size of X_dev or dropping the feature (X not representative of X_dev for this feature).\n",
"WARNING: No robust combination for Numerical('num_dependents'). Consider increasing the size of X_dev or dropping the feature (X not representative of X_dev for this feature).\n"
]
},
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" library | \n",
" fit_s | \n",
" transform_s | \n",
" train_auc | \n",
" test_auc | \n",
" auc_drop | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" AutoCarver | \n",
" 1.617 | \n",
" 0.0180 | \n",
" 0.8496 | \n",
" 0.8044 | \n",
" 0.0451 | \n",
"
\n",
" \n",
" | 1 | \n",
" optbinning | \n",
" 1.238 | \n",
" 0.0321 | \n",
" 0.8523 | \n",
" 0.7931 | \n",
" 0.0592 | \n",
"
\n",
" \n",
" | 2 | \n",
" KBinsDiscretizer | \n",
" 0.003 | \n",
" 0.0010 | \n",
" 0.8401 | \n",
" 0.7943 | \n",
" 0.0458 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" library fit_s transform_s train_auc test_auc auc_drop\n",
"0 AutoCarver 1.617 0.0180 0.8496 0.8044 0.0451\n",
"1 optbinning 1.238 0.0321 0.8523 0.7931 0.0592\n",
"2 KBinsDiscretizer 0.003 0.0010 0.8401 0.7943 0.0458"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y_train_full = pd.concat([y_train, y_dev])\n",
"\n",
"runs = [(\n",
" 'AutoCarver',\n",
" lambda: bin_with_autocarver(X_train, y_train, X_dev, y_dev, X_test, categoricals, quantitatives, 'binary'),\n",
")]\n",
"if HAS_OPTBINNING:\n",
" runs.append((\n",
" 'optbinning',\n",
" lambda: bin_with_optbinning(X_train, y_train, X_dev, y_dev, X_test, categoricals, quantitatives, 'binary'),\n",
" ))\n",
"runs.append((\n",
" 'KBinsDiscretizer',\n",
" lambda: bin_with_kbins(X_train, X_dev, X_test, categoricals, quantitatives),\n",
"))\n",
"\n",
"rows = []\n",
"for name, run in runs:\n",
" X_tr, X_te, fit_t, transform_t, carver = run()\n",
" scores = fit_eval_binary(X_tr, X_te, y_train_full, y_test)\n",
" rows.append({\n",
" 'library': name,\n",
" 'fit_s': round(fit_t, 3),\n",
" 'transform_s': round(transform_t, 4),\n",
" 'train_auc': round(scores['train_auc'], 4),\n",
" 'test_auc': round(scores['test_auc'], 4),\n",
" 'auc_drop': round(scores['train_auc'] - scores['test_auc'], 4),\n",
" })\n",
"\n",
"binary_results = pd.DataFrame(rows)\n",
"binary_results"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "8003c457",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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qiuggnn6DHUd1Xdf56pPnz7uOr/xxhvd+rVvibn77rttxowZbt8eeught0+6FrQt7777blBZHReVU/fZHj162LRp01xgTUEynRsAADI9HwAAOczRo0fVXMXXqVOnBO/99ttvvkOHDvmnEydO+N+76qqrfHXr1vX9+eef/nnx8fG+5s2b+6pVq+af9+qrr7r1t2jRwvf3338Hrb9ly5buvVmzZvnnbd682c2LioryrVy50j9/8eLFbr7W5wncHs+KFStcuZkzZybYhjZt2rht9Dz44IO+3Llz+44cOZLo8fn222/dsv379/elxPbt2135woUL+w4ePBj0XkqP2YABA9w6vv76a/88ratIkSJuvj4j8Bhq8nzzzTcJjlNG6NGjh9uOfv36Be1r+/btfXnz5nXXk0flYmNj/a/1t+bdddddQeu84YYbfCVKlAiaF+4aiI6O9lWpUiVoXsWKFd06Fy1aFDRf5z5//vy+wYMHB81/4IEHfIUKFfIdP3480X3Usuedd56vadOmvj/++CPovcDrTMdCn5/Udp86dcpXp04d35VXXumfN27cOLfNgccqVMeOHX21a9f2JcW7/gOvG22T9i+UtlPveWJiYtyy8+bNS1DW20d9r0+ePJng3lG6dOmgc6j9CD3Xoefcs379evf67rvvDir38MMPu/mffvpp0DZr3hdffBH0fcmXL5/voYceSvLYAACQWdBlDwCQ46h1g6ilSCi1NFLLBm+aNGmSm//rr7+6pMhqwaLWEGoxoemXX35xrSJ+/PFH27NnT9C61HIiNI+N97lq4eRRqxW1MlFrn8AWW97f6gLmCWwx9Ndff7nPV0svLa8WNaHUaiiwW5BafqgVj7r+JXd8wnXVS8pNN93k76KU2mOmPFVqVaTuRx6tS61pshq1QPPo2Ou1WgSFdr8MR63jAul86Xh55yT0Gjh69Kg7puo+p+tErwOptVVoFzx1MevYsaO9+eab/q6EuibUak4t15LKdabudDqXasUWmgcrtPtZqMDtVitBbav2L/C61XUs7733Xtjuml4ZtVRSi670oJZ76tIY2uIxcB/1vfZyUWk7da2rFZy6Dob7HqaEvgOiLraB1FJKFixYEDRfXTW9FoHe90X3ksD7BQAAmRld9gAAOY4XaFHXnFDKsaMH7gMHDtjtt98e1D1GD++PP/64m8JR0mR1TQsM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WbPGWrdu7QJa69atCypXu3Zt27dvn3/66quvUrtpAAAAAAAAyALypHaBa665xk0pNX78+KDXo0aNsvfee88++OADu/jii/+3IXnyWJkyZVK7OQAAAAAAAMhiIp5DKj4+3n7//XcrXrx40Pwff/zRdQOsUqWK3XbbbbZr165IbxoAAAAAAAAyYwups/X888/b8ePH7ZZbbvHPa9q0qU2fPt2qV6/uuuuNGDHCLr/8ctu4caOdd955CdZx8uRJN3mOHTsWse0HAAAAAABAFgpIzZo1ywWb1GWvVKlS/vmBXQDr1avnAlQVK1a0t956y3r16pVgPaNHj3brAQAAAAAAOFsdHnov2x7ED8Z0tBzdZW/27Nl29913uyBTmzZtkixbtGhRu+iii2zr1q1h3x8yZIgdPXrUP+3evTudthoAAAAAAABZsoXUm2++aXfddZcLSrVv3z7Z8urSt23bNrvjjjvCvp8vXz43AWeCyDeQOWn01ueee87279/vRnN94YUXrEmTJomWP3LkiD322GM2b948+/XXX13LWg2kce2110Z0uwEASA/8mxVAdpfqgJSCRYEtl7Zv327r1693ScovuOAC13ppz549NnPmTH83vR49etiECRNcVzw9aEiBAgWsSJEi7u+HH37YOnTo4B4m9u7da7GxsZY7d27r1q1b2u0pACDTmjNnjg0cONAmT57s6goFlqKjo23Lli1BXbw9p06dsquvvtq99/bbb1v58uVt586droUtAAAAgMwv1V32Vq9ebRdffLGbRA8Q+jsmJsa9VlLywBHypkyZYn///bfdf//9VrZsWf/Uv39/f5mff/7ZBZ+U1FzJzkuUKGErV660888/P232EgCQqY0dO9Z69+5tPXv2tFq1arnAVMGCBW3atGlhy2u+WkXNnz/fLrvsMqtUqZK1bNnStawCAAAAkA1bSLVq1cp8Pl+i72u0vEDLli1Ldp3qygcAyJnU2mnNmjWuha0nKirK5RtcsWJF2GXef/99a9asmfuxQwNl6AeMW2+91QYPHuxa2IZidFYAAAAghyY1BwAgnMOHD9vp06etdOnSQfP12uvmHeqnn35yXfW03MKFC+3xxx+3MWPG2JNPPhm2vEZnVTdxb6pQoQInAwAAAMhABKQAAFlOfHy8yx+lbuGNGjWyLl26uATn6uoXDqOzAgAAADlwlD0AABJT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C88880yCZbxAXrhtQ9qg3qDeSA/UG9Qb1BvZF/UG9UZay+51hvC8kX2fN2gh9X8UiJo5c6aNGTPG35pDk6KBCvy8+eabLjDz+++/W1xcnP8Aqkxo4Of06dNB82rWrGm7d+92k+e///2vHTlyxLWUEj1caxSwdevWJThJCnrpM3/55RcXkR42bJhrqaH1ei04UkKjny1atMiN2Pfpp5+6156GDRu61lelSpVywYPAScGDrEItjXQO/vOf/wQdP4205h1rWblypf9vHcMffvjBHc/EzmG45bzX3nKZhVp76Vo7cOCAf572Pz0pSPr555/b/v37XVBK35PEqEJTcPePP/446yDugw8+6IJHugG/+uqrSZbX502ZMsWuuOKKoCBr6LapdZ1u6vou6lp49913XYBNQSyNEpga+l7pOxv6ndKkY+BV+Ildbx79SqEWYf/+97/t0ksvTfAZ+k5r+0I/gyBU+qLeoN6g3jhz1BvhUW9kb9QbWb/e4FkjY541hHoje9YbBKT+z4cffugCE7169XItjgKnm266yf2a0bRpU9e9Ss3Y1DRNrYymT58edEB1krZv3+4CVYcPH7aTJ0+6bkVqeqoA0Nq1a123pe7du1vLli39Te4GDBjgHoQVaJo0aZILhP3000/21ltvuQtCN+9ixYq5oRn1UK3udQoqqetSSulBXE0rtR2VK1d2++PRPLUC6dixo3355ZduHxQZfeCBB+znn3+2rEJfiD59+tigQYNc8E2BPzVZVHNFnVuPmjUquLBx40a788473b536tTJfw4VBdb7OoeBTR0V6Hr22WddAEvnae7cuda/f3/LTK6++mpXWfbo0cM2bNjgtllBzMS6qqUVNd3VNaPubeqWd+zYMTdf3wEFqjSphVK/fv38UfYzocBS37593Wep25/2T5VgaGBQ26HP1HdHraf0/dL5fOmll8KuVy2hRo0aZatXr3ZdYOfNm2eHDh3yr1etshSw/te//uXWqe+yWlElJSYmxgW6FeDSTVz7r23xzod4gS5ta7gAs+ar5ZdaNuq4esdS2yb333+//frrr64rr46D7k2LFy+2nj17Jlvx4OxQb1BvUG+cHeqNhKg3sjfqjaxfb/CskXHPGkK9kQ3rjVRnncqmNNLatddeG/Y9JUDWofr2229dMjBlnFfyPS0zZcqUoKTmGgZUo9cVLVrUn81elAFfw5oqsbMSMmu40/379wd9jpbVCG4aNc8bdl7DaSp5szcqmxLGaRQ2Demokfk0Clm4RNpeguhQGg1N78fExCR4T4npunfv7hK5a/1VqlTx9e7d2yVAD5dcO7NS4ut+/fr590PH0Ese7yW81giKGqlNCbk1KoTObaB7773XDVepst5wmkpqPmLECHfuNMxmmTJlfBMmTAhaLrlzoUTbmpeaRPnhkpprOM1Ael/lQodi1f5plD/tr9a5aNEiX1oKd038/PPPvmrVqvkuvfRSlxAvcOhQXftKwK7RIpNLah6YlFzvaZ7KKsll165dfRUqVHD7p2Tjffv29Sc895bXpNEH9ZlK2KcRHwKTL4Zuv0Z6iY6OdiNI6Lq56KKLfC+88EJQ+cmTJ7vk/0qcr0SBus48iSVy1DFX8lLdMzQChq433Tc877//vrunKDlkuGFYA/cnsWFYNdKgjrXuO/ocnXMlPPQGKQh3zeDsUW9QbwSi3kgZ6g3qjZyMeiN71Bs8a0TuWUOoN/pl6+eNXP+3Y0COoF9hlOtIkWEllc8p9MtFixYtXMu6wATqAICkUW9QbwBASlFnUGcgdUhqDmRDynuk0Q+qVavmglDqVqguawSjAADUGwAAnjWQGRCQArIhJRUfPHiwy4WkvvrKY6b8RwAAUG8AAHjWQGZAlz0AAAAAAABEFKPsAQAAAAAAIKIISAEAAAAAACCiCEgBAAAAAAAgoghIAQAAAAAAIKIISAEAAAAAACCiCEgBAAAAAAAgoghIAQAAAAAAIKIISAEAAAAAACCiCEgBAAAAAADAIun/AUa7hT4g4njRAAAAAElFTkSuQmCC",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot_bars(binary_results, ['fit_s', 'test_auc', 'auc_drop'], 'German Credit \\u2014 binary classification')"
]
},
{
"cell_type": "markdown",
"id": "regression-md",
"metadata": {},
"source": [
"## Regression — California Housing\n",
"\n",
"6 numeric demographic features (Latitude / Longitude dropped — see comment in the next cell), 20,640 rows, target = median house value. Same 60 / 20 / 20 split."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "load-regression",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"train=12384, dev=4128, test=4128\n",
"numericals=8 (['MedInc', 'HouseAge', 'AveRooms', 'AveBedrms', 'Population', 'AveOccup', 'Latitude', 'Longitude'])\n"
]
}
],
"source": [
"housing = fetch_california_housing(as_frame=True)\n",
"X_reg = housing.frame.drop(columns=['MedHouseVal'])\n",
"y_reg = housing.frame['MedHouseVal']\n",
"\n",
"X_train, X_rest, y_train, y_rest = train_test_split(X_reg, y_reg, test_size=0.4, random_state=SEED)\n",
"X_dev, X_test, y_dev, y_test = train_test_split(X_rest, y_rest, test_size=0.5, random_state=SEED)\n",
"\n",
"quantitatives = list(X_reg.columns)\n",
"categoricals = []\n",
"\n",
"print(f'train={len(X_train)}, dev={len(X_dev)}, test={len(X_test)}')\n",
"print(f'numericals={len(quantitatives)} ({quantitatives})')"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "adebc1c4",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" library | \n",
" fit_s | \n",
" transform_s | \n",
" train_r2 | \n",
" test_r2 | \n",
" r2_drop | \n",
"
\n",
" \n",
" \n",
" \n",
" | 0 | \n",
" AutoCarver | \n",
" 3.910 | \n",
" 0.0189 | \n",
" 0.6652 | \n",
" 0.6595 | \n",
" 0.0057 | \n",
"
\n",
" \n",
" | 1 | \n",
" optbinning | \n",
" 2.871 | \n",
" 0.0113 | \n",
" 0.5145 | \n",
" 0.5077 | \n",
" 0.0068 | \n",
"
\n",
" \n",
" | 2 | \n",
" KBinsDiscretizer | \n",
" 0.008 | \n",
" 0.0016 | \n",
" 0.6181 | \n",
" 0.6192 | \n",
" -0.0011 | \n",
"
\n",
" \n",
"
\n",
"
"
],
"text/plain": [
" library fit_s transform_s train_r2 test_r2 r2_drop\n",
"0 AutoCarver 3.910 0.0189 0.6652 0.6595 0.0057\n",
"1 optbinning 2.871 0.0113 0.5145 0.5077 0.0068\n",
"2 KBinsDiscretizer 0.008 0.0016 0.6181 0.6192 -0.0011"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"y_train_full = pd.concat([y_train, y_dev])\n",
"\n",
"runs = [(\n",
" 'AutoCarver',\n",
" lambda: bin_with_autocarver(X_train, y_train, X_dev, y_dev, X_test, categoricals, quantitatives, 'continuous'),\n",
")]\n",
"if HAS_OPTBINNING:\n",
" runs.append((\n",
" 'optbinning',\n",
" lambda: bin_with_optbinning(X_train, y_train, X_dev, y_dev, X_test, categoricals, quantitatives, 'continuous'),\n",
" ))\n",
"runs.append((\n",
" 'KBinsDiscretizer',\n",
" lambda: bin_with_kbins(X_train, X_dev, X_test, categoricals, quantitatives),\n",
"))\n",
"\n",
"rows = []\n",
"for name, run in runs:\n",
" X_tr, X_te, fit_t, transform_t, carver = run()\n",
" scores = fit_eval_regression(X_tr, X_te, y_train_full, y_test)\n",
" rows.append({\n",
" 'library': name,\n",
" 'fit_s': round(fit_t, 3),\n",
" 'transform_s': round(transform_t, 4),\n",
" 'train_r2': round(scores['train_r2'], 4),\n",
" 'test_r2': round(scores['test_r2'], 4),\n",
" 'r2_drop': round(scores['train_r2'] - scores['test_r2'], 4),\n",
" })\n",
"\n",
"regression_results = pd.DataFrame(rows)\n",
"regression_results"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "b7da7c9b",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABJEAAAFcCAYAAAB4AC9/AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUNNJREFUeJzt3Ql0FFXWwPEbEpOwJYDIjoRNIbJEgiCLIoqCIhJHEXFJRMSNKEwUFMSETaMIGLYBRRFFGBhUUBGjyKKjIMgqiCAoCIpsKgEiBAn1nfu+Uz3dSSeVpbP/f+cUoatfLV1V3a/r9nv3+VmWZQkAAAAAAACQjXLZPQkAAAAAAAAQRAIAAAAAAECO0BIJAAAAAAAAjggiAQAAAAAAwBFBJAAAAAAAADgiiAQAAAAAAABHBJEAAAAAAADgiCASAAAAAAAAHBFEAgAAAAAAgCOCSAAAFIBrrrnGTLZ9+/aJn5+fzJkzx6NccnKyRERESHBwsHn++PHjhXo+dH90u7p/ZcmoUaPM60bJtnr1anMe9S8AACh4BJEAABCRH3/8UR566CFp1KiRCeiEhIRIp06dZPLkyXL69OkCOUa///673HHHHVK+fHmZPn26zJ07VypWrFiiz4cdlNqwYYPX5zWw1qJFi0LfLwAAAORfgA/WAQBAifbRRx9Jnz59JCgoSKKjo02Q4+zZs/Lll1/K0KFD5bvvvpNXX301X9to0KCBCUZdcMEFrnnffPONnDx5UsaOHSvdunWTonDvvffKnXfeaV57WTJy5Eh5+umni3o3kE9XX321eV8FBgZyLAEAKAQEkQAAZdrevXtNEEWDPCtXrpTatWu7nhs0aJDs2bPHBJnyS1vnaAsnd0eOHDF/q1SpIr6Smpqaq9ZM/v7+ZiprAgICzFSaWZYlZ86cMS3dCkNurz1fKFeuXKb3FQAAKMC6l4MLACjLxo8fL6dOnZLXX3/dI4Bka9KkiQwePNj1+I033pBrr71WatSoYVrvhIeHy4wZMxy3kzEnknbriomJMf+/4oorzHP33Xefq/yiRYskMjLSBACqV68u99xzj/z6668e69TylSpVMl3xbrrpJqlcubLcfffd5jldX2xsrCxZssS0rNJ9veyyy0wOJqecSO+//7707NlT6tSpY5Zr3LixaS2Vnp4uBeHcuXNm/bod3V5YWJiMGDFC0tLSPMrpfmouo4y0vPux+/vvv2X06NHStGlTE2C48MILpXPnzrJ8+fJscyLl9JgpzcHTtm1bs37d71deeaXI8yzpcbj55pvlk08+Mfum147ul9JcW0OGDJH69eub16XX9Ysvvijnz5/P1MVSW6dpd04Nbuo1unXr1kz5vLK79nSdSUlJ5tjp8alZs6bpKvrnn396bEu7PHbv3t1c37qvDRs2lPvvv9+jzIIFC8z7QNev+9SyZUvTxdQpJ1Ju3j86Pyoqyvz/oosukieffLLArnUAAEq60v0THAAADj788EOTB6ljx445OlYaMNKb41tuucW0ZNHlH330UXPjrC2XcuqZZ56RSy+91HSTGzNmjLmB1mCE0pv1/v37m+BSYmKiHD582Nw4f/XVV7J582aPlksagNEbcQ2STJgwQSpUqOB6Trvjvffee2b/9CZ8ypQpctttt8n+/ftNYCUrun29oY6LizN/tYVWfHy8nDhxQl566aUcvb6UlBQ5duxYpvka4MnogQcekDfffFNuv/12eeKJJ2TdunXmdX///feyePFiyS0N5ujyut527dqZ/daAxaZNm+T666/PdtmcHDM9Bz169DBBRw1WacBBz6EGIIrarl27pF+/fiZoM3DgQHON/fXXX9KlSxcTLNH5F198saxZs0aGDx8uv/32mwn4KL2Ge/XqJevXr5dHHnlEmjVrZgKKdrAzo6yuPd2GfQ0//vjjprXftGnTzHHTa1i7dGorvBtuuMEcM+1WqNe0BjL12Ns06Kev5brrrjMBL6XXhK7DPbCbUW7eP3ru9DW0b9/evIbPPvtMJk6caN6LegwAAEAGFgAAZVRKSoqlVWHv3r1zvMxff/2VaV737t2tRo0aeczr0qWLmWx79+4123rjjTdc8/T/Ou+bb75xzTt79qxVo0YNq0WLFtbp06dd85cuXWrKxsfHu+bFxMSYeU8//XSmfdL5gYGB1p49e1zztm7dauZPnTo10z7o/mX3Gh966CGrQoUK1pkzZ7I9Pvb6spsuu+wyV/ktW7aYeQ888IDHep588kkzf+XKlR6vKSEhIdM2GzRoYI6FrXXr1lbPnj2z3U9dT8avQTk9Zr169TLH4tdff3XN2717txUQEJBpnYVJj4NuPzk52WP+2LFjrYoVK1o//PCDx3y9bvz9/a39+/ebx++++65ZPikpyVUmPT3duvbaazNd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H/Zvvvmm+b9OumxOLViwQCIjI6Vy5coSEhIiLVu2lMmTJxfIawIA+IbWBdoKSTVs2ND1+W//ovv222+bz/by5ctLtWrV5M477zTdyNzt3r1bbrvtNvOLsrZurVevnimXkpKS7/pF90PLa2uppKQkady4sQQFBcmOHTvksssu8wggKX3upptukl9++UVOnjzpo6MEAMiOfV+hn8133XWX6WKswaOrr77aI4CkdJ7WJ99//32uD6p2i27RooWpa/Tv4sWLc1VvqJUrV8pVV11l8sTqD+y9e/fOtC/269HeHHfccYe5t7nwwgtl8ODBcubMGS4G5AnNN1BiPPTQQ1K3bl2Tj+Lxxx+XK664QmrWrClfffWVRznNk6Rd3TS3xIMPPmjm6YduTixfvlz69esn1113nbz44otmnn4Y6zb0wxYAUDz94x//kB9++EH+/e9/m5x5dlDmoosukueee06effZZ8wVa64ejR4/K1KlTzQ3A5s2bzZfvs2fPSvfu3SUtLU0ee+wxE0j69ddfZenSpSbHUmhoaL7qF9sbb7xhvrjr8nozoDcgWTl06JDpKqETAKDw2N3W9L4jq+wvp06dMlPGHwGcfPrpp+YHi/DwcElMTJTff//dtDjVHy5yWm9ozrwbb7zR5GbSQNHp06dNvaZdorUrdMYu3Vr/6Tzd3tdffy1TpkyRP//803SdBnJNcyIBJcWqVav0U9xatGiRa15CQoKZ565ixYpWTExMrtc/ePBgKyQkxDp37pxP9hcAUHheeuklUx/s3bvXNW/fvn2Wv7+/9dxzz3mU3bZtmxUQEOCav3nz5kz1izd5rV90n3T9WsccOXLEsfzu3but4OBg69577831tgAAeWPfV/Tr18+x7NixY03ZFStW5GobERERVu3ata3jx4+75n366admXQ0aNMhRvaHrqFGjhvX777+75m3dutUqV66cFR0dnen13HLLLR7LP/roo2a+LgPkFt3ZADf6a7R2VdAWSQCAku+9994z+SP0V9hjx465Jm1ppL8yr1q1ypTTlkbqk08+kb/++qvA9kd/fdbWUdnR7euv4Nr17oUXXiiwfQEAeKd56bLzxRdfmFE1tW659tprc3wYdTQ3TcQdExPjqnfU9ddfb1om5aTesNeh3andW7O2atXKrGfZsmWZ1jFo0CCPx9riVnkrCzghiAS4efTRR83Q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JBdeeKEUlLL+viyOqFNypqxfu2WpTqE++R/qE+QWdUrOlOU6pSzVJ4o6pRTWKVYJoxG4SpUqWTt37rT69u1rPffcc9lGhDWCZ79Mfd69ZYZOOk/9/PPP1i233GJVrFjRqly5stWnTx/r0KFDrvVoCyRt/bRp06ZM+6StkE6dOmX+//HHH1udOnUy+6EtP3r27Gnt2bPHVXbv3r1muwsWLLCuvvpq02pk8uTJVnBwsLVs2TKP9b733nvmtaampprH+/fvN/ul665atarZX11fxqjvuHHjrNq1a1thYWFWUdOWQI899ph10UUXmdeqx2b9+vUerVaWLl1qtWzZ0jzfvn17a9u2bR7Pu0/aksxuiTRmzBjrzjvvtCpUqGDVqVPHmjZtWpYRXPu4v/vuu9Y111xjWpJpizL9tSjj9ZOcnGw1a9bMXAvacse9BU3GyHqXLl3M69OWNnpOatas6dpH2/fff29et76+5s2bW8uXL/caXc4t933R61OvIb1mstpX9d///tds+8iRIx7HZfPmzR7H/LPPPrMiIyPNcerQoYN5v9m2bNlijqFem/peadOmjfXNN994LJ+xhdFff/1lXXrppeY4ZLV/2oqqRYsW5nXoe+e6665zva/U66+/boWHh1uBgYFWrVq1rEGDBrme023+61//snr16mWuB/scLFmyxLr88svNsW/YsKE1atQoV6srvYbcry197K21YsZr0L2s0uu1R48e5nqpUaOGdc8991hHjx71uEZ0XwcPHmxaL+qxQ/FBnUKdosp6nUJ9Qn0C36BOKTl1Cvco3KMo7lEG5ekepcQFkfRGsm3btub/H374odW4cWPr/PnzOQoi6UXyxBNPWJdddpnpwqOTzktPTzddbjp37mxt2LDB+vrrr80NtH6Zs+mXwxtuuMFx/9555x3zpXL37t3mxlxvajVAottw/+KpH5xa7qeffjJfKG+//XZz8+nutttuc83TQJV+Wbz//vutb7/91tqxY4d11113mRtz7WZnfzjrjf29995rbd++3UxF7fHHHzdfxjVA9t1335l91Irl999/dwUc9HV9+umn5nXdfPPN5tjo69XXlZSUZLpE2edLK2f7C78GMBITE61du3ZZU6ZMsfz9/c16svvCr1/kNWily+gx1/XYQQW9fi644AKrW7duJiiyceNGs296nLP7wq/7p8GJH374wXrzzTdNNy57P7S7mJ6j66+/3gRfNIjTrl07nwaRhg0bZs67Bn68PW/TY/fQQw9ZTZo0yXQ9ZgwiaTBv9erV5pxdddVVptumTd8/el3qjYy+5v/85z/mtbkv762bmnY/1OcOHz6caf/0PRAQEGBNmjTJ7JNeC9OnT3edbw0QaXBJrwc9dxqIdO/OqOvVAI52Lf3xxx9NUPiLL74w52bOnDlmnp4Tvbb0XCkNpNmBZL227MBaxiCSfe3ppAFhPX76HlP6OjVAOnz4cHM8NMis57pr164e14ieH70p1GCce0AORY86hTpFlfU6hfqE+gS+QZ1ScuoU7lG4R7FxjzI01/coJS6IpDezeiOp9Ita9erVzY1rToJIWeVE0i9n+mVRW/rY9OZZl7NbzeivjPphk1vaIkHXY7eusb942q/BfT/dWx2lpKSYm2Zt2aTmzp1rPojtgJnSD2Xdr08++cT14ay/Wtof1kVNW5HoF+h58+a55mlwSINK48ePdwUctFWWTYNL+poWLlzo2N9YW3+405ZpN954Y7Zf+F977bVM51hv/u1t6WP3lmMayNBjmt0Xfg0+urviiiusp556yvxfz58GRzQAYfNlSyRtlaPrWrFihdfn9brWX7910nL6y4/eyNiya4lk++ijj8y806dPm8d6o6WBGW+yCyLpsdDn1q1bl+lY6j7pc/v27fO6Xr1mnnnmmSyPhS47ZMgQj3nakun555/3mKfvIz0G7stlPA9Z5U3T996tt95qAswafFZjx47NFFw+cOCAWa/eVNrXiLaGQvFEnUKdosp6nUJ98j/UJ8gP6pSSUadwj8I9ijvuUUp5TiTNs6J5iPr162ceax6Xvn37mr7H+fH9999L/fr1zWQLDw83/Ur1OfX/3yuc7d692+xfo0aNJCQkxGToV/v37/co17ZtW4/HN910k1xwwQXywQcfmMfvvvuuWb5bt27m8datW2XPnj1SuXJl0x9Sp2rVqsmZM2fkxx9/dK2nZcuWEhgYKMWB7pf2++zUqZNrnr7Gdu3auY6r6tChg+v/+pouvfRSj+ez4r6c/dhpuVatWrn+X7t2bfP3yJEjrnkVKlSQxo0be5Rxf95pnRmX0WtWryvt+2zT1+8rum29xhISEuTUqVOZnu/atasZeU0nfe9oXq8bb7xRfv755zwfp7i4OHnggQfMtak5wtyvv+zY7yEdDS6j1q1by3XXXWeu3z59+sisWbPkzz//dG1X8yvp89nJ+J7S98yYMWNc7xedBg4caPJD/fXXX5JbI0aMkLVr18r7778v5cuXd21j1apVHtto1qyZec79uERGRuZ6eyh41CnUKe7Kep1CffI/1CfIC+qUklOncI/CPYo77lFyz7fZdAuYBovOnTsnderU8Tjpmphs2rRpUq5cuUzBHl8lrb7kkktk586djuV69eolDRo0MDfBup/nz583CYUzJqqqWLGix2P9QL399ttl/vz5cuedd5q/GiCzEx5rgEBvROfNm5dpmxdddFGW64UnDWLZ7GCGniNvz9tlnAKI3pZxX2dBqlu3rrzzzjsmWKRJtD/++GNTgbtfD02aNPFIbKeJ9/T6HDduXJ6Okw4vedddd8lHH31ktqcBrAULFsitt96a7b7aN2N2YNWdJvhevny5SVj/6aefmmT1zzzzjKxbt06qV6+eo2OR8drX98zo0aPlH//4R6aywcHBkhtvv/22vPzyy7J69WpzzN23oe/5F198MdMy9g2lt31D8UCdQp2SX6WpTqE++R/qE+QFdQp1Sn6UpvpEUaeU7jqlxLRE0uDRW2+9JRMnTnS1rNBJWwJosObf//63CaacPHlSUlNTXctpmYzBmvT0dI95zZs3lwMHDpjJtmPHDjl+/LhpkaT0pllHmNq8eXOmfdNAlW7z999/N79CjBw50rSa0PXarSlyQkfWSk5ONiO9rVy50jy2tWnTxrRyqlGjhgkKuE8aFCiO9NdXPd5fffWVx7HSEbvs46q+/vpr1//1eP3www/m2GV1vrwtZz+2lysutFWVXleHDx92zdPX70satPz888/l0KFDJpCk74GsaOWhwdbTp0/nO6j6z3/+0wR89APwjTfeyLa8bu/VV1+Vq6++2iPomXHftNWafqjq+0zP/eLFi01QTANPOrpcbuh7Rt+PGd8vOukxsCvXrK4vm7Y+0pZXr7zyilx55ZWZtqHvV92/jNsgcFS8UadQp2REnUJ9khXqE1CnlK77FO5RuEexcY8ipTuItHTpUhNgGDBggGnZ4z7ddtttJvrfvn1703Rcu51oM0VtzTNnzhyP9ejN3t69e01w6dixY5KWlma65WjzSv0w3LRpk+n2Ex0dLV26dHE1aR4yZIi5wdXg0PTp003w6qeffpL//Oc/5sZSPzirVq1qht3Tm2XteqYfsNr1J6f0BlubqOt+NGzY0Lwem87TFhm9e/eW//73v+Y1aMTx8ccfl19++UWKI72JfuSRR2To0KGm0tHAnHYn0q5Eeh5t2uVIAwTbt2+X++67z7zOqKgo1/nSSKo+r+fLvRuSBqfGjx9vgk56ThYtWiSDBw+W4uT66683FVVMTIx8++23Zp81yJhVt6680u4Nej1olwftsnbixAkzX69vDS7ppC2BHnvsMVdkOi/0gzY2NtZsS7vE6evRoFjG4J3uh25T3xfaSknfO3r+ZsyY4XW92uLo+eeflw0bNpiun++9954cPXrUtV5t/aQB5ClTpph16vtUWytlJz4+3gSeNSilX3j09eu+2Mdf2cEp3VdvAV+dry2stHWgHlf7WOq+qUGDBskff/xhurDqcdDPnU8++UT69+/vGJxC0aJOoU6hTvGO+iQz6hNQp5Su+xTuUbhH4R4ln6wSQkftuummm7w+p4l69aVs3brVJJbU0ZM0kZsu8+qrr3ok1tbhHHXUsypVqrhGZlI6mpMORakJiDVxcJ8+faxDhw55bEeX1ZFbdLQ1exhyHWZXkwzbo7FogksdfUWH3tUR3XSEK2/JOO1ExhnpSFv6fHx8fKbnNJFmdHS0SSau62/UqJE1cOBAk4Q7qyHdi5omY9bhiu191uNlJyu3kzDrKHs64pcmidZRZvQ8unv44YfNsINa1h7qWJOgjh492pwnHY5Zh3yfPHmyx3JOx12TP+u83CRm95YEVYdFdKfPa7mMwzHr69ORfPT16jp12Of88Ha+f/nlF6tp06bWlVdeaZJAuw9Lr9e1JmjVEQSdEmu7J8bW53SeltVkiDoEdv369c3r0YTXsbGxrqTb9vI66YhCuk1NUq0jk7kngs24/zqKhw59rSOd6XVyySWXWFOnTvUoP3PmTJNcXpO1a3Jsva5sWSWV1WOsSS7180BHPNLrSz8TbB988IH5vNBEtXpNZUys7f563Ce7rNIRlPRY62eKbkfPsSb5tpPge7tGUPSoU6hTqFP+h/qE+gTUKWXtPoV7FO5RuEfpkud7FD/9J7+BKCAv9BcKzeWjLUA0iXlZoa13OnfubFqruSdcBQDkHXUKdQoA+AL1CfUJSlFibaAk0rw+OkpF06ZNTeBIu9xp9y4CSAAA6hQAQFHgHgV5RRAJKGCa6Pqpp54yuX60v7jm4NL8PgAAUKcAAIoC9yjIK7qzAQAAAAAAoPSMzgYAAAAAAICiQxAJAAAAAAAAjggiAQAAAAAAwBFBJAAAAAAAADgiiAQAAAAAAABHBJEAAAAAAADgiCASAAAAAAAAHBFEAgAAAAAAgCOCSAAAAAAAABAn/wegbT2aq5sFFAAAAABJRU5ErkJggg==",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plot_bars(regression_results, ['fit_s', 'test_r2', 'r2_drop'], 'California Housing \\u2014 regression')"
]
},
{
"cell_type": "markdown",
"id": "notes-md",
"metadata": {},
"source": [
"## How to read these numbers\n",
"\n",
"- **`fit_s` / `transform_s`** measure only `.fit` / `.transform` wall-clock — not data loading, not one-hot encoding, not the downstream model.\n",
"- **`test_auc` / `test_r2`** are the headline metric. They reflect how well a *simple* downstream model performs on each library's binned output. A tree-based downstream model would tell a different (and less binning-sensitive) story.\n",
"- **`auc_drop` / `r2_drop`** are `train - test` and measure how much each library's bins overfit. Lower is more robust. AutoCarver's dev-set veto is designed to keep this small.\n",
"- **Same data, same seed, same downstream model** across libraries — but a single run, on one machine, with one set of hyper-parameters. Treat as illustrative.\n",
"\n",
"## When the result will move\n",
"\n",
"- **Bigger `max_n_mod` / smaller `min_freq`** will improve AutoCarver and optbinning's in-sample scores at the cost of `*_drop`. KBins doesn't have a target, so it's mostly insensitive.\n",
"- **Different downstream model.** Gradient-boosted trees on the raw features beat any binning + linear pipeline. The point of binning is interpretability, not raw accuracy.\n",
"- **Different dataset.** German Credit is small; on a 10M-row credit-risk dataset, `fit_s` is what dominates the comparison.\n",
"\n",
"See [comparison.rst](../../comparison.html) for the qualitative scope and algorithmic comparison."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "AutoCarver",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.15"
}
},
"nbformat": 4,
"nbformat_minor": 5
}