Propensity Score
Instituto de Matemática, Estatística e Computação Científica (IMECC),
Universidade Estadual de Campinas (UNICAMP).
Assumindo uma amostragem IID, temos quatro variáveis aleatórias associadas a cada unidade \(i\):
Ademais, \[\begin{align} P(X, Z, Y(1), Y(0)) &= P(Z, Y(1), Y(0) | X) \times P(X), \\ &= P(Z | X, Y(1), Y(0)) \times P(Y(1), Y(0) | X) \times P(X) \end{align}\]
O Teorema anterior motiva um método para estimar efeitos causais: Propensity Score Stratification.
Estratificamos e estimamos o efeito causal médio.
neyman_sre <- function(y, z, x) {
x_levels <- unique(x)
K <- length(x_levels)
pi_k <- rep(0, K)
tau_k <- rep(0, K)
v_k <- rep(0, K)
n <- length(z)
for (k in 1:K) {
x_k <- x_levels[k]
z_k <- z[x == x_k]
y_k <- y[x == x_k]
pi_k[k] <- length(z_k)/n
tau_k[k] <- mean(y_k[z_k == 1]) - mean(y_k[z_k == 0])
v_k[k] <- var(y_k[z_k == 1]) / sum(z_k) + var(y_k[z_k == 0]) / sum(1 - z_k)
}
tau_s <- sum(pi_k * tau_k)
v_s <- sum(pi_k^2 * v_k)
return(c(tau_s, sqrt(v_s)))
}
K <- c(5, 10, 20, 50, 80)
resultados <- sapply(K, FUN = function(k){
q_prop_score = quantile(prop_score, (1:(k - 1))/k)
ps_estrato = cut(prop_score, breaks = c(0, q_prop_score, 1), labels = 1:k)
neyman_sre(y, z, ps_estrato)
})
colnames(resultados) <- K
rownames(resultados) <- c("Estim", "SE")
resultados
5 10 20 50 80
Estim -0.1160964 -0.1776036 -0.1996877 -0.2647423 -0.2037701
SE 0.2833304 0.2824724 0.2789719 0.2724072 NA
Como seriam os resultados se não utilizamos propensity score?
library(car)
naive <- lm(y ~ z)
fisher <- lm(y ~ z + x)
lin <- lm(y ~ z*x)
resultados <- matrix(c(coef(naive)[2], sqrt(hccm(naive)[2, 2]),
coef(fisher)[2], sqrt(hccm(fisher)[2, 2]),
coef(lin)[2], sqrt(hccm(lin)[2, 2])), ncol = 3)
colnames(resultados) <- c("Naive", "Fisher", "Lin")
rownames(resultados) <- c("Estim", "SE")
resultados
Naive Fisher Lin
Estim 0.5339044 0.06124785 -0.01695389
SE 0.2254186 0.22673582 0.22587296
O estimador de Lin e o método de propensity score stratification fornecem, qualitativamente, os mesmo resultados. Já o estimador Naive difere bastante dos outros métodos (Why?) enquanto que Fisher devolve um efeito positivo, porém, insignificante.
[,1]
[1,] 619.0012
O (alto) nível de desbalanceameto explica o motivo pelo qual o método Naive difere tanto dos outros métodos.
Demostração (no quadro)
O estimador IPW (Inverse Propensity Score Weighting), também conhecido como estimador HT (Horvitz-Thompson) é motivado pelo Teorema anterior e é dado por \[\hat{\tau}^{IPW} = \dfrac{1}{n} \displaystyle \sum_{i = 1}^n \dfrac{Z_i Y_i}{\hat{e}(X_i)} - \dfrac{1}{n} \displaystyle \sum_{i = 1}^n \dfrac{(1 - Z_i)Y_i}{1- \hat{e}(X_i)},\] em que \(\hat{e}(X_i)\) é o propensity score estimado.
O estimador \(\hat{\tau}^{IPW}\) apresenta um sério problema: não é invariante a transformações de locação de \(Y\) 🙍.
library(ggplot2)
balance_check <- sapply(1:ncol(x),
FUN = function(px){
q_prop_score = quantile(prop_score, (1:4)/5)
prop_score_strata = cut(prop_score, breaks = c(0, q_prop_score, 1), labels = 1:5)
neyman_sre(x[, px], z, prop_score_strata)
})
dat_balance <- data.frame(est = balance_check[1, ],
upper = balance_check[1, ] + 1.96*balance_check[2, ],
lower = balance_check[1, ] - 1.96*balance_check[2, ],
cov = factor(1:11))
ggplot(dat_balance) +
geom_errorbar(aes(x = cov, ymin = lower, ymax = upper), alpha = 0.6) +
geom_point(aes(x = cov, y = est), alpha = 0.6) +
geom_hline(aes(yintercept = 0), alpha = 0.3) +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.title.y = element_blank()) +
xlab("Verificando balanceamento com base na estratificação (K = 5)")
balance_check <- sapply(1:dim(x)[2],
FUN = function(px){
bootstrap_ipw(z, x[, px], x)[2, ]
})
dat_balance <- data.frame(est = balance_check[1, ],
upper = balance_check[1, ] + 1.96*balance_check[2, ],
lower = balance_check[1, ] - 1.96*balance_check[2, ],
cov = factor(1:11))
ggplot(dat_balance) +
geom_errorbar(aes(x = cov, ymin = lower, ymax = upper), alpha = 0.6) +
geom_point(aes(x = cov, y = est), alpha = 0.6) +
geom_hline(aes(yintercept = 0), alpha = 0.3) +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.title.y=element_blank()) +
xlab("balance check based on weighting")
Carlos Trucíos (IMECC/UNICAMP) | ME920/MI628 - Inferência Causal | ctruciosm.github.io