diff --git a/.github/workflows/deploy_docu_dev.yml b/.github/workflows/deploy_docu_dev.yml index bec1c65e..b2bd991d 100644 --- a/.github/workflows/deploy_docu_dev.yml +++ b/.github/workflows/deploy_docu_dev.yml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} @@ -42,7 +42,7 @@ jobs: run: | sudo apt install dirmngr gnupg apt-transport-https ca-certificates software-properties-common sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 - sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' + sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/' - name: Install R run: | sudo apt-get update diff --git a/.github/workflows/deploy_docu_stable.yml b/.github/workflows/deploy_docu_stable.yml index 831b968c..9260259b 100644 --- a/.github/workflows/deploy_docu_stable.yml +++ b/.github/workflows/deploy_docu_stable.yml @@ -9,7 +9,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Check out the repo containing the docu source @@ -31,7 +31,7 @@ jobs: run: | sudo apt install dirmngr gnupg apt-transport-https ca-certificates software-properties-common sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 - sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' + sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/' - name: Install R run: | sudo apt-get update diff --git a/.github/workflows/test_build_docu_dev.yml b/.github/workflows/test_build_docu_dev.yml index 01b58c10..a9a69e5c 100644 --- a/.github/workflows/test_build_docu_dev.yml +++ b/.github/workflows/test_build_docu_dev.yml @@ -27,7 +27,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} @@ -69,7 +69,7 @@ jobs: run: | sudo apt install dirmngr gnupg apt-transport-https ca-certificates software-properties-common sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 - sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' + sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/' - name: Install R run: | sudo apt-get update diff --git a/.github/workflows/test_build_docu_released.yml b/.github/workflows/test_build_docu_released.yml index 772aba94..518fe29c 100644 --- a/.github/workflows/test_build_docu_released.yml +++ b/.github/workflows/test_build_docu_released.yml @@ -18,7 +18,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Check out the repo containing the docu source @@ -40,7 +40,7 @@ jobs: run: | sudo apt install dirmngr gnupg apt-transport-https ca-certificates software-properties-common sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 - sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' + sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/' - name: Install R run: | sudo apt-get update diff --git a/doc/api/api.rst b/doc/api/api.rst index 498d0d06..5844055a 100644 --- a/doc/api/api.rst +++ b/doc/api/api.rst @@ -38,6 +38,16 @@ Double machine learning models DoubleMLCVAR DoubleMLQTE +Other models +------------------------------ +.. currentmodule:: doubleml + +.. autosummary:: + :toctree: generated/ + :template: class.rst + + rdd.RDFlex + Datasets module --------------- @@ -73,6 +83,7 @@ Dataset generators datasets.make_confounded_irm_data datasets.make_heterogeneous_data datasets.make_irm_data_discrete_treatments + rdd.datasets.make_simple_rdd_data Utility classes and functions ----------------------------- diff --git a/doc/conf.py b/doc/conf.py index 0b848a55..90686507 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -197,6 +197,12 @@ 'https://glmnet.stanford.edu/reference/cv.glmnet.html', # Valid URL (error not replicable), Causes 409 Client Error: Too Many Requests for url 'http://dx.doi.org/10.2139/ssrn.3619201', + # Valid URL, Causes ConnectTimeoutError + 'https://folia.unifr.ch/global/documents/306524', + # Valid DOI; Causes 403 Client Error: Forbidden for url:... + 'https://doi.org/10.1146/annurev-economics-051520-021409', + # Valdi DOI; Causes 504 Server Error: Gateway Time-out for ... + 'https://doi.org/10.1017/CBO9781139060035.008' ] # To execute R code via jupyter-execute one needs to install the R kernel for jupyter diff --git a/doc/examples/index.rst b/doc/examples/index.rst index 7daddaae..f363e19e 100644 --- a/doc/examples/index.rst +++ b/doc/examples/index.rst @@ -30,6 +30,8 @@ General Examples py_double_ml_did_pretest.ipynb py_double_ml_basic_iv.ipynb py_double_ml_plm_irm_hetfx.ipynb + py_double_ml_meets_flaml.ipynb + py_double_ml_rdflex.ipynb Effect Heterogeneity diff --git a/doc/examples/py_double_ml_apo.ipynb b/doc/examples/py_double_ml_apo.ipynb index fc378910..d36d136b 100644 --- a/doc/examples/py_double_ml_apo.ipynb +++ b/doc/examples/py_double_ml_apo.ipynb @@ -6,7 +6,7 @@ "source": [ "# Python: Average Potential Outcome (APO) Models\n", "\n", - "In this example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to estimate average potential outcomes (APOs) in an interactive regression model (see [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm)).\n", + "In this example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to estimate average potential outcomes (APOs) in an interactive regression model (see [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#binary-interactive-regression-model-irm)).\n", "\n", "The goal is to estimate the average potential outcome\n", "\n", @@ -172,8 +172,8 @@ "source": [ "## Single Average Potential Outcome Models (APO)\n", "\n", - "Further, we have to specify machine learning algorithms. As in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm) model, we have to set ``ml_m`` as a classifier and ``ml_g`` as a regressor (since the outcome is continuous). As in the \n", - "[DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm) model, the classifier ``ml_m`` is used to estimate the conditional probability of receiving treatment level $d$ given the covariates $X$\n", + "Further, we have to specify machine learning algorithms. As in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm) model, we have to set ``ml_m`` as a classifier and ``ml_g`` as a regressor (since the outcome is continuous). As in the \n", + "[DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm) model, the classifier ``ml_m`` is used to estimate the conditional probability of receiving treatment level $d$ given the covariates $X$\n", "\n", "$$m_{0,d}(X) = \\mathbb{E}[1\\{D=d\\}|X]$$\n", "\n", diff --git a/doc/examples/py_double_ml_cate.ipynb b/doc/examples/py_double_ml_cate.ipynb index 3b5afd81..62b7966b 100644 --- a/doc/examples/py_double_ml_cate.ipynb +++ b/doc/examples/py_double_ml_cate.ipynb @@ -9,7 +9,7 @@ "source": [ "# Python: Conditional Average Treatment Effects (CATEs) for IRM models\n", "\n", - "In this simple example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to estimate conditional average treatment effects with B-splines for one or two-dimensional effects in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm) model." + "In this simple example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to estimate conditional average treatment effects with B-splines for one or two-dimensional effects in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm) model." ] }, { diff --git a/doc/examples/py_double_ml_gate.ipynb b/doc/examples/py_double_ml_gate.ipynb index 2d6c23c5..382a6476 100644 --- a/doc/examples/py_double_ml_gate.ipynb +++ b/doc/examples/py_double_ml_gate.ipynb @@ -9,7 +9,7 @@ "source": [ "# Python: Group Average Treatment Effects (GATEs) for IRM models\n", "\n", - "In this simple example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to estimate group average treatment effects in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm) model." + "In this simple example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to estimate group average treatment effects in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm) model." ] }, { @@ -167,7 +167,7 @@ "metadata": {}, "source": [ "## Interactive Regression Model (IRM)\n", - "The first step is to fit a [DoubleML IRM Model](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm) to the data." + "The first step is to fit a [DoubleML IRM Model](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm) to the data." ] }, { diff --git a/doc/examples/py_double_ml_gate_sensitivity.ipynb b/doc/examples/py_double_ml_gate_sensitivity.ipynb index 20ecb705..7810ad26 100644 --- a/doc/examples/py_double_ml_gate_sensitivity.ipynb +++ b/doc/examples/py_double_ml_gate_sensitivity.ipynb @@ -6,7 +6,7 @@ "source": [ "# Python: GATE Sensitivity Analysis\n", "\n", - "In this simple example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to perfrom a sensitivity analysis for group average treatment effects in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm) model.\n" + "In this simple example, we illustrate how the [DoubleML](https://docs.doubleml.org/stable/index.html) package can be used to perfrom a sensitivity analysis for group average treatment effects in the [DoubleMLIRM](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm) model.\n" ] }, { diff --git a/doc/examples/py_double_ml_meets_flaml.ipynb b/doc/examples/py_double_ml_meets_flaml.ipynb new file mode 100644 index 00000000..544754dc --- /dev/null +++ b/doc/examples/py_double_ml_meets_flaml.ipynb @@ -0,0 +1,944 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# DoubleML meets FLAML - How to tune learners automatically within `DoubleML`\n", + "\n", + "Recent advances in automated machine learning make it easier to tune hyperparameters of ML estimators automatically. These optimized learners can be used for the estimation part within DoubleML. In this notebook we are going to explore how to tune learners with AutoML for the DoubleML framework.\n", + "\n", + "This notebook will use [FLAML](https://github.com/microsoft/FLAML), but there are also many other AutoML frameworks. Particularly useful for DoubleML are packages that provide some way to export the model in `sklearn`-style.\n", + "\n", + "Examples are: [TPOT](https://epistasislab.github.io/tpot/), [autosklearn](https://automl.github.io/auto-sklearn/master/), [H20](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/automl.html) or [Gama](https://openml-labs.github.io/gama/master/)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Data Generation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We create synthetic data using the [make_plr_CCDDHNR2018()](https://docs.doubleml.org/stable/api/generated/doubleml.datasets.make_plr_CCDDHNR2018.html) process, with $1000$ observations of $50$ covariates as well as $1$ treatment variable and an outcome. We calibrate the process such that hyperparameter tuning becomes more important." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "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", + " \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", + " \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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
X1X2X3X4X5X6X7X8X9X10...X43X44X45X46X47X48X49X50yd
01.0653681.1625931.0899640.8246570.157733-1.228404-0.675775-0.2239280.1662380.124480...-2.021823-1.662975-2.100385-1.225670-1.2231580.397536-0.4500310.5112570.845534-0.784792
10.2144581.6996163.2228823.5502422.6924601.8219701.223617-0.100154-0.2344310.375844...-0.695711-0.819507-1.465424-0.341472-0.0235370.436016-0.503374-1.3426321.9873070.835035
20.725820-0.310145-0.586921-0.8790580.2392670.6384610.1310240.459436-1.140081-0.583692...-0.0023880.7168010.0759421.4399580.674747-0.2683430.6821220.9783030.154890-0.168089
30.2657440.4796550.0133131.4177360.9087671.7860900.996892-0.026822-0.8672010.433753...-0.482616-0.172628-0.309539-0.609522-0.830263-0.883953-1.249986-2.6886411.2540350.161288
41.5818270.9269012.3023820.803112-0.152896-0.389164-0.569590-0.1243060.055439-0.383531...0.048220-0.698751-0.754678-0.6896000.7266580.7800681.4755170.7777181.7737691.786563
\n", + "

5 rows × 52 columns

\n", + "
" + ], + "text/plain": [ + " X1 X2 X3 X4 X5 X6 X7 \\\n", + "0 1.065368 1.162593 1.089964 0.824657 0.157733 -1.228404 -0.675775 \n", + "1 0.214458 1.699616 3.222882 3.550242 2.692460 1.821970 1.223617 \n", + "2 0.725820 -0.310145 -0.586921 -0.879058 0.239267 0.638461 0.131024 \n", + "3 0.265744 0.479655 0.013313 1.417736 0.908767 1.786090 0.996892 \n", + "4 1.581827 0.926901 2.302382 0.803112 -0.152896 -0.389164 -0.569590 \n", + "\n", + " X8 X9 X10 ... X43 X44 X45 X46 \\\n", + "0 -0.223928 0.166238 0.124480 ... -2.021823 -1.662975 -2.100385 -1.225670 \n", + "1 -0.100154 -0.234431 0.375844 ... -0.695711 -0.819507 -1.465424 -0.341472 \n", + "2 0.459436 -1.140081 -0.583692 ... -0.002388 0.716801 0.075942 1.439958 \n", + "3 -0.026822 -0.867201 0.433753 ... -0.482616 -0.172628 -0.309539 -0.609522 \n", + "4 -0.124306 0.055439 -0.383531 ... 0.048220 -0.698751 -0.754678 -0.689600 \n", + "\n", + " X47 X48 X49 X50 y d \n", + "0 -1.223158 0.397536 -0.450031 0.511257 0.845534 -0.784792 \n", + "1 -0.023537 0.436016 -0.503374 -1.342632 1.987307 0.835035 \n", + "2 0.674747 -0.268343 0.682122 0.978303 0.154890 -0.168089 \n", + "3 -0.830263 -0.883953 -1.249986 -2.688641 1.254035 0.161288 \n", + "4 0.726658 0.780068 1.475517 0.777718 1.773769 1.786563 \n", + "\n", + "[5 rows x 52 columns]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from doubleml.datasets import make_plr_CCDDHNR2018\n", + "import doubleml as dml\n", + "from flaml import AutoML\n", + "from xgboost import XGBRegressor\n", + "\n", + "# Generate synthetic data\n", + "data = make_plr_CCDDHNR2018(alpha=0.5, n_obs=1000, dim_x=50, return_type=\"DataFrame\", a0=0, a1=1, s1=0.25, s2=0.25)\n", + "data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuning on the full Sample\n", + "\n", + "In this section, we manually tune two [XGBoost](https://xgboost.readthedocs.io/en/stable/) models using FLAML for a [Partially Linear Regression Model](https://docs.doubleml.org/stable/guide/models.html#partially-linear-regression-model-plr). In the PLR (using the default score) we have to estimate a nuisance $\\eta$ consisting of\n", + "\n", + "$$\\eta := \\{m_0(x), \\ell_0(x)\\} = \\{\\mathbb{E}[D|X], \\mathbb{E}[Y|X]\\}.$$\n", + "\n", + "We initialize two `FLAML` AutoML objects and fit them accordingly. Once the tuning has been completed, we pass the learners to `DoubleML`.\n", + "\n", + "#### Step 1: Initialize and Train the AutoML Models:\n", + "\n", + "*Note: This cell will optimize the nuisance models for 4 minutes in total.*" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize AutoML for outcome model (ml_l): Predict Y based on X\n", + "automl_l = AutoML()\n", + "settings_l = {\n", + " \"time_budget\": 120,\n", + " \"metric\": 'rmse',\n", + " \"estimator_list\": ['xgboost'],\n", + " \"task\": 'regression',\n", + "}\n", + "automl_l.fit(X_train=data.drop(columns=[\"y\", \"d\"]).values, y_train=data[\"y\"].values, verbose=2, **settings_l)\n", + "\n", + "# Initialize AutoML for treatment model (ml_m): Predict D based on X\n", + "automl_m = AutoML()\n", + "settings_m = {\n", + " \"time_budget\": 120,\n", + " \"metric\": 'rmse',\n", + " \"estimator_list\": ['xgboost'],\n", + " \"task\": 'regression',\n", + "}\n", + "automl_m.fit(X_train=data.drop(columns=[\"y\", \"d\"]).values, y_train=data[\"d\"].values, verbose=2, **settings_m)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Step 2: Evaluate the Tuned Models \n", + "\n", + "`FLAML` reports the best loss during training as `best_loss` attribute. For more details, we refer to the [FLAML documentation](https://microsoft.github.io/FLAML/docs/Getting-Started)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best RMSE during tuning (ml_m): 1.0078540263583833\n", + "Best RMSE during tuning (ml_l): 1.1155142425200442\n" + ] + } + ], + "source": [ + "rmse_oos_ml_m = automl_m.best_loss\n", + "rmse_oos_ml_l = automl_l.best_loss\n", + "print(\"Best RMSE during tuning (ml_m):\",rmse_oos_ml_m)\n", + "print(\"Best RMSE during tuning (ml_l):\",rmse_oos_ml_l)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Step 3: Create and Fit DoubleML Model\n", + "\n", + "We create a `DoubleMLData` object with the dataset, specifying $y$ as the outcome variable and $d$ as the treatment variable. We then initialize a `DoubleMLPLR` model using the tuned `FLAML` estimators for both the treatment and outcome components. `DoubleML` will use copies with identical configurations on each fold." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " coef std err t P>|t| 2.5 % 97.5 %\n", + "d 0.498286 0.032738 15.220407 2.589147e-52 0.434121 0.562452\n" + ] + } + ], + "source": [ + "obj_dml_data = dml.DoubleMLData(data, \"y\", \"d\")\n", + "\n", + "obj_dml_plr_fullsample = dml.DoubleMLPLR(obj_dml_data, ml_m=automl_m.model.estimator,\n", + " ml_l=automl_l.model.estimator)\n", + "\n", + "print(obj_dml_plr_fullsample.fit().summary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`DoubleML`'s built-in learner evaluation reports the out-of-sample error during cross-fitting. We can compare this measure to the best loss during training from above." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "RMSE evaluated by DoubleML (ml_m): 1.0156853566737638\n", + "RMSE evaluated by DoubleML (ml_l): 1.1309844442144665\n" + ] + } + ], + "source": [ + "rmse_dml_ml_l_fullsample = obj_dml_plr_fullsample.evaluate_learners()['ml_l'][0][0]\n", + "rmse_dml_ml_m_fullsample = obj_dml_plr_fullsample.evaluate_learners()['ml_m'][0][0]\n", + "\n", + "print(\"RMSE evaluated by DoubleML (ml_m):\", rmse_dml_ml_m_fullsample)\n", + "print(\"RMSE evaluated by DoubleML (ml_l):\", rmse_dml_ml_l_fullsample)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The best RMSE during automated tuning and the out-of-sample error in nuisance prediction are similar, which hints that there is no overfitting. We don't expect large amounts of overfitting, since FLAML uses cross-validation internally and reports the best loss on a hold-out sample." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuning on the Folds\n", + "\n", + "Instead of externally tuning the `FLAML` learners, it is also possible to tune the AutoML learners internally. We have to define custom classes for integrating `FLAML` to `DoubleML`. The tuning will be automatically be started when calling `DoubleML`'s `fit()` method. Training will occure $K$ times, so each fold will have an individualized optimal set of hyperparameters.\n", + "\n", + "#### Step 1: Custom API for FLAML Models within `DoubleML`\n", + "\n", + "The following API is designed to facilitate automated machine learning model tuning for both regression and classification tasks. In this example however, we will only need the Regressor API as the treatment is continous." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.utils.multiclass import unique_labels\n", + "\n", + "class FlamlRegressorDoubleML:\n", + " _estimator_type = 'regressor'\n", + "\n", + " def __init__(self, time, estimator_list, metric, *args, **kwargs):\n", + " self.auto_ml = AutoML(*args, **kwargs)\n", + " self.time = time\n", + " self.estimator_list = estimator_list\n", + " self.metric = metric\n", + "\n", + " def set_params(self, **params):\n", + " self.auto_ml.set_params(**params)\n", + " return self\n", + "\n", + " def get_params(self, deep=True):\n", + " dict = self.auto_ml.get_params(deep)\n", + " dict[\"time\"] = self.time\n", + " dict[\"estimator_list\"] = self.estimator_list\n", + " dict[\"metric\"] = self.metric\n", + " return dict\n", + "\n", + " def fit(self, X, y):\n", + " self.auto_ml.fit(X, y, task=\"regression\", time_budget=self.time, estimator_list=self.estimator_list, metric=self.metric, verbose=False)\n", + " self.tuned_model = self.auto_ml.model.estimator\n", + " return self\n", + "\n", + " def predict(self, x):\n", + " preds = self.tuned_model.predict(x)\n", + " return preds\n", + " \n", + "class FlamlClassifierDoubleML:\n", + " _estimator_type = 'classifier'\n", + "\n", + " def __init__(self, time, estimator_list, metric, *args, **kwargs):\n", + " self.auto_ml = AutoML(*args, **kwargs)\n", + " self.time = time\n", + " self.estimator_list = estimator_list\n", + " self.metric = metric\n", + "\n", + " def set_params(self, **params):\n", + " self.auto_ml.set_params(**params)\n", + " return self\n", + "\n", + " def get_params(self, deep=True):\n", + " dict = self.auto_ml.get_params(deep)\n", + " dict[\"time\"] = self.time\n", + " dict[\"estimator_list\"] = self.estimator_list\n", + " dict[\"metric\"] = self.metric\n", + " return dict\n", + "\n", + " def fit(self, X, y):\n", + " self.classes_ = unique_labels(y)\n", + " self.auto_ml.fit(X, y, task=\"classification\", time_budget=self.time, estimator_list=self.estimator_list, metric=self.metric, verbose=False)\n", + " self.tuned_model = self.auto_ml.model.estimator\n", + " return self\n", + "\n", + " def predict_proba(self, x):\n", + " preds = self.tuned_model.predict_proba(x)\n", + " return preds" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Step 2: Using the API when calling `DoubleML`'s `.fit()` Method\n", + "\n", + "We initialize a `FlamlRegressorDoubleML` and hand it without fitting into the DoubleML object. When calling `.fit()` on the DoubleML object, copies of the API object will be created on the folds and a seperate set of hyperparameters is created. Since we fit $K$ times, we reduce the computation time accordingly to ensure comparibility to the full sample case." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " coef std err t P>|t| 2.5 % 97.5 %\n", + "d 0.502016 0.033265 15.091263 1.848688e-51 0.436817 0.567215\n" + ] + } + ], + "source": [ + "# Define the FlamlRegressorDoubleML\n", + "ml_l = FlamlRegressorDoubleML(time=24, estimator_list=['xgboost'], metric='rmse')\n", + "ml_m = FlamlRegressorDoubleML(time=24, estimator_list=['xgboost'], metric='rmse')\n", + "\n", + "# Create DoubleMLPLR object using the new regressors\n", + "dml_plr_obj_onfolds = dml.DoubleMLPLR(obj_dml_data, ml_m, ml_l)\n", + "\n", + "# Fit the DoubleMLPLR model\n", + "print(dml_plr_obj_onfolds.fit(store_models=True).summary)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best RMSE during tuning (ml_m): 1.0068101213851626\n", + "Best RMSE during tuning (ml_l): 1.1151610541568202\n", + "RMSE evaluated by DoubleML (ml_m): 1.0084871742256079\n", + "RMSE evaluated by DoubleML (ml_l): 1.1272404618426184\n" + ] + } + ], + "source": [ + "rmse_oos_onfolds_ml_l = np.mean([dml_plr_obj_onfolds.models[\"ml_l\"][\"d\"][0][i].auto_ml.best_loss for i in range(5)])\n", + "rmse_oos_onfolds_ml_m = np.mean([dml_plr_obj_onfolds.models[\"ml_m\"][\"d\"][0][i].auto_ml.best_loss for i in range(5)])\n", + "print(\"Best RMSE during tuning (ml_m):\",rmse_oos_onfolds_ml_m)\n", + "print(\"Best RMSE during tuning (ml_l):\",rmse_oos_onfolds_ml_l)\n", + "\n", + "rmse_dml_ml_l_onfolds = dml_plr_obj_onfolds.evaluate_learners()['ml_l'][0][0]\n", + "rmse_dml_ml_m_onfolds = dml_plr_obj_onfolds.evaluate_learners()['ml_m'][0][0]\n", + "\n", + "print(\"RMSE evaluated by DoubleML (ml_m):\", rmse_dml_ml_m_onfolds)\n", + "print(\"RMSE evaluated by DoubleML (ml_l):\", rmse_dml_ml_l_onfolds)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similar to the above case, we see no hints for overfitting." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Comparison to AutoML with less Computation time and Untuned XGBoost Learners\n", + "\n", + "#### AutoML with less Computation time\n", + "\n", + "As a baseline, we can compare the learners above that have been tuned using two minutes of training time each with ones that only use ten seconds.\n", + "\n", + "Note: These tuning times are examples. For this setting, we found 10s to be insuffienct and 120s to be sufficient. In general, necessary tuning time can depend on data complexity, data set size, computational power of the machine used, etc.. For more info on how to use ``FLAML`` properly please refer to [the documentation](https://microsoft.github.io/FLAML/docs/Getting-Started/) and [the paper](https://arxiv.org/pdf/1911.04706)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# Initialize AutoML for outcome model similar to above, but use a smaller time budget.\n", + "automl_l_lesstime = AutoML()\n", + "settings_l = {\n", + " \"time_budget\": 10,\n", + " \"metric\": 'rmse',\n", + " \"estimator_list\": ['xgboost'],\n", + " \"task\": 'regression',\n", + "}\n", + "automl_l_lesstime.fit(X_train=data.drop(columns=[\"y\", \"d\"]).values, y_train=data[\"y\"].values, verbose=2, **settings_l)\n", + "\n", + "# Initialize AutoML for treatment model similar to above, but use a smaller time budget.\n", + "automl_m_lesstime = AutoML()\n", + "settings_m = {\n", + " \"time_budget\": 10,\n", + " \"metric\": 'rmse',\n", + " \"estimator_list\": ['xgboost'],\n", + " \"task\": 'regression',\n", + "}\n", + "automl_m_lesstime.fit(X_train=data.drop(columns=[\"y\", \"d\"]).values, y_train=data[\"d\"].values, verbose=2, **settings_m)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " coef std err t P>|t| 2.5 % 97.5 %\n", + "d 0.436394 0.031007 14.073929 5.493102e-45 0.375621 0.497168\n" + ] + } + ], + "source": [ + "obj_dml_plr_lesstime = dml.DoubleMLPLR(obj_dml_data, ml_m=automl_m_lesstime.model.estimator,\n", + " ml_l=automl_l_lesstime.model.estimator)\n", + "\n", + "print(obj_dml_plr_lesstime.fit().summary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can check the performance again." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best RMSE during tuning (ml_m): 0.9158080176561963\n", + "Best RMSE during tuning (ml_l): 1.2197237644227434\n", + "RMSE evaluated by DoubleML (ml_m): 1.0739130271918385\n", + "RMSE evaluated by DoubleML (ml_l): 1.1362430723104844\n" + ] + } + ], + "source": [ + "rmse_dml_ml_l_lesstime = obj_dml_plr_lesstime.evaluate_learners()['ml_l'][0][0]\n", + "rmse_dml_ml_m_lesstime = obj_dml_plr_lesstime.evaluate_learners()['ml_m'][0][0]\n", + "\n", + "\n", + "print(\"Best RMSE during tuning (ml_m):\", automl_m_lesstime.best_loss)\n", + "print(\"Best RMSE during tuning (ml_l):\", automl_l_lesstime.best_loss)\n", + "print(\"RMSE evaluated by DoubleML (ml_m):\", rmse_dml_ml_m_lesstime)\n", + "print(\"RMSE evaluated by DoubleML (ml_l):\", rmse_dml_ml_l_lesstime)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We see a more severe difference in oos RMSE between AutoML and DML estimations. This could hint that the learner underfits, i.e. training time was not sufficient." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Untuned (default parameter) XGBoost\n", + "\n", + "As another baseline, we set up DoubleML with an XGBoost learner that has not been tuned at all, i.e. using the default set of hyperparameters." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "xgb_untuned_m, xgb_untuned_l = XGBRegressor(), XGBRegressor()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " coef std err t P>|t| 2.5 % 97.5 %\n", + "d 0.431253 0.03258 13.236884 5.373218e-40 0.367398 0.495108\n" + ] + } + ], + "source": [ + "# Create DoubleMLPLR object using AutoML models\n", + "dml_plr_obj_untuned = dml.DoubleMLPLR(obj_dml_data, xgb_untuned_l, xgb_untuned_m)\n", + "print(dml_plr_obj_untuned.fit().summary)\n", + "\n", + "rmse_dml_ml_l_untuned = dml_plr_obj_untuned.evaluate_learners()['ml_l'][0][0]\n", + "rmse_dml_ml_m_untuned = dml_plr_obj_untuned.evaluate_learners()['ml_m'][0][0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Comparison and summary\n", + "\n", + "We combine the summaries from various models: full-sample and on-the-folds tuned AutoML, untuned XGB, and dummy models." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
coefstd errtP>|t|2.5 %97.5 %
Model TypeMetric
Full Sampled0.4982860.03273815.2204072.589147e-520.4341210.562452
On the foldsd0.5020160.03326515.0912631.848688e-510.4368170.567215
Defaultd0.4312530.03258013.2368845.373218e-400.3673980.495108
Less timed0.4363940.03100714.0739295.493102e-450.3756210.497168
\n", + "
" + ], + "text/plain": [ + " coef std err t P>|t| 2.5 % \\\n", + "Model Type Metric \n", + "Full Sample d 0.498286 0.032738 15.220407 2.589147e-52 0.434121 \n", + "On the folds d 0.502016 0.033265 15.091263 1.848688e-51 0.436817 \n", + "Default d 0.431253 0.032580 13.236884 5.373218e-40 0.367398 \n", + "Less time d 0.436394 0.031007 14.073929 5.493102e-45 0.375621 \n", + "\n", + " 97.5 % \n", + "Model Type Metric \n", + "Full Sample d 0.562452 \n", + "On the folds d 0.567215 \n", + "Default d 0.495108 \n", + "Less time d 0.497168 " + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "summary = pd.concat([obj_dml_plr_fullsample.summary, dml_plr_obj_onfolds.summary, dml_plr_obj_untuned.summary, obj_dml_plr_lesstime.summary],\n", + " keys=['Full Sample', 'On the folds', 'Default', 'Less time'])\n", + "summary.index.names = ['Model Type', 'Metric']\n", + "\n", + "summary" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Plot Coefficients and 95% Confidence Intervals\n", + "\n", + "This section generates a plot comparing the coefficients and 95% confidence intervals for each model type." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAJOCAYAAACqS2TfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/TGe4hAAAACXBIWXMAAA9hAAAPYQGoP6dpAACVj0lEQVR4nOzdeVxU1f/H8fewDiKLiICouZv7EiZfLXdcslxKc19TNMtKSUtb3AvLIi011NTMFk3TNC1SUcsStTTLPTW3FHBlEQUE7u8Pf4xOgIIxIfp6Ph7zkDn33Hs/d5i58p5777kmwzAMAQAAAACAfGdX0AUAAAAAAHC3InQDAAAAAGAjhG4AAAAAAGyE0A0AAAAAgI0QugEAAAAAsBFCNwAAAAAANkLoBgAAAADARgjdAAAAAADYCKEbAAAAAAAbIXQDwB3EZDJp/PjxBV3Gv7Zo0SJVrVpVjo6O8vT0LOhybiqnWqdOnaoKFSrI3t5edevWlSSVK1dO/fv3z9Pyjx07JpPJpI8//jjfar7bNGvWTM2aNSvoMu4aaWlpeumll1SmTBnZ2dmpU6dOknK/fxk/frxMJpNti0S+4ncG3NkI3QDuKEeOHNGQIUNUoUIFmc1mubu766GHHtL06dN15cqVgi4PuXDgwAH1799fFStW1Ny5czVnzpxbzrNr1y717t1bZcqUkbOzs7y8vBQUFKQFCxYoPT39P6917dq1eumll/TQQw9pwYIFevPNN21WQ36ZNWvWXR/sr169qgkTJqhChQpydnZWhQoVNHnyZKWlpVn127Rpk0wmU7aPrVu3WvWdPXu2ypcvLy8vL/Xp00cJCQlW0zMyMlSvXr08vweSk5P13nvvKTAwUB4eHjKbzapSpYqGDRumP//88/ZegFyaP3++pk6dqi5dumjhwoUaMWKETdd3p8oMoufOncvzvKdPn9b48eO1a9eu/C8MwD3HoaALAIBMa9as0ZNPPilnZ2f17dtXNWvWVGpqqn766SeNGjVKe/fuzVWAK8yuXLkiB4fCvWvetGmTMjIyNH36dFWqVOmW/T/66CM9/fTT8vX1VZ8+fVS5cmUlJiYqMjJSAwcOVHR0tF555ZX/tNYNGzbIzs5O8+bNk5OTk6X94MGDsrPL2/fVZcuW1ZUrV+To6JhvdWdn1qxZ8vb2zvOR+MKkd+/eWrp0qZ566inVr19fW7du1euvv64TJ05ku294/vnn9eCDD1q13fh7/umnnzR06FA9//zzqlChgkJDQzVq1CjNnj3b0mfu3LmKj4/Xiy++mOs6z507p7Zt22rHjh167LHH1LNnTxUtWlQHDx7U4sWLNWfOHKWmpt7GK5A7GzZsUKlSpfTee+9Ztd8N+5f/yunTpzVhwgSVK1fOcqYLANwu9rwA7ghHjx5V9+7dVbZsWW3YsEElS5a0THv22Wd1+PBhrVmzpgArtJ2MjAylpqbKbDbLbDYXdDn/2pkzZyQpV6eVb926VU8//bQaNmyob7/9Vm5ubpZpw4cP16+//qo9e/bYqtQcaz1z5oxcXFysArckOTs753kdJpPprvi9FrRffvlFX375pV5//XVNnDhRkvT000/L29tbYWFhGjZsmGrXrm01T+PGjdWlS5ccl7l69Wo1a9ZM06ZNkyS5u7trzJgxltAdFxen1157TbNnz87T775///767bfftGzZMnXu3Nlq2qRJk/Tqq6/melm348yZM9l+/ngfFrykpCS5uroWdBkA/mOcXg7gjvD222/r0qVLmjdvnlXgzlSpUiW98MILludpaWmaNGmSKlasKGdnZ5UrV06vvPKKUlJSrOYrV66cHnvsMW3atEn169eXi4uLatWqpU2bNkmSli9frlq1aslsNisgIEC//fab1fz9+/dX0aJF9ddff6lNmzZydXWVv7+/Jk6cKMMwrPq+8847atSokYoXLy4XFxcFBARo2bJlWbbFZDJp2LBh+uyzz1SjRg05OzsrIiLCMu3Gay4TExM1fPhwlStXTs7OzvLx8VGrVq20c+dOq2UuXbpUAQEBcnFxkbe3t3r37q1Tp05luy2nTp1Sp06dVLRoUZUoUUIjR47M9Sncs2bNstTs7++vZ599VnFxcVav97hx4yRJJUqUuOU1pBMmTJDJZNJnn31mFbgz1a9f3+rIbVJSkl588UXLaej333+/3nnnnSy/C0n69NNPLa+Jl5eXunfvrpMnT96yVpPJpAULFigpKclySnLmadvZXdMdFxenESNGWH5HpUuXVt++fS2ntOZ0TfeBAwfUpUsXeXl5yWw2q379+lq1apVVn48//lgmk0k///yzQkJCVKJECbm6uurxxx/X2bNnrbZl7969+uGHHyw1Z14jnXlKduXKlWU2m1W8eHE9/PDDWrduXY6/F0m6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ln6ffvqpZs6cqbZt22rs2LEymUz6/PPPVa9ePVWrVq0AtwCALezcuVPVq1e3DFq0evVq9ejRQyNHjrTsB2JjYzVgwAANGTJEZrNZP//8s7Zu3arExET16NFD999/fwFvBYA7QeZZucnJyZZ9ypYtW1SrVi1t3rxZs2bNUnp6ul555RU1btzYErz37t2rtm3bauTIkXdk8CZ03wNuPKU8LS1Nf/zxh6ZNm6YFCxbI3t5eR48e1YcffqhVq1YpODhYL774oqRr12B+/fXXCgoK0tChQxnwCLiHRUZGqm/fvlq+fLkCAwMt7T179lRiYqK++eYbq/5Dhw7Vb7/9pq1bt/7XpQL4Dw0bNky7d+/WypUr5enpqePHj+vJJ59Uz549NXz4cCUlJalq1apydnaWi4uLhgwZouDgYMv1lwDwT3///bceeeQRffvtt9q6dat69+6tdevWqUmTJlq9erVmzJghk8lkFbz79euns2fP6ssvv5SXl1dBb0IWjF5+D8gM3JMnT1bz5s31yiuv6Ny5c5bTNcqXL6+hQ4eqY8eOmj9/viZMmCBJGjt2rHr06KGlS5dqwYIFjFYO3MMSExN15coVBQYGasWKFerWrZskqUKFCjp58qSOHDlidQ/u5s2bKyEhQefPny+okgHY2JYtW/TVV19p0qRJ8vT0VFpamtzd3dW7d291795dMTExqlOnjjp06KC9e/fKbDZr1qxZCgsLU2pqakGXD+AOVaxYMfn5+SkwMFDdu3fX3Llz1aRJE0nSY489pmHDhskwDL355pv66aef5OTkpIULF2rRokV3ZOCWCN13tYyMDMvP06dP1/Tp09WwYUM5OTkpIiJCb7zxhmV6ZvB++OGHdeDAAV29elWSNGrUKL3wwgsaMGAA13ID97CWLVuqRo0aKl++vJ588kk9+uijkqSXX35ZcXFxevrpp3X48GHLl3M//PCDSpQocUee4gUgfyQkJOjq1auqXbu2vvjiC/Xv319FihRRt27d5Ofnp3feeUcPPPCAQkND5ezsrP/97386f/68oqKidOnSpYIuH8AdytXVVU8//bRiYmLk5eWl//3vf5KuD+KaGbzt7e310ksvKSoqSk5OTipZsmRBln1TnF5+D4iKitLu3bvl7++vxx57TLGxsZo/f77eeustvfzyyxozZoylb3R0tPz8/GQymXT16lXLYEkA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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Extract model labels and coefficient values\n", + "model_labels = summary.index.get_level_values('Model Type')\n", + "coef_values = summary['coef'].values\n", + "\n", + "# Calculate errors\n", + "errors = np.full((2, len(coef_values)), np.nan)\n", + "errors[0, :] = summary['coef'] - summary['2.5 %']\n", + "errors[1, :] = summary['97.5 %'] - summary['coef']\n", + "\n", + "# Plot Coefficients and 95% Confidence Intervals\n", + "plt.figure(figsize=(10, 6))\n", + "plt.errorbar(model_labels, coef_values, fmt='o', yerr=errors, capsize=5)\n", + "plt.axhline(0.5, color='red', linestyle='--')\n", + "plt.xlabel('Model')\n", + "plt.ylabel('Coefficients and 95%-CI')\n", + "plt.title('Comparison of Coefficients and 95% Confidence Intervals')\n", + "plt.xticks(rotation=45)\n", + "plt.tight_layout()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Compare Metrics for Nuisance Estimation\n", + "\n", + "In this section, we compare metrics for different models and plot a bar chart to visualize the differences in their performance." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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ffs2AMDMzAxmZmbFt3BERETvEU1zdkxMDEJCQhAVFYXGjRtL+djExAQWFhYA3lxeHhMTgx07dqBcuXJSGwsLC5iYmBTzEhIREWmPTl8nFh8fj1atWin0DwkJQXR0NEJDQ5GSkoL4+HhpmLLr552cnJCSkgIAcHZ2xo0bNxTaTJ48GVOmTFErroyMDFhYWODx48e8X4yIiIpVSc1BmuZsX19fHD58WGV7QPU9catWrUJoaKhacZXU9UVERO8/TXJQiXmPd0nCJE5ERLrCHKQZri8iItIVTXJQ3m/5JiIiIiIiIqJCYeFNREREREREpEUsvImIiIiIiIi0iIU3ERERERERkRax8CYiIiIiIiLSIp2+x5uIiIiIiEo+5/Gxug6hVEuZ5a/rEEjHeMabiIiIiIiISItYeBMRERERERFpEQtvIiIiIiIiIi1i4U1ERERERESkRSy8iYiIiIiIiLSITzUnIp3iU1ILh09JJSIiIir5eMabiIiIiIiISItYeBMRERERERFpEQtvIiIiIiIiIi1i4U1ERERERESkRSy8iYiIiIiIiLSIhTcRERERERGRFrHwJiIiIiIiItIiFt5EREREREREWsTCm4iIiIiIiEiLWHgTERERERERaRELbyIiIiIiIiItYuFNREREREREpEUsvImIiIiIiIi0iIU3ERERERERkRax8CYiIiIiIiLSIhbeRERERERERFrEwpuIiIiIiIhIi1h4ExEREREREWkRC28iIiIiIiIiLWLhTURERERERKRFLLyJiIiIiIiItIiFNxEREREREZEW6bTwPnLkCAICAmBvbw+ZTIbt27fn2T4tLQ3BwcFwc3ODnp4eRowYobTd5s2b4e7uDmNjY9SuXRu7du0q+uCJiIg+IJrm7K1bt6Jt27awtraGubk5vL29sXfvXoV2ixcvhrOzM4yNjdG4cWOcOnVKS0tARESkOzotvJ8+fQpPT08sXrxYrfZZWVmwtrbGxIkT4enpqbTN8ePHERQUhH79+uHPP/9EYGAgAgMDceHChaIMnYiI6IOiac4+cuQI2rZti127diExMRGtWrVCQEAA/vzzT6nNxo0bMWrUKEyePBlnz56Fp6cn/Pz8cOfOHW0tBhERkU7IhBBC10EAgEwmw7Zt2xAYGKhWe19fX9StWxcLFiyQ69+jRw88ffoUv//+u9SvSZMmqFu3LpYuXarWtDMyMmBhYYHHjx/D3Nxc3UUgogJwHh+r6xBKtZRZ/roOgYpYachBmubsXDVr1kSPHj0wadIkAEDjxo3RsGFDLFq0CACQk5MDBwcHDB06FOPHj1drmqVhfRG9D5ivC4f5+v2kSQ567+7xTkhIQJs2beT6+fn5ISEhQeU4WVlZyMjIkPsQERFR0cnJycGTJ09Qvnx5AMDLly+RmJgol7P19PTQpk0b5mwiInrvvHeF9+3bt2FjYyPXz8bGBrdv31Y5zsyZM2FhYSF9HBwctB0mERHRB2Xu3LnIzMxE9+7dAQD37t1DdnY2czYREX0Q3rvCuyDCw8Px+PFj6ZOamqrrkIiIiN4bMTExiIiIwKZNm1CpUqVCTYs5m4iISqMyug6gqNna2iI9PV2uX3p6OmxtbVWOY2RkBCMjI22HRkRE9MHZsGED+vfvj82bN8tdVl6xYkXo6+szZxMR0QfhvTvj7e3tjQMHDsj1i4uLg7e3t44iIiIi+jCtX78eYWFhWL9+Pfz95R8sZGhoiPr168vl7JycHBw4cIA5m4iI3js6PeOdmZmJa9euSd3JyclISkpC+fLl4ejoiPDwcNy6dQtr1qyR2iQlJUnj3r17F0lJSTA0NESNGjUAAMOHD4ePjw8iIyPh7++PDRs24MyZM1i+fHmxLhsREdH7RNOcHRMTg5CQEERFRaFx48bSfdsmJiawsLAAAIwaNQohISFo0KABGjVqhAULFuDp06cICwsr/gUkIiLSIp0W3mfOnEGrVq2k7lGjRgEAQkJCEB0djbS0NNy8eVNuHC8vL+n/iYmJiImJgZOTE1JSUgAATZs2RUxMDCZOnIhvvvkG1atXx/bt21GrVi3tLxAREdF7StOcvXz5crx+/RpDhgzBkCFDpP657YE3rwC9e/cuJk2ahNu3b6Nu3brYs2ePwgPXiIiISrsS8x7vkoTvBCUqPnwvaOHwvaDvH+YgzXB9ERUP5uvCYb5+P33Q7/EmIiIiIiIiKklYeBMRERERERFpEQtvIiIiIiIiIi1i4U1ERERERESkRSy8iYiIiIiIiLSIhTcRERERERGRFrHwJiIiIiIiItIiFt5EREREREREWsTCm4iIiIiIiEiLWHgTERERERERaRELbyIiIiIiIiItYuFNREREREREpEUsvImIiIiIiIi0iIU3ERERERERkRax8CYiIiIiIiLSIhbeRERERERERFrEwpuIiIiIiIhIi1h4ExEREREREWkRC28iIiIiIiIiLWLhTURERERERKRFLLyJiIiIiIiItIiFNxEREREREZEWsfAmIiIiIiIi0iIW3kRERERERERaxMKbiIiIiIiISItYeBMRERERERFpEQtvIiIiIiIiIi1i4U1ERERERESkRSy8iYiIiIiIiLSIhTcRERERERGRFrHwJiIiIiIiItIiFt5EREREREREWsTCm4iIiIiIiEiLWHgTERERERERaZFOC+8jR44gICAA9vb2kMlk2L59e77jxMfHo169ejAyMkK1atUQHR0tNzw7OxvffvstXFxcYGJiAldXV0ybNg1CCO0sBBER0QdA05ydlpaG4OBguLm5QU9PDyNGjFDabsGCBfjoo49gYmICBwcHjBw5Ei9evCj6BSAiItIhnRbeT58+haenJxYvXqxW++TkZPj7+6NVq1ZISkrCiBEj0L9/f+zdu1dqM3v2bPz4449YtGgRLl26hNmzZ+P777/HwoULtbUYRERE7z1Nc3ZWVhasra0xceJEeHp6Km0TExOD8ePHY/Lkybh06RJ+/vlnbNy4Ed98801Rhk5ERKRzZXQ58w4dOqBDhw5qt1+6dClcXFwQGRkJAPDw8MDRo0cxf/58+Pn5AQCOHz+Ozp07w9/fHwDg7OyM9evX49SpU0W/AERERB8ITXO2s7MzoqKiAAArV65U2ub48eNo1qwZgoODpXGCgoJw8uTJwgdMRERUgpSqe7wTEhLQpk0buX5+fn5ISEiQups2bYoDBw7gn3/+AQCcO3cOR48ezfOPhaysLGRkZMh9iIiISLuaNm2KxMRE6cfxf//9F7t27ULHjh1VjsOcTUREpZFOz3hr6vbt27CxsZHrZ2Njg4yMDDx//hwmJiYYP348MjIy4O7uDn19fWRnZ2P69Ono1auXyunOnDkTERER2g6fiIiI3hIcHIx79+6hefPmEELg9evXGDRoUJ6XmjNnExFRaVSqznirY9OmTVi3bh1iYmJw9uxZrF69GnPnzsXq1atVjhMeHo7Hjx9Ln9TU1GKMmIiI6MMUHx+PGTNmYMmSJTh79iy2bt2K2NhYTJs2TeU4zNlERFQalaoz3ra2tkhPT5frl56eDnNzc5iYmAAAxowZg/Hjx6Nnz54AgNq1a+PGjRuYOXMmQkJClE7XyMgIRkZG2g2eiIiI5Hz77bfo3bs3+vfvD+BNzn769CkGDBiACRMmQE9P8fwAczYREZVGpeqMt7e3Nw4cOCDXLy4uDt7e3lL3s2fPFBK1vr4+cnJyiiVGIiIiUo+qnA2ArwElIqL3ik7PeGdmZuLatWtSd3JyMpKSklC+fHk4OjoiPDwct27dwpo1awAAgwYNwqJFizB27Fj07dsXBw8exKZNmxAbGytNIyAgANOnT4ejoyNq1qyJP//8E/PmzUPfvn2LffmIiIjeF5rmbABISkqSxr179y6SkpJgaGiIGjVqAHiTs+fNmwcvLy80btwY165dw7fffouAgACpACciInof6LTwPnPmDFq1aiV1jxo1CgAQEhKC6OhopKWl4ebNm9JwFxcXxMbGYuTIkYiKikKVKlXw008/Sa8SA4CFCxfi22+/xZdffok7d+7A3t4eAwcOxKRJk4pvwYiIiN4zmuZsAPDy8pL+n5iYiJiYGDg5OSElJQUAMHHiRMhkMkycOBG3bt2CtbW19AM6ERHR+0QmeC2XgoyMDFhYWODx48cwNzfXdThE7zXn8bH5NyKVUmb56zoEKmLMQZrh+iIqHszXhcN8/X7SJAeVqnu8iYiIiIiIiEobFt5EREREREREWsTCm4iIiIiIiEiLWHgTERERERERaRELbyIiIiIiIiItYuFNREREREREpEUsvImIiIiIiIi0qIyuAyAiopKD72ktOL6jlYiIiFThGW8iIiIiIiIiLWLhTURERERERKRFLLyJiIiIiIiItIiFNxEREREREZEWsfAmIiIiIiIi0iIW3kRERERERERaxMKbiIiIiIiISItYeBMRERERERFpEQtvIiIiIiIiIi1i4U1ERERERESkRSy8iYiIiIiIiLSIhTcRERERERGRFrHwJiIiIiIiItIiFt5EREREREREWsTCm4iIiIiIiEiLNCq879y5k+fw169f49SpU4UKiIiIiAqPOZuIiKjk0KjwtrOzk0vktWvXRmpqqtR9//59eHt7F110REREVCDM2URERCWHRoW3EEKuOyUlBa9evcqzDRERERU/5mwiIqKSo8jv8ZbJZEU9SSIiItIC5mwiIqLiwYerEREREREREWlRGU0ay2QyPHnyBMbGxhBCQCaTITMzExkZGQAg/UtERES6xZxNRERUcmhUeAsh4ObmJtft5eUl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Ngz169cHABw9elShXadOnYomOiIiIioQ5mwiIqKSQ+Onqty8eRPTpk1TOVzX7wQlIiKiN5iziYiISgaNCu+cnJx82zx79qzAwRAREVHRYM4mIiIqOQr8VPN3ZWVlYd68eahatWpRTZKIiIi0gDmbiIioeGlUeGdlZSE8PBwNGjRA06ZNsX37dgDAypUr4eLigvnz52PkyJHaiJOIiIg0wJxNRERUcmh0qfmkSZOwbNkytGnTBsePH0e3bt0QFhaGEydOYN68eejWrRv09fW1FSsRERGpiTmbiIio5NCo8N68eTPWrFmDTp064cKFC6hTpw5ev36Nc+fOQSaTaStGIiIi0hBzNhERUcmh0aXm//33n/RKklq1asHIyAgjR45kAiciIiphmLOJiIhKDo0K7+zsbBgaGkrdZcqUgZmZWZEHRURERIXDnE1ERFRyaHSpuRACoaGhMDIyAgC8ePECgwYNgqmpqVy7rVu3Fl2EREREpDHmbCIiopJDo8I7JCRErvvzzz8v0mCIiIioaDBnExERlRwaFd6rVq3SVhxERERUhJiziYiISg6N7vEmIiIiIiIiIs2w8CYiIiIiIiLSIhbeRERERERERFrEwpuIiIiIiIhIi1h4ExEREREREWkRC28iIiIiIiIiLWLhTURERERERKRFLLyJiIiIiIiItIiFNxEREREREZEWsfAmIiIiIiIi0iIW3kRERERERERaxMKbiIiIiIiISItYeBMRERERERFpEQtvIiIiIiIiIi1i4U1ERERERESkRSy8iYiIiIiIiLSIhTcRERERERGRFrHwJiIiIiIiItIiFt5EREREREREWqTTwvvIkSMICAiAvb09ZDIZtm/fnu848fHxqFevHoyMjFCtWjVER0erbDtr1izIZDKMGDGiyGImIiL6EGmas9PS0hAcHAw3Nzfo6enlm4s3bNgAmUyGwMDAIouZiIiopNBp4f306VN4enpi8eLFarVPTk6Gv78/WrVqhaSkJIwYMQL9+/fH3r17FdqePn0ay5YtQ506dYo6bCIiog+Opjk7KysL1tbWmDhxIjw9PfNsm5KSgtGjR6NFixZFESoREVGJU0aXM+/QoQM6dOigdvulS5fCxcUFkZGRAAAPDw8cPXoU8+fPh5+fn9QuMzMTvXr1wooVK/Ddd9/lO92srCxkZWVJ3RkZGRosBRER0ftP05zt7OyMqKgoAMDKlStVtsvOzkavXr0QERGBP/74A48ePcpzuszZRERUGpWqe7wTEhLQpk0buX5+fn5ISEiQ6zdkyBD4+/srtFVl5syZsLCwkD4ODg5FFjMRERGpNnXqVFSqVAn9+vVTqz1zNhERlUalqvC+ffs2bGxs5PrZ2NggIyMDz58/B/DmHrGzZ89i5syZak83PDwcjx8/lj6pqalFGjcREREpOnr0KH7++WesWLFC7XGYs4mIqDTS6aXmRS01NRXDhw9HXFwcjI2N1R7PyMgIRkZGWoyMiIiI3vbkyRP07t0bK1asQMWKFdUejzmbiIhKo1JVeNva2iI9PV2uX3p6OszNzWFiYoLExETcuXMH9erVk4ZnZ2fjyJEjWLRoEbKysqCvr1/cYRMREdE7rl+/jpSUFAQEBEj9cnJyAABlypTBlStX4OrqqqvwiIiIilSpKry9vb2xa9cuuX5xcXHw9vYGAHz88cf466+/5IaHhYXB3d0d48aNY9FNRERUQri7uyvk7IkTJ+LJkyeIiorivdtERPRe0WnhnZmZiWvXrkndycnJSEpKQvny5eHo6Ijw8HDcunULa9asAQAMGjQIixYtwtixY9G3b18cPHgQmzZtQmxsLACgXLlyqFWrltw8TE1NUaFCBYX+REREpD5NczYAJCUlSePevXsXSUlJMDQ0RI0aNWBsbKyQmy0tLQGAOZuIiN47Oi28z5w5g1atWkndo0aNAgCEhIQgOjoaaWlpuHnzpjTcxcUFsbGxGDlyJKKiolClShX89NNPcq8SIyIioqKnac4GAC8vL+n/iYmJiImJgZOTE1JSUoolZiIiopJCp4W3r68vhBAqh0dHRysd588//1R7HvHx8QWIjIiIiN5WkJydV3t1p0FERPQ+KFWvEyMiIiIiIiIqbUrVw9WIiIiIiIg+dM7jY3UdQqmWMsu/2OfJM95EREREREREWsTCm4iIiIiIiEiLWHgTERERERERaRELbyIiIiIiIiItYuFNREREREREpEUsvImIiIiIiIi0iIU3ERERERERkRax8CYiIiIiIiLSIhbeRERERERERFrEwpuIiIiIiIhIi1h4ExEREREREWlRGV0HQERERFRSOI+P1XUIpVrKLH9dh0BEVCLxjDcRERERERGRFrHwJiIiIiIiItIiFt5EREREREREWsTCm4iIiIiIiEiLWHgTERERERERaRELbyIiIiIiIiItYuFNREREREREpEV8jzcRERERlUh8r3rB8Z3qRCULz3gTERERERERaRELbyIiIiIiIiItYuFNREREREREpEUsvImIiIiIiIi0iIU3ERERERERkRax8CYiIiIiIiLSIhbeRERERERERFrEwpuIiIiIiIhIi1h4ExEREREREWkRC28iIiIiIiIiLWLhTURERERERKRFLLyJiIiIiIiItEinhfeRI0cQEBAAe3t7yGQybN++Pd9x4uPjUa9ePRgZGaFatWqIjo6WGz5z5kw0bNgQ5cqVQ6VKlRAYGIgrV65oZwGIiIg+EJrm7LS0NAQHB8PNzQ16enoYMWKEQpsVK1agRYsWsLKygpWVFdq0aYNTp05pZwGIiIh0SKeF99OnT+Hp6YnFixer1T45ORn+/v5o1aoVkpKSMGLECPTv3x979+6V2hw+fBhDhgzBiRMnEBcXh1evXqFdu3Z4+vSpthaDiIjovadpzs7KyoK1tTUmTpwIT09PpW3i4+MRFBSEQ4cOISEhAQ4ODmjXrh1u3bpVlKETERHpXBldzrxDhw7o0KGD2u2XLl0KFxcXREZGAgA8PDxw9OhRzJ8/H35+fgCAPXv2yI0THR2NSpUqITExES1btlQ63aysLGRlZUndGRkZmi4KERHRe03TnO3s7IyoqCgAwMqVK5W2WbdunVz3Tz/9hF9//RUHDhxAnz59lI7DnE1ERKVRqbrHOyEhAW3atJHr5+fnh4SEBJXjPH78GABQvnx5lW1mzpwJCwsL6ePg4FA0ARMREZHanj17hlevXjFnExHRe6dUFd63b9+GjY2NXD8bGxtkZGTg+fPnCu1zcnIwYsQINGvWDLVq1VI53fDwcDx+/Fj6pKamFnnsRERElLdx48bB3t5e4Uf2tzFnExFRaaTTS821bciQIbhw4QKOHj2aZzsjIyMYGRkVU1RERET0rlmzZmHDhg2Ij4+HsbGxynbM2UREVBqVqsLb1tYW6enpcv3S09Nhbm4OExMTuf5fffUVfv/9dxw5cgRVqlQpzjCJiIhIA3PnzsWsWbOwf/9+1KlTR9fhEBERFblSdam5t7c3Dhw4INcvLi4O3t7eUrcQAl999RW2bduGgwcPwsXFpbjDJCIiIjV9//33mDZtGvbs2YMGDRroOhwiIiKt0OkZ78zMTFy7dk3qTk5ORlJSEsqXLw9HR0eEh4fj1q1bWLNmDQBg0KBBWLRoEcaOHYu+ffvi4MGD2LRpE2JjY6VpDBkyBDExMdixYwfKlSuH27dvAwAsLCwUzooTERGRejTN2QCQlJQkjXv37l0kJSXB0NAQNWrUAADMnj0bkyZNQkxMDJydnaWcbWZmBjMzs+JbOCIiIi3TaeF95swZtGrVSuoeNWoUACAkJATR0dFIS0vDzZs3peEuLi6IjY3FyJEjERUVhSpVquCnn36SXiUGAD/++CMAwNfXV25eq1atQmhoqPYWhoiI6D2mac4GAC8vL+n/iYmJiImJgZOTE1JSUgC8ydkvX75E165d5cabPHkypkyZop0FISIi0gGdFt6+vr4QQqgcHh0drXScP//8U+U4eU2PiIiICqYgOTu/nJxbgBMREb3vStU93kRERERERESlDQtvIiIiIiIiIi0qVa8TK62cx8fm34hUSpnlr+sQiIiIiIiICoxnvImIiIiIiIi0iIU3ERERERERkRax8CYiIiIiIiLSIhbeRERERERERFrEwpuIiIiIiIhIi1h4ExEREREREWkRC28iIiIiIiIiLWLhTURERERERKRFLLyJiIiIiIiItIiFNxEREREREZEWsfAmIiIiIiIi0iIW3kRERERERERaxMKbiIiIiIiISItYeBMRERERERFpEQtvIiIiIiIiIi1i4U1ERERERESkRWV0HQBRcXMeH6vrEEq1lFn+ug6BiIiIiKhU4RlvIiIiIiIiIi1i4U1ERERERESkRSy8iYiIiIiIiLSIhTcRERERERGRFrHwJiIiIiIiItIiFt5EREREREREWsTCm4iIiIiIiEiLWHgTERERERERaRELbyIiIiIiIiItYuFNREREREREpEUsvImIiIiIiIi0iIU3ERERERERkRax8CYiIiIiIiLSIhbeRERERERERFqk08L7yJEjCAgIgL29PWQyGbZv357vOPHx8ahXrx6MjIxQrVo1REdHK7RZvHgxnJ2dYWxsjMaNG+PUqVNFHzwREdEHRNOcnZaWhuDgYLi5uUFPTw8jRoxQ2m7z5s1wd3eHsbExateujV27dhV98ERERDqm08L76dOn8PT0xOLFi9Vqn5ycDH9/f7Rq1QpJSUkYMWIE+vfvj71790ptNm7ciFGjRmHy5Mk4e/YsPD094efnhzt37mhr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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = plt.subplots(1,2,figsize=(10,4))\n", + "axs = axs.flatten()\n", + "\n", + "axs[0].bar(x = ['Full Sample', 'On the folds', 'Default', 'Less time'],\n", + " height=[rmse_dml_ml_m_fullsample, rmse_dml_ml_m_onfolds, rmse_dml_ml_m_untuned, rmse_dml_ml_m_lesstime])\n", + "\n", + "axs[1].bar(x = ['Full Sample', 'On the folds', 'Default', 'Less time'],\n", + " height=[rmse_dml_ml_l_fullsample, rmse_dml_ml_l_onfolds, rmse_dml_ml_l_untuned, rmse_dml_ml_l_lesstime])\n", + "\n", + "axs[0].set_xlabel(\"Tuning Method\")\n", + "axs[0].set_ylim((1,1.12))\n", + "axs[0].set_ylabel(\"RMSE\")\n", + "axs[0].set_title(\"OOS RMSE for Different Tuning Methods (ml_m)\")\n", + "\n", + "axs[1].set_xlabel(\"Tuning Method\")\n", + "axs[1].set_ylim((1.1,1.22))\n", + "axs[1].set_ylabel(\"RMSE\")\n", + "axs[1].set_title(\"OOS RMSE for Different Tuning Methods (ml_l)\")\n", + "\n", + "fig.suptitle(\"Out of Sample RMSE in Nuisance Estimation by Tuning Method\")\n", + "fig.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusion\n", + "\n", + "This notebook highlights that tuning plays an important role and can be easily done using FLAML AutoML. In our [recent study](https://arxiv.org/abs/2402.04674) we provide more evidence for tuning with AutoML, especially that the full sample case in all investigated cases performed similarly to the full sample case and thus tuning time and complexity can be saved by tuning externally.\n", + "\n", + "See also our fully automated API for tuning DoubleML objects using AutoML, called [AutoDoubleML](https://github.com/OliverSchacht/AutoDoubleML) which can be installed from Github for python." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "flaml", + "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.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/examples/py_double_ml_policy_tree.ipynb b/doc/examples/py_double_ml_policy_tree.ipynb index 0a4109bc..3c981f97 100644 --- a/doc/examples/py_double_ml_policy_tree.ipynb +++ b/doc/examples/py_double_ml_policy_tree.ipynb @@ -173,7 +173,7 @@ "metadata": {}, "source": [ "## Interactive Regression Model (IRM)\n", - "The first step is to fit a [DoubleML IRM Model](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm) to the data." + "The first step is to fit a [DoubleML IRM Model](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm) to the data." ] }, { diff --git a/doc/examples/py_double_ml_rdflex.ipynb b/doc/examples/py_double_ml_rdflex.ipynb new file mode 100644 index 00000000..f2517305 --- /dev/null +++ b/doc/examples/py_double_ml_rdflex.ipynb @@ -0,0 +1,612 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Flexible Covariate Adjustment in Regression Discontinuity Designs (RDD)\n", + "\n", + "This notebook demonstrates how to use RDD within ``DoubleML``. Our implementation, ``RDFlex``, is based on the paper _\"Flexible Covariate Adjustments in Regression Discontinuity Designs\"_ by [Noack, Olma and Rothe (2024)](https://arxiv.org/abs/2107.07942). \n", + "\n", + "In regression discontinuity designs (RDD), treatment assignment is determined by a continuous running variable $S$ (or \"score\") crossing a known threshold $c$ (or \"cutoff\"). We aim to estimate the average treatment effect locally at the cutoff,\n", + "\n", + "$$\\tau_{0} = \\mathbb{E}[Y_i(1)-Y_i(0)\\mid S_i = c]$$\n", + "\n", + "with $Y_i(1)$ and $Y_i(0)$ denoting the potential outcomes of an individual with and without treatment, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "import plotly.express as px\n", + "import plotly.graph_objs as go\n", + "from statsmodels.nonparametric.kernel_regression import KernelReg\n", + "\n", + "from lightgbm import LGBMRegressor, LGBMClassifier\n", + "\n", + "from rdrobust import rdrobust\n", + "\n", + "import doubleml as dml\n", + "from doubleml.rdd import RDFlex\n", + "from doubleml.rdd.datasets import make_simple_rdd_data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sharp RDD\n", + "\n", + "In the sharp design, the treatment assignment is deterministic given the score. Namely, all the individuals with a score higher than the cutoff, receive the treatment $$D_i = \\mathbb{I}[S_i \\geq c].$$\n", + "\n", + "Without loss of generality, for the whole example we consider the cutoff to be normalized to $c=0$ and formulas are given accordingly.\n", + "\n", + "In sharp RDD, the treatment effect defined above is identified by\n", + "\n", + "$$\\tau_0 = \\lim_{s \\to c^+} \\mathbb{E}[Y_i \\mid S_i = s] - \\lim_{s \\to c^-} \\mathbb{E}[Y_i \\mid S_i = s].$$\n", + "\n", + "A key assumption for this identification is the **continuity** of the conditional expectations of the potential outcomes $\\mathbb{E}[Y_i(d)\\mid S_i=c]$ for $d \\in \\{0, 1\\}$.\n", + " \n", + "This implies that units cannot perfectly manipulate their score to either receive or avoid treatment exactly at the cutoff.\n", + "\n", + "### Generate Sharp Data\n", + "\n", + "The function ``make_simple_rdd_data()`` can be used to generate data of a rather standard RDD setting. If we set ``fuzzy = False``, the generated data follows a sharp RDD. We also generate covariates $X$ that can be used to adjust the estimation at a later stage.\n", + "By default, the cutoff is normalized to ``c = 0``. The true RDD effect can be controlled by ``tau`` and is set to a value of $2.0$ in this example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(1234)\n", + "\n", + "true_tau = 2.0\n", + "data_dict = make_simple_rdd_data(n_obs=1000, fuzzy=False, tau=true_tau)\n", + "\n", + "cov_names = ['x' + str(i) for i in range(data_dict['X'].shape[1])]\n", + "df = pd.DataFrame(\n", + " np.column_stack((data_dict['Y'], data_dict['D'], data_dict['score'], data_dict['X'])),\n", + " columns=['y', 'd', 'score'] + cov_names,\n", + ")\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comparing the observed outcomes, we can clearly see a discontinuity at the cutoff value of $c = 0$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = px.scatter(\n", + " x=df['score'],\n", + " y=df['y'],\n", + " color=df['d'].astype(bool),\n", + " labels={\n", + " \"x\": \"Score\", \n", + " \"y\": \"Outcome\",\n", + " \"color\": \"Treatment\"\n", + " },\n", + " title=\"Scatter Plot of Outcome vs. Score by Treatment Status\"\n", + ")\n", + "\n", + "fig.update_layout(\n", + " xaxis_title=\"Score\",\n", + " yaxis_title=\"Outcome\"\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sharp RDD Without Adjustment\n", + "\n", + "The standard RDD estimator for the sharp design takes the form \n", + "\n", + "$$\\hat{\\tau}_{\\text{base}}(h) = \\sum_{i=1}^n w_i(h)Y_i,$$\n", + "\n", + "where the $w_i(h)$ are local linear regression weights that depend on the data through the realizations of the running variable only and $h > 0$ is a bandwidth.\n", + "\n", + "The packages ``rdrobust`` implements this estimation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdrobust_sharp_noadj = rdrobust(y=df['y'], x=df['score'], fuzzy=df['d'], c=0.0)\n", + "rdrobust_sharp_noadj" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sharp RDD with Linear Adjustment\n", + "\n", + "The linearly adjusted RDD estimator for the sharp design takes the form \n", + "\n", + "$$\\hat{\\tau}_{lin}(h) = \\sum_{i=1}^n w_i(h)(Y_i-X_i^T\\hat{\\gamma}_h)$$\n", + "\n", + "where $w_i(h)$ are local linear regression weights that depend on the data through the realizations of the running variable $S_i$ only and $h>0$ is a bandwidth. $\\hat{\\gamma}_h$ is a minimizer from the regression\n", + "\n", + "$$\\underset{\\beta,\\gamma}{\\mathrm{arg\\,min}} \\, \\sum K_h(S_i) (Y_i - Q_i^\\top\\beta- X_i^{\\top}\\gamma )^2.$$\n", + "\n", + "with $Q_i =(D_i, S_i, D_i S_i,1)^T$ (for more details, see our [User Guide](https://docs.doubleml.org/stable/guide/models.html)), $K_h(v)=K(v/h)/h$ with $K(\\cdot)$ a kernel function.\n", + "\n", + "The packages ``rdrobust`` implements this estimation with a linear adjustment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdrobust_sharp = rdrobust(y=df['y'], x=df['score'], fuzzy=df['d'], covs=df[cov_names], c=0.0)\n", + "rdrobust_sharp" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sharp RDD with Flexible Adjustment\n", + "\n", + "[Noack, Olma and Rothe (2024)](https://arxiv.org/abs/2107.07942) propose an estimator that reduces the variance of the above esimator, using a flexible adjustment of the outcome by machine learning. For more details, see our [User Guide](https://docs.doubleml.org/stable/guide/models.html). The estimator here takes the form \n", + "\n", + "$$\\hat{\\tau}_{\\text{RDFlex}}(h;\\eta) = \\sum_{i=1}^n w_i(h)M_i(\\eta),\\quad M_i(\\eta) = Y_i - \\eta(X_i),$$\n", + "\n", + "with $\\eta(\\cdot)$ being potentially nonlinear adjustment functions.\n", + "\n", + "We initialize a `DoubleMLData` object using the usual package syntax:\n", + "\n", + " - `y_col` refers to the observed outcome, on which we want to estimate the effect at the cutoff\n", + " - `s_col` refers to the score\n", + " - `x_cols` refers to the covariates to be adjusted for\n", + " - `d_cols` is an indicator whether an observation is treated or not. In the sharp design, this should be identical to an indicator whether an observation is left or right of the cutoff ($D_i = \\mathbb{I}[S_i \\geq c]$)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dml_data_sharp = dml.DoubleMLData(df, y_col='y', d_cols='d', x_cols=cov_names, s_col='score')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ``RDFlex`` object is intialized with only one learner, that adjusts the outcome based on the covariates." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ml_g = LGBMRegressor(n_estimators=500, learning_rate=0.01, verbose=-1)\n", + "\n", + "rdflex_sharp = RDFlex(dml_data_sharp,\n", + " ml_g,\n", + " cutoff=0,\n", + " fuzzy=False,\n", + " n_folds=5,\n", + " n_rep=1)\n", + "rdflex_sharp.fit(n_iterations=2)\n", + "\n", + "print(rdflex_sharp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is visible that the flexible adjustment decreases the standard error in the estimation and therefore provides tighter confidence intervals. For coverage simulations, see the [DoubleML Coverage Repository](https://docs.doubleml.org/doubleml-coverage/dev/rdd/rdd.html).\n", + "\n", + "`RDFlex` uses an iterative fitting approach to determine a preliminary bandwidth selections for the local adjustments. The default number of iterations is `n_iterations=2`, according to [Noack, Olma and Rothe (2024)](https://arxiv.org/abs/2107.07942)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fuzzy RDD\n", + "\n", + "In the fuzzy RDDs, the treatment assignment is still deterministic given the score $\\left(T_i = \\mathbb{I}[S_i \\geq c]\\right)$.\n", + "However, in the neighborhood of the cutoff, there is a probability of non-compliance. Thus, the treatment received might differ from the assigned one $(D_i \\neq T_i)$ for some units. These observations cause the jump in the probability of treatment at the cutoff to be smaller than 1 but larger than 0. In other words, around the cutoff there can be treatment randomization on both sides.\n", + "\n", + "The parameter of interest in the Fuzzy RDD is the average treatment effect at the cutoff, for all individuals that comply with the assignment\n", + "\n", + "$$\\theta_{0} = \\mathbb{E}[Y_i(1)-Y_i(0)\\mid S_i = c, \\{i\\in \\text{compliers}\\}].$$\n", + "\n", + "This effect can be identified by\n", + "\n", + "$$\\theta_{0} = \\frac{\\lim_{s \\to c^+} \\mathbb{E}[Y_i \\mid S_i = s] - \\lim_{s \\to c^-} \\mathbb{E}[Y_i \\mid S_i = s]}{\\lim_{s \\to c^+} \\mathbb{E}[D_i \\mid S_i = s] - \\lim_{s \\to c^-} \\mathbb{E}[D_i \\mid S_i = s]}.$$\n", + "\n", + "### Generate Fuzzy Data\n", + "\n", + "The function ``make_simple_rdd_data()`` with ``fuzzy = True`` generates basic data for the fuzzy case. The cutoff is still set to $c = 0$ and we set the true effect to be ``tau = 2.0`` again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "np.random.seed(1234)\n", + "\n", + "data_dict = make_simple_rdd_data(n_obs=1000, fuzzy=True, tau=true_tau)\n", + "\n", + "cov_names = ['x' + str(i) for i in range(data_dict['X'].shape[1])]\n", + "df = pd.DataFrame(\n", + " np.column_stack((data_dict['Y'], data_dict['D'], data_dict['score'], data_dict['X'])),\n", + " columns=['y', 'd', 'score'] + cov_names,\n", + ")\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comparing the observed outcomes, the discontinuity is less pronounced than in the sharp case. We see some degree of randomization left and right of the cutoff." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = px.scatter(\n", + " x=df['score'],\n", + " y=df['y'],\n", + " color=df['d'].astype(bool),\n", + " labels={\n", + " \"x\": \"Score\", \n", + " \"y\": \"Outcome\",\n", + " \"color\": \"Treatment\"\n", + " },\n", + " title=\"Scatter Plot of Outcome vs. Score by Treatment Status\"\n", + ")\n", + "\n", + "fig.update_layout(\n", + " xaxis_title=\"Score\",\n", + " yaxis_title=\"Outcome\"\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fuzzy RDD Without Adjustment\n", + "\n", + "The standard RDD estimator for the fuzzy design takes the form \n", + "\n", + "$$\\hat{\\theta}_{base}(h) = \\frac{\\hat{\\tau}_{\\text{Y}, base}(h)}{\\hat{\\tau}_{\\text{D}, base}(h)} = \\frac{\\sum_{i=1}^n w_i(h)Y_i}{\\sum_{i=1}^n w_i(h)D_i}$$\n", + "\n", + "The packages ``rdrobust`` implements this estimation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdrobust_fuzzy_noadj = rdrobust(y=df['y'], x=df['score'], fuzzy=df['d'], c=0.0)\n", + "rdrobust_fuzzy_noadj" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fuzzy RDD with Linear Adjustment\n", + "\n", + "The linearly adjusted RDD estimator for the fuzzy design takes the form \n", + "\n", + "$$\\hat{\\theta}_{lin}(h) = \\frac{\\hat{\\tau}_{\\text{Y}, lin}(h)}{\\hat{\\tau}_{\\text{D}, lin}(h)} = \\frac{\\sum_{i=1}^n w_i(h)(Y_i-X_i^T\\hat{\\gamma}_{Y, h})}{\\sum_{i=1}^n w_i(h)(D_i-X_i^T\\hat{\\gamma}_{D, h})}$$\n", + "\n", + "Under similar assumptions as in the sharp case and that there are no *Defiers* (= individuals that would always pick the opposite treatment of their assigned one), this effect estimates the average treatment effect at the cutoff. The package ``rdrobust`` implements this estimation with a linear adjustment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdrobust_fuzzy = rdrobust(y=df['y'], x=df['score'], fuzzy=df['d'], covs=df[cov_names], c=0.0)\n", + "rdrobust_fuzzy" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The fuzzy design usually has much larger standard errors than the sharp design, as the jump in treatment probability adds further estimation uncertainty." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fuzzy RDD with Flexible Adjustment\n", + "\n", + "[Noack, Olma and Rothe (2024)](https://arxiv.org/abs/2107.07942) propose an estimator that reduces the variance of the above esimator, using a flexible adjustment of the outcome by ML. For more details, see our [User Guide](https://docs.doubleml.org/stable/guide/models.html). The estimator here takes the form \n", + "\n", + "$$\\hat{\\theta}_{\\text{RDFlex}}(h; \\eta) = \\frac{\\sum_{i=1}^n w_i(h)(Y_i - \\hat{\\eta}_Y(X_i))}{\\sum_{i=1}^n w_i(h)(D_i - \\hat{\\eta}_D(X_i))},$$\n", + "\n", + "\n", + "with $\\eta_Y(\\cdot), \\eta_D(\\cdot)$ being potentially nonlinear adjustment functions.\n", + "\n", + "We initialize a `DoubleMLData` object using the usual package syntax:\n", + "\n", + " - `y_col` refers to the observed outcome, on which we want to estimate the effect at the cutoff\n", + " - `s_col` refers to the score\n", + " - `x_cols` refers to the covariates to be adjusted for\n", + " - `d_cols` is an indicator whether an observation is treated or not. In the fuzzy design, this should __not__ be identical to an indicator whether an observation is left or right of the cutoff ($D_i \\neq \\mathbb{I}[S_i \\geq c]$)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dml_data_fuzzy = dml.DoubleMLData(df, y_col='y', d_cols='d', x_cols=cov_names, s_col='score')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This time, we also have to specify a classifier that adjusts the treatment assignment probabilities." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ml_g = LGBMRegressor(n_estimators=500, learning_rate=0.01, verbose=-1)\n", + "ml_m = LGBMClassifier(n_estimators=500, learning_rate=0.01, verbose=-1)\n", + "\n", + "rdflex_fuzzy = RDFlex(dml_data_fuzzy,\n", + " ml_g,\n", + " ml_m,\n", + " cutoff=0,\n", + " fuzzy=True,\n", + " n_folds=5,\n", + " n_rep=1)\n", + "rdflex_fuzzy.fit(n_iterations=2)\n", + "\n", + "print(rdflex_fuzzy)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Also for the fuzzy case, we observe a significant decrease in estimation standard error." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Advanced: Global and Local Learners, Stacked Ensembles\n", + "\n", + "By default, ``RDFlex`` fits ML methods using kernel weights, resulting in a \"local\" fit around the cutoff. If the adjustment should also include \"global\" information from the full support of $S$ available in the data, the use of a the ``GlobalLearner`` wrapper is recommended.\n", + "\n", + "The ``GlobalLearner`` allows to ignore the weights and fit the ML method on the full support of the data, even if weights are provided.\n", + "\n", + "The learners can also be stacked. All learners have to support the `sample_weight` in their `fit` method. By stacking and using local and global learners, it is possible to further tune the estimation and potentially reduce standard errors even further." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from doubleml.utils import GlobalRegressor, GlobalClassifier\n", + "\n", + "from sklearn.linear_model import LinearRegression, LogisticRegression\n", + "from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier\n", + "from sklearn.ensemble import StackingClassifier, StackingRegressor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "reg_estimators = [\n", + " ('lr local', LinearRegression()),\n", + " ('rf local', RandomForestRegressor()),\n", + " ('lr global', GlobalRegressor(base_estimator=LinearRegression())),\n", + " ('rf global', GlobalRegressor(base_estimator=RandomForestRegressor()))\n", + "]\n", + "\n", + "class_estimators = [\n", + " ('lr local', LogisticRegression()),\n", + " ('rf local', RandomForestClassifier()),\n", + " ('lr global', GlobalClassifier(base_estimator=LogisticRegression())),\n", + " ('rf global', GlobalClassifier(base_estimator=RandomForestClassifier()))\n", + "]\n", + "\n", + "ml_g = StackingRegressor(\n", + " estimators=reg_estimators,\n", + " final_estimator=LinearRegression(),\n", + ")\n", + "\n", + "ml_m = StackingClassifier(\n", + " estimators=class_estimators,\n", + " final_estimator=LogisticRegression(),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We first repeat the estimation of the sharp design and observe an even smaller standard error." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdflex_sharp_stack = RDFlex(dml_data_sharp,\n", + " ml_g,\n", + " fuzzy=False,\n", + " n_folds=5,\n", + " n_rep=1)\n", + "rdflex_sharp_stack.fit(n_iterations=2)\n", + "\n", + "print(rdflex_sharp_stack)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The same applies for the fuzzy case." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rdflex_fuzzy_stack = RDFlex(dml_data_fuzzy,\n", + " ml_g,\n", + " ml_m,\n", + " fuzzy=True,\n", + " n_folds=5,\n", + " n_rep=1)\n", + "rdflex_fuzzy_stack.fit(n_iterations=2)\n", + "\n", + "print(rdflex_fuzzy_stack)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To conclude, we look at a visualization of the estimated coefficient and the confidence intervals. We see that by using the flexible adjustment, it is possible to shrink confidence intervals." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "df_sharp = pd.DataFrame({\"coef\": [rdrobust_sharp_noadj.coef.values[0][0], rdrobust_sharp.coef.values[0][0], rdflex_sharp.coef[0], rdflex_sharp_stack.coef[0]],\n", + " \"CI lower\": [rdrobust_sharp_noadj.ci.values[0][0], rdrobust_sharp.ci.values[0][0], rdflex_sharp.confint().values[0][0], rdflex_sharp_stack.confint().values[0][0]],\n", + " \"CI upper\": [rdrobust_sharp_noadj.ci.values[0][1], rdrobust_sharp.ci.values[0][1], rdflex_sharp.confint().values[0][1], rdflex_sharp_stack.confint().values[0][1]],\n", + " \"method\": [\"No Adj.\", \"Linear Adj.\", \"Flexible Adj.\", \"Flexible Adj. (Stacked)\"]})\n", + "df_fuzzy = pd.DataFrame({\"coef\": [rdrobust_fuzzy_noadj.coef.values[0][0], rdrobust_fuzzy.coef.values[0][0], rdflex_fuzzy.coef[0], rdflex_fuzzy_stack.coef[0]],\n", + " \"CI lower\": [rdrobust_fuzzy_noadj.ci.values[0][0], rdrobust_fuzzy.ci.values[0][0], rdflex_fuzzy.confint().values[0][0], rdflex_fuzzy_stack.confint().values[0][0]],\n", + " \"CI upper\": [rdrobust_fuzzy_noadj.ci.values[0][1], rdrobust_fuzzy.ci.values[0][1], rdflex_fuzzy.confint().values[0][1], rdflex_fuzzy_stack.confint().values[0][1]],\n", + " \"method\": [\"No Adj.\", \"Linear Adj.\", \"Flexible Adj.\", \"Flexible Adj. (Stacked)\"]})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))\n", + "\n", + "axes[0].errorbar(\n", + " df_sharp['method'],\n", + " df_sharp['coef'],\n", + " yerr=(df_sharp['coef'] - df_sharp['CI lower'], df_sharp['CI upper'] - df_sharp['coef']),\n", + " fmt='o',\n", + " capsize=5,\n", + " capthick=2\n", + ")\n", + "axes[0].set_title('Sharp Design')\n", + "axes[0].set_ylabel('Coefficient')\n", + "axes[0].set_xlabel('Method')\n", + "axes[0].axhline(true_tau, linestyle=\"--\", color=\"r\")\n", + "axes[0].tick_params(axis='x', rotation=30)\n", + "\n", + "axes[1].errorbar(\n", + " df_fuzzy['method'],\n", + " df_fuzzy['coef'],\n", + " yerr=(df_fuzzy['coef'] - df_fuzzy['CI lower'], df_fuzzy['CI upper'] - df_fuzzy['coef']),\n", + " fmt='o',\n", + " capsize=5,\n", + " capthick=2\n", + ")\n", + "axes[1].set_title('Fuzzy Design')\n", + "axes[1].set_ylabel('Coefficient') \n", + "axes[1].set_xlabel('Method')\n", + "axes[1].axhline(true_tau, linestyle=\"--\", color=\"r\")\n", + "axes[1].tick_params(axis='x', rotation=30)\n", + "\n", + "plt.tight_layout()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "didnotebook", + "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.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/examples/py_double_ml_sensitivity_booking.ipynb b/doc/examples/py_double_ml_sensitivity_booking.ipynb index ce41354c..fe8677ad 100644 --- a/doc/examples/py_double_ml_sensitivity_booking.ipynb +++ b/doc/examples/py_double_ml_sensitivity_booking.ipynb @@ -85,7 +85,7 @@ "\n", "### 1. Formulation of Causal Model & Identification Assumptions\n", "\n", - "In the use case, we focused on a nonparametric model for the treatment effect, also called [Interactive Regression Model (IRM)](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-model-irm). Under the assumptions of consistency, overlap and unconfoundedness, this model can be used to identify the Average Treatment Effect (ATE) and the Average Treatment Effect on the Treated (ATT). The identification strategy was based on a DAG.\n", + "In the use case, we focused on a nonparametric model for the treatment effect, also called [Interactive Regression Model (IRM)](https://docs.doubleml.org/stable/guide/models.html#interactive-regression-models-irm). Under the assumptions of consistency, overlap and unconfoundedness, this model can be used to identify the Average Treatment Effect (ATE) and the Average Treatment Effect on the Treated (ATT). The identification strategy was based on a DAG.\n", "\n", "![DAG underlying to the causal analysis.](figures/dag_usecase_revised.png)\n", "\n", diff --git a/doc/guide/models.rst b/doc/guide/models.rst index d1ba8f6e..3977c6d4 100644 --- a/doc/guide/models.rst +++ b/doc/guide/models.rst @@ -507,3 +507,8 @@ Estimation is conducted via its ``fit()`` method: dml_ssm = dml.DoubleMLSSM(dml_data, ml_g, ml_m, ml_pi, score='nonignorable') dml_ssm.fit() print(dml_ssm) + +Regression Discontinuity Designs (RDD) +++++++++++++++++++++++++++++++++++++++ + +.. include:: ../shared/models/rdd.rst diff --git a/doc/literature/literature.rst b/doc/literature/literature.rst index 768ebeca..5bbb0e74 100644 --- a/doc/literature/literature.rst +++ b/doc/literature/literature.rst @@ -178,6 +178,19 @@ Double machine learning literature **Efficient Difference-in-Differences Estimation with High-Dimensional Common Trend Confounding** |br| *arXiv preprint arXiv:1809.01643 [econ.EM], 2018* |br| :octicon:`link` :bdg-link-dark:`arXiv ` + |hr| + + - Claudia Noack, Tomasz Olma, Christoph Rothe |br| + **Flexible Covariate Adjustments in Regression Discontinuity Designs** |br| + *arXiv preprint arXiv:2107.07942v3 [econ.EM], 2024* |br| + :octicon:`link` :bdg-link-dark:`arXiv ` + |hr| + + - Matias D. Cattaneo,Rocío Titiunik |br| + **Regression Discontinuity Designs** |br| + *Annual Review of Economics, 14, Pages 821-851, 2022* |br| + :octicon:`link` :bdg-link-dark:`url ` + |hr| .. dropdown:: Debiased sparsity-based inference / theoretical foundations :class-title: sd-bg-primary sd-font-weight-bold diff --git a/doc/release/release.rst b/doc/release/release.rst index a6b384a0..6f389a71 100644 --- a/doc/release/release.rst +++ b/doc/release/release.rst @@ -7,6 +7,36 @@ Release notes .. tab-item:: Python + .. dropdown:: DoubleML 0.9.1 + :class-title: sd-bg-primary sd-font-weight-bold + :open: + + - **Release highlight:** Regression Discontinuity Designs with Flexible Covariate Adjustment + via ``RDFlex`` class (in cooperation with `Claudia Noack `_ + and `Tomasz Olma `_; see `their paper `_) + `#276 `_ + + - Add ``cov_type=HC0`` and enable key-worded arguments to ``DoubleMLBLP`` + `#270 `_ + `#271 `_ + + - Update User Guide and Example Gallery + `#204 `_ + + - Add AutoML example for tuning DoubleML estimators + `#199 `_ + + - Maintainance package + `#268 `_ + `#278 `_ + `#279 `_ + `#281 `_ + `#282 `_ + + - Maintenance documentation + `#201 `_ + `#203 `_ + .. dropdown:: DoubleML 0.9.0 :class-title: sd-bg-primary sd-font-weight-bold :open: @@ -657,4 +687,4 @@ Release notes .. |br| raw:: html -
\ No newline at end of file +
diff --git a/doc/shared/models/rdd.rst b/doc/shared/models/rdd.rst new file mode 100644 index 00000000..96d1e75d --- /dev/null +++ b/doc/shared/models/rdd.rst @@ -0,0 +1,212 @@ +**Regression Discontinuity Designs (RDD)** are causal inference methods used when treatment assignment is determined by a continuous running variable ("score") crossing a known threshold ("cutoff"). These designs exploit discontinuities in the probability of receiving treatment at the cutoff to estimate the average treatment effect. RDDs are divided into two main types: **Sharp** and **Fuzzy**. + +The key idea behind RDD is that units just above and just below the threshold are assumed to be comparable, differing only in the treatment assignment. This allows estimating the causal effect at the threshold by comparing outcomes of treated and untreated units. + +Our implementation follows work from `Noack, Olma and Rothe (2024) `_. + +Let :math:`Y_i` be the observed outcome of an individual and :math:`D_i` the treatment it received. By using a set of additional covariates :math:`X_i` for each observation, :math:`Y_i` and :math:`D_i` can be adjusted in a first stage, to reduce the standard deviation in the estimation of the causal effect. + +.. note:: + To fit into the package syntax, our notation differs as follows from the one used in most standard RDD works (as for example `Cattaneo and Titiunik (2022) `_): + - :math:`S_i` the **score** (instead of :math:`X_i`) + - :math:`X_i` the **covariates** (instead of :math:`Z_i`) + - :math:`D_i` the **treatment received** (in sharp RDD instead of :math:`T_i`) + - :math:`T_i` the **treatment assigned** (only relevant in fuzzy RDD) + +Sharp Regression Discontinuity Design +************************************* + +In a **Sharp RDD**, the treatment :math:`D_i` is deterministically assigned at the cutoff (:math:`D_i = \mathbb{1}\{S_i \geq c\}`). + +Let :math:`S_i` represent the score, and let :math:`c` denote the cutoff point. Further, let :math:`Y_i(1)` and :math:`Y_i(0)` denote the potential outcomes with and without treatment, respectively. Then, the treatment effect at the cutoff + +.. math:: + + \tau_0 = \mathbb{E}[Y_i(1)-Y_i(0)\mid S_i = c] + +is identified as the difference in the conditional expectation of :math:`Y_i` at the cutoff from both sides + +.. math:: + + \tau_0 = \lim_{s \to c^+} \mathbb{E}[Y_i \mid S_i = s] - \lim_{s \to c^-} \mathbb{E}[Y_i \mid S_i = s] + +The key assumption for identifying this effect in a sharp RDD is: + +- **Continuity:** The conditional mean of the potential outcomes :math:`\mathbb{E}[Y_i(d)\mid S_i=s]` for :math:`d \in \{0, 1\}` is continuous at the cutoff level :math:`c`. + +This includes the necessary condition of exogeneity, implying units cannot perfectly manipulate their value of :math:`S_i` to either receive or avoid treatment exactly at the cutoff. + +Without the use of covariates, :math:`\tau_{0}` is typically estimated by running separate local linear regressions on each side of the cutoff, yielding an estimator of the form: + +.. math:: + + \hat{\tau}_{\text{base}}(h) = \sum_{i=1}^n w_i(h)Y_i, + +where the :math:`w_i(h)` are local linear regression weights that depend on the data through the realizations of the running variable only and :math:`h > 0` is a bandwidth. + +Under standard conditions, which include that the running variable is continuously distributed, and that the bandwidth :math:`h` tends to zero at an appropriate rate, the estimator :math:`\hat{\tau}_{\text{base}}(h)` is approximately normally distributed in large samples, with bias of order :math:`h^2` and variance of order :math:`(nh)^{-1}`: + +.. math:: + \hat{\tau}_{\text{base}}(h) \stackrel{a}{\sim} N\left(\tau + h^2 B_{\text{base}},(nh)^{-1}V_{\text{base}}\right). + +If covariates are available, they can be used to improve the accuracy of empirical RD estimates. The most popular strategy is to include them linearly and without kernel localization in the local linear regression. By simple least squares algebra, this "linear adjustment" estimator can be written as a no-covariates estimator with the covariate-adjusted outcome :math:`Y_i - X_i^{\top} \widehat{\gamma}_h`: + +.. math:: + \widehat{\tau}_{\text{lin}}(h) = \sum_{i=1}^n w_i(h)\left(Y_i - X_i^{\top} \widehat{\gamma}_h\right). + +Here, :math:`\widehat{\gamma}_h` is the minimizer from the regression + +.. math:: + \underset{\beta,\gamma}{\mathrm{arg\,min}} \, \sum_{i=1}^n K_h(S_i) (Y_i - Q_i^\top\beta- X_i^{\top}\gamma )^2, + +with :math:`Q_i =(D_i, S_i, D_i S_i, 1)^T` (see ``fs_specification`` in :ref:`Implementation Details `), :math:`K_h(v)=K(v/h)/h` with :math:`K(\cdot)` a kernel function. + +If :math:`\mathbb{E}[X_i | S_i = s]` is twice continuously differentiable around the cutoff, then the distribution of :math:`\widehat{\tau}_{\text{lin}}(h)` is similar to the one of the base estimator with potentially smaller variance term :math:`V_{\text{lin}}`. + +As this linear adjustment might not exploit the available covariate information efficiently, DoubleML features an RDD estimator with flexible covariate adjustment based on potentially nonlinear adjustment functions :math:`\eta`. The estimator takes the following form: + +.. math:: + \widehat{\tau}_{\text{RDFlex}}(h; \eta) = \sum_{i=1}^n w_i(h) M_i(\eta), \quad M_i(\eta) = Y_i - \eta(X_i). + +Similar to other algorithms in DoubleML, :math:`\eta` is estimated by ML methods and with crossfitting. Different than in other models, there is no orthogonal score, but a similar global insensitivity property holds (for details see `Noack, Olma and Rothe (2024) `_). We adjust the outcome variable by the influence of the covariates. + +This reduces the variance in the estimation potentially even further to: + +.. math:: + V(\eta) = \frac{\bar{\kappa}}{f_X(0)} \left( \mathbb{V}[M_i(\eta) | S_i = 0^+] + \mathbb{V}[M_i(\eta) | S_i = 0^-] \right). + +with :math:`\bar{\kappa}` being a kernel constant. To maximize the precision of the estimator :math:`\widehat\tau(h;\eta)` for any particular bandwidth :math:`h`, :math:`\eta` has to be chosen such that :math:`V(\eta)` is as small as possible. The equally-weighted average of the left and right limits of the conditional expectation function :math:`\mathbb{E}[Y_i|S_i=s,X_i=x]` at the cutoff achieves this goal. According to `Noack, Olma and Rothe (2024) `_, it holds: + +.. math:: + V(\eta) \geq V(\eta_0) \text{ for all } \eta, + +where: + +.. math:: + \eta_0(x) = \frac{1}{2} \left( \mu_0^+(x) + \mu_0^-(x) \right), \quad \mu_0^\star(x) = \mathbb{E}[Y_i | S_i = 0^\star, X_i = x] \text{ for } \star \in \{+, -\}. + +``RDFlex`` implements this regression discontinuity design with :math:`\eta_0` being estimated by user-specified ML methods. The indicator ``fuzzy=False`` indicates a sharp design. The ``DoubleMLData`` object has to be defined with the arguments: + + - ``y_col`` refers to the observed outcome, on which we want to estimate the effect at the cutoff + - ``s_col`` refers to the score + - ``x_cols`` refers to the covariates to be adjusted for + - ``d_cols`` is an indicator of whether an observation is treated or not. In the sharp design, this should be identical to an indicator of whether an observation is left or right of the cutoff (:math:`D_i = \mathbb{I}[S_i > c]`) + +Estimation is conducted via its ``fit()`` method: + +.. tab-set:: + + .. tab-item:: Python + :sync: py + + .. ipython:: python + :okwarning: + + import numpy as np + import pandas as pd + from sklearn.linear_model import LassoCV + from doubleml.rdd.datasets import make_simple_rdd_data + from doubleml.rdd import RDFlex + import doubleml as dml + + np.random.seed(42) + data_dict = make_simple_rdd_data(n_obs=1000, fuzzy=False) + cov_names = ['x' + str(i) for i in range(data_dict['X'].shape[1])] + df = pd.DataFrame(np.column_stack((data_dict['Y'], data_dict['D'], data_dict['score'], data_dict['X'])), columns=['y', 'd', 'score'] + cov_names) + + dml_data = dml.DoubleMLData(df, y_col='y', d_cols='d', x_cols=cov_names, s_col='score') + + ml_g = LassoCV() + + rdflex_obj = RDFlex(dml_data, ml_g, fuzzy=False) + rdflex_obj.fit() + + print(rdflex_obj) + + +Fuzzy Regression Discontinuity Design +************************************* + +In a **Fuzzy RDD**, treatment assignment :math:`T_i` is identical to the sharp RDD (:math:`T_i = \mathbb{1}\{S_i \geq c\}`), however, compliance is limited around the cutoff which leads to a different treatment received :math:`D_i` than assigned (:math:`D_i \neq T_i`) for some units. + +The parameter of interest in the Fuzzy RDD is the average treatment effect at the cutoff, for all individuals that comply with the assignment + +.. math:: + \theta_{0} = \mathbb{E}[Y_i(1)-Y_i(0)\mid S_i = c, \{i\in \text{compliers}\}] + +with :math:`Y_i(D_i(T_i))` being the potential outcome under the potential treatments. This effect is identified by + +.. math:: + \theta_{0} = \frac{\lim_{s \to c^+} \mathbb{E}[Y_i \mid S_i = s] - \lim_{s \to c^-} \mathbb{E}[Y_i \mid S_i = s]}{\lim_{s \to c^+} \mathbb{E}[D_i \mid S_i = s] - \lim_{s \to c^-} \mathbb{E}[D_i \mid S_i = s]} + +The assumptions for identifying the ATT in a fuzzy RDD are: + +- **Continuity of Potential Outcomes:** Similar to sharp RDD, the conditional mean of the potential outcomes :math:`\mathbb{E}[Y_i(d)\mid S_i=s]` for :math:`d \in \{0, 1\}` is continuous at the cutoff level :math:`c`. + +- **Continuity of Treatment Assignment Probability:** The probability of receiving treatment :math:`\mathbb{E}[D_i | S_i = s]` must change discontinuously at the cutoff, but there should be no other jumps in the probability. + +- **Monotonicity:** There must be no "defiers", meaning individuals for whom the treatment assignment goes in the opposite direction of the score. + +Under similar considerations as in the sharp case, an estimator using flexible covariate adjustment can be derived as: + +.. math:: + \hat{\theta}(h; \widehat{\eta}_Y, \widehat{\eta}_D) = \frac{\hat{\tau}_Y(h; \widehat{\eta}_Y)}{\hat{\tau}_D(h; \widehat{\eta}_D)} + = \frac{\sum_{i=1}^n w_{i}(h) (Y_i - \widehat{\eta}_{Y}(X_i))}{\sum_{i=1}^n w_{i}(h) (T_i - \widehat{\eta}_{D}(X_i))}, + +where :math:`\eta_Y` and :math:`\eta_D` are defined as in the sharp RDD setting, with the respective outcome. + +``RDFlex`` implements this fuzzy RDD with flexible covariate adjustment. The indicator ``fuzzy=True`` indicates a fuzzy design. The ``DoubleMLData`` object has to be defined with the arguments: + + - ``y_col`` refers to the observed outcome, on which we want to estimate the effect at the cutoff + - ``s_col`` refers to the score + - ``x_cols`` refers to the covariates to be adjusted for + - ``d_cols`` is an indicator of whether an observation is treated or not. In the fuzzy design, this should **not** be identical to an indicator of whether an observation is left or right of the cutoff (:math:`D_i \neq \mathbb{I}[S_i > c]`) + +Estimation is conducted via its ``fit()`` method: + +.. tab-set:: + + .. tab-item:: Python + :sync: py + + .. ipython:: python + :okwarning: + + import numpy as np + import pandas as pd + from sklearn.linear_model import LassoCV, LogisticRegressionCV + from doubleml.rdd.datasets import make_simple_rdd_data + from doubleml.rdd import RDFlex + import doubleml as dml + + np.random.seed(42) + data_dict = make_simple_rdd_data(n_obs=1000, fuzzy=True) + cov_names = ['x' + str(i) for i in range(data_dict['X'].shape[1])] + df = pd.DataFrame(np.column_stack((data_dict['Y'], data_dict['D'], data_dict['score'], data_dict['X'])), columns=['y', 'd', 'score'] + cov_names) + + dml_data = dml.DoubleMLData(df, y_col='y', d_cols='d', x_cols=cov_names, s_col='score') + + ml_g = LassoCV() + ml_m = LogisticRegressionCV() + + rdflex_obj = RDFlex(dml_data, ml_g, ml_m, fuzzy=True) + rdflex_obj.fit() + + print(rdflex_obj) + +.. _rdd_imp_details: + +Implementation Details +************************************* + +There are some specialities in the ``RDFlex`` implementation that differ from the rest of the package and thus deserve to be pointed out here. + +1. **Bandwidth Selection**: The bandwidth is a crucial tuning parameter for RDD algorithms. By default, our implementation uses the ``rdbwselect`` method from the ``rdrobust`` library for an initial selection. This can be overridden by the user using the parameter ``h_fs``. Since covariate adjustment and RDD fitting are interacting, by default, we repeat the bandwidth selection and nuisance estimation steps once in the ``fit()`` method. This can be adjusted by ``n_iterations``. +2. **Kernel Selection**: Another crucial decision when estimating with RDD is the kernel determining the weights for observations around the cutoff. For this, the parameters ``fs_kernel`` and ``kernel`` are important. The latter is a key-worded argument and is used in the RDD estimation, while the ``fs_kernel`` specifies the kernel used in the nuisance estimation. By default, both of them are ``triangular``. +3. **Local and Global Learners**: ``RDFlex`` estimates the nuisance functions locally around the cutoff. In certain scenarios, it can be desirable to rather perform a global fit on the full support of the score :math:`S`. For this, the ``Global Learners`` in ``doubleml.utils`` can be used (see our example notebook in the :ref:`Example Gallery `). +4. **First Stage Specifications**: In nuisance estimation, we have to add variable(s) to add information about the location of the observation left or right of the cutoff. Available options are: + In the default case ``fs_specification="cutoff"``, this is an indicator of whether the observation is left or right. + If ``fs_specification="cutoff and score"``, additionally the score is added. + In the case of ``fs_specification="interacted cutoff and score"``, also an interaction term of the cutoff indicator and the score is added. +5. **Intention-to-Treat Effects**: Above, we demonstrated how to estimate the ATE at the cutoff in a fuzzy RDD. To estimate an Intention-to-Treat effect instead, the parameter ``fuzzy=False`` can be selected. +6. **Key-worded Arguments**: ``rdrobust`` as the underlying RDD library has additional parameters to tune the estimation. You can use ``**kwargs`` to add them via ``RDFlex``. diff --git a/requirements.txt b/requirements.txt index 244fda10..d68f28b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,5 @@ plotly seaborn xgboost lightgbm +flaml +rdrobust