@@ -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