{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Analyzing RCT reemployment experiment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Analyzing RCT with Precision by Adjusting for Baseline Covariates" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Jonathan Roth's DGP\n", "\n", "Here we set up a DGP with heterogenous effects. In this example, with is due to Jonathan Roth, we have:\n", "\n", "$$\n", "\\begin{align}\n", "E [Y(0) | Z] = - Z, \\quad E [Y(1) |Z] = Z, \\quad Z \\sim N(0,1).\n", "\\end{align}\n", "$$\n", "\n", "The CATE is\n", "\n", "$$\n", "\\begin{align}\n", "E [Y(1) - Y(0) | Z ]= 2 Z.\n", "\\end{align}\n", "$$\n", "\n", "and the ATE is\n", "\n", "$$\n", "\\begin{align}\n", "2 E Z = 0.\n", "\\end{align}\n", "$$\n", "\n", "We would like to estimate ATE as precisely as possible.\n", "\n", "An economic motivation for this example could be provided as follows: Let D be the treatment of going to college, and $Z$ academic skills. Suppose that academic skills cause lower earnings Y(0) in jobs that don't require college degree, and cause higher earnings Y(1) in jobs that require college degrees. This type of scenario is reflected in the DGP set-up above.\n", "\n" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.181" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Import relevant packages for splitting data\n", "import numpy as np\n", "import random\n", "import math\n", "import pandas as pd\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "# Set Seed\n", "# to make the results replicable (generating random numbers)\n", "np.random.seed(12345676) # set MC seed\n", "\n", "n = 1000 # sample size\n", "Z = np.random.normal(0, 1, 1000).reshape((1000, 1)) # generate Z\n", "Y0 = -Z + np.random.normal(0, 1, 1000).reshape((1000, 1)) # conditional average baseline response is -Z\n", "Y1 = Z + np.random.normal(0, 1, 1000).reshape((1000, 1)) # conditional average treatment effect is +Z\n", "D = (np.random.uniform(0, 1, n)<.2).reshape((1000, 1)) # treatment indicator; only 20% get treated\n", "np.mean(D)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "Y = Y1*D + Y0*(1-D) # observed Y\n", "D = D - np.mean(D) # demean D\n", "Z = Z - np.mean(Z) # demean Z" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Analyze the RCT data with Precision Adjustment\n", "\n", "Consider \n", "\n", "* classical 2-sample approach, no adjustment (CL)\n", "* classical linear regression adjustment (CRA)\n", "* interactive regression adjusment (IRA)\n", "\n", "Carry out inference using robust inference, using the sandwich formulas (Eicker-Huber-White). \n", "\n", "Observe that CRA delivers estimates that are less efficient than CL (pointed out by Freedman), whereas IRA delivers more efficient approach (pointed out by Lin). In order for CRA to be more efficient than CL, we need the CRA to be a correct model of the conditional expectation function of Y given D and X, which is not the case here." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | D | \n", "Z | \n", "Z_times_D | \n", "
---|---|---|---|
0 | \n", "-0.181 | \n", "1.408649 | \n", "-0.254966 | \n", "
1 | \n", "-0.181 | \n", "0.466085 | \n", "-0.084361 | \n", "
2 | \n", "-0.181 | \n", "0.365742 | \n", "-0.066199 | \n", "
3 | \n", "-0.181 | \n", "-1.038993 | \n", "0.188058 | \n", "
4 | \n", "-0.181 | \n", "0.222988 | \n", "-0.040361 | \n", "
... | \n", "... | \n", "... | \n", "... | \n", "
995 | \n", "-0.181 | \n", "0.161225 | \n", "-0.029182 | \n", "
996 | \n", "-0.181 | \n", "-0.472047 | \n", "0.085440 | \n", "
997 | \n", "-0.181 | \n", "1.010122 | \n", "-0.182832 | \n", "
998 | \n", "-0.181 | \n", "-0.177596 | \n", "0.032145 | \n", "
999 | \n", "-0.181 | \n", "0.392989 | \n", "-0.071131 | \n", "
1000 rows × 3 columns
\n", "