## ----setup, include = FALSE---------------------------------------------------
knitr::opts_chunk$set(
  collapse   = TRUE,
  comment    = "#>",
  fig.width  = 7,
  fig.height = 4.5,
  out.width  = "100%"
)

## ----library------------------------------------------------------------------
library(causalsim)

## ----dgp----------------------------------------------------------------------
dgp <- causalsim_dgp(
  n = 500,
  n_confounders = 1,
  effect = 2,
  propensity = "moderate",
  baseline = "moderate"
)
dgp

## ----draw---------------------------------------------------------------------
dat <- causalsim_draw(dgp, seed = 1L)
head(dat)

## ----confounding-check--------------------------------------------------------
aggregate(W ~ A, data = dat, FUN = mean)

## ----estimators---------------------------------------------------------------
# Naive: regresses Y on A only, omits the confounder
naive_est <- function(data) {
  fit <- lm(Y ~ A, data = data)
  est <- coef(fit)[["A"]]
  se <- sqrt(vcov(fit)["A", "A"])
  c(estimate = est, ci_lower = est - 1.96 * se, ci_upper = est + 1.96 * se)
}

# OLS: adjusts for the observed confounder W
ols_est <- function(data) {
  fit <- lm(Y ~ A + W, data = data)
  est <- coef(fit)[["A"]]
  se <- sqrt(vcov(fit)["A", "A"])
  c(estimate = est, ci_lower = est - 1.96 * se, ci_upper = est + 1.96 * se)
}

## ----estimator-list, eval = FALSE---------------------------------------------
# ols_est <- function(data) {
#   fit <- lm(Y ~ A + W, data = data)
#   ci <- confint(fit)["A", ]
#   list(
#     estimate = coef(fit)["A"],
#     ci_lower = ci[1],
#     ci_upper = ci[2]
#   )
# }

## ----eval-naive---------------------------------------------------------------
eval_naive <- causalsim_eval(dgp, naive_est, reps = 300L, seed = 1L)
eval_naive

## ----eval-ols-----------------------------------------------------------------
eval_ols <- causalsim_eval(dgp, ols_est, reps = 300L, seed = 1L)
eval_ols

## ----summary-ols--------------------------------------------------------------
summary(eval_ols)

## ----plot-ols, fig.cap = "Distribution of OLS estimates over 300 replications. Solid line: true ATE. Dashed line: mean estimate."----
plot(eval_ols)

## ----grid-n-------------------------------------------------------------------
grid_n <- causalsim_eval_grid(
  dgp = dgp,
  estimator = ols_est,
  vary = list(n = c(100L, 250L, 500L, 1000L)),
  reps = 300L,
  metrics = c("bias", "rmse"),
  seed = 1L
)
grid_n

## ----grid-confounding---------------------------------------------------------
conf_levels <- list(propensity = c("low", "moderate", "high"))

grid_naive <- causalsim_eval_grid(dgp, naive_est,
                             vary = conf_levels,
                             reps = 300L,
                             metrics = "bias",
                             seed = 1L)

grid_ols <- causalsim_eval_grid(dgp, ols_est,
                           vary = conf_levels,
                           reps = 300L,
                           metrics = "bias",
                           seed = 1L)

comparison <- rbind(
  cbind(estimator = "naive", grid_naive$results),
  cbind(estimator = "ols",   grid_ols$results)
)
comparison <- comparison[order(comparison$propensity, comparison$estimator), ]
rownames(comparison) <- NULL
comparison

## ----het-dgp------------------------------------------------------------------
het_dgp <- causalsim_dgp(
  n = 4000,
  covariates = list(
    W = causalsim_covar("normal", role = "confounder"),
    V = causalsim_covar("binary", role = "effect_modifier", prob = 0.5)
  ),
  effect = function(V) 2 + 3 * V,   # effect is 2 when V = 0, 5 when V = 1
  propensity = function(W) plogis(0.5 * W),
  baseline = function(W) W
)
het_dgp

## ----het-truth----------------------------------------------------------------
d <- causalsim_draw(het_dgp, seed = 1L)
tapply(d$.tau, d$V, mean)   # 2 for V = 0, 5 for V = 1

## ----het-estimators-----------------------------------------------------------
overall <- lm(Y ~ A + W, data = d)         # assumes a constant effect
interact <- lm(Y ~ A * V + W, data = d)     # allows the effect to vary with V

c(
  average = coef(overall)[["A"]],
  subgroup_v0 = coef(interact)[["A"]],
  subgroup_v1 = coef(interact)[["A"]] + coef(interact)[["A:V"]]
)

## ----session-info-------------------------------------------------------------
sessionInfo()

