crabbymetrics
  • Home
  • API
  • Binding Crash Course
  • Supervised Learning
    • OLS
    • Ridge
    • Fixed Effects OLS
    • ElasticNet
    • Synthetic Control
    • Logit
    • Multinomial Logit
    • Poisson
    • TwoSLS
    • GMM
    • FTRL
    • MEstimator Poisson
  • Semiparametrics
    • Balancing Weights
    • EPLM
    • Average Derivative
    • Double ML And AIPW
    • Richer Regression
  • Unsupervised Learning
    • PCA And Kernel Basis
  • Ablations
    • Variance Estimators
    • Semiparametric Estimator Comparisons
    • Bridging Finite And Superpopulation
  • Optimization
    • Optimizers
    • GMM With Optimizers
  • Ding: First Course
    • Overview And TOC
    • Ch 1 Correlation And Simpson
    • Ch 2 Potential Outcomes
    • Ch 3 CRE And Fisher RT
    • Ch 4 CRE And Neyman
    • Ch 9 Bridging Finite And Superpopulation
    • Ch 11 Propensity Score
    • Ch 12 Double Robust ATE
    • Ch 13 Double Robust ATT
    • Ch 21 Experimental IV
    • Ch 23 Econometric IV

On this page

  • 1 Build A Population With Heterogeneous Effects
  • 2 A Perfect Doctor And A Coin-Flip Doctor
  • 3 Selection Bias Is Not The Same As The Effect
  • 4 Takeaway

First Course Ding: Chapter 2

Potential outcomes and treatment selection

Chapter 2 is the basic potential-outcomes setup: treatment effects are individual-level objects, while observed outcomes depend on both the potential outcomes and the treatment rule.

Show code
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

np.set_printoptions(precision=4, suppress=True)

1 Build A Population With Heterogeneous Effects

Show code
rng = np.random.default_rng(2)
n = 1000
y0 = rng.normal(loc=0.0, scale=1.0, size=n)
tau = -0.4 + 0.9 * y0
y1 = y0 + tau

ate = tau.mean()
att_if_treat_positive = tau[tau >= 0.0].mean()

pd.DataFrame(
    {
        "estimand": ["ATE", "Share with positive treatment effect", "ATT if only gainers are treated"],
        "value": [ate, np.mean(tau >= 0.0), att_if_treat_positive],
    }
)
estimand value
0 ATE -0.420168
1 Share with positive treatment effect 0.311000
2 ATT if only gainers are treated 0.623081
Show code
fig, ax = plt.subplots(figsize=(6, 4))
ax.hist(tau, bins=40, color="tab:blue", alpha=0.75)
ax.axvline(0.0, color="black", linestyle="--")
ax.set_xlabel("Individual treatment effect")
ax.set_ylabel("Count")
ax.set_title("Treatment effects are heterogeneous")
fig.tight_layout()

2 A Perfect Doctor And A Coin-Flip Doctor

The perfect doctor treats exactly when the individual treatment effect is nonnegative. The coin-flip doctor ignores the potential outcomes entirely.

Show code
z_perfect = (tau >= 0.0).astype(float)
z_coin = rng.binomial(1, 0.5, size=n).astype(float)

y_perfect = z_perfect * y1 + (1.0 - z_perfect) * y0
y_coin = z_coin * y1 + (1.0 - z_coin) * y0

def observed_difference(y_obs, z_obs):
    treated = z_obs == 1.0
    control = ~treated
    return y_obs[treated].mean() - y_obs[control].mean()

selection_table = pd.DataFrame(
    {
        "rule": ["Perfect doctor", "Coin-flip doctor"],
        "observed_difference": [
            observed_difference(y_perfect, z_perfect),
            observed_difference(y_coin, z_coin),
        ],
        "treated_share": [z_perfect.mean(), z_coin.mean()],
        "average_effect_among_treated": [
            tau[z_perfect == 1.0].mean(),
            tau[z_coin == 1.0].mean(),
        ],
    }
)
selection_table
rule observed_difference treated_share average_effect_among_treated
0 Perfect doctor 2.305471 0.311 0.623081
1 Coin-flip doctor -0.356470 0.503 -0.400482

3 Selection Bias Is Not The Same As The Effect

Under coin-flip treatment, the observed difference is estimating the ATE. Under targeted treatment, the observed difference mixes causal effects with positive selection on untreated potential outcomes.

Show code
selection_bias = y0[z_perfect == 1.0].mean() - y0[z_perfect == 0.0].mean()

pd.DataFrame(
    {
        "quantity": [
            "ATE",
            "Observed difference under perfect doctor",
            "Average effect among treated under perfect doctor",
            "Selection bias in untreated outcomes",
        ],
        "value": [
            ate,
            observed_difference(y_perfect, z_perfect),
            tau[z_perfect == 1.0].mean(),
            selection_bias,
        ],
    }
)
quantity value
0 ATE -0.420168
1 Observed difference under perfect doctor 2.305471
2 Average effect among treated under perfect doctor 0.623081
3 Selection bias in untreated outcomes 1.682389

4 Takeaway

Potential outcomes make it obvious why the same observed contrast can have different meanings under different assignment rules. Chapter 2 is mostly estimand bookkeeping, but it is the bookkeeping that the rest of the Ding material depends on.