Weighting Methods for Causal Inference

R
Causal Inference
Author

Shuhan (Alice) Ai

Published

May 10, 2025

In previous posts, we’ve explored the Potential Outcomes framework and matching methods for causal inference. Today, we’ll dive deeper into propensity score methods, which offer a powerful and flexible approach to addressing selection bias in observational studies.

1. Introduction

The propensity score is defined as:

\[p(X_i) = P(D_i = 1 | X_i)\] Where:

  • \(D_i\) is the treatment indicator (1 if treated, 0 if control)
  • \(X_i\) is a vector of observed covariates for unit \(i\)

If we have two districts with the same propensity score, they have the same probability of receiving treatment based on their observed characteristics. This means that among districts with similar propensity scores, treatment assignment is assuming random - aiming to achieve a quasi-experimental design.

A property of propensity scores is that if treatment assignment is strongly ignorable given \(X\), then:

\[Y_i(1), Y_i(0) \perp\!\!\!\perp D_i \mid \mathbf{X}_i\]

This means that conditioning on the propensity score is sufficient to achieve independence between potential outcomes and treatment assignment. In other words, within strata of units with similar propensity scores, the distribution of covariates should be similar between treated and control groups.

1.1 Propensity Score Methods

There are three main ways to use propensity scores:

  1. Matching: Pair treated and control units with similar propensity scores
  2. Inverse Probability of Treatment Weighting (IPTW): Weight observations by the inverse of their probability of receiving their observed treatment
  3. Stratification: Divide the sample into strata based on propensity score ranges

In this post, we’ll focus on IPTW, with a practical example using educational data.

2. Inverse Probability of Treatment Weighting (IPTW)

2.1 Mathematical Foundation

IPTW is based on creating a pseudo-population where treatment assignment is independent of confounders. The key insight is that by weighting each observation by the inverse of its probability of receiving the treatment it actually received, we can balance the covariate distributions between treatment groups.

For the Average Treatment Effect (ATE), the weights are:

\[w_i^{ATE} = \frac{D_i}{p(X_i)} + \frac{1-D_i}{1-p(X_i)}\]

  • For treated units (\(D_i = 1\)): \(w_i = \frac{1}{p(X_i)}\)
  • For control units (\(D_i = 0\)): \(w_i = \frac{1}{1-p(X_i)}\)

For the Average Treatment Effect of Treated (ATT), the weights are:

\[w_i^{ATT} = D_i + (1-D_i)\frac{p(X_i)}{1-p(X_i)}\]

  • For treated units (\(D_i = 1\)): \(w_i = 1\)
  • For control units (\(D_i = 0\)): \(w_i = \frac{p(X_i)}{1-p(X_i)}\)

For the Average Treatment Effect of Untreated (ATU), the weights are:

\[w_i^{ATU} = D_i\frac{1-p(X_i)}{p(X_i)} + (1-D_i)\]

  • For treated units (\(D_i = 1\)): \(w_i = \frac{1-p(X_i)}{p(X_i)}\)
  • For control units (\(D_i = 0\)): \(w_i = 1\)

Intuition Behind IPTW

The weights create a pseudo-population where:

  • Treated units who were unlikely to be treated (low \(p(X_i)\)) get high weights
  • Control units who were likely to be treated (high \(p(X_i)\)) get high weights
  • This “fills in” the missing counterfactuals by upweighting similar units from the opposite group

3. HSLS:09 Science Museum Practical Example

Leveraging the High School Longitudinal Study (HSLS:09), this example aims to answer the following questions:

  • Treament: What individual and contextual factors are associated with students’ likelihood of visiting science museums or planetariums during high school?

  • Science Identity Formation: Does participation in informal science learning experiences—such as visiting a science museum or planetarium between 9th and 11th grade—positively influence the development of a science identity?

  • STEM Major Intentions: Does exposure to science museums or planetariums during high school increase the likelihood of pursuing a STEM major in college?

Note

The analysis data file include 6,481 9th grade students with observed treatment group status and no-zero value of sample weight (W3W1W2STU).Missing rate range from 1% to 19%. Dealing with missing values using likewise deletion is problematic, given the sample size will be reduced considerably and will introduce bias unless the data is missing completely at random (Enders, 2010). In this study we impute single values using stochastic imputations and chained equations embedded in mice r package.

The final analysis data file includes 8,106 college-bound students and the following variables:

  • STU_ID = unique HSLS student identifier (persistent 2009 – 2013)
  • SCH_ID = unique HSLS school identifier (persistent 2009 – 2013)
  • W3W1W2STU = W3 Student Longitudinal Analytic Weight BY-F1-U13
  • W1SCHOOL = Baseline year school level Weight

Treatment indicator:
- p2museum = visited a science-related museum/planetarium between 9th and 11th grade (1 = Yes, 0 = No)
Dependent variables (DVs):
- x2sciid = 11th-grade science identity scale (standardized z-score)
- collegeSTEMmajorasp = binary indicator of whether the student aspired to or declared a STEM major while enrolled in college (1 = Yes, 0 = No)
Pre-treatment covariates:
- x1sciid = 9th-grade baseline science identity scale (standardized z-score)
- stemoccasp9th = binary indicator of whether the student aspired to a STEM occupation in 9th grade
School-level covariates (baseline, 9th grade):
- x1locale = school locale: City, Suburb, Town, Rural (ref)
- x1control = school control: Public, Catholic or other Private School (ref)
- x1region = U.S. census region of the school: Northeast (ref), Midwest, South, West
- a1mspdintrst = binary indicator for whether the school sponsored a math or science after-school program (Yes/No)
- a1msmentor = binary indicator for whether the school paired students with mentors in math or science (Yes/No)
Student-level IVs: background characteristics:
- x1race = White (ref), Asian, Black, Latino, Multiracial, Native American, Other
- x1sex = binary sex indicator (0 = Male, 1 = Female)
- x1sesq5 = socioeconomic status quintile, First quintile (ref, lowest), Second, Third, Fourth, and Fifth quintile (highest)
- x1txmscr = 9th-grade math achievement score (IRT-scaled)
- x1sciint = science course interest scale (standardized z-score)
- x1stuedexpct = student’s highest educational expectation, categorized as Uncertain (ref), High School, Bachelor’s, Master’s, or PhD
- x1schoolbel = 9th-grade student-perceived school belonging scale (standardized z-score)
Student-leve IVs: formal and informal STEM experiences (9th grade):
- s1alg1m09 = binary indicator of whether the student took Algebra I in 9th grade (Yes/No)
- s1sfall09 = binary indicator of whether the student took a science course in fall 2009 (Yes/No)
- p1campms = binary indicator of whether the student participated in math or science camp outside of school in last year (Yes/No)
- p1fixed = binary indicator of whether parents built or fixed something with 9th grader in last year (Yes/No)
- p1sciproj = binary indicator of whether the helped 9th grader with a school science fair project in last year with 9th grader in last year (Yes/No)

Analysis the outcome, ATE, ATT, ATU:

  • Used doubly robust approach
  • Consider using MLM random slop (two levels: student level and school level)
  • Then conduct robustness check, use sensemakr to run the sensitivity analysis
  • Use student’s 9th grade science identity as benchmark covariates
Code
rm(list = ls())
library(tidyverse)
library(skimr)
library(flextable)
library(table1)
library(tableone)
library(knitr)
library(MatchIt)
library(WeightIt)
library(cobalt) #for love plots 
library(marginaleffects)
library(sensemakr) #sensitivity analysis
library(patchwork)
library(car)
library(naniar)
library(mice)
library(haven)
library(lm.beta) #standardized beta coef (for comparing effect sizes)
library(lme4) #for fixed models
library(survey) #for weights and robust SEs
library(broom) #convert model into tidy data frames
library(lme4)
library(merTools)
library(lmerTest)
library(WeMix) 

data <- readRDS("hslspsm_imputed.rds")

3.1 Descriptive Analysis

Code
length(unique(data$STU_ID))
[1] 6481
Code
length(unique(data$SCH_ID))
[1] 933
Code
autofit(as_flextable(table(data$p2museum)))

Var1

Count

Percent

Control

3,805

58.7%

Treatment

2,676

41.3%

Total

6,481

100.0%

Code
#combine Native American and Other other all into others categories due to small subpopulation
data <- data %>% 
  mutate(x1race = fct_collapse(x1race,
                               Other = c("Native American", "Other")   # <- names to merge
                               ))

#Descriptive Analysis
descr1 <- table1(~ x1sciid + stemoccasp9th + #pre-IVs
                   x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                   x1race + x1sex + x1sesq5 +  x1schoolbel + 
                   x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                   s1alg1m09 + s1sfall09 + 
                   p1campms + p1fixed + p1sciproj +
                   x2sciid + collegeSTEMmajorasp | as.factor(p2museum), data = data)
descr1
Control
(N=3805)
Treatment
(N=2676)
Overall
(N=6481)
x1sciid
Mean (SD) 0.0450 (0.998) 0.214 (1.01) 0.115 (1.01)
Median [Min, Max] -0.300 [-2.25, 2.15] 0.270 [-2.89, 2.27] 0.270 [-2.89, 2.27]
stemoccasp9th
non-STEM 2558 (67.2%) 1705 (63.7%) 4263 (65.8%)
STEM 1247 (32.8%) 971 (36.3%) 2218 (34.2%)
x1locale
Rural 924 (24.3%) 553 (20.7%) 1477 (22.8%)
City 1100 (28.9%) 841 (31.4%) 1941 (29.9%)
Suburb 1330 (35.0%) 996 (37.2%) 2326 (35.9%)
Town 451 (11.9%) 286 (10.7%) 737 (11.4%)
x1control
Catholic or other private 664 (17.5%) 600 (22.4%) 1264 (19.5%)
Public 3141 (82.5%) 2076 (77.6%) 5217 (80.5%)
x1region
Northeast 604 (15.9%) 416 (15.5%) 1020 (15.7%)
Midwest 948 (24.9%) 757 (28.3%) 1705 (26.3%)
South 1579 (41.5%) 1029 (38.5%) 2608 (40.2%)
West 674 (17.7%) 474 (17.7%) 1148 (17.7%)
a1mspdintrst
No 1815 (47.7%) 1258 (47.0%) 3073 (47.4%)
Yes 1990 (52.3%) 1418 (53.0%) 3408 (52.6%)
a1msmentor
No 2521 (66.3%) 1694 (63.3%) 4215 (65.0%)
Yes 1284 (33.7%) 982 (36.7%) 2266 (35.0%)
x1race
White 2062 (54.2%) 1546 (57.8%) 3608 (55.7%)
Asian 302 (7.9%) 205 (7.7%) 507 (7.8%)
Black 469 (12.3%) 245 (9.2%) 714 (11.0%)
Latino 589 (15.5%) 403 (15.1%) 992 (15.3%)
Multiracial 335 (8.8%) 250 (9.3%) 585 (9.0%)
Other 48 (1.3%) 27 (1.0%) 75 (1.2%)
x1sex
Male 2005 (52.7%) 1221 (45.6%) 3226 (49.8%)
Female 1800 (47.3%) 1455 (54.4%) 3255 (50.2%)
x1sesq5
First quintile (lowest) 662 (17.4%) 274 (10.2%) 936 (14.4%)
Second quintile 691 (18.2%) 342 (12.8%) 1033 (15.9%)
Third quintile 739 (19.4%) 459 (17.2%) 1198 (18.5%)
Fourth quintile 748 (19.7%) 579 (21.6%) 1327 (20.5%)
Fifth quintile (highest) 965 (25.4%) 1022 (38.2%) 1987 (30.7%)
x1schoolbel
Mean (SD) 0.118 (0.982) 0.206 (0.956) 0.155 (0.972)
Median [Min, Max] 0.0100 [-3.87, 2.88] 0.180 [-3.87, 2.64] 0.100 [-3.87, 2.88]
x1txmscr
Mean (SD) 41.2 (11.9) 43.0 (11.7) 41.9 (11.9)
Median [Min, Max] 41.3 [15.8, 69.9] 43.0 [13.6, 69.9] 42.1 [13.6, 69.9]
x1sciint
Mean (SD) 0.0291 (0.981) 0.123 (0.998) 0.0681 (0.989)
Median [Min, Max] 0.160 [-3.15, 3.05] 0.160 [-2.59, 3.46] 0.160 [-3.15, 3.46]
x1stuedexpct
Uncertain 795 (20.9%) 488 (18.2%) 1283 (19.8%)
High school 453 (11.9%) 217 (8.1%) 670 (10.3%)
Bachelor 922 (24.2%) 613 (22.9%) 1535 (23.7%)
Master 817 (21.5%) 639 (23.9%) 1456 (22.5%)
PhD 818 (21.5%) 719 (26.9%) 1537 (23.7%)
s1alg1m09
No 1653 (43.4%) 1267 (47.3%) 2920 (45.1%)
Yes 2152 (56.6%) 1409 (52.7%) 3561 (54.9%)
s1sfall09
No 629 (16.5%) 355 (13.3%) 984 (15.2%)
Yes 3176 (83.5%) 2321 (86.7%) 5497 (84.8%)
p1campms
No 3693 (97.1%) 2529 (94.5%) 6222 (96.0%)
Yes 112 (2.9%) 147 (5.5%) 259 (4.0%)
p1fixed
No 2180 (57.3%) 1385 (51.8%) 3565 (55.0%)
Yes 1625 (42.7%) 1291 (48.2%) 2916 (45.0%)
p1sciproj
No 2397 (63.0%) 1562 (58.4%) 3959 (61.1%)
Yes 1408 (37.0%) 1114 (41.6%) 2522 (38.9%)
x2sciid
Mean (SD) 0.0465 (0.995) 0.189 (1.00) 0.105 (1.00)
Median [Min, Max] 0.0400 [-2.15, 2.54] 0.0800 [-2.83, 2.10] 0.0400 [-2.83, 2.54]
collegeSTEMmajorasp
No 3029 (79.6%) 2034 (76.0%) 5063 (78.1%)
Yes 776 (20.4%) 642 (24.0%) 1418 (21.9%)

Compute the navie effect without adding PS and Weighting

Unconditional Navie Estimate (No PS, no sample weight, single level without covaraite adjustment)
Code
m00 <- lm(x2sciid ~ p2museum, data = data)
summary(m00)

Call:
lm(formula = x2sciid ~ p2museum, data = data)

Residuals:
     Min       1Q   Median       3Q      Max 
-3.01610 -0.58651 -0.00651  0.61349  2.49201 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        0.04651    0.01617   2.875  0.00405 ** 
p2museumTreatment  0.14217    0.02517   5.648 1.69e-08 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.9977 on 6479 degrees of freedom
Multiple R-squared:  0.004899,  Adjusted R-squared:  0.004746 
F-statistic:  31.9 on 1 and 6479 DF,  p-value: 1.694e-08
Code
sqrt(hccm(m00)[2,2])
[1] 0.0252022
Code
m000 <- glm(collegeSTEMmajorasp ~ p2museum, data = data, family = "binomial" )
summary(m000)

Call:
glm(formula = collegeSTEMmajorasp ~ p2museum, family = "binomial", 
    data = data)

Coefficients:
                  Estimate Std. Error z value Pr(>|z|)    
(Intercept)       -1.36184    0.04023 -33.848  < 2e-16 ***
p2museumTreatment  0.20866    0.06056   3.445  0.00057 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 6809.9  on 6480  degrees of freedom
Residual deviance: 6798.1  on 6479  degrees of freedom
AIC: 6802.1

Number of Fisher Scoring iterations: 4

3.2 Define Distance Measure - Propensity score - glm

Proposentity score without sample weight

Code
#estimate logistic model
pscore.m <- glm(p2museum ~ x1sciid + stemoccasp9th + #pre-IVs
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                           s1alg1m09 + s1sfall09 + 
                           p1campms + p1fixed + p1sciproj, data = data, family = "binomial")

table.pscore.m <- tidy.logit.table(pscore.m)
knitr::kable(table.pscore.m, digits = 3, caption = "Multiple logistic regression results predicting science museum or or planetariums visit")
Multiple logistic regression results predicting science museum or or planetariums visit
Variable Coefficient SE OR p_value Sig
(Intercept) (Intercept) -1.380 0.200 0.252 0.000 ***
x1sciid x1sciid 0.084 0.031 1.088 0.007 **
stemoccasp9thSTEM stemoccasp9thSTEM -0.023 0.059 0.978 0.701
x1localeCity x1localeCity 0.092 0.077 1.097 0.227
x1localeSuburb x1localeSuburb 0.119 0.072 1.127 0.098 .
x1localeTown x1localeTown 0.011 0.097 1.011 0.911
x1controlPublic x1controlPublic -0.059 0.073 0.942 0.414
x1regionMidwest x1regionMidwest 0.234 0.083 1.263 0.005 **
x1regionSouth x1regionSouth 0.009 0.080 1.009 0.910
x1regionWest x1regionWest 0.039 0.092 1.039 0.676
a1mspdintrstYes a1mspdintrstYes 0.020 0.054 1.021 0.707
a1msmentorYes a1msmentorYes 0.133 0.056 1.142 0.018 *
x1raceAsian x1raceAsian -0.191 0.102 0.826 0.061 .
x1raceBlack x1raceBlack -0.288 0.092 0.750 0.002 **
x1raceLatino x1raceLatino 0.143 0.080 1.153 0.075 .
x1raceMultiracial x1raceMultiracial 0.047 0.093 1.048 0.617
x1raceOther x1raceOther -0.031 0.250 0.970 0.901
x1sexFemale x1sexFemale 0.388 0.055 1.475 0.000 ***
x1sesq5Second quintile x1sesq5Second quintile 0.170 0.101 1.186 0.092 .
x1sesq5Third quintile x1sesq5Third quintile 0.400 0.099 1.491 0.000 ***
x1sesq5Fourth quintile x1sesq5Fourth quintile 0.584 0.099 1.793 0.000 ***
x1sesq5Fifth quintile (highest) x1sesq5Fifth quintile (highest) 0.866 0.099 2.377 0.000 ***
x1schoolbel x1schoolbel -0.011 0.029 0.989 0.697
x1txmscr x1txmscr -0.004 0.003 0.996 0.194
x1sciint x1sciint 0.032 0.031 1.033 0.302
x1stuedexpctHigh school x1stuedexpctHigh school -0.025 0.105 0.975 0.809
x1stuedexpctBachelor x1stuedexpctBachelor 0.079 0.080 1.083 0.323
x1stuedexpctMaster x1stuedexpctMaster 0.139 0.082 1.149 0.090 .
x1stuedexpctPhD x1stuedexpctPhD 0.217 0.084 1.243 0.010 **
s1alg1m09Yes s1alg1m09Yes -0.054 0.056 0.947 0.335
s1sfall09Yes s1sfall09Yes 0.070 0.075 1.072 0.356
p1campmsYes p1campmsYes 0.504 0.133 1.655 0.000 ***
p1fixedYes p1fixedYes 0.296 0.055 1.344 0.000 ***
p1sciprojYes p1sciprojYes 0.151 0.054 1.163 0.006 **
Code
#get predicted probabilities/and probabilies on log-odds scale
data <- data %>%
  mutate(pscore = predict(pscore.m, type = "response"),
         pscorelogodd = predict(pscore.m))

#summary(data$pscorelogodd[data$p2museum == "Treatment"])
#summary(data$pscorelogodd[data$p2museum == "Control"])
#summary(data$pscore[data$p2museum == "Treatment"])
#summary(data$pscore[data$p2museum == "Control"])
Examine Common Support (overlap) without weight
Code
#examine common support(overlap)
#create plots
p1 <- ggplot(data, aes(x = pscore, fill = p2museum)) +
geom_histogram(binwidth = 0.01, position = "identity", alpha = 0.6, color = "black") +
scale_fill_manual(values = c("#ee84a8", "#71bced"), labels = c("Control", "Treatment")) +
labs(title = "Propensity Score (Linear Scale)", x = "Propensity Score", y = "Frequency", fill = "Science Museum") + theme_minimal() +
theme(plot.title = element_text(size =12))

p2 <- ggplot(data, aes(x = pscore, y = p2museum, color = p2museum)) +
  geom_jitter(height = 0.2, alpha = 0.4, size = 1) +
  scale_color_manual(values = c("#ee84a8", "#71bced"), labels = c("Control", "Treatment")) +
  labs(title = "Common Support by Group", x = "Propensity Score", y = NULL, color = "Science Museum") +
  theme_minimal() +
  theme(plot.title = element_text(size =12))

p3 <- ggplot(data, aes(x = pscorelogodd, y = p2museum, color = p2museum)) +
  geom_jitter(height = 0.2, alpha = 0.4, size = 1) +
  scale_color_manual(values = c("#ee84a8", "#71bced"), labels = c("Control", "Treatment")) + 
  scale_x_continuous(breaks = seq(floor(min(data$pscorelogodd, na.rm = TRUE)),
                                  ceiling(max(data$pscorelogodd, na.rm = TRUE)), by = 0.2)) +
  labs(title = "Common Support by Group", x = "Logit Score", y = NULL, color = "Science Museum") +
  theme_minimal() +
  theme(plot.title = element_text(size =12))

#combine all
combined_plot <- p1 / p2 / p3 + plot_layout(ncol = 1, heights = c(4, 3, 3))
combined_plot

Trim the data PS without weight
Code
table(data$STU_ID[data$pscorelogodd < -1.6])

10381 10976 12758 12762 15195 15953 16031 16769 18304 22894 23386 23621 23791 
    1     1     1     1     1     1     1     1     1     1     1     1     1 
24144 26696 30294 30615 30910 31759 32693 33787 34150 34306 
    1     1     1     1     1     1     1     1     1     1 
Code
table(data$STU_ID[data$pscorelogodd > 1.0])

11427 13066 13853 15422 16464 20410 24342 26772 27444 28433 28489 28903 29366 
    1     1     1     1     1     1     1     1     1     1     1     1     1 
30427 31053 
    1     1 
Code
data_trimmed <- data %>%
  filter(pscorelogodd >= -1.6 & pscorelogodd <= 1.0)

table(data_trimmed$p2museum)

  Control Treatment 
     3783      2660 
Code
#total loss 41 cases

3.3 IPTW, balance with IPTW*W3W1W2STU

Compare two weight methods:

  • First, use non-weighted PSM, them multiply by the ate/att/atc, we get: w.ate_W3W1W2STU; w.att_W3W1W2STU; w.atc_W3W1W2STU
  • Second, use sample weight in the propensity score stage, then compute final weights as the product of the sampling weight and the propensity score weight: w.ate.w_W3W1W2STU; w.att.w_W3W1W2STU; w.atc.w_W3W1W2STU.

Combined weight with non-weighted PSM

Code
#normalize the panel weight
data_trimmed <- data_trimmed %>%
  mutate(W3W1W2STU_norm = W3W1W2STU / mean(W3W1W2STU, na.rm = TRUE))

#calculate IPTW (ATE, ATT, ATU)
data_trimmed <- data_trimmed %>%
  #weight based on the treatment
  mutate(D = ifelse(p2museum == "Treatment", 1, 0),
  w.ate = (D * (1/pscore)) + ((1 - D) * (1/(1 - pscore))),
  w.att = (D) + ((1 - D) * (pscore/(1 - pscore))),
  w.atc = (D * ((1 - pscore)/pscore)) + (1 - D),
  #balance the weight based on the control level
  w.ate_W3W1W2STU = w.ate*W3W1W2STU_norm,
  w.att_W3W1W2STU = w.att*W3W1W2STU_norm,
  w.atc_W3W1W2STU = w.atc*W3W1W2STU_norm
  )

table(data_trimmed$D)

   0    1 
3783 2660 
Code
table(data_trimmed$p2museum)

  Control Treatment 
     3783      2660 
Check the data balance

SMD is <0.10 for all covariates

Code
#question? check the balance only for w.ate or for w.ate_W3W1W2STU

covars <- c("x1sciid", 
            "x1locale", "x1control", "x1region", "a1mspdintrst", "a1msmentor",
            "x1race", "x1sex", "x1sesq5", "x1schoolbel", 
            "x1txmscr", "x1sciint", "x1stuedexpct", 
            "s1alg1m09", "s1sfall09",
            "p1campms", "p1fixed", "p1sciproj") 

bal_ate <- bal.tab(
  x = data_trimmed[, covars],
  treat = data_trimmed$D,
  weights = data_trimmed$w.ate_W3W1W2STU,
  estimand = "ATE",
  s.d.denom = "pooled",            # std-diff denominator
  m.threshold = .1,                # ±0.10 rule of thumb
  un = TRUE
)
bal_ate
Balance Measures
                                    Type Diff.Un Diff.Adj    M.Threshold
x1sciid                          Contin.  0.1619  -0.0336 Balanced, <0.1
x1locale_Rural                    Binary -0.0354  -0.0058 Balanced, <0.1
x1locale_City                     Binary  0.0248   0.0091 Balanced, <0.1
x1locale_Suburb                   Binary  0.0206  -0.0033 Balanced, <0.1
x1locale_Town                     Binary -0.0101   0.0001 Balanced, <0.1
x1control_Public                  Binary -0.0477  -0.0048 Balanced, <0.1
x1region_Northeast                Binary -0.0032  -0.0089 Balanced, <0.1
x1region_Midwest                  Binary  0.0306   0.0152 Balanced, <0.1
x1region_South                    Binary -0.0285  -0.0133 Balanced, <0.1
x1region_West                     Binary  0.0011   0.0069 Balanced, <0.1
a1mspdintrst_Yes                  Binary  0.0082   0.0132 Balanced, <0.1
a1msmentor_Yes                    Binary  0.0292  -0.0028 Balanced, <0.1
x1race_White                      Binary  0.0343  -0.0038 Balanced, <0.1
x1race_Asian                      Binary -0.0016   0.0075 Balanced, <0.1
x1race_Black                      Binary -0.0307  -0.0049 Balanced, <0.1
x1race_Latino                     Binary -0.0049   0.0116 Balanced, <0.1
x1race_Multiracial                Binary  0.0054  -0.0099 Balanced, <0.1
x1race_Other                      Binary -0.0025  -0.0004 Balanced, <0.1
x1sex_Female                      Binary  0.0672   0.0091 Balanced, <0.1
x1sesq5_First quintile (lowest)   Binary -0.0684   0.0023 Balanced, <0.1
x1sesq5_Second quintile           Binary -0.0536   0.0051 Balanced, <0.1
x1sesq5_Third quintile            Binary -0.0228  -0.0053 Balanced, <0.1
x1sesq5_Fourth quintile           Binary  0.0199   0.0042 Balanced, <0.1
x1sesq5_Fifth quintile (highest)  Binary  0.1248  -0.0064 Balanced, <0.1
x1schoolbel                      Contin.  0.0878   0.0345 Balanced, <0.1
x1txmscr                         Contin.  0.1499   0.0269 Balanced, <0.1
x1sciint                         Contin.  0.0914  -0.0488 Balanced, <0.1
x1stuedexpct_Uncertain            Binary -0.0247   0.0122 Balanced, <0.1
x1stuedexpct_High school          Binary -0.0362   0.0061 Balanced, <0.1
x1stuedexpct_Bachelor             Binary -0.0139   0.0036 Balanced, <0.1
x1stuedexpct_Master               Binary  0.0231  -0.0224 Balanced, <0.1
x1stuedexpct_PhD                  Binary  0.0516   0.0005 Balanced, <0.1
s1alg1m09_Yes                     Binary -0.0389   0.0260 Balanced, <0.1
s1sfall09_Yes                     Binary  0.0316  -0.0047 Balanced, <0.1
p1campms_Yes                      Binary  0.0213   0.0012 Balanced, <0.1
p1fixed_Yes                       Binary  0.0522  -0.0038 Balanced, <0.1
p1sciproj_Yes                     Binary  0.0439   0.0331 Balanced, <0.1

Balance tally for mean differences
                   count
Balanced, <0.1        37
Not Balanced, >0.1     0

Variable with the greatest mean difference
 Variable Diff.Adj    M.Threshold
 x1sciint  -0.0488 Balanced, <0.1

Effective sample sizes
           Control Treated
Unadjusted 3783.   2660.  
Adjusted   1557.81  933.45
Code
#questions? what's the Effective sample sizes mean here?
#The effective numbers after applying the IPTW*W3W1W2STU weights had a sample of about 1,574 controls and 959 treated cases.

love.plot(bal_ate,
          stats       = "mean.diffs",
          abs         = FALSE, 
          binary      = "std",       
          thresholds  = c(m = .10),
          colors      = c("#ee84a8", "#71bced"),
          title       = "Covariate Balance after IPTW (ATE)") +
  theme_bw()

Get the descriptive stats after weighting (ATE)
Code
#create survey design object using IPTW weights
design <- svydesign(ids = ~1, weights = ~w.ate_W3W1W2STU, data = data_trimmed)

#create post-weighting descriptive table by treatment group
table_weighted <- svyCreateTableOne(vars = covars, strata = "p2museum", data = design)

print(table_weighted)
                             Stratified by p2museum
                              Control         Treatment       p      test
  n                           6444.30         6446.19                    
  x1sciid (mean (SD))            0.06 (1.01)     0.03 (0.99)   0.392     
  x1locale (%)                                                 0.964     
     Rural                     1430.7 (22.2)   1393.6 (21.6)             
     City                      2074.1 (32.2)   2133.1 (33.1)             
     Suburb                    2214.1 (34.4)   2193.3 (34.0)             
     Town                       725.4 (11.3)    726.2 (11.3)             
  x1control = Public (%)       5995.7 (93.0)   5966.2 (92.6)   0.450     
  x1region (%)                                                 0.707     
     Northeast                 1069.1 (16.6)   1012.3 (15.7)             
     Midwest                   1315.3 (20.4)   1413.8 (21.9)             
     South                     2419.8 (37.5)   2335.0 (36.2)             
     West                      1640.2 (25.5)   1685.0 (26.1)             
  a1mspdintrst = Yes (%)       3482.6 (54.0)   3568.5 (55.4)   0.521     
  a1msmentor = Yes (%)         2342.4 (36.3)   2325.1 (36.1)   0.891     
  x1race (%)                                                   0.854     
     White                     3218.9 (49.9)   3195.1 (49.6)             
     Asian                      211.6 ( 3.3)    260.1 ( 4.0)             
     Black                      879.5 (13.6)    848.0 (13.2)             
     Latino                    1490.9 (23.1)   1566.2 (24.3)             
     Multiracial                560.2 ( 8.7)    496.3 ( 7.7)             
     Other                       83.3 ( 1.3)     80.5 ( 1.2)             
  x1sex = Female (%)           3183.8 (49.4)   3243.5 (50.3)   0.660     
  x1sesq5 (%)                                                  0.980     
     First quintile (lowest)   1305.6 (20.3)   1321.2 (20.5)             
     Second quintile           1246.7 (19.3)   1279.8 (19.9)             
     Third quintile            1290.0 (20.0)   1256.3 (19.5)             
     Fourth quintile           1196.6 (18.6)   1224.4 (19.0)             
     Fifth quintile (highest)  1405.3 (21.8)   1364.6 (21.2)             
  x1schoolbel (mean (SD))        0.04 (0.97)     0.07 (0.95)   0.389     
  x1txmscr (mean (SD))          40.15 (11.86)   40.47 (11.53)  0.493     
  x1sciint (mean (SD))           0.05 (0.98)     0.00 (0.99)   0.225     
  x1stuedexpct (%)                                             0.735     
     Uncertain                 1299.5 (20.2)   1378.7 (21.4)             
     High school                795.2 (12.3)    834.7 (12.9)             
     Bachelor                  1493.0 (23.2)   1516.9 (23.5)             
     Master                    1447.7 (22.5)   1303.5 (20.2)             
     PhD                       1409.0 (21.9)   1412.4 (21.9)             
  s1alg1m09 = Yes (%)          3612.0 (56.0)   3780.5 (58.6)   0.199     
  s1sfall09 = Yes (%)          5448.6 (84.5)   5420.0 (84.1)   0.764     
  p1campms = Yes (%)            260.5 ( 4.0)    268.3 ( 4.2)   0.885     
  p1fixed = Yes (%)            2973.8 (46.1)   2950.5 (45.8)   0.854     
  p1sciproj = Yes (%)          2460.4 (38.2)   2674.6 (41.5)   0.103     

3.4 ATE, ATT, ATC

Nested Result with IPW science identity

Code
#estimate navie ATE of museum on sciid
ate_sciid <- lm(x2sciid ~ x1sciid + #pre-IVs
                          p2museum, data = data_trimmed, weights = w.ate_W3W1W2STU)

sqrt(hccm(ate_sciid)[3,3])
[1] 0.03523017
Code
table.ate_sciid <- tidy.coeftable(ate_sciid) 
knitr::kable(table.ate_sciid, digits = 3, caption = "nested1 ATE weighted results of linear regression on science identity")
nested1 ATE weighted results of linear regression on science identity
Variable Coefficient SE Beta p_value Sig
(Intercept) 0.018 0.015 NA 0.239
x1sciid 0.446 0.011 0.451 0.000 ***
p2museumTreatment 0.054 0.022 0.028 0.013 *
Code
ate_sciid2 <- lm(x2sciid ~ x1sciid + #pre-IVs
                           p2museum +
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct, data = data_trimmed, weights = w.ate_W3W1W2STU)

sqrt(hccm(ate_sciid2)[3,3])
[1] 0.03512001
Code
table.ate_sciid2 <- tidy.coeftable(ate_sciid2)
knitr::kable(table.ate_sciid2, digits = 3, caption = "nested 2 ATE weighted results of linear regression on science identity")
nested 2 ATE weighted results of linear regression on science identity
Variable Coefficient SE Beta p_value Sig
(Intercept) -0.221 0.077 NA 0.004 **
x1sciid 0.369 0.013 0.374 0.000 ***
p2museumTreatment 0.058 0.022 0.029 0.008 **
x1localeCity -0.042 0.032 -0.020 0.191
x1localeSuburb -0.066 0.031 -0.032 0.031 *
x1localeTown -0.112 0.040 -0.036 0.005 **
x1controlPublic 0.053 0.044 0.014 0.224
x1regionMidwest 0.010 0.036 0.004 0.784
x1regionSouth 0.007 0.033 0.003 0.840
x1regionWest 0.002 0.036 0.001 0.958
a1mspdintrstYes -0.003 0.022 -0.001 0.910
a1msmentorYes -0.005 0.023 -0.002 0.825
x1raceAsian 0.138 0.060 0.026 0.021 *
x1raceBlack 0.005 0.036 0.002 0.890
x1raceLatino -0.119 0.032 -0.052 0.000 ***
x1raceMultiracial -0.052 0.042 -0.015 0.211
x1raceOther 0.208 0.098 0.024 0.034 *
x1sexFemale -0.120 0.022 -0.061 0.000 ***
x1sesq5Second quintile -0.020 0.035 -0.008 0.579
x1sesq5Third quintile 0.014 0.036 0.006 0.695
x1sesq5Fourth quintile -0.001 0.038 0.000 0.974
x1sesq5Fifth quintile (highest) 0.072 0.039 0.030 0.067 .
x1schoolbel -0.023 0.012 -0.023 0.059 .
x1txmscr 0.006 0.001 0.068 0.000 ***
x1sciint 0.076 0.013 0.076 0.000 ***
x1stuedexpctHigh school 0.069 0.039 0.023 0.080 .
x1stuedexpctBachelor 0.026 0.033 0.011 0.438
x1stuedexpctMaster 0.082 0.034 0.034 0.016 *
x1stuedexpctPhD 0.194 0.035 0.081 0.000 ***

Result with IPW science identity

Code
#estimate ATE of museum on sciid
ate_dr_sciid <- lm(x2sciid ~ x1sciid + #pre-IVs
                           p2museum + 
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                           s1alg1m09 + s1sfall09 + 
                           p1campms + p1fixed + p1sciproj, data = data_trimmed, weights = w.ate_W3W1W2STU)

sqrt(hccm(ate_dr_sciid)[3,3])
[1] 0.03497568
Code
table.ate_dr_sciid <- tidy.coeftable(ate_dr_sciid)
knitr::kable(table.ate_dr_sciid, digits = 3, caption = "ATE weighted results of linear regression on science identity")
ATE weighted results of linear regression on science identity
Variable Coefficient SE Beta p_value Sig
(Intercept) -0.239 0.086 NA 0.005 **
x1sciid 0.368 0.013 0.373 0.000 ***
p2museumTreatment 0.055 0.022 0.028 0.010 *
x1localeCity -0.042 0.032 -0.020 0.192
x1localeSuburb -0.065 0.031 -0.031 0.034 *
x1localeTown -0.111 0.040 -0.036 0.006 **
x1controlPublic 0.052 0.044 0.014 0.238
x1regionMidwest 0.012 0.036 0.005 0.740
x1regionSouth 0.000 0.034 0.000 0.995
x1regionWest -0.002 0.036 -0.001 0.949
a1mspdintrstYes 0.001 0.022 0.001 0.957
a1msmentorYes -0.001 0.023 -0.001 0.954
x1raceAsian 0.137 0.060 0.026 0.024 *
x1raceBlack -0.005 0.037 -0.002 0.899
x1raceLatino -0.125 0.032 -0.054 0.000 ***
x1raceMultiracial -0.054 0.042 -0.015 0.195
x1raceOther 0.202 0.098 0.023 0.040 *
x1sexFemale -0.116 0.023 -0.059 0.000 ***
x1sesq5Second quintile -0.019 0.036 -0.008 0.583
x1sesq5Third quintile 0.015 0.036 0.006 0.682
x1sesq5Fourth quintile -0.003 0.038 -0.001 0.926
x1sesq5Fifth quintile (highest) 0.073 0.040 0.030 0.066 .
x1schoolbel -0.024 0.012 -0.023 0.055 .
x1txmscr 0.006 0.001 0.073 0.000 ***
x1sciint 0.075 0.013 0.075 0.000 ***
x1stuedexpctHigh school 0.067 0.039 0.023 0.085 .
x1stuedexpctBachelor 0.022 0.033 0.010 0.501
x1stuedexpctMaster 0.083 0.034 0.035 0.015 *
x1stuedexpctPhD 0.192 0.035 0.081 0.000 ***
s1alg1m09Yes 0.020 0.024 0.010 0.404
s1sfall09Yes -0.034 0.031 -0.013 0.263
p1campmsYes 0.047 0.055 0.010 0.394
p1fixedYes 0.014 0.023 0.007 0.528
p1sciprojYes 0.037 0.023 0.018 0.108
Code
#estimate ATT of museum on sciid
att_dr_sciid <- lm(x2sciid ~ x1sciid + #pre-IVs
                           p2museum + 
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                           s1alg1m09 + s1sfall09 + 
                           p1campms + p1fixed + p1sciproj, data = data_trimmed, weights = w.att_W3W1W2STU)

sqrt(hccm(att_dr_sciid)[3,3])
[1] 0.03375646
Code
table.att_dr_sciid <- tidy.coeftable(att_dr_sciid)
knitr::kable(table.att_dr_sciid, digits = 3, caption = "ATT weighted results of linear regression on science identity")
ATT weighted results of linear regression on science identity
Variable Coefficient SE Beta p_value Sig
(Intercept) -0.296 0.087 NA 0.001 ***
x1sciid 0.374 0.013 0.374 0.000 ***
p2museumTreatment 0.053 0.022 0.027 0.015 *
x1localeCity -0.055 0.032 -0.026 0.088 .
x1localeSuburb -0.071 0.031 -0.034 0.021 *
x1localeTown -0.112 0.041 -0.035 0.007 **
x1controlPublic 0.062 0.041 0.017 0.130
x1regionMidwest 0.014 0.036 0.006 0.704
x1regionSouth -0.004 0.034 -0.002 0.916
x1regionWest -0.003 0.036 -0.001 0.930
a1mspdintrstYes -0.004 0.023 -0.002 0.863
a1msmentorYes -0.008 0.023 -0.004 0.716
x1raceAsian 0.112 0.060 0.021 0.063 .
x1raceBlack -0.032 0.039 -0.010 0.412
x1raceLatino -0.112 0.032 -0.047 0.001 ***
x1raceMultiracial -0.081 0.041 -0.023 0.047 *
x1raceOther 0.126 0.106 0.013 0.235
x1sexFemale -0.118 0.023 -0.059 0.000 ***
x1sesq5Second quintile 0.019 0.040 0.007 0.628
x1sesq5Third quintile 0.041 0.039 0.016 0.297
x1sesq5Fourth quintile 0.018 0.040 0.007 0.655
x1sesq5Fifth quintile (highest) 0.111 0.040 0.050 0.006 **
x1schoolbel -0.024 0.012 -0.023 0.049 *
x1txmscr 0.007 0.001 0.079 0.000 ***
x1sciint 0.080 0.013 0.079 0.000 ***
x1stuedexpctHigh school 0.081 0.043 0.025 0.058 .
x1stuedexpctBachelor 0.030 0.034 0.013 0.370
x1stuedexpctMaster 0.082 0.035 0.035 0.017 *
x1stuedexpctPhD 0.213 0.035 0.093 0.000 ***
s1alg1m09Yes 0.030 0.024 0.015 0.209
s1sfall09Yes -0.034 0.032 -0.012 0.288
p1campmsYes 0.052 0.048 0.012 0.271
p1fixedYes 0.024 0.023 0.012 0.294
p1sciprojYes 0.033 0.023 0.016 0.151
Code
#estimate ATC of museum on sciid
atc_dr_sciid <- lm(x2sciid ~ x1sciid + #pre-IVs
                           p2museum + 
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                           s1alg1m09 + s1sfall09 + 
                           p1campms + p1fixed + p1sciproj, data = data_trimmed, weights = w.atc_W3W1W2STU)

sqrt(hccm(atc_dr_sciid)[3,3])
[1] 0.03711254
Code
table.atc_dr_sciid <- tidy.coeftable(atc_dr_sciid)
knitr::kable(table.atc_dr_sciid, digits = 3, caption = "ATC weighted results of linear regression on science identity")
ATC weighted results of linear regression on science identity
Variable Coefficient SE Beta p_value Sig
(Intercept) -0.208 0.086 NA 0.016 *
x1sciid 0.364 0.013 0.370 0.000 ***
p2museumTreatment 0.057 0.022 0.029 0.008 **
x1localeCity -0.033 0.032 -0.016 0.295
x1localeSuburb -0.061 0.031 -0.029 0.045 *
x1localeTown -0.110 0.040 -0.036 0.006 **
x1controlPublic 0.043 0.047 0.011 0.354
x1regionMidwest 0.010 0.037 0.004 0.783
x1regionSouth 0.002 0.034 0.001 0.943
x1regionWest -0.002 0.036 -0.001 0.953
a1mspdintrstYes 0.005 0.022 0.002 0.833
a1msmentorYes 0.003 0.023 0.002 0.884
x1raceAsian 0.152 0.060 0.029 0.012 *
x1raceBlack 0.008 0.035 0.003 0.811
x1raceLatino -0.133 0.032 -0.058 0.000 ***
x1raceMultiracial -0.035 0.042 -0.010 0.413
x1raceOther 0.240 0.094 0.029 0.011 *
x1sexFemale -0.115 0.023 -0.059 0.000 ***
x1sesq5Second quintile -0.037 0.034 -0.016 0.269
x1sesq5Third quintile 0.004 0.035 0.002 0.909
x1sesq5Fourth quintile -0.010 0.037 -0.004 0.784
x1sesq5Fifth quintile (highest) 0.049 0.040 0.019 0.219
x1schoolbel -0.023 0.012 -0.023 0.057 .
x1txmscr 0.006 0.001 0.069 0.000 ***
x1sciint 0.072 0.013 0.073 0.000 ***
x1stuedexpctHigh school 0.060 0.037 0.021 0.108
x1stuedexpctBachelor 0.019 0.032 0.008 0.567
x1stuedexpctMaster 0.086 0.034 0.035 0.012 *
x1stuedexpctPhD 0.177 0.035 0.072 0.000 ***
s1alg1m09Yes 0.013 0.023 0.007 0.569
s1sfall09Yes -0.034 0.030 -0.013 0.257
p1campmsYes 0.040 0.064 0.007 0.534
p1fixedYes 0.008 0.023 0.004 0.719
p1sciprojYes 0.040 0.023 0.020 0.082 .

Nested Result with IPW STEM Major Asp

Code
ate_stemma <- glm(collegeSTEMmajorasp ~ 
                           stemoccasp9th +  p2museum,
                           data = data_trimmed, family = binomial(), weights = w.ate_W3W1W2STU)

table.ate_stemma <- tidy.logit.table(ate_stemma)
knitr::kable(table.ate_stemma, digits = 3, caption = "nested1 ATE weighted multiple logistic regression results of STEM major aspiration")
nested1 ATE weighted multiple logistic regression results of STEM major aspiration
Variable Coefficient SE OR p_value Sig
(Intercept) (Intercept) -1.708 0.037 0.181 0.00 ***
stemoccasp9thSTEM stemoccasp9thSTEM 0.598 0.046 1.818 0.00 ***
p2museumTreatment p2museumTreatment 0.115 0.045 1.122 0.01 *
Code
ate_stemma2 <- glm(collegeSTEMmajorasp ~ 
                           stemoccasp9th +  #pre-IVs
                           p2museum + 
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct,
                           data = data_trimmed, family = binomial(), weights = w.ate_W3W1W2STU)

table.ate_stemma2 <- tidy.logit.table(ate_stemma2)
knitr::kable(table.ate_stemma2, digits = 3, caption = "nested2 ATE weighted multiple logistic regression results of STEM major aspiration")
nested2 ATE weighted multiple logistic regression results of STEM major aspiration
Variable Coefficient SE OR p_value Sig
(Intercept) (Intercept) -3.714 0.177 0.024 0.000 ***
stemoccasp9thSTEM stemoccasp9thSTEM 0.581 0.053 1.789 0.000 ***
p2museumTreatment p2museumTreatment 0.142 0.048 1.152 0.003 **
x1localeCity x1localeCity 0.150 0.070 1.162 0.033 *
x1localeSuburb x1localeSuburb 0.092 0.068 1.096 0.180
x1localeTown x1localeTown 0.096 0.091 1.100 0.295
x1controlPublic x1controlPublic -0.110 0.090 0.896 0.218
x1regionMidwest x1regionMidwest 0.026 0.079 1.026 0.746
x1regionSouth x1regionSouth 0.075 0.073 1.078 0.300
x1regionWest x1regionWest -0.080 0.079 0.923 0.315
a1mspdintrstYes a1mspdintrstYes -0.173 0.050 0.841 0.001 ***
a1msmentorYes a1msmentorYes 0.040 0.051 1.041 0.430
x1raceAsian x1raceAsian 0.302 0.113 1.353 0.008 **
x1raceBlack x1raceBlack 0.137 0.084 1.147 0.102
x1raceLatino x1raceLatino -0.101 0.073 0.904 0.170
x1raceMultiracial x1raceMultiracial 0.029 0.090 1.029 0.747
x1raceOther x1raceOther 0.478 0.214 1.613 0.026 *
x1sexFemale x1sexFemale -1.139 0.051 0.320 0.000 ***
x1sesq5Second quintile x1sesq5Second quintile 0.076 0.089 1.079 0.392
x1sesq5Third quintile x1sesq5Third quintile 0.178 0.088 1.195 0.042 *
x1sesq5Fourth quintile x1sesq5Fourth quintile 0.386 0.087 1.471 0.000 ***
x1sesq5Fifth quintile (highest) x1sesq5Fifth quintile (highest) 0.273 0.090 1.314 0.002 **
x1schoolbel x1schoolbel -0.033 0.028 0.967 0.226
x1txmscr x1txmscr 0.051 0.003 1.053 0.000 ***
x1sciint x1sciint 0.160 0.026 1.173 0.000 ***
x1stuedexpctHigh school x1stuedexpctHigh school 0.115 0.101 1.122 0.253
x1stuedexpctBachelor x1stuedexpctBachelor 0.151 0.076 1.163 0.047 *
x1stuedexpctMaster x1stuedexpctMaster 0.184 0.078 1.202 0.018 *
x1stuedexpctPhD x1stuedexpctPhD 0.239 0.079 1.270 0.002 **

Result with IPW STEM Major Asp

Code
#estimate ATE of museum on STEM major
# Outcome model with doubly robust estimator using ATE weights
ate_dr_stemma <- glm(collegeSTEMmajorasp ~ 
                           stemoccasp9th +  #pre-IVs
                           p2museum + 
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                           s1alg1m09 + s1sfall09 + 
                           p1campms + p1fixed + p1sciproj,
                             data = data_trimmed, family = binomial(), weights = w.ate_W3W1W2STU)

table.ate_dr_stemma <- tidy.logit.table(ate_dr_stemma)
knitr::kable(table.ate_dr_stemma, digits = 3, caption = "ATE weighted multiple logistic regression results of STEM major aspiration")
ATE weighted multiple logistic regression results of STEM major aspiration
Variable Coefficient SE OR p_value Sig
(Intercept) (Intercept) -3.456 0.198 0.032 0.000 ***
stemoccasp9thSTEM stemoccasp9thSTEM 0.573 0.053 1.773 0.000 ***
p2museumTreatment p2museumTreatment 0.144 0.048 1.155 0.003 **
x1localeCity x1localeCity 0.152 0.071 1.164 0.033 *
x1localeSuburb x1localeSuburb 0.095 0.069 1.100 0.168
x1localeTown x1localeTown 0.082 0.092 1.085 0.378
x1controlPublic x1controlPublic -0.154 0.090 0.857 0.088 .
x1regionMidwest x1regionMidwest 0.019 0.080 1.020 0.808
x1regionSouth x1regionSouth 0.021 0.074 1.021 0.781
x1regionWest x1regionWest -0.118 0.080 0.888 0.141
a1mspdintrstYes a1mspdintrstYes -0.178 0.050 0.837 0.000 ***
a1msmentorYes a1msmentorYes 0.067 0.052 1.070 0.193
x1raceAsian x1raceAsian 0.272 0.115 1.312 0.018 *
x1raceBlack x1raceBlack 0.140 0.085 1.150 0.101
x1raceLatino x1raceLatino -0.111 0.074 0.895 0.135
x1raceMultiracial x1raceMultiracial 0.042 0.090 1.042 0.645
x1raceOther x1raceOther 0.525 0.217 1.691 0.015 *
x1sexFemale x1sexFemale -1.081 0.053 0.339 0.000 ***
x1sesq5Second quintile x1sesq5Second quintile 0.085 0.090 1.088 0.345
x1sesq5Third quintile x1sesq5Third quintile 0.176 0.088 1.193 0.046 *
x1sesq5Fourth quintile x1sesq5Fourth quintile 0.357 0.088 1.429 0.000 ***
x1sesq5Fifth quintile (highest) x1sesq5Fifth quintile (highest) 0.223 0.091 1.249 0.014 *
x1schoolbel x1schoolbel -0.032 0.028 0.968 0.247
x1txmscr x1txmscr 0.046 0.003 1.047 0.000 ***
x1sciint x1sciint 0.153 0.026 1.165 0.000 ***
x1stuedexpctHigh school x1stuedexpctHigh school 0.102 0.101 1.107 0.315
x1stuedexpctBachelor x1stuedexpctBachelor 0.153 0.077 1.166 0.045 *
x1stuedexpctMaster x1stuedexpctMaster 0.152 0.078 1.164 0.052 .
x1stuedexpctPhD x1stuedexpctPhD 0.189 0.080 1.209 0.018 *
s1alg1m09Yes s1alg1m09Yes -0.351 0.053 0.704 0.000 ***
s1sfall09Yes s1sfall09Yes 0.047 0.074 1.048 0.521
p1campmsYes p1campmsYes 0.498 0.108 1.645 0.000 ***
p1fixedYes p1fixedYes 0.310 0.050 1.363 0.000 ***
p1sciprojYes p1sciprojYes 0.007 0.051 1.007 0.885
Code
# Outcome model with doubly robust estimator using ATT weights
att_dr_stemma <- glm(collegeSTEMmajorasp ~ 
                           stemoccasp9th +  #pre-IVs
                           p2museum + 
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                           s1alg1m09 + s1sfall09 + 
                           p1campms + p1fixed + p1sciproj,
                           data = data_trimmed, family = binomial(), weights = w.att_W3W1W2STU)

table.att_dr_stemma <- tidy.logit.table(att_dr_stemma)
knitr::kable(table.ate_dr_stemma, digits = 3, caption = "ATT weighted multiple logistic regression results of STEM major aspiration")
ATT weighted multiple logistic regression results of STEM major aspiration
Variable Coefficient SE OR p_value Sig
(Intercept) (Intercept) -3.456 0.198 0.032 0.000 ***
stemoccasp9thSTEM stemoccasp9thSTEM 0.573 0.053 1.773 0.000 ***
p2museumTreatment p2museumTreatment 0.144 0.048 1.155 0.003 **
x1localeCity x1localeCity 0.152 0.071 1.164 0.033 *
x1localeSuburb x1localeSuburb 0.095 0.069 1.100 0.168
x1localeTown x1localeTown 0.082 0.092 1.085 0.378
x1controlPublic x1controlPublic -0.154 0.090 0.857 0.088 .
x1regionMidwest x1regionMidwest 0.019 0.080 1.020 0.808
x1regionSouth x1regionSouth 0.021 0.074 1.021 0.781
x1regionWest x1regionWest -0.118 0.080 0.888 0.141
a1mspdintrstYes a1mspdintrstYes -0.178 0.050 0.837 0.000 ***
a1msmentorYes a1msmentorYes 0.067 0.052 1.070 0.193
x1raceAsian x1raceAsian 0.272 0.115 1.312 0.018 *
x1raceBlack x1raceBlack 0.140 0.085 1.150 0.101
x1raceLatino x1raceLatino -0.111 0.074 0.895 0.135
x1raceMultiracial x1raceMultiracial 0.042 0.090 1.042 0.645
x1raceOther x1raceOther 0.525 0.217 1.691 0.015 *
x1sexFemale x1sexFemale -1.081 0.053 0.339 0.000 ***
x1sesq5Second quintile x1sesq5Second quintile 0.085 0.090 1.088 0.345
x1sesq5Third quintile x1sesq5Third quintile 0.176 0.088 1.193 0.046 *
x1sesq5Fourth quintile x1sesq5Fourth quintile 0.357 0.088 1.429 0.000 ***
x1sesq5Fifth quintile (highest) x1sesq5Fifth quintile (highest) 0.223 0.091 1.249 0.014 *
x1schoolbel x1schoolbel -0.032 0.028 0.968 0.247
x1txmscr x1txmscr 0.046 0.003 1.047 0.000 ***
x1sciint x1sciint 0.153 0.026 1.165 0.000 ***
x1stuedexpctHigh school x1stuedexpctHigh school 0.102 0.101 1.107 0.315
x1stuedexpctBachelor x1stuedexpctBachelor 0.153 0.077 1.166 0.045 *
x1stuedexpctMaster x1stuedexpctMaster 0.152 0.078 1.164 0.052 .
x1stuedexpctPhD x1stuedexpctPhD 0.189 0.080 1.209 0.018 *
s1alg1m09Yes s1alg1m09Yes -0.351 0.053 0.704 0.000 ***
s1sfall09Yes s1sfall09Yes 0.047 0.074 1.048 0.521
p1campmsYes p1campmsYes 0.498 0.108 1.645 0.000 ***
p1fixedYes p1fixedYes 0.310 0.050 1.363 0.000 ***
p1sciprojYes p1sciprojYes 0.007 0.051 1.007 0.885
Code
# Outcome model with doubly robust estimator using ATU weights
atc_dr_stemma <- glm(collegeSTEMmajorasp ~ 
                           stemoccasp9th +  #pre-IVs
                           p2museum + 
                           x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                           x1race + x1sex + x1sesq5 +  x1schoolbel + 
                           x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                           s1alg1m09 + s1sfall09 + 
                           p1campms + p1fixed + p1sciproj,
                           data = data_trimmed, family = binomial(), weights = w.atc_W3W1W2STU)

table.atc_dr_stemma <- tidy.logit.table(atc_dr_stemma)
knitr::kable(table.atc_dr_stemma, digits = 3, caption = "ATC weighted multiple logistic regression results of STEM major aspiration")
ATC weighted multiple logistic regression results of STEM major aspiration
Variable Coefficient SE OR p_value Sig
(Intercept) (Intercept) -3.354 0.255 0.035 0.000 ***
stemoccasp9thSTEM stemoccasp9thSTEM 0.580 0.070 1.785 0.000 ***
p2museumTreatment p2museumTreatment 0.133 0.063 1.142 0.034 *
x1localeCity x1localeCity 0.188 0.092 1.207 0.041 *
x1localeSuburb x1localeSuburb 0.141 0.089 1.151 0.114
x1localeTown x1localeTown 0.128 0.118 1.136 0.279
x1controlPublic x1controlPublic -0.193 0.124 0.824 0.118
x1regionMidwest x1regionMidwest 0.032 0.104 1.032 0.761
x1regionSouth x1regionSouth 0.033 0.095 1.033 0.731
x1regionWest x1regionWest -0.170 0.104 0.844 0.104
a1mspdintrstYes a1mspdintrstYes -0.187 0.065 0.829 0.004 **
a1msmentorYes a1msmentorYes 0.068 0.068 1.070 0.315
x1raceAsian x1raceAsian 0.309 0.148 1.362 0.037 *
x1raceBlack x1raceBlack 0.098 0.107 1.103 0.362
x1raceLatino x1raceLatino -0.067 0.096 0.936 0.490
x1raceMultiracial x1raceMultiracial -0.001 0.120 0.999 0.995
x1raceOther x1raceOther 0.561 0.267 1.752 0.036 *
x1sexFemale x1sexFemale -1.073 0.070 0.342 0.000 ***
x1sesq5Second quintile x1sesq5Second quintile 0.122 0.109 1.130 0.260
x1sesq5Third quintile x1sesq5Third quintile 0.214 0.109 1.239 0.049 *
x1sesq5Fourth quintile x1sesq5Fourth quintile 0.384 0.111 1.467 0.001 ***
x1sesq5Fifth quintile (highest) x1sesq5Fifth quintile (highest) 0.251 0.117 1.285 0.032 *
x1schoolbel x1schoolbel -0.031 0.036 0.970 0.389
x1txmscr x1txmscr 0.044 0.003 1.045 0.000 ***
x1sciint x1sciint 0.159 0.034 1.172 0.000 ***
x1stuedexpctHigh school x1stuedexpctHigh school 0.126 0.123 1.134 0.306
x1stuedexpctBachelor x1stuedexpctBachelor 0.169 0.097 1.185 0.081 .
x1stuedexpctMaster x1stuedexpctMaster 0.111 0.101 1.117 0.273
x1stuedexpctPhD x1stuedexpctPhD 0.164 0.104 1.178 0.114
s1alg1m09Yes s1alg1m09Yes -0.380 0.068 0.684 0.000 ***
s1sfall09Yes s1sfall09Yes 0.032 0.092 1.032 0.730
p1campmsYes p1campmsYes 0.568 0.159 1.764 0.000 ***
p1fixedYes p1fixedYes 0.322 0.066 1.380 0.000 ***
p1sciprojYes p1sciprojYes -0.033 0.067 0.967 0.618

3.5 ATE in MLM

For unweighted PS

ICC for the end of 11th sciid is 5%.

Code
#convert the SCH_ID into a f
data_trimmed$SCH_ID <- as.factor(data_trimmed$SCH_ID)

#run the unconditional model 
m0_mlm <- lmer(x2sciid ~ 1 + (1 | SCH_ID), data = data_trimmed, weights = w.ate_W3W1W2STU)
summary(m0_mlm)
Linear mixed model fit by REML. t-tests use Satterthwaite's method [
lmerModLmerTest]
Formula: x2sciid ~ 1 + (1 | SCH_ID)
   Data: data_trimmed
Weights: w.ate_W3W1W2STU

REML criterion at convergence: 21378.5

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-9.3214 -0.5040 -0.0082  0.5308  5.6548 

Random effects:
 Groups   Name        Variance Std.Dev.
 SCH_ID   (Intercept) 0.0931   0.3051  
 Residual             1.7354   1.3173  
Number of obs: 6443, groups:  SCH_ID, 933

Fixed effects:
             Estimate Std. Error        df t value Pr(>|t|)    
(Intercept)   0.06608    0.01682 867.97365   3.928 9.23e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Code
#icc for sciid is 5%
icc0 <- 0.09 / (0.09 + 1.73)
icc0
[1] 0.04945055

3.6 Heterogenity, Moderation Effect by Race and Gender

Applied for unweighted PS model + (IPW*sample weight) for single level outcome model
Code
#estimate ATE Heterogenity on Race and Gender of museum on sciid
ate_dr_sciid_hete <- lm(x2sciid ~ x1sciid + #pre-IVs
                                  p2museum + 
                                  x1locale + x1control + x1region +  a1mspdintrst + a1msmentor +  #school-level IV
                                  x1race + x1sex + x1sesq5 +  x1schoolbel + 
                                  x1txmscr + x1sciint + x1stuedexpct + #student-level IV background
                                  s1alg1m09 + s1sfall09 + 
                                  p1campms + p1fixed + p1sciproj +
                                  p2museum:x1race + p2museum:x1sex, data = data_trimmed, weights = w.ate_W3W1W2STU)

table.ate_dr_sciid_hete <- tidy.coeftable(ate_dr_sciid_hete)
knitr::kable(table.ate_dr_sciid_hete, digits = 3, caption = "Racial and gender variation in science club effects on science identity")
Racial and gender variation in science club effects on science identity
Variable Coefficient SE Beta p_value Sig
(Intercept) -0.216 0.087 NA 0.013 *
x1sciid 0.370 0.013 0.375 0.000 ***
p2museumTreatment 0.015 0.037 0.008 0.681
x1localeCity -0.046 0.032 -0.022 0.152
x1localeSuburb -0.061 0.031 -0.030 0.045 *
x1localeTown -0.105 0.040 -0.034 0.009 **
x1controlPublic 0.049 0.044 0.013 0.266
x1regionMidwest 0.011 0.036 0.004 0.770
x1regionSouth -0.001 0.034 -0.001 0.974
x1regionWest -0.006 0.036 -0.002 0.877
a1mspdintrstYes -0.001 0.023 0.000 0.980
a1msmentorYes 0.000 0.023 0.000 0.991
x1raceAsian -0.144 0.088 -0.027 0.103
x1raceBlack -0.052 0.049 -0.018 0.287
x1raceLatino -0.093 0.042 -0.040 0.029 *
x1raceMultiracial -0.064 0.057 -0.018 0.259
x1raceOther 0.068 0.137 0.008 0.618
x1sexFemale -0.132 0.031 -0.067 0.000 ***
x1sesq5Second quintile -0.025 0.036 -0.010 0.486
x1sesq5Third quintile 0.008 0.036 0.003 0.834
x1sesq5Fourth quintile -0.010 0.038 -0.004 0.800
x1sesq5Fifth quintile (highest) 0.068 0.040 0.029 0.084 .
x1schoolbel -0.026 0.012 -0.025 0.037 *
x1txmscr 0.006 0.001 0.074 0.000 ***
x1sciint 0.076 0.013 0.076 0.000 ***
x1stuedexpctHigh school 0.065 0.039 0.022 0.096 .
x1stuedexpctBachelor 0.020 0.033 0.009 0.549
x1stuedexpctMaster 0.085 0.034 0.035 0.014 *
x1stuedexpctPhD 0.192 0.035 0.081 0.000 ***
s1alg1m09Yes 0.023 0.024 0.011 0.333
s1sfall09Yes -0.034 0.031 -0.012 0.271
p1campmsYes 0.053 0.055 0.011 0.336
p1fixedYes 0.016 0.023 0.008 0.482
p1sciprojYes 0.035 0.023 0.018 0.121
p2museumTreatment:x1raceAsian 0.515 0.118 0.074 0.000 ***
p2museumTreatment:x1raceBlack 0.101 0.067 0.025 0.132
p2museumTreatment:x1raceLatino -0.061 0.054 -0.020 0.257
p2museumTreatment:x1raceMultiracial 0.023 0.082 0.004 0.782
p2museumTreatment:x1raceOther 0.272 0.194 0.022 0.163
p2museumTreatment:x1sexFemale 0.035 0.043 0.015 0.421

3.7 Sensitivity Analysis

For Navie Estimate (No PS, no sample weight, single level with covaraite adjustment)

Code
sen.m01 <- sensemakr(model = m01, treatment = "p2museumTreatment",
                  benchmark_covariates = "x1sciid",
                  kd = 0.25:0.75, # strength of unobserved confounding with treatment
                  ky = 0.25:0.75, # strength of unobserve confounding with outcome
                  q = 1, # threshold for nullifying the estimate
                  alpha = 0.05, # significance level
                  reduce = TRUE # test whether confounding could reduce effect
                  )
summary(sen.m01)
Sensitivity Analysis to Unobserved Confounding

Model Formula: x2sciid ~ x1sciid + p2museum + x1locale + x1control + x1region + 
    a1mspdintrst + a1msmentor + x1race + x1sex + x1sesq5 + x1schoolbel + 
    x1txmscr + x1sciint + x1stuedexpct + s1alg1m09 + s1sfall09 + 
    p1campms + p1fixed + p1sciproj

Null hypothesis: q = 1 and reduce = TRUE 
-- This means we are considering biases that reduce the absolute value of the current estimate.
-- The null hypothesis deemed problematic is H0:tau = 0 

Unadjusted Estimates of 'p2museumTreatment': 
  Coef. estimate: 0.0464 
  Standard Error: 0.0227 
  t-value (H0:tau = 0): 2.0456 

Sensitivity Statistics:
  Partial R2 of treatment with outcome: 6e-04 
  Robustness Value, q = 1: 0.0252 
  Robustness Value, q = 1, alpha = 0.05: 0.0011 

Verbal interpretation of sensitivity statistics:

-- Partial R2 of the treatment with the outcome: an extreme confounder (orthogonal to the covariates) that explains 100% of the residual variance of the outcome, would need to explain at least 0.06% of the residual variance of the treatment to fully account for the observed estimated effect.

-- Robustness Value, q = 1: unobserved confounders (orthogonal to the covariates) that explain more than 2.52% of the residual variance of both the treatment and the outcome are strong enough to bring the point estimate to 0 (a bias of 100% of the original estimate). Conversely, unobserved confounders that do not explain more than 2.52% of the residual variance of both the treatment and the outcome are not strong enough to bring the point estimate to 0.

-- Robustness Value, q = 1, alpha = 0.05: unobserved confounders (orthogonal to the covariates) that explain more than 0.11% of the residual variance of both the treatment and the outcome are strong enough to bring the estimate to a range where it is no longer 'statistically different' from 0 (a bias of 100% of the original estimate), at the significance level of alpha = 0.05. Conversely, unobserved confounders that do not explain more than 0.11% of the residual variance of both the treatment and the outcome are not strong enough to bring the estimate to a range where it is no longer 'statistically different' from 0, at the significance level of alpha = 0.05.

Bounds on omitted variable bias:

--The table below shows the maximum strength of unobserved confounders with association with the treatment and the outcome bounded by a multiple of the observed explanatory power of the chosen benchmark covariate(s).

   Bound Label R2dz.x R2yz.dx         Treatment Adjusted Estimate Adjusted Se
 0.25x x1sciid  3e-04  0.0314 p2museumTreatment             0.041      0.0223
 Adjusted T Adjusted Lower CI Adjusted Upper CI
     1.8378           -0.0027            0.0848
Code
plot(sen.m01 )

For non-weighted PS model + IPTW*sampl weight for outcome model

Code
sen1 <- sensemakr(model = ate_dr_sciid, treatment = "p2museumTreatment",
                  benchmark_covariates = "x1sciid",
                  kd = 0.25:0.75, # strength of unobserved confounding with treatment
                  ky = 0.25:0.75, # strength of unobserve confounding with outcome
                  q = 1, # threshold for nullifying the estimate
                  alpha = 0.05, # significance level
                  reduce = TRUE # test whether confounding could reduce effect
                  )
summary(sen1)
Sensitivity Analysis to Unobserved Confounding

Model Formula: x2sciid ~ x1sciid + p2museum + x1locale + x1control + x1region + 
    a1mspdintrst + a1msmentor + x1race + x1sex + x1sesq5 + x1schoolbel + 
    x1txmscr + x1sciint + x1stuedexpct + s1alg1m09 + s1sfall09 + 
    p1campms + p1fixed + p1sciproj

Null hypothesis: q = 1 and reduce = TRUE 
-- This means we are considering biases that reduce the absolute value of the current estimate.
-- The null hypothesis deemed problematic is H0:tau = 0 

Unadjusted Estimates of 'p2museumTreatment': 
  Coef. estimate: 0.0555 
  Standard Error: 0.0216 
  t-value (H0:tau = 0): 2.5634 

Sensitivity Statistics:
  Partial R2 of treatment with outcome: 0.001 
  Robustness Value, q = 1: 0.0315 
  Robustness Value, q = 1, alpha = 0.05: 0.0075 

Verbal interpretation of sensitivity statistics:

-- Partial R2 of the treatment with the outcome: an extreme confounder (orthogonal to the covariates) that explains 100% of the residual variance of the outcome, would need to explain at least 0.1% of the residual variance of the treatment to fully account for the observed estimated effect.

-- Robustness Value, q = 1: unobserved confounders (orthogonal to the covariates) that explain more than 3.15% of the residual variance of both the treatment and the outcome are strong enough to bring the point estimate to 0 (a bias of 100% of the original estimate). Conversely, unobserved confounders that do not explain more than 3.15% of the residual variance of both the treatment and the outcome are not strong enough to bring the point estimate to 0.

-- Robustness Value, q = 1, alpha = 0.05: unobserved confounders (orthogonal to the covariates) that explain more than 0.75% of the residual variance of both the treatment and the outcome are strong enough to bring the estimate to a range where it is no longer 'statistically different' from 0 (a bias of 100% of the original estimate), at the significance level of alpha = 0.05. Conversely, unobserved confounders that do not explain more than 0.75% of the residual variance of both the treatment and the outcome are not strong enough to bring the estimate to a range where it is no longer 'statistically different' from 0, at the significance level of alpha = 0.05.

Bounds on omitted variable bias:

--The table below shows the maximum strength of unobserved confounders with association with the treatment and the outcome bounded by a multiple of the observed explanatory power of the chosen benchmark covariate(s).

   Bound Label R2dz.x R2yz.dx         Treatment Adjusted Estimate Adjusted Se
 0.25x x1sciid  3e-04    0.03 p2museumTreatment            0.0505      0.0213
 Adjusted T Adjusted Lower CI Adjusted Upper CI
     2.3677            0.0087            0.0923
Code
plot(sen1)

4. IPTW and Matching Comparsion

Matching vs Weighting

The Core Difference

  • Matching is like pruning a tree. You discard the observations that don’t have a “match” in the other group. You end up with a smaller, cleaner dataset where treated and control units look like twins.

  • Weighting is like sculpting clay. You keep everyone in the dataset, but you “stretch” (up-weight) or “squish” (down-weight) specific observations so that the total treated pile looks identical to the total control pile.

Feature Matching Weighting (IPTW)
Data Retention Low. You often discard unmatched control units (and sometimes treated units), reducing sample size. High. Uses the full sample (no data loss), which generally preserves statistical power.
Target Population Changes. Usually estimates the ATT (effect on the treated) or a subset of it (the “matchable” treated). Flexible. Can easily estimate ATE (whole population), ATT, or ATU depending on the formula used.
Extreme Values Robust. Units with extreme propensity scores (no overlap) are simply dropped/ignored. Sensitive. Units with extreme propensity scores get massive weights, causing unstable estimates and high variance.
Variance Lower Variance (usually), but potentially higher bias if matches are poor. Higher Variance, especially if weights are unstable (though “trimming” helps).
Interpretability High. “We compared Student A to Student B who looked just like them.” Easy to explain to stakeholders. Low. “We up-weighted Student A by 3.5x to represent a pseudo-population.” Harder to explain.
Balance Check Must check balance after matching. Must check balance after weighting.

References

  • Rosenbaum, P. R., & Rubin, D. B. (1983). The central role of the propensity score in observational studies for causal effects. Biometrika, 70(1), 41-55.

  • Stuart, E. A. (2010). Matching methods for causal inference: A review and a look forward. Statistical Science, 25(1), 1-21.

  • Guo, S., & Fraser, M. W. (2014). Propensity score analysis: Statistical methods and applications. Sage.

  • Austin, P. C. (2011). An introduction to propensity score methods for reducing the effects of confounding in observational studies. Multivariate Behavioral Research, 46(3), 399-424.

  • Imbens, G. W., & Wooldridge, J. M. (2009). Recent developments in the econometrics of program evaluation. Journal of Economic Literature, 47(1), 5-86.

  • Li, F., Morgan, K. L., & Zaslavsky, A. M. (2018). Balancing covariates via propensity score weighting. Journal of the American Statistical Association, 113(521), 390-400.


This tutorial is based on lecture materials from Stat 256: Causality (UCLA) and EDUC 255C: Introduction to Causal Inference in Education Research (UCLA), Spring 2025.