{ "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": null, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
libraryfit_stransform_strain_auctest_aucauc_drop
0AutoCarver1.4800.01660.84960.80440.0451
1optbinning1.2540.02550.85230.79310.0592
2KBinsDiscretizer0.0040.00120.84010.79430.0458
\n", "
" ], "text/plain": [ " library fit_s transform_s train_auc test_auc auc_drop\n", "0 AutoCarver 1.480 0.0166 0.8496 0.8044 0.0451\n", "1 optbinning 1.254 0.0255 0.8523 0.7931 0.0592\n", "2 KBinsDiscretizer 0.004 0.0012 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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Tu/5ej/W5snncz/WHrHVhd6PNbeUllV5aVM5d7TccrcOd/O3aNHC8bmzvN+nATINvuXNTwUUhhxOQAmqXbu2aeesb8U1WaAOb7zxhnljoG2mAQBlR2sC6f9A673Z398/3+f65tc2depUkwT8gw8+kE8//dTkv5g8ebJs2LDBvFC4EF999ZXJoaF5o1599VXzBvmiiy4y5YVzUtmC3obn5ORc0PcDAP6P5unTe+8TTzwhnTp1MjVc9d6rOZ2cE3SX9/sxOZZQXhBwAorgQpofaGJWbdaggxZUWutJe4rQJLX6lgAAUDb3cU3qrTWc9E2w1kY9F+1lVIexY8eapLLXXXedzJo1y/QUVNB3FMV7771najbpywl9o27Thx5nl156qflX37Q7y/tWWt/Gay9IO3bsOK/tAYCKSpNnDxw40LxksJ05cybffdf5fqzN7Qq6H9udR+j9+EL+v79hw4amVq675ODFWYe7+Xft2uX43Fne79Pycs+ePdKmTZtibDkqOprUAUWgPUHkLWiKQtt9u/zg/PwcN2nt7QgAUHr3ceV8L9fe67Rm04QJE8z/SDvTcfsers3TtGcgZxp40nu68738fMsK3QYNVjm/GdcmC5qHyZkGkbR5x5dffukyXWtFOdPt6t27t3z00UeyadOmfN+Xd18BAH/ej/PeI7Xn0rw1l+xAkvP9WPO25m3B0L17d5MHSWvEauDqfO/Ft912m6lRu3HjRpfm02+//Xax1qHLr1+/3mWbtbe7sLAwadmypcv8CxcuNPmnnINxhw8fNqlCgKKihhNQBOHh4fLZZ5/JtGnTpF69euZteERExDmX0y6yf/31V7nppptMkwt966GFVrt27RztpQEApXMfV88884xpGqFN1rTmqdZO0m6qNcCjQRp9MNi/f7+8//77JpHqU089JZ9//rkMHTpU+vbta2pCafDpzTffNA8m99xzzwWXFT179jTL9OjRw3SRrbmjNEeUvg3fvn17vnLlhRdeMP9qQnB92Pnxxx/zrXPSpEmm6V/Xrl3NfmiZow8KS5culf/85z8ub+QBAP/n9ttvN/d3bUqnARgNzuh9vVatWvkCSZdffrkMHjxYRowYYcqDefPmmRqmKSkpLi8KXn75ZXPPvuaaa8w9XmtHffvtt6YTiqKm2Hj66afNdmk5ERsba15waKBIayXlLScKoonL33nnHRMw0mbhmi9Qv1/LPK1pqy8rnOnnmmB80KBBkp6eLtOnTzflUkxMDJcLio6O+lCR2d1+Ond17a6b0127dlk33HCDVaVKFfNZUbu9XrZsmdW9e3erdu3aVkBAgHX55Zdbf/vb36zDhw97fF8AAIWbOHGi6ebZz8/PpRvp9957z+rSpYtVrVo1MzRv3tx67LHHrN27d5vP9+3bZz300EPWFVdcYQUFBVk1a9a0unXrZn322WceKSvU3LlzrSZNmliBgYHm+7VrbXflkXbZPXjwYNMVtna1fe+995qusXU+nd/ZwYMHrejoaOuyyy4z623cuLHZr6ysLC4VAHDjt99+swYNGmSFhIRYF198sRUVFWXu7Q0bNsx3T9+8ebMVERHh+H/8adOmmXu3c/li+/DDD63OnTub8qF69epWx44drXfeeadY52D79u1W165dTTmkZZmWaVp25P0+3daePXu6XcfevXutPn36WDVq1DDr0e34+OOP3T4f6faNHj3aPMfodus6tVwBiqOS/qcY8SkAAAAAAOCDkpOTpVu3bqZGbJ8+fcp6c+DlyOEEAAAAAAAAjyKHE3AeNHGgJuorjHan7dylNgCgYqGsAAAU5vTp05KRkVHoPJpLSXu9BrwRASfgPKSmpppksIVJSEiQ8ePHc3wBoIKirAAAFGbJkiUmKXdhvvjiC7nxxhs5kPBK5HACzoN2a6q9/BSmcePGZgAAVEyUFQCAwmjvod9//32h82gPqNqzHeCNCDgBAAAAAADAo0gaDgAAAAAAgIqXwyk3N1d+/vlnueSSS6RSpUplvTkAUC5ZliUnT56UevXqiZ9fxX6fQLkBAOdGuUG5AQAlWW54RcBJg00NGjQo680AAK9JVPyXv/xFKjLKDQAoOsoNyg0AKIlywysCTlqzyd6p6tWrl/XmAEC5dOLECROct++ZFRnlBgCcG+UG5QYAlGS54RUBJ7sZnQabCDgBQNHumRUZ5QYAFP+eWZFRbgCA58uNip3kAwAAAAAAAB5HwAkAAABAqUlMTJSwsDAJCgqSiIgI2bhxY6HzL126VJo3b27mb926taxatSrfPDt37pQ77rhDgoODpVq1anLNNddISkpKCe4FAOBcCDgBAAAAKBVLliyRuLg4SUhIkC1btkjbtm0lKipKjhw54nb+devWyYABA2Tw4MGydetW6d27txl27NjhmGfv3r3SpUsXE5RKTk6W7du3y7hx40yACgBQdipZ2q+dFySm0rcVGRkZ5HACAO6VlBsA4KX/j601mrT20cyZM814bm6uSUA7bNgwGTVqVL75+/XrJ5mZmfLxxx87pl177bXSrl07mTVrlhnv37+/XHTRRfLmm2+e93bxvAEAnr9XUsMJAAAAQInLzs6WzZs3S2Rk5J8PI35+Znz9+vVul9HpzvMrrRFlz68Bq5UrV0rTpk3N9Nq1a5ug1ooVK0p4bwAA50LACQAAAECJO3bsmOTk5EidOnVcput4Wlqa22V0emHza1O833//XV544QXp0aOHfPrpp3LXXXfJ3XffLf/+978L3JasrCzzpt55AAB4FgEnAEC5SxA7ffp0adasmVSpUsU0tRg+fLicOXOGMwUAcKE1nNSdd95pygptaqdN826//XZHkzt3Jk+ebJqF2IOWNQAAzyLgBAAoVwliFy1aZB4WdH7tdWju3LlmHWPGjOFMAYAXCwkJEX9/f0lPT3eZruOhoaFul9Hphc2v66xcubK0bNnSZZ4WLVoU2kvd6NGjTQ4Se0hNTb2APQMAuEPACQBQoqZNmyYxMTEyaNAg80Cgb5yrVq0q8+bNK7BHouuuu07uu+8+Uyuqe/fupoeic9WKAgCUbwEBARIeHi5r1651qaGk4506dXK7jE53nl+tWbPGMb+uU5OQ796922WeH3/8URo2bFjgtgQGBpqEt84DAMCzKnt4fT6h15MfiC/7aOqdZb0JACpYglh9k1zUBLGdO3eWt956ywSYOnbsKPv27ZNVq1bJAw88UGguDh1spZ2Lg3IDAIpGa7wOHDhQOnToYO7x2oRae6HTlxIqOjpa6tevb5q8qdjYWOnatatMnTpVevbsKYsXL5ZNmzbJ7NmzHescMWKE6c3uhhtukG7duklSUpJ89NFHkpyczGmBx/lymc9zIjyNgBMAoEwSxO7atcvtMlqzSZfr0qWLWJYlZ8+elUcffbTQJnX6YDJhwgSPbz8AwLM0MHT06FGJj483ib8155IGiOxyQpvB6YsJ55cQ2tR67Nixphxo0qSJ6YGuVatWjnk0SbjWntWy4PHHHzc5AN977z1TjgAAyg4BJwBAuaJvpCdNmiSvvvqqSTC+Z88e84Z74sSJMm7cOLfLaA0qfWvuXMOJBLAAUD4NHTrUDO64q5XUt29fMxTmoYceMgMAoPwg4AQAKFcJYjWopM3nHn74YTPeunVr09zikUcekWeeecblzbdzLg4dgOKiaYR38uXzpmjWAgDwBSQNBwCUqwSxp06dyhdU0qCV0iZ2AAAAAMo/ajgBAMpVgthevXqZnu2uvvpqR5M6rfWk0+3AEwAAAIDyjYATAKBcJYjVxLCVKlUy/x46dEguu+wyE2x6/vnnOVMAAACAlyDgBAAoVwliK1euLAkJCWYAAAAAUEFyOH355ZfmTXO9evXMG2jtlrSo/vvf/5oHCX27DQAAAAAAAN9U7ICT5t1o27atJCYmFmu548ePmzwdN998c3G/EgAAAAAAAL7cpO7WW281Q3E9+uijct9995mEr8WpFQUAAAAAAAAfr+F0Pt544w3Zt29fkfNxZGVlyYkTJ1wGAAAAAAAAeIcSDzj99NNPMmrUKHnrrbdM/qai0K6xg4ODHUODBg1KejMBAAAAAADgDQGnnJwc04xuwoQJ0rRp0yIvN3r0aMnIyHAMqampJbmZAAAAAAAAKMscTsVx8uRJ2bRpk2zdutXRHXZubq5YlmVqO3366ady00035VsuMDDQDAAAAAAAAPA+JRpwql69unz33Xcu01599VX5/PPPZdmyZdKoUaOS/HoAAAAAAAB4Q8Dp999/lz179jjG9+/fL9u2bZOaNWvK5ZdfbprDHTp0SBYuXCh+fn7SqlUrl+Vr164tQUFB+aYDAAAAAACgggactIlct27dHONxcXHm34EDB8r8+fPl8OHDkpKS4tmtBAAAAAAAgO8GnG688UaTg6kgGnQqzPjx480AAAAAAAAA31SivdQBAAAAAACg4iHgBAAAAAAAAI8i4AQAAAAAAACPIuAEAAAAAAAAjyLgBAAAAAAAAI8i4AQAAAAAAACPIuAEAAAAAAAAjyLgBAAAAAAAAI8i4AQAAAAAAACPIuAEAAAAAAAAjyLgBAAAAAAAAI8i4AQAAAAAAACPIuAEAAAAoNQkJiZKWFiYBAUFSUREhGzcuLHQ+ZcuXSrNmzc387du3VpWrVrl8vmDDz4olSpVchl69OhRwnsBADgXAk4AAAAASsWSJUskLi5OEhISZMuWLdK2bVuJioqSI0eOuJ1/3bp1MmDAABk8eLBs3bpVevfubYYdO3a4zKcBpsOHDzuGd955hzMKAGWMgBMAAACAUjFt2jSJiYmRQYMGScuWLWXWrFlStWpVmTdvntv5Z8yYYYJJI0aMkBYtWsjEiROlffv2MnPmTJf5AgMDJTQ01DFceumlnFEAKGMEnAAAAACUuOzsbNm8ebNERkb++TDi52fG169f73YZne48v9IaUXnnT05Oltq1a0uzZs1kyJAh8ssvvxS6LVlZWXLixAmXAQDgWZU9vD6gTPV68gOfPQMfTb2zrDcBAADgvB07dkxycnKkTp06LtN1fNeuXW6XSUtLczu/TrdpDai7775bGjVqJHv37pUxY8bIrbfeaoJS/v7+btc7efJkmTBhAmcTAEoQAScAAAAAXqt///6OvzWpeJs2beSKK64wtZ5uvvlmt8uMHj3a5JKyaQ2nBg0alMr2AkBFQZM6AAAAACUuJCTE1DhKT093ma7jmnfJHZ1enPlV48aNzXft2bOnwHk051P16tVdBgCAZxFwAgAAAFDiAgICJDw8XNauXeuYlpuba8Y7derkdhmd7jy/WrNmTYHzq//9738mh1PdunU9uPUAgBIPOH355ZfSq1cvqVevnlSqVElWrFhR6PzLly+XW265RS677DLz5kALh9WrVxd7QwEAAAB4N23GNmfOHFmwYIHs3LnTJPjOzMw0vdap6Oho09zNFhsbK0lJSTJ16lST52n8+PGyadMmGTp0qPn8999/Nz3YbdiwQQ4cOGCCU3feeadceeWVJrk4AMCLAk5aILRt21YSExOLHKDSgNOqVatMrxTdunUzAautW7eez/YCAAAA8FL9+vWTl156SeLj46Vdu3aybds2E1CyE4OnpKTI4cOHHfN37txZFi1aJLNnzzbPIMuWLTMvvFu1amU+1yZ627dvlzvuuEOaNm0qgwcPNrWovvrqK9NsDgDgRUnDtccHHYpq+vTpLuOTJk2SDz74QD766CO5+uqri/v1AAAAALyY1k6yayjlpYm+8+rbt68Z3KlSpQqtJwCgnCr1Xuq0nfbJkyelZs2aBc6TlZVlBudeIwAAAAAAAOAdSj1puFah1bbW9957b4HzTJ48WYKDgx0DXZQCAAAAAAB4j1INOGn76wkTJsi7774rtWvXLnA+TRSYkZHhGFJTU0tzMwEAAAAAAOANTeoWL14sDz/8sCxdulQiIyMLnVcT/JHkDwAAAAAAwDuVSg2nd955x3R1qv/27NmzNL4SAAAAAAAA3lLDSfMv7dmzxzG+f/9+052pJgG//PLLTXO4Q4cOycKFCx3N6AYOHCgzZsyQiIgISUtLc/QoofmZAAAAAAAAUMFrOG3atEmuvvpqM6i4uDjzd3x8vBk/fPiwpKSkOOafPXu2nD17Vh577DGpW7euY4iNjfXkfgAAAAAAAMBbazjdeOONYllWgZ/Pnz/fZTw5Ofn8tgwAAAAAAABeqVR7qQMAAAAAAIDvI+AEAAAAAACAsm1SBwAAAAAA4C16PfmB+LKPpt4p5RE1nAAAAAAAAOBR1HACAJS4xMRE+cc//iFpaWnStm1beeWVV6Rjx44Fzn/8+HF55plnZPny5fLrr79Kw4YNZfr06XLbbbdxtgAAXs+Xa1uU15oWAEofAScAQIlasmSJxMXFyaxZsyQiIsIEjqKiomT37t1Su3btfPNnZ2fLLbfcYj5btmyZ1K9fXw4ePCg1atTgTAEAAABegoATAKBETZs2TWJiYmTQoEFmXANPK1eulHnz5smoUaPyza/TtVbTunXr5KKLLjLTwsLCOEsAAACAFyGHEwCgxGhtpc2bN0tkZOSfBY+fnxlfv36922U+/PBD6dSpkzz22GNSp04dadWqlUyaNElycnIK/J6srCw5ceKEywAAAACg7BBwAgCUmGPHjplAkQaOnOm45nNyZ9++faYpnS63atUqGTdunEydOlWee+65Ar9n8uTJEhwc7BgaNGjg8X0BAAAAUHQEnAAA5Upubq7J3zR79mwJDw+Xfv36mQTi2hSvIKNHj5aMjAzHkJqaWqrbDAAAAMAVOZwAACUmJCRE/P39JT093WW6joeGhrpdpm7duiZ3ky5na9GihakRpU30AgIC8i0TGBhoBgAAAADlAzWcAAAlRoNDWktp7dq1LjWYdFzzNLlz3XXXyZ49e8x8th9//NEEotwFmwAAAACUPwScAAAlKi4uTubMmSMLFiyQnTt3ypAhQyQzM9PRa110dLRpEmfTz7WXutjYWBNo0h7tNGm4JhEHAAAA4B1oUgcAKFGag+no0aMSHx9vmsW1a9dOkpKSHInEU1JSTM91Nk34vXr1ahk+fLi0adNG6tevb4JPI0eO5EwBAAAAXoKAEwCgxA0dOtQM7iQnJ+ebps3tNmzYwJkBAAAAvBRN6gAAAAAAAOBRBJwAAAAAAADgUQScAAAAAAAA4FEEnAAAAAAAAOBRBJwAAAAAAABQtgGnL7/8Unr16iX16tWTSpUqyYoVK865jPZA1L59ewkMDJQrr7xS5s+ff77bCwAAAMCLJSYmSlhYmAQFBUlERIRs3Lix0PmXLl0qzZs3N/O3bt1aVq1aVeC8jz76qHlGmT59eglsOQCgRANOmZmZ0rZtW1NQFMX+/fulZ8+e0q1bN9m2bZs88cQT8vDDD8vq1auL+9UAAAAAvNiSJUskLi5OEhISZMuWLea5IioqSo4cOeJ2/nXr1smAAQNk8ODBsnXrVundu7cZduzYkW/e999/XzZs2GBejAMAvDDgdOutt8pzzz0nd911V5HmnzVrljRq1EimTp0qLVq0kKFDh0qfPn3k5ZdfPp/tBQAAAOClpk2bJjExMTJo0CBp2bKleVaoWrWqzJs3z+38M2bMkB49esiIESPMs8TEiRNNy4mZM2e6zHfo0CEZNmyYvP3223LRRReV0t4AAMo0h9P69eslMjLSZZq+xdDpBcnKypITJ064DAAAAAC8V3Z2tmzevNnl2cDPz8+MF/RsUJRnidzcXHnggQdMUOqqq64q0rbwvAEAPhBwSktLkzp16rhM03ENIp0+fdrtMpMnT5bg4GDH0KBBg5LeTAAAAAAl6NixY5KTk+P22UCfGYrzLOE8/5QpU6Ry5cry+OOPF3lbeN4AgAraS93o0aMlIyPDMaSmppb1JgEAAAAoZ7TGlDa7006JNFl4UfG8AQAlr3JJf0FoaKikp6e7TNPx6tWrS5UqVdwuo73Z6QAAAADAN4SEhIi/v7/bZwN9ZijOs4Q9/1dffWUSjl9++eWOz7UW1ZNPPml6qjtw4IDb9fK8AQA+UMOpU6dOsnbtWpdpa9asMdMBAAAAVAwBAQESHh7u8myg+Zd0vKBng3M9S2jupu3bt5vesO1Be6nTfE70ig0AXlbD6ffff5c9e/Y4xvfv329u7DVr1jRvFrR6qvYSsXDhQvP5o48+anqRePrpp+Whhx6Szz//XN59911ZuXKlZ/cEAAAAQLkWFxcnAwcOlA4dOkjHjh1NLaTMzEzTa52Kjo6W+vXrmxxLKjY2Vrp27Wp6vO7Zs6csXrxYNm3aJLNnzzaf16pVywzOtJc6rQHVrFmzMthDAMB5B5z0Bt+tWzeXQkNpwaFtpw8fPiwpKSmOzxs1amSCS8OHDzftq//yl7/I66+/bnqXAAAAAFBx9OvXT44ePSrx8fEm8Xe7du0kKSnJkRhcnyO05zpb586dZdGiRTJ27FgZM2aMNGnSRFasWCGtWrUqw70AAJRIwOnGG28Uy7IK/FyDTu6W2bp1a3G/CgAAAICPGTp0qBncSU5Ozjetb9++ZiiqgvI2AQBKV7nspQ4AAAAAAADei4ATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAyj7glJiYKGFhYRIUFCQRERGycePGQuefPn26NGvWTKpUqSINGjSQ4cOHy5kzZ853mwEAAAAAAOBLAaclS5ZIXFycJCQkyJYtW6Rt27YSFRUlR44ccTv/okWLZNSoUWb+nTt3yty5c806xowZ44ntBwAAAAAAgLcHnKZNmyYxMTEyaNAgadmypcyaNUuqVq0q8+bNczv/unXr5LrrrpP77rvP1Irq3r27DBgw4Jy1ogAAAAAAAFABAk7Z2dmyefNmiYyM/HMFfn5mfP369W6X6dy5s1nGDjDt27dPVq1aJbfddluB35OVlSUnTpxwGQAAAAAAAOAdKhdn5mPHjklOTo7UqVPHZbqO79q1y+0yWrNJl+vSpYtYliVnz56VRx99tNAmdZMnT5YJEyYUZ9MAAAAAAABQUXqpS05OlkmTJsmrr75qcj4tX75cVq5cKRMnTixwmdGjR0tGRoZjSE1NLenNBAAAAAAAQFnUcAoJCRF/f39JT093ma7joaGhbpcZN26cPPDAA/Lwww+b8datW0tmZqY88sgj8swzz5gmeXkFBgaaAQAAAAAAAD5ewykgIEDCw8Nl7dq1jmm5ublmvFOnTm6XOXXqVL6gkgatlDaxAwAAAFBxJCYmms6EgoKCJCIi4pydCS1dulSaN29u5teX15oP1tn48ePN59WqVZNLL73U5Jf9+uuvS3gvAAAeb1IXFxcnc+bMkQULFsjOnTtlyJAhpsaS9lqnoqOjTZM4W69eveS1116TxYsXy/79+2XNmjWm1pNOtwNPAAAAAHzfkiVLzPNEQkKCSbfRtm1biYqKkiNHjhTY47X2cD148GDZunWr9O7d2ww7duxwzNO0aVOZOXOmfPfdd/Kf//zH0TP20aNHS3HPAAAX1KRO9evXz9y84+PjJS0tTdq1aydJSUmOROIpKSkuNZrGjh0rlSpVMv8eOnRILrvsMhNsev7554v71QAAAAC82LRp0yQmJsbxsnrWrFkmv+u8efNk1KhR+eafMWOG9OjRQ0aMGGHGNQ+svsDWAJMua3dSlPc75s6dK9u3b5ebb765VPYLAOChpOFDhw6VgwcPSlZWlqmuqlVhnZOEz58/3zFeuXJl8wZjz549cvr0aROQ0mq0NWrUOJ+vBgBUgOYTNq0dqy8t9G02AMC7ZWdny+bNm02TN5u+qNbx9evXu11GpzvPr7RGVEHz63fMnj1bgoODTe0pAIAP91IHAKjYitt8wnbgwAF56qmn5Prrry+1bQUAlJxjx45JTk6Oo2WETce15YQ7Or0o83/88cdy8cUXmxcbL7/8sqkFpR0eFURfnJ84ccJlAAB4FgEnAECpNZ9o2bKlaQJRtWpV03yiIPpAcv/998uECROkcePGnCEAQKG6desm27ZtMzmftAnevffeW+iLjcmTJ5taUPbQoEEDjjAAeBgBJwBAuWo+oZ599lmpXbu2SRJbFLypBoDyT2scaadB6enpLtN1PDQ01O0yOr0o82sPdVdeeaVce+21Jn+TpvXQfwuinRxlZGQ4htTU1AvaNwBAfgScAADlqvmE9jCkDwnaI2pR8aYaAMq/gIAACQ8Pl7Vr1zqm5ebmmvFOnTq5XUanO8+vtLlcQfM7r1dfRhQkMDBQqlev7jIAADyLgBMAoNw4efKkPPDAAybYVFjujbx4Uw0A3kFz+uk9fsGCBbJz504ZMmSIZGZmOnqti46ONvd0W2xsrOkRe+rUqbJr1y4ZP368bNq0yXRipHTZMWPGyIYNG0ynRlqr9qGHHjK9Y/ft27fM9hMAIFKZgwAAKC/NJ/bu3WuShffq1cvlLbXS5hG7d++WK664wu2bah0AAOVbv3795OjRoxIfH29qurZr184ElOyasNqjtTa9tnXu3FkWLVokY8eONYGlJk2ayIoVK6RVq1bmcy1jNBClASytVVurVi255ppr5KuvvpKrrrqqzPYTAEDACQBQSs0nevfu7dJ8wn477ax58+by3XffuUzThwyt+TRjxgySugKAD9D7v7syQCUnJ+ebpjWVCqqtpL3SLV++3OPbCAC4cNRwAgCUePOJgQMHSocOHaRjx44yffr0fM0n6tevb/Iw6YOD/dbaVqNGDfNv3ukAAAAAyi8CTgCActV8AgAAAID3I+AEACh3zSeczZ8/v4S2CgAAAEBJ4ZUyAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAyj7glJiYKGFhYRIUFCQRERGycePGQuc/fvy4PPbYY1K3bl0JDAyUpk2byqpVq853mwEAAAAAAFCOVS7uAkuWLJG4uDiZNWuWCTZNnz5doqKiZPfu3VK7du1882dnZ8stt9xiPlu2bJnUr19fDh48KDVq1PDUPgAAAAAAAMCbA07Tpk2TmJgYGTRokBnXwNPKlStl3rx5MmrUqHzz6/Rff/1V1q1bJxdddJGZprWjAAAAAAAA4JuK1aROaytt3rxZIiMj/1yBn58ZX79+vdtlPvzwQ+nUqZNpUlenTh1p1aqVTJo0SXJycgr8nqysLDlx4oTLAAAAAAAAAB8MOB07dswEijRw5EzH09LS3C6zb98+05ROl9O8TePGjZOpU6fKc889V+D3TJ48WYKDgx1DgwYNirOZAAAAAAAA8OVe6nJzc03+ptmzZ0t4eLj069dPnnnmGdMUryCjR4+WjIwMx5CamlrSmwkAAAAAAICyyOEUEhIi/v7+kp6e7jJdx0NDQ90uoz3Tae4mXc7WokULUyNKm+gFBATkW0Z7stMBAAAAAAAAPl7DSYNDWktp7dq1LjWYdFzzNLlz3XXXyZ49e8x8th9//NEEotwFmwAAAAAAAFDBmtTFxcXJnDlzZMGCBbJz504ZMmSIZGZmOnqti46ONk3ibPq59lIXGxtrAk3ao50mDdck4gAAAAAAAKjgTeqU5mA6evSoxMfHm2Zx7dq1k6SkJEci8ZSUFNNznU0Tfq9evVqGDx8ubdq0kfr165vg08iRIz27JwAAAAAAAPDOgJMaOnSoGdxJTk7ON02b223YsOF8vgoAAAAAAABepsR7qQMAAAAAAEDFQsAJAAAAAAAAHkXACQAAAAAAAB5FwAkAAAAAAAAeRcAJAAAAQKlJTEyUsLAwCQoKkoiICNm4cWOh8y9dulSaN29u5m/durWsWrXK8dkff/xher/W6dWqVZN69epJdHS0/Pzzz6WwJwCAwhBwAgAAAFAqlixZInFxcZKQkCBbtmyRtm3bSlRUlBw5csTt/OvWrZMBAwbI4MGDZevWrdK7d28z7Nixw3x+6tQps55x48aZf5cvXy67d++WO+64gzMKAGWMgBMAAACAUjFt2jSJiYmRQYMGScuWLWXWrFlStWpVmTdvntv5Z8yYIT169JARI0ZIixYtZOLEidK+fXuZOXOm+Tw4OFjWrFkj9957rzRr1kyuvfZa89nmzZslJSWFswoAZYiAEwAAAIASl52dbQJBkZGRfz6M+PmZ8fXr17tdRqc7z6+0RlRB86uMjAypVKmS1KhRw4NbDwAorsocMgAAAAAl7dixY5KTkyN16tRxma7ju3btcrtMWlqa2/l1ujtnzpwxOZ20GV716tUL3JasrCwz2E6cOFHMvQEAnAs1nAAAAAB4PU0grk3rLMuS1157rdB5J0+ebJrj2UODBg1KbTsBoKIg4AQAAACgxIWEhIi/v7+kp6e7TNfx0NBQt8vo9KLMbwebDh48aHI6FVa7SY0ePdo0vbOH1NTU894vAIB7BJwAAAAAlLiAgAAJDw+XtWvXOqbl5uaa8U6dOrldRqc7z680oOQ8vx1s+umnn+Szzz6TWrVqnXNbAgMDTVDKeQAAeBY5nAAAAACUiri4OBk4cKB06NBBOnbsKNOnT5fMzEzTa52Kjo6W+vXrmyZvKjY2Vrp27SpTp06Vnj17yuLFi2XTpk0ye/ZsR7CpT58+smXLFvn4449Njig7v1PNmjVNkAsAUDYIOAEAAAAoFf369ZOjR49KfHy8CQy1a9dOkpKSHInBU1JSTM91ts6dO8uiRYtk7NixMmbMGGnSpImsWLFCWrVqZT4/dOiQfPjhh+ZvXZezL774Qm688UbOLACUEQJOAAAAAErN0KFDzeBOcnJyvml9+/Y1gzthYWEmSTgAoPwhhxMAAAAAAAA8ioATAAAAAAAAPIqAEwAAAAAAADyKgBMAAAAAAAA8ioATAAAAAAAAyj7glJiYaHqECAoKkoiICNm4cWORllu8eLFUqlRJevfufT5fCwAAAAAAAF8MOC1ZskTi4uIkISFBtmzZIm3btpWoqCg5cuRIocsdOHBAnnrqKbn++usvZHsBAAAAAADgawGnadOmSUxMjAwaNEhatmwps2bNkqpVq8q8efMKXCYnJ0fuv/9+mTBhgjRu3PhCtxkAAAAAAAC+EnDKzs6WzZs3S2Rk5J8r8PMz4+vXry9wuWeffVZq164tgwcPLtL3ZGVlyYkTJ1wGAAAAAAAA+GDA6dixY6a2Up06dVym63haWprbZf7zn//I3LlzZc6cOUX+nsmTJ0twcLBjaNCgQXE2EwAAAAAAAL7aS93JkyflgQceMMGmkJCQIi83evRoycjIcAypqakluZkAgBJWnM4mtMzQfH+XXnqpGbQWbVE7pwAAAABQPlQuzswaNPL395f09HSX6ToeGhqab/69e/eaZOG9evVyTMvNzf2/L65cWXbv3i1XXHFFvuUCAwPNAADwfnZnE5rzT4NN06dPN51NaBmgza3zSk5OlgEDBkjnzp1NgGrKlCnSvXt3+f7776V+/fplsg8AAAAASrCGU0BAgISHh8vatWtdAkg63qlTp3zzN2/eXL777jvZtm2bY7jjjjukW7du5m+aygGA7ytuZxNvv/22/P3vf5d27dqZcuT11193lDUAAAAAfLCGk9K31AMHDpQOHTpIx44dzZvqzMxM8yChoqOjzRtozcOkb6ZbtWrlsnyNGjXMv3mnAwB8j93ZhDaVLk5nE85OnTolf/zxh9SsWbPQziZ0sNHZBAAAAOBlAad+/frJ0aNHJT4+3iQK1zfQSUlJjkTiKSkp5mECAIDCOpvYtWtXkQ7QyJEjpV69ei49pOalLzkmTJjAAQcAAAC8NeCkhg4dagZ3NPdGYebPn38+XwkAqIBeeOEFWbx4sSlbtNZsQbQGldbAda7hRLNtAAAAwMsCTgAAlERnE85eeuklE3D67LPPpE2bNoXOS2cTAAAAQPlC2zcAQIkpbmcTthdffFEmTpxommxrzkAAAAAA3oUaTgCAElWczibUlClTTJ7ARYsWSVhYmMkXqC6++GIzAAAAACj/CDgBAEpUcTubeO2110zvdn369HFZT0JCgowfP56zBQAAAHgBAk4AgBJXnM4mDhw4wBkBAAAAvBw5nAAAAAAAAOBRBJwAAAAAAADgUQScAAAAAAAA4FEEnAAAAAAAAOBRBJwAAAAAAADgUQScAAAAAAAA4FEEnAAAAAAAAOBRBJwAAAAAlJrExEQJCwuToKAgiYiIkI0bNxY6/9KlS6V58+Zm/tatW8uqVatcPl++fLl0795datWqJZUqVZJt27aV8B4AAIqCgBMAAACAUrFkyRKJi4uThIQE2bJli7Rt21aioqLkyJEjbudft26dDBgwQAYPHixbt26V3r17m2HHjh2OeTIzM6VLly4yZcoUziIAlCMEnAAAAACUimnTpklMTIwMGjRIWrZsKbNmzZKqVavKvHnz3M4/Y8YM6dGjh4wYMUJatGghEydOlPbt28vMmTMd8zzwwAMSHx8vkZGRnEUAKEcIOAEAAAAocdnZ2bJ582aXwJCfn58ZX79+vdtldHreQJLWiCpofgBA+VG5rDcAAAAAgO87duyY5OTkSJ06dVym6/iuXbvcLpOWluZ2fp1+IbKyssxgO3HixAWtDwCQHzWcAAAAAFQokydPluDgYMfQoEGDst4kAPA5BJwAAAAAlLiQkBDx9/eX9PR0l+k6Hhoa6nYZnV6c+Ytq9OjRkpGR4RhSU1MvaH0AgPwIOAEAAAAocQEBARIeHi5r1651TMvNzTXjnTp1cruMTneeX61Zs6bA+YsqMDBQqlev7jIAAMpBwCkxMVHCwsIkKChIIiIiZOPGjQXOO2fOHLn++uvl0ksvNYMm/StsfgAAAAC+KS4uzjwfLFiwQHbu3ClDhgyRzMxM02udio6ONrWPbLGxsZKUlCRTp041eZ7Gjx8vmzZtkqFDhzrm+fXXX2Xbtm3yww8/mPHdu3eb8QvN8wQAKOWA05IlS0xBkZCQIFu2bJG2bduaniKOHDnidv7k5GQZMGCAfPHFF6Y3CW0f3b17dzl06NAFbjoAAAAAb9KvXz956aWXJD4+Xtq1a2cCQxpQshODp6SkyOHDhx3zd+7cWRYtWiSzZ882zx3Lli2TFStWSKtWrRzzfPjhh3L11VdLz549zXj//v3N+KxZs8pgDwEA591L3bRp0yQmJsbxFkJv5CtXrpR58+bJqFGj8s3/9ttvu4y//vrr8t5775mqsfoGAwAAAEDFobWTnGso5X1ZnVffvn3NUJAHH3zQDAAAL67hlJ2dLZs3bzbN4hwr8PMz41p7qShOnTolf/zxh9SsWbPAebSLUu2a1HkAAAAAAACADwacjh07Jjk5OY4qrzYdL2ob6ZEjR0q9evVcglZ50U0pAAAAAACA9yrVXupeeOEFWbx4sbz//vsm4XhB6KYUAAAAAACgguRwCgkJEX9/f0lPT3eZruOhoaGFLqvJATXg9Nlnn0mbNm3O2U2pDgAAAAAAAPDxGk4BAQESHh5uEn7bcnNzzXinTp0KXO7FF1+UiRMnmh4oOnTocGFbDAAAAAAAAN/qpS4uLk4GDhxoAkcdO3aU6dOnS2ZmpqPXOu15rn79+iYPk5oyZYrp9lS7Mw0LC3Pkerr44ovNAAAAAAAAgAoecOrXr58cPXrUBJE0eNSuXTtTc8lOJJ6SkmJ6rrO99tprpne7Pn36uKwnISFBxo8f74l9AAAAAAAAgDcHnNTQoUPN4E5ycrLL+IEDB85vywAAAAAAAOCVSrWXOgAAAAAAAPg+Ak4AAAAAAADwKAJOAAAAAAAA8CgCTgAAAAAAAPAoAk4AAAAAAADwKAJOAAAAAAAA8CgCTgAAAAAAAPAoAk4AAAAAAADwKAJOAAAAAAAA8CgCTgAAAAAAAPAoAk4AAAAAAADwKAJOAAAAAAAA8CgCTgAAAAAAAPAoAk4AAAAAAADwKAJOAAAAAAAA8CgCTgAAAAAAAPAoAk4AAAAAAADwKAJOAAAAAAAA8CgCTgAAAAAAAPAoAk4AAAAAAAAo+4BTYmKihIWFSVBQkERERMjGjRsLnX/p0qXSvHlzM3/r1q1l1apV57u9AAAvRLkBACipMsGyLImPj5e6detKlSpVJDIyUn766ScOOAB4W8BpyZIlEhcXJwkJCbJlyxZp27atREVFyZEjR9zOv27dOhkwYIAMHjxYtm7dKr179zbDjh07PLH9AIByjnIDAFCSZcKLL74o//znP2XWrFny9ddfS7Vq1cw6z5w5w4EHAG8KOE2bNk1iYmJk0KBB0rJlS3Njr1q1qsybN8/t/DNmzJAePXrIiBEjpEWLFjJx4kRp3769zJw50xPbDwAo5yg3AAAlVSZo7abp06fL2LFj5c4775Q2bdrIwoUL5eeff5YVK1Zw4AGgDFUuzszZ2dmyefNmGT16tGOan5+fqba6fv16t8vodH2L4UzfOBRWAGRlZZnBlpGRYf49ceKElIY/sk6JLyut41gWfPnc+fJ5g2evEf2f7/KCcsM3+PL9h3LDO/nyeSvN31xplxslUSbs379f0tLSzDpswcHBpqmeLtu/f3+36+V5o+T4cpnh6/cfXz53vnzeynO5UayA07FjxyQnJ0fq1KnjMl3Hd+3a5XYZLQDcza/TCzJ58mSZMGFCvukNGjQozuaiAMGJHBpvxHlDUZ08edL8z3Z5QLnhG7j/eCfOm/cq7XNXWuVGSZQJ9r88b5Qf3Hu8F+fOewWX03KjWAGn0qJvPZzfZOTm5sqvv/4qtWrVkkqVKokv0QihBtJSU1OlevXqZb05KAbOnXfy5fOmbxr05l+vXj2paCg3UN758r3H1/nyuaPc4HkD5Zcv33t8nS+fO6uYzxvFCjiFhISIv7+/pKenu0zX8dDQULfL6PTizK8CAwPN4KxGjRriy/RC9LWLsaLg3HknXz1v5aVmk41yo+T46jXs6zhv3stXz11plhslUSbY/+o07aXOeZ527doVuC08b8Bb+Oq9pyKo7qPnrjjlRrGShgcEBEh4eLisXbvWpfaRjnfq1MntMjrdeX61Zs2aAucHAPgOyg0AQEmWCY0aNTJBJ+d5tHaB9lbH8wYAlK1iN6nTpm4DBw6UDh06SMeOHU2vEJmZmaanCRUdHS3169c3eZhUbGysdO3aVaZOnSo9e/aUxYsXy6ZNm2T27Nme3xsAQLlDuQEAKKkyQdNtPPHEE/Lcc89JkyZNTABq3LhxprlH7969OfAA4E0Bp379+snRo0clPj7eJOnTqqpJSUmORH0pKSmmtwlb586dZdGiRaar0jFjxpiCQHuVaNWqlWf3xEtpdd6EhIR8TQhR/nHuvBPnrfRRbngW17B34rx5L85d+S8Tnn76aRO0euSRR+T48ePSpUsXs86goCAPb7134hr2Tpw378W5+1Mlqzz1nw0AAAAAAACvV6wcTgAAAAAAAMC5EHACAAAAAACARxFwAgAAAAAAgEcRcAJEJCwszPSSUhjtBUWTVHrSgw8+6NM9qBw4cMAct23btpX6d5fXYzt+/HiTIBWAd6PcKBmUG/lRbgC+gXKjZFBulPNyw6qA1q1bZ/n5+Vm33XZbsZdNSEiw2rZte17fm5WVZU2ZMsVq06aNVaVKFatWrVpW586drXnz5lnZ2dnntU4UzxtvvGEFBwfnm96wYUPr5ZdfLnTZw4cPW2fOnPHoIT9+/Lj122+/WeXRwIEDrTvvvNNl2tKlS63AwEDrpZdeMp/rLcQeatasaUVFRVnffvutY/6zZ8+a4/bHH394ZJu++OILx/dVqlTJql69utWuXTtrxIgR1s8//1zujq1u5/vvv+8y7eTJk9axY8fKbJtwfig3Ki7KjaKj3LhwlBu+g3Kj4qLcKDrKDd8vNypkDae5c+fKsGHD5Msvv5Sff/65VL4zOztboqKi5IUXXjBdtq5bt042btwojz32mLzyyivy/fffn/e6//jjDympbcafQkNDTReXnhQcHCw1atTwisP8+uuvy/333y+vvfaaPPnkk2Zajx495PDhw2ZYu3atVK5cWW6//XbHMv7+/ua46XRP2r17t/ntfvPNNzJy5Ej57LPPTPfI3333Xakc2wv5zV188cVSq1YtKSn8bksG5QbX3/mg3KDcsFFuVDyUG0XD/7e4otyg3PC5csOqYDTad/HFF1u7du2y+vXrZz3//POFRqM1WmgfJv3cuUaHDjpNHTx40LrjjjusatWqWZdcconVt29fKy0tzbEerdmktaq2bNmSb5u0dtPvv/9u/v7kk0+s6667zmyH1hjp2bOntWfPHse8+/fvN9+7ePFi64YbbjC1TWbMmGEFBQVZq1atclnv8uXLzb5mZmaa8ZSUFLNduu5LL73UbK+uL2+E+bnnnrPq1q1rhYWFWeWN1jAaNmyYddlll5l912O1ceNGl9ovH3/8sdW6dWvzeUREhPXdd9+5fO48aI01u4bTs88+a/Xv39+qWrWqVa9ePWvmzJkFRo/t8/Dee+9ZN954o6mxpjXX9G1W3uspKSnJat68ubk2tAaQc02cvFH9rl27mv3TGjt6jurUqePYRtvOnTvNfuv+tWjRwlqzZo3byPaFct42vX71GtNrqqBtV1999ZXZliNHjrgcp61bt7qcg88++8wKDw83x61Tp07m92jbtm2bOaZ67epvqX379tY333zjsnzemkunTp2ymjVrZo5LQduntbNatWpl9kN/WzfffLPjd6fmzp1rtWzZ0goICLBCQ0Otxx57zPGZfuerr75q9erVy1wf9jlZsWKFdfXVV5tz0ahRI2v8+PGO2lx6TTlfazrurpZk3mvSeV6l12+PHj3M9VO7dm3rr3/9q3X06FGXa0a3NTY21tSa1GMHz6LcoNyg3Cgayg3KDVBu8LzB80ZxUG485vPPGxUu4KQPlR06dDB/f/TRR9YVV1xh5ebmFingpA+1Tz75pHXVVVeZZkI66LScnBzTrKdLly7Wpk2brA0bNpiHaT0xNg1GdO/e/Zzbt2zZMhPE+Omnn8xDul5wGjzR73B+gNdgkM63b98+E8Do06ePuTCc3XPPPY5pGtTS4MRDDz1kbd++3frhhx+s++67zzyka1M/+wevD/kPPPCAtWPHDjOUN48//rgJBmlw7fvvvzfbrIGZX375xRGM0P389NNPzX7efvvt5ljp/ut+Tp8+3TTDss+fPkgq/cFpcGPy5MnW7t27rX/+85+Wv7+/WU9hAScNJGmAS5fRc6DrsW8Aej1ddNFFVmRkpAmYbN682WybHvfCAk66fXoj+fHHH60FCxaYpmP2dmgTNT1nt9xyiwnMaICnY8eOJRpwevrpp811oUEid5/b9Fj+7W9/s6688sp812vegJMGApOTk805vP76603TUpv+vvS61cCaHoN3333X7Kvz8u6aymmTSP0sPT093/bpb6Ry5crWtGnTzDbptZGYmOg4/3pz10CUXh96LjWI6dzEUterN19t/rp3714TYP7yyy/NuZo/f76ZpudIrzU9d0qDbnZQWq81OwiXtwCwr0UdNLisx09/g0r3U4Oro0ePNsdDA9Z67rt16+Zyzej50SClBu6cg3fwDMoNyg3KjaKh3KDcAOUGzxs8bxQH5cbLPv+8UeECTvpgqw+VSgMDISEh5iG2KAGngnI46YnX4IRG9G36IK3L2bVvtCaHBkuKSyOLuh67lo79AG/vg/N2OtdmysjIMA/QWmNKvfnmmyZQYQfXlAZgdLtWr17t+MFrjRo7AFXeaG0UDeC8/fbbjmkaSNIA1IsvvugIRmjtL5sGonQflyxZcs421RrVdaY14G699dZCA06vv/56vnOuP1T7u3TcuYaaBjn0GBcWcNLApbNrrrnGGjlypPlbz6cGTvRmYSvJGk5a20fXvXbtWref63WvkXAddD6tGaeBNVthNZxsK1euNNNOnz5txjXwpzdVdwoLOOmx0c++/vrrfMdWt0k/O3DggNv16jX0zDPPFHgsdNknnnjCZZrWkJo0aZLLNP2d6TFwXi7veSkoD5z+Nu+66y4TrNZAtpo4cWK+QHVqaqpZrwbG7GtG33qg5FBuUG5QbhQN5cafKDcqNsoNyg3KjaKh3PD9cqNC5XDSvC+aN2nAgAFmXPPK9OvXz7SxvhA7d+6UBg0amMHWsmVLkz9GP1P/dy2c208//WS2r3HjxlK9enXTm4FKSUlxma9Dhw4u47fddptcdNFF8uGHH5rx9957zywfGRlpxr/99lvZs2ePXHLJJaZNpw41a9aUM2fOyN69ex3rad26tQQEBEh5pNupbVmvu+46xzTd544dOzqOs+rUqZPjb93HZs2auXxeEOfl7PFzLdemTRvH33Xr1jX/HjlyxDGtatWqcsUVV7jM4/z5udaZdxm9hvU60/bdNt3/kqLbotdgQkKC/P777/k+79atm+mBTgf9bWmesltvvVUOHjx4zvUWdNzi4uLk4YcfNteu5jxzvj4LY//GtFe8vNq2bSs333yzub779u0rc+bMkd9++83xvZoPSj8vTN7fnP6mnn32WcfvSYeYmBiTz+rUqVNSXGPGjJH169fLBx98IFWqVHF8xxdffOHyHc2bNzefOR+X8PDwYn8fioZyg3KjMJQb+VFu/Ilyo2Ki3KDcKAzlRn6UG75dbng2k285p4Gls2fPSr169VweUjUR9MyZM8XPzy9fYMhTCbmbNm0qu3btOud8vXr1koYNG5oHYt3O3Nxckww5b2KuatWquYxrkKhPnz6yaNEi6d+/v/lXg2l2smYNFuhF8vbbb+f7zssuu6zA9aJwGvCy2YEOPWfuPrfnOVfw0d0yzussTfXr15dly5aZwJImCP/kk09M0NL5ernyyitdEotrsm69fp977rnzOm7ajed9990nK1euNN+nwa7FixfLXXfdVei22sFBO0jrTJOXr1mzxiTr//TTT02i/meeeUa+/vprCQkJKdKxyPvb0N/UhAkT5O677843b1BQkBTHW2+9JS+//LIkJyebY+78HXpPmDJlSr5l7ECdu22D51BuUG54GuUG5Qblhm+j3KDc8DTKDcqNu734eaPC1HDSQNPChQtl6tSpjhoZOmhETwM777zzjgm8nDx5UjIzMx3L6Tx5Azs5OTku01q0aCGpqalmsP3www9y/PhxU9NJ6QO09qS1devWfNumQS39zl9++cW8FRk7dqypbaHrtWthFIX2IJaUlGR6vPv888/NuK19+/am9lTt2rVNgMB50ACBN9CaQnr8//vf/7ocO+2pzD7OasOGDY6/9fj9+OOP5lgWdP7cLWeP28uVF1pbS6+z9PR0xzTd/5KkAdB///vfkpaWZoJO+hspiAaPNHB7+vTpCw7QDh8+3ASH9Ab7xhtvFDq/ft/s2bPlhhtucAmg5t02rR2nQSL9Heq18P7775sAmgaptJe94tDflP5e8/6edNBjYP8PQkHXm03fMmiNrn/9619y7bXX5vsO/T3r9uX9DoJMJY9yg3JDUW4UH+WGe5Qbvo9yg3JDUW4UH+WG75YbFSbg9PHHH5vgw+DBg02NIefhnnvuMW8jIiIiTBMorWqm1ce0ltD8+fNd1qMnYv/+/SYQdezYMcnKyjJNf7SpjgZ4tmzZYpoWRUdHS9euXR3V4p544gnzsKuBpMTERBPo2rdvn7z77rvmpGsw6NJLLzXdF+qDszZ/06CRNi8qKn3Y1qZWuh2NGjUy+2PTaVqT484775SvvvrK7INGNx9//HH53//+J95AL/ghQ4bIiBEjTGBNg3papVCrE+p5tWm1Qw0e7NixQx588EGz371793acP43i6ud6/pyrImog68UXXzQBKj1HS5culdjYWClPbrnlFhN4GzhwoGzfvt1sswYoC2pK5inajE+vF21+ps3mTpw4Yabr9a+BKB20htGwYcMcUfLzoYGjoUOHmu/SZnm6fxpQyxv40+3Q79TfjdZ+0t+Wns/XXnvN7Xq1JtOkSZNk06ZNpnnq8uXL5ejRo471aq0qDUb/85//NOvU37HWgipMfHy8CWJrAEtv0rr/ui32+VB2IEu31V3wWKdrzS2tlajH1T6Wum3qsccek19//dU0s9XjoPel1atXy6BBg85ZsODCUW5Qbti/Y8qN4qPcyI9yw/dRblBuKMqN80O54aPlhlVBaG9lt912m9vPNMmwHopvv/3WJNzSrO2aaFqXmT17tkvS8DNnzpje32rUqOHICK80i/wdd9xhkidr0uO+fftaaWlpLt+jy2ovaNrrnN01u3bjrgmS7Z7NNAG09u6g3R5qz3bak5e7ZNV2Eua8tEcx/Tw+Pj7fZ5poOjo62iRK1/U3btzYiomJMQnGC+rmvrzRxNLDhg1z7IMePzsxu51QWnsf1J7ONOG19uCm59XZo48+arpz1Hnt7iY1afiECRPMedNuKENDQ60ZM2a4LHeu86CJrHVacZLQu0sart1NOtPPdT6bJiXX/db9017ydH91nUlJSZYnubse/ve//1lNmjSxrr32WpNwzrlrTb3uNcG59rR4rqThzkm/9TOdpvNqwvr+/ftbDRo0MPunybyHDh3qSChuL6+D9t6n36kJ8bTHBOdE6nm3X3tljIqKMj0w6HXTtGlT65VXXnGZf9asWSaxviam10R8ep3ZCkrKrsdcE4Pq/UJ7kNDrTe8Ztg8//NDcTzTRu7tuSp33p6BuSrWnPj3Wes/R79FzrgkF7Q4A3F0z8AzKDcoNG+VG0VBuUG5UdJQblBs2yo2iodwY5vPPG5X+/84BXk9rxWiuIY3sasL2ikJrAXXp0sXUinNOUA4AKBzlBuUGABQH5QblBoqnQiUNB3yB5h3S3gOaNGligkza7E+blBFsAgBQbgAAeN5AeUHACfAymrR75MiRJheR5qfSHGKafwgAAMoNAADPGygvaFIHAAAAAAAAj6owvdQBAAAAAACgdBBwAgAAAAAAgEcRcAIAAAAAAIBHEXACAAAAAACARxFwAgAAAAAAgEcRcAIAAAAAAIBHEXACAAAAAACARxFwAgAAAAAAgEcRcAIAAAAAAIB40v8D/NxOnL+ByMAAAAAASUVORK5CYII=", "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
libraryfit_stransform_strain_r2test_r2r2_drop
0AutoCarver6.6490.09690.66520.65950.0057
1optbinning2.8830.00970.51450.50770.0068
2KBinsDiscretizer0.0120.00260.61810.6192-0.0011
\n", "
" ], "text/plain": [ " library fit_s transform_s train_r2 test_r2 r2_drop\n", "0 AutoCarver 6.649 0.0969 0.6652 0.6595 0.0057\n", "1 optbinning 2.883 0.0097 0.5145 0.5077 0.0068\n", "2 KBinsDiscretizer 0.012 0.0026 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": "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8op5X7/u9lSqVCkrJibG2rx5s6v8hQsXzLn6+++/HdmnFStWuLYXFBRkhYeHW1FRUdbAgQOt/fv357rzqfu5cOFCj3knTpywjhw5kmP7hJxDHZO/UMdkjDrm8lHHwB11TP5CHZMx6pjAr2PybEunN998Ux5//HH56quvZP/+/dmyzfPnz0tMTIy8+OKL8tBDD8mqVaskOTlZ+vbtK5MmTZLvv/8+y+v++++/xV/7nN9FRESYISudVLx4cSlRooTkVm+88Ybce++9MnXqVHnyySfNvHbt2snvv/9upmXLlklISIjcdtttrmWCg4PNudL5TtqxY4f5jn777bfy9NNPy5dffin16tWTLVu2ZMv5vJzvVtGiRaV06dLiL3w/cy/qmMzhM0wdQx1DHQPqGO5j/If7GO5jAuI+xsqDNGpXtGhRa/v27Va3bt2s559/PsNIskb97EPV991bfOik89Qvv/xi3X777VaRIkWsYsWKWV27drUOHDjgWo+2cNLWVRs2bEizT9rK6eTJk+b/P/vsM6tFixZmP7RFSceOHa1du3a5yu7evdtsd968eVarVq1Ma5QJEyZYYWFh1pIlSzzW++GHH5pjPXXqlHm9d+9es1+67pIlS5r91fWljhSPGjXKKl++vBUZGWnlBtrS6PHHH7euvPJKc7x6fpKTkz1axCxevNiqX7++eb9p06bWli1bPN53n7S1mt3SacSIEdbdd99tFS5c2KpQoYI1efLkdCO/9rn/4IMPrDZt2pjWatpqTZ84pf4MJSUlWbVr1zafB20V5N46J3VEvnXr1ub4tBWPXpdy5cq59tH2ww8/mOPW46tTp461dOlSr1HprHDfH/2c6mdJPzvp7a/673//a7Z/6NAhj3OzceNGj/P+5ZdfWtHR0eZcNWvWzHzvbJs2bTLnUT+j+p1p1KiR9e2333osn7oF0+nTp62rr77anIv09k9badWrV88ch36Hbr75Ztf3S7355ptW3bp1rdDQUCsiIsLq27ev6z3d5muvvWZ16tTJfCbs67Bo0SLr2muvNee/atWq1rBhw1ytuvRz5P750tfeWkWm/hy6l1X6mW3Xrp35zJQtW9a67777rMOHD3t8TnRf4+PjTStJPXfIfahjqGOoYzxRx1DHgDrGxn0M9zHcx3AfU9rH+5g8GXTSG87GjRub///kk0+s6tWrWxcvXsxU0ElveJ988knrmmuuMV2JdNJ5KSkpputPy5YtrXXr1llr1qwxN9p6k2jT4MStt956yf17//33TVBj586d5gZeb341mKLbcP+x1oCQlvv5559NQKNLly7mJtXdnXfe6ZqngS39kj/wwAPWd999Z23bts265557zA28dvuz/2GoAYD777/f2rp1q5lygyeeeMIEhDSo9v3335v91ODMH3/84QpO6LF98cUX5thuu+02c370mPXYxo8fb7pn2ddMbwqV3vBrsGP06NHWjh07rIkTJ1rBwcFmPRkFnTSYpEEuXUbPu67HDkDoZ+iKK66w2rZtawIo69evN/um5zqjoJPunwYyfvzxR+utt94yXcrs/dCua3qdbrnlFhOo0YBPkyZNHA86DRo0yFx/DRR5e9+m5+/hhx+2atSokeZzmTropAHAlStXmut2ww03mO6kNv0e6edTA2p63O+99545PvflvXWb0y6R+t7BgwfT7J9+F0JCQqxx48aZfdLPw5QpU1zXXANKGozSz4RePw1eunex1PVqwEe7vP70008mmPzVV1+Z6zN79mwzT6+Lfr70eikNvNkBaP182YG41EEn+/OnkwaS9fzpd03pcWpQdfDgweZ8aHBar/eNN97o8TnR66PBSQ3euQfwkHtQx1DHUMd4oo6hjgF1DPcx3MdwH/MP7mMG+nwfkyeDTnrTqzecSgMFZcqUMTe4mQk6pZfTSW9CNVihLYlsepOty9ktcrSlhwZPfKUtHXQ9dssd++bePgb3/XRv1XTs2DFzc60tp9ScOXNM4MIOsCkNyOh+ff75565/GGorGzsIlRtoCxUN4rz77ruueRpM0iDUmDFjXMEJbfll02CUHtf8+fMv2RdaW5a409Zv7du3zzDo9MYbb6S5zhoosLelr91bp2nQQ89rRkEnDVi6u+6666ynn37a/L9eQw2kaLDC5nRLJ231o+tbtmyZ1/f1860tcHTSctoSTgNqtoxaOtk+/fRTM+/MmTPmtQb8NJDjTUZBJz0f+t7atWvTnE/dJ31vz549Xtern5tnn3023XOhy/bv399jnraUeuGFFzzm6fdJz4H7cqmvRXr53/Q7eMcdd5jAtAat1ciRI9MEpfft22fWq8Ex+3Oira2Qu1HHUMfYqGP+QR3zP9QxoI7hPob7GO5juI8J8JxOmh9G8yh1797dvNb8M926dTP5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", "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 }