Skip to contents

Overview

propensity makes it easy to calculate propensity score weights and use them to estimate causal effects. It supports:

  • Six estimands for binary exposures (ATE, ATT, ATU, ATO, ATM, and entropy weights)
  • Binary, categorical, and continuous exposures
  • Trimming, truncation, and calibration for extreme propensity scores
  • Inverse probability weighted estimation with standard errors that account for propensity score estimation

You can learn more in vignette("propensity").

Installation

You can install propensity from CRAN with:

install.packages("propensity")

You can install the development version of propensity from GitHub with:

# install.packages("pak")
pak::pak("r-causal/propensity")

Usage

library(propensity)

# Simulate data with a confounder, binary exposure, and binary outcome
n <- 200
x1 <- rnorm(n)
z <- rbinom(n, 1, plogis(0.5 * x1))
y <- rbinom(n, 1, plogis(-0.5 + 0.8 * z + 0.3 * x1))
dat <- data.frame(x1, z, y)

# Step 1: Fit a propensity score model
ps_mod <- glm(z ~ x1, data = dat, family = binomial())

# Step 2: Calculate ATE weights and fit a weighted outcome model
wts <- wt_ate(ps_mod)
outcome_mod <- glm(y ~ z, data = dat, family = quasibinomial(), weights = wts)

# Step 3: Estimate causal effects with correct standard errors
ipw(ps_mod, outcome_mod)
#> Inverse Probability Weight Estimator
#> Estimand: ATE 
#> Effects: marginal (population-averaged) 
#> 
#> Weight Estimator:
#>   Call: glm(formula = z ~ x1, family = binomial(), data = dat) 
#> 
#> Outcome Model:
#>   Call: glm(formula = y ~ z, family = quasibinomial(), data = dat, weights = wts) 
#> 
#> Marginal estimates:
#>                estimate  std.err       z  ci.lower ci.upper conf.level p.value    
#> mean 0         0.439827 0.050624  8.6881 0.3406055  0.53905       0.95 < 2e-16 ***
#> mean 1         0.582131 0.048746 11.9421 0.4865905  0.67767       0.95 < 2e-16 ***
#> rd 1 vs 0      0.142304 0.070204  2.0270 0.0047068  0.27990       0.95 0.04266 *  
#> log(rr) 1 vs 0 0.280314 0.142195  1.9713 0.0016172  0.55901       0.95 0.04869 *  
#> log(or) 1 vs 0 0.573392 0.286710  1.9999 0.0114518  1.13533       0.95 0.04551 *  
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

By default, ipw() computes standard errors by M-estimation, stacking the propensity score and outcome estimating equations so the uncertainty of estimating the propensity scores is carried into the standard errors. Set se_method = "linearization" for the influence-function method (binary exposures with an exposure-only outcome model).

A continuous exposure follows the same three steps. The propensity score model is a model of the dose, such as an lm(), and the weights are a ratio of densities rather than a function of a propensity score:

# A dose, and an outcome that depends on it
dat$a <- 0.4 + 0.7 * dat$x1 + rnorm(n)
dat$y_dose <- 1 + 0.5 * dat$a + 0.3 * dat$x1 + rnorm(n)

# The weights are stabilized by default for a continuous exposure
ps_dose <- lm(a ~ x1, data = dat)
wts_dose <- wt_ate(ps_dose)
#> ℹ Using exposure variable "a" from the propensity score model
#> ℹ Treating `.exposure` as continuous

# The outcome model is a marginal structural model of the dose
msm <- lm(y_dose ~ a, data = dat, weights = wts_dose)
ipw(ps_dose, msm)
#> Inverse Probability Weight Estimator
#> Estimand: ATE 
#> Effects: marginal (population-averaged) 
#> 
#> Weight Estimator:
#>   Call: lm(formula = a ~ x1, data = dat) 
#> 
#> Outcome Model:
#>   Call: lm(formula = y_dose ~ a, data = dat, weights = wts_dose) 
#> 
#> Marginal estimates:
#>       estimate std.err      z ci.lower ci.upper conf.level   p.value    
#> slope  0.62419 0.14291 4.3679   0.3441  0.90428       0.95 1.255e-05 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Estimands

Each weight function targets a different population:

Estimand Target population Function
ATE Entire population wt_ate()
ATT Treated units wt_att()
ATU Untreated units wt_atu() (alias: wt_atc())
ATO Overlap population wt_ato()
ATM Matched population wt_atm()
Entropy Entropy-tilted population wt_entropy()

ATO and ATM weights are bounded by construction, making them a good alternative when ATE weights are highly variable.

Learn more