Skip to content

Commit c649374

Browse files
authored
Merge pull request #26 from Genentech/spaces
styler
2 parents 1da53cc + 497fe12 commit c649374

7 files changed

Lines changed: 366 additions & 328 deletions

R/EC_AIPW_OPT.R

Lines changed: 157 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -102,176 +102,176 @@ EC_AIPW_OPT <- function(data,
102102
#
103103
# }else
104104
# {
105-
# propensity score model
106-
piS.model <- glm(model_form_piS, data = df, family = "binomial")
107-
# outcome regression model
108-
Y0.model <- lapply(model_form_mu0_ext, function(x) {
109-
lm(as.formula(x), data = filter(df, A == 0))
110-
})
111-
Y0.model.dummy <- lapply(model_form_mu0_ext, function(x) {
112-
lm(as.formula(x), data = filter(df))
113-
})
114-
115-
# predict Y0 from outcome regression models
116-
Y0 <- data.frame(sapply(1:T_follow, function(x) {
117-
predict(Y0.model[[x]], newdata = df)
118-
}))
119-
colnames(Y0) <- paste0("y", 1:T_follow, "_0")
120-
# for residual
121-
Yr <- Y - Y0
122-
colnames(Yr) <- paste0("y", 1:T_follow, "_r")
123-
124-
# estimate ATE
125-
temp <- df |>
126-
cbind(Y0, Yr) |>
127-
mutate(
128-
piA = sum(A[S == 1]) / n,
129-
piS = sum(S) / (n + m),
130-
piSX = predict(piS.model, newdata = df, type = "response"),
131-
rx = (piSX / (1 - piSX)) * ((1 - piS) / piS)
132-
) |>
133-
mutate(w11 = piA, w10 = 1 - piA, w00 = rx)
134-
135-
### create outcomes: obs * T
136-
Ys <- as.matrix(Yr)
137-
138-
139-
potential <- (temp$S * temp$A / temp$w11 + temp$S * (1 - temp$A) / temp$w10 + (1 - temp$S) * temp$w00) * Ys
140-
mu1 <- colSums(potential[temp$S == 1 & temp$A == 1, , drop = FALSE]) / n
141-
mu10 <- colSums(potential[temp$S == 1 & temp$A == 0, , drop = FALSE]) / n
142-
mu00 <- colSums(potential[temp$S == 0, , drop = FALSE]) / (sum((1 - temp$S) * temp$w00))
143-
144-
145-
# variance
146-
147-
## bread
148-
A11 <- diag(rep(-1, T_follow), nrow = T_follow)
149-
A22 <- diag(rep(-1, T_follow), nrow = T_follow)
150-
A33 <- diag(rep(-mean((1 - temp$S) * temp$w00 / (1 - temp$piS)), T_follow), nrow = T_follow)
151-
A34 <- t((as.vector((1 - temp$S) * temp$`piSX` /
152-
(temp$`piS` * (1 - temp$`piSX`))) *
153-
sweep(Ys, 2, mu00))) %*% model.matrix(piS.model) / (n + m)
154-
piS.beta <- t(model.matrix(piS.model)) %*%
155-
diag(-temp$`piSX` * (1 - temp$`piSX`)) %*%
156-
model.matrix(piS.model) / (n + m)
157-
A44 <- piS.beta
158-
159-
n1 <- dim(A11)[1]
160-
n2 <- dim(A22)[1]
161-
n3 <- dim(A33)[1]
162-
n4 <- dim(A44)[1]
163-
164-
A0 <- as.matrix(bdiag(A11, A22, A33, A44))
165-
A0[(n1 + n2 + 1):(n1 + n2 + n3), (n1 + n2 + n3 + 1):(n1 + n2 + n3 + n4)] <- A34
166-
167-
168-
Y0.model.dim <- sapply(1:T_follow, function(x) {
169-
dim(model.matrix(Y0.model.dummy[[x]]))[2]
170-
})
171-
n5 <- sum(Y0.model.dim)
172-
173-
A45 <- matrix(0, nrow = n4, ncol = n5)
174-
A51 <- matrix(0, nrow = n5, ncol = n1 + n2 + n3 + n4)
175-
176-
# cat(length(as.vector(-temp$S*temp$A/(temp$piS*(temp$piA)))))
177-
# cat(dim(model.matrix(Y0.model.dummy[[1]])))
178-
179-
Phi1.gamma.list <- lapply(
180-
1:T_follow,
181-
function(x) {
182-
as.vector(-temp$S * temp$A / (temp$piS * (temp$piA))) %*% model.matrix(Y0.model.dummy[[x]]) / (n + m)
183-
}
184-
)
185-
Phi1.gamma <- as.matrix(do.call(bdiag, Phi1.gamma.list))
186-
187-
Phi2.gamma.list <- lapply(
188-
1:T_follow,
189-
function(x) {
190-
as.vector(-temp$S * (1 - temp$A) / (temp$piS * (1 - temp$piA))) %*% model.matrix(Y0.model.dummy[[x]]) / (n + m)
191-
}
192-
)
193-
Phi2.gamma <- as.matrix(do.call(bdiag, Phi2.gamma.list))
105+
# propensity score model
106+
piS.model <- glm(model_form_piS, data = df, family = "binomial")
107+
# outcome regression model
108+
Y0.model <- lapply(model_form_mu0_ext, function(x) {
109+
lm(as.formula(x), data = filter(df, A == 0))
110+
})
111+
Y0.model.dummy <- lapply(model_form_mu0_ext, function(x) {
112+
lm(as.formula(x), data = filter(df))
113+
})
114+
115+
# predict Y0 from outcome regression models
116+
Y0 <- data.frame(sapply(1:T_follow, function(x) {
117+
predict(Y0.model[[x]], newdata = df)
118+
}))
119+
colnames(Y0) <- paste0("y", 1:T_follow, "_0")
120+
# for residual
121+
Yr <- Y - Y0
122+
colnames(Yr) <- paste0("y", 1:T_follow, "_r")
123+
124+
# estimate ATE
125+
temp <- df |>
126+
cbind(Y0, Yr) |>
127+
mutate(
128+
piA = sum(A[S == 1]) / n,
129+
piS = sum(S) / (n + m),
130+
piSX = predict(piS.model, newdata = df, type = "response"),
131+
rx = (piSX / (1 - piSX)) * ((1 - piS) / piS)
132+
) |>
133+
mutate(w11 = piA, w10 = 1 - piA, w00 = rx)
134+
135+
### create outcomes: obs * T
136+
Ys <- as.matrix(Yr)
137+
138+
139+
potential <- (temp$S * temp$A / temp$w11 + temp$S * (1 - temp$A) / temp$w10 + (1 - temp$S) * temp$w00) * Ys
140+
mu1 <- colSums(potential[temp$S == 1 & temp$A == 1, , drop = FALSE]) / n
141+
mu10 <- colSums(potential[temp$S == 1 & temp$A == 0, , drop = FALSE]) / n
142+
mu00 <- colSums(potential[temp$S == 0, , drop = FALSE]) / (sum((1 - temp$S) * temp$w00))
143+
144+
145+
# variance
146+
147+
## bread
148+
A11 <- diag(rep(-1, T_follow), nrow = T_follow)
149+
A22 <- diag(rep(-1, T_follow), nrow = T_follow)
150+
A33 <- diag(rep(-mean((1 - temp$S) * temp$w00 / (1 - temp$piS)), T_follow), nrow = T_follow)
151+
A34 <- t((as.vector((1 - temp$S) * temp$`piSX` /
152+
(temp$`piS` * (1 - temp$`piSX`))) *
153+
sweep(Ys, 2, mu00))) %*% model.matrix(piS.model) / (n + m)
154+
piS.beta <- t(model.matrix(piS.model)) %*%
155+
diag(-temp$`piSX` * (1 - temp$`piSX`)) %*%
156+
model.matrix(piS.model) / (n + m)
157+
A44 <- piS.beta
158+
159+
n1 <- dim(A11)[1]
160+
n2 <- dim(A22)[1]
161+
n3 <- dim(A33)[1]
162+
n4 <- dim(A44)[1]
163+
164+
A0 <- as.matrix(bdiag(A11, A22, A33, A44))
165+
A0[(n1 + n2 + 1):(n1 + n2 + n3), (n1 + n2 + n3 + 1):(n1 + n2 + n3 + n4)] <- A34
166+
167+
168+
Y0.model.dim <- sapply(1:T_follow, function(x) {
169+
dim(model.matrix(Y0.model.dummy[[x]]))[2]
170+
})
171+
n5 <- sum(Y0.model.dim)
172+
173+
A45 <- matrix(0, nrow = n4, ncol = n5)
174+
A51 <- matrix(0, nrow = n5, ncol = n1 + n2 + n3 + n4)
175+
176+
# cat(length(as.vector(-temp$S*temp$A/(temp$piS*(temp$piA)))))
177+
# cat(dim(model.matrix(Y0.model.dummy[[1]])))
178+
179+
Phi1.gamma.list <- lapply(
180+
1:T_follow,
181+
function(x) {
182+
as.vector(-temp$S * temp$A / (temp$piS * (temp$piA))) %*% model.matrix(Y0.model.dummy[[x]]) / (n + m)
183+
}
184+
)
185+
Phi1.gamma <- as.matrix(do.call(bdiag, Phi1.gamma.list))
194186

195-
Phi3.gamma.list <- lapply(
196-
1:T_follow,
197-
function(x) {
198-
as.vector(-(1 - temp$S) * temp$rx / (1 - temp$piS)) %*% model.matrix(Y0.model.dummy[[x]]) / (n + m)
199-
}
200-
)
201-
Phi3.gamma <- as.matrix(do.call(bdiag, Phi3.gamma.list))
187+
Phi2.gamma.list <- lapply(
188+
1:T_follow,
189+
function(x) {
190+
as.vector(-temp$S * (1 - temp$A) / (temp$piS * (1 - temp$piA))) %*% model.matrix(Y0.model.dummy[[x]]) / (n + m)
191+
}
192+
)
193+
Phi2.gamma <- as.matrix(do.call(bdiag, Phi2.gamma.list))
202194

203-
Y0.gamma.list <- lapply(
204-
1:T_follow,
205-
function(x) {
206-
-t(model.matrix(Y0.model.dummy[[x]])) %*%
207-
diag((1 - temp$A) / (1 - mean(temp$A))) %*%
208-
model.matrix(Y0.model.dummy[[x]]) / (n + m)
209-
}
210-
)
211-
Y0.gamma <- as.matrix(do.call(bdiag, Y0.gamma.list))
212-
213-
214-
# cat(c(n1, n2, n3, n4, n5))
215-
A.left <- rbind(A0, A51)
216-
A.right <- rbind(Phi1.gamma, Phi2.gamma, Phi3.gamma, A45, Y0.gamma)
217-
A <- cbind(A.left, A.right)
218-
219-
## meat
220-
phi1 <- temp$S * temp$A * sweep(Ys, 2, mu1) / temp$`piA` / temp$piS # influence from rct treated
221-
phi2 <- temp$S * (1 - temp$A) * sweep(Ys, 2, mu10) / (1 - temp$`piA`) / temp$piS # influence from rct control
222-
phi3 <- (1 - temp$S) * sweep(Ys, 2, mu00) * temp$w00 / (1 - temp$piS)
223-
phi.piS <- (temp$S - temp$`piSX`) * model.matrix(piS.model)
224-
phi.Y0 <- do.call(cbind,
225-
args = lapply(1:T_follow, function(x) {
226-
((1 - temp$A) / (1 - mean(temp$A))) * (Ys[, x] * model.matrix(Y0.model.dummy[[x]]))
227-
})
228-
)
195+
Phi3.gamma.list <- lapply(
196+
1:T_follow,
197+
function(x) {
198+
as.vector(-(1 - temp$S) * temp$rx / (1 - temp$piS)) %*% model.matrix(Y0.model.dummy[[x]]) / (n + m)
199+
}
200+
)
201+
Phi3.gamma <- as.matrix(do.call(bdiag, Phi3.gamma.list))
202+
203+
Y0.gamma.list <- lapply(
204+
1:T_follow,
205+
function(x) {
206+
-t(model.matrix(Y0.model.dummy[[x]])) %*%
207+
diag((1 - temp$A) / (1 - mean(temp$A))) %*%
208+
model.matrix(Y0.model.dummy[[x]]) / (n + m)
209+
}
210+
)
211+
Y0.gamma <- as.matrix(do.call(bdiag, Y0.gamma.list))
212+
213+
214+
# cat(c(n1, n2, n3, n4, n5))
215+
A.left <- rbind(A0, A51)
216+
A.right <- rbind(Phi1.gamma, Phi2.gamma, Phi3.gamma, A45, Y0.gamma)
217+
A <- cbind(A.left, A.right)
218+
219+
## meat
220+
phi1 <- temp$S * temp$A * sweep(Ys, 2, mu1) / temp$`piA` / temp$piS # influence from rct treated
221+
phi2 <- temp$S * (1 - temp$A) * sweep(Ys, 2, mu10) / (1 - temp$`piA`) / temp$piS # influence from rct control
222+
phi3 <- (1 - temp$S) * sweep(Ys, 2, mu00) * temp$w00 / (1 - temp$piS)
223+
phi.piS <- (temp$S - temp$`piSX`) * model.matrix(piS.model)
224+
phi.Y0 <- do.call(cbind,
225+
args = lapply(1:T_follow, function(x) {
226+
((1 - temp$A) / (1 - mean(temp$A))) * (Ys[, x] * model.matrix(Y0.model.dummy[[x]]))
227+
})
228+
)
229229

230-
## big Phi should be n by 4+2+p, observations of same phi in the same column
231-
Phi <- cbind(phi1, phi2, phi3, phi.piS, phi.Y0)
230+
## big Phi should be n by 4+2+p, observations of same phi in the same column
231+
Phi <- cbind(phi1, phi2, phi3, phi.piS, phi.Y0)
232232

233-
B <- (t(Phi) %*% Phi) / (n + m)
233+
B <- (t(Phi) %*% Phi) / (n + m)
234234

235235

236-
## sandwich
237-
sigma <- solve(A) %*% B %*% t(solve(A))
236+
## sandwich
237+
sigma <- solve(A) %*% B %*% t(solve(A))
238238

239-
## Optimal weight as proposed in manuscript
240-
# fit1 = lm(as.formula(paste("Y2","~",form_x)), data = filter(temp,S==1&A==0) )
241-
# sigma10 = (summary(fit1)$sigma)**2
242-
num <- sum(temp$S * (1 - temp$A) / temp$w10^2 / (sum(temp$S * (1 - temp$A) / temp$w10))^2)
239+
## Optimal weight as proposed in manuscript
240+
# fit1 = lm(as.formula(paste("Y2","~",form_x)), data = filter(temp,S==1&A==0) )
241+
# sigma10 = (summary(fit1)$sigma)**2
242+
num <- sum(temp$S * (1 - temp$A) / temp$w10^2 / (sum(temp$S * (1 - temp$A) / temp$w10))^2)
243243

244-
#+ sum(temp$S*(1-temp$A))*var(
245-
#+ temp$S*(1-temp$A)*predict(fit1,newdata=temp)/temp$w10/
246-
#+ sum(temp$S*(1-temp$A)/temp$w10))
244+
#+ sum(temp$S*(1-temp$A))*var(
245+
#+ temp$S*(1-temp$A)*predict(fit1,newdata=temp)/temp$w10/
246+
#+ sum(temp$S*(1-temp$A)/temp$w10))
247247

248-
# fit0=lm(as.formula(paste("Y2","~",form_x)), data = filter(temp,S==0) )
249-
# sigma00=(summary(fit0)$sigma)**2
250-
denom <- sum((1 - temp$S) * temp$w00^2 / (sum((1 - temp$S) * temp$w00))^2)
251-
#+ sum((1-temp$S))*var((1-temp$S)*predict(fit0,newdata =temp)*temp$w00/sum((1-temp$S)*temp$w00))
252-
w.opt <- num / (num + denom)
253-
if (optimal_weight_flag) {
254-
wt <- w.opt
255-
}
248+
# fit0=lm(as.formula(paste("Y2","~",form_x)), data = filter(temp,S==0) )
249+
# sigma00=(summary(fit0)$sigma)**2
250+
denom <- sum((1 - temp$S) * temp$w00^2 / (sum((1 - temp$S) * temp$w00))^2)
251+
#+ sum((1-temp$S))*var((1-temp$S)*predict(fit0,newdata =temp)*temp$w00/sum((1-temp$S)*temp$w00))
252+
w.opt <- num / (num + denom)
253+
if (optimal_weight_flag) {
254+
wt <- w.opt
255+
}
256256

257-
# final hybrid estimate as combination of RCT control and external control
258-
mu0 <- (1 - wt) * mu10 + wt * mu00
259-
tau <- mu1 - mu0
257+
# final hybrid estimate as combination of RCT control and external control
258+
mu0 <- (1 - wt) * mu10 + wt * mu00
259+
tau <- mu1 - mu0
260260

261-
names(tau) <- paste0("tau", 1:T_follow)
261+
names(tau) <- paste0("tau", 1:T_follow)
262262

263-
# ATE as linear comb of parameters
264-
## TODO: too many zeros; maybe can be improved?
265-
coef.mat <- cbind(
266-
diag(T_follow),
267-
-(1 - wt) * diag(T_follow),
268-
-wt * diag(T_follow),
269-
matrix(0, nrow = T_follow, ncol = dim(piS.beta)[2] + n5)
270-
)
271-
# get standard error using the linear comb of var-cov matrix
272-
sd.tau <- sqrt(diag(coef.mat %*% sigma %*% t(coef.mat) / (n + m)))
263+
# ATE as linear comb of parameters
264+
## TODO: too many zeros; maybe can be improved?
265+
coef.mat <- cbind(
266+
diag(T_follow),
267+
-(1 - wt) * diag(T_follow),
268+
-wt * diag(T_follow),
269+
matrix(0, nrow = T_follow, ncol = dim(piS.beta)[2] + n5)
270+
)
271+
# get standard error using the linear comb of var-cov matrix
272+
sd.tau <- sqrt(diag(coef.mat %*% sigma %*% t(coef.mat) / (n + m)))
273273

274-
names(sd.tau) <- paste0("sd.tau", 1:T_follow)
274+
names(sd.tau) <- paste0("sd.tau", 1:T_follow)
275275

276276
cutoff <- qnorm(1 - alpha / 2, lower.tail = TRUE)
277277

0 commit comments

Comments
 (0)