Skip to content

Commit a0ba548

Browse files
author
Matt Secrest
committed
markdowns
1 parent 6eb6022 commit a0ba548

3 files changed

Lines changed: 156 additions & 96 deletions

File tree

_pkgdown.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ navbar:
1818
aria-label: GitHub
1919

2020
articles:
21+
- title: Getting Started
22+
contents:
23+
- introduction
2124
- title: Primary Analysis
2225
contents:
2326
- primary_analysis_workflow

vignettes/introduction.Rmd

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: "Introduction to rdborrow"
3+
author: "Matt Secrest"
4+
date: "`r Sys.Date()`"
5+
output: rmarkdown::html_vignette
6+
vignette: >
7+
%\VignetteIndexEntry{Introduction to rdborrow}
8+
%\VignetteEngine{knitr::rmarkdown}
9+
%\VignetteEncoding{UTF-8}
10+
---
11+
12+
```{r setup, include=FALSE}
13+
library(rdborrow)
14+
```
15+
16+
## Overview
17+
18+
**rdborrow** implements causal inference methods for incorporating external
19+
controls in randomized controlled trials (RCTs) with longitudinal outcomes.
20+
The package provides tools for both analysis and simulation, enabling
21+
researchers to evaluate different borrowing strategies and design trials
22+
that leverage external data.
23+
24+
The methods are motivated by the SUNFISH trial for spinal muscular atrophy
25+
(SMA), where external controls from the olesoxime trial augment the
26+
randomized data to improve statistical efficiency.
27+
28+
## Methods
29+
30+
### Primary analysis (placebo-controlled phase)
31+
32+
These methods estimate the average treatment effect (ATE) during the
33+
placebo-controlled phase by borrowing from external controls.
34+
35+
| Method | Function | Description |
36+
|--------|----------|-------------|
37+
| EC-IPW | `ec_ipw()` | Inverse probability weighting with external control borrowing |
38+
| EC-AIPW | `ec_aipw()` | Augmented IPW (doubly robust) |
39+
40+
Both methods support:
41+
42+
- **No borrowing** (`weight = 0`): uses only RCT data
43+
- **Optimal weight** (`weight = NULL`): data-adaptive weight minimizing variance
44+
- **Fixed weight** (`weight = 0.3`): user-specified borrowing amount
45+
- **Sandwich variance** or **bootstrap inference**
46+
47+
See `vignette("primary_analysis_workflow")` for usage.
48+
49+
### OLE phase analysis (open-label extension)
50+
51+
After the placebo-controlled phase, control subjects cross over to
52+
treatment. These methods estimate the long-term treatment effect using
53+
external controls who remain untreated.
54+
55+
| Method | Function | Description |
56+
|--------|----------|-------------|
57+
| DID-EC-IPW | `did_ec_ipw()` | Difference-in-differences with IPW |
58+
| DID-EC-AIPW | `did_ec_aipw()` | DID with augmented IPW |
59+
| DID-EC-OR | `did_ec_or()` | DID with outcome regression |
60+
| SCM | `scm()` | Synthetic control method |
61+
62+
All OLE methods use bootstrap for inference.
63+
64+
See `vignette("OLE_analysis_workflow")` for usage.
65+
66+
### Simulation
67+
68+
The simulation module evaluates estimator performance via Monte Carlo:
69+
70+
- `simulate_trial()` generates synthetic trial + external control data
71+
- `setup_simulation_primary()` / `setup_simulation_OLE()` configure simulations
72+
- `run_simulation()` runs Monte Carlo experiments and reports bias, variance, MSE, coverage, power
73+
74+
See `vignette("primary_simulation_workflow")` and
75+
`vignette("OLE_simulation_workflow")` for usage.
76+
77+
## Quick start
78+
79+
```{r}
80+
# create an EC-IPW method with optimal borrowing weight
81+
method <- ec_ipw(ps_formula = "S ~ x1 + x2 + x3 + x4 + x5")
82+
83+
# set up the analysis
84+
analysis <- setup_analysis_primary(
85+
data = SyntheticData,
86+
trial_status_col_name = "S",
87+
treatment_col_name = "A",
88+
outcome_col_name = c("y1", "y2"),
89+
covariates_col_name = c("x1", "x2", "x3", "x4", "x5"),
90+
method_weighting_obj = method
91+
)
92+
93+
# run
94+
run_analysis(analysis)
95+
```
96+
97+
## References
98+
99+
- Zhou X, Zhu J, Drake C, Pang H (2024). "Causal estimators for incorporating external controls in randomized trials with longitudinal outcomes." *Journal of the Royal Statistical Society Series A: Statistics in Society*. doi: [10.1093/jrsssa/qnae075](https://doi.org/10.1093/jrsssa/qnae075).
100+
- Zhou X, Pang H, Drake C, Burger HU, Zhu J (2024). "Estimating treatment effect in randomized trial after control to treatment crossover using external controls." *Journal of Biopharmaceutical Statistics*. doi: [10.1080/10543406.2024.2444222](https://doi.org/10.1080/10543406.2024.2444222).
101+
- Shi L, Pang H, Chen C, Zhu J (2025). "rdborrow: an R package for causal inference incorporating external controls in randomized controlled trials with longitudinal outcomes." *Journal of Biopharmaceutical Statistics*, 35(6), 1043-1066. doi: [10.1080/10543406.2025.2489283](https://doi.org/10.1080/10543406.2025.2489283).

vignettes/primary_analysis_workflow.Rmd

Lines changed: 52 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ vignette: >
1313
library(rdborrow)
1414
```
1515

16-
17-
1816
## Primary analysis
1917

2018
This vignette demonstrates the primary analysis workflow using the
@@ -23,27 +21,21 @@ EC-IPW and EC-AIPW weighting estimators proposed in
2321
for incorporating external controls in randomized trials with
2422
longitudinal outcomes.
2523

26-
### 1 load and visualize data
24+
### 1 Load data
2725
```{r}
28-
# load the simulated dataset
2926
head(SyntheticData)
3027
```
3128

29+
### 2 EC-IPW
3230

33-
### 2 Estimation and inference:
34-
35-
#### 2.1 Inverse probability weighting (IPW)
36-
1) IPW with zero weight (wt = 0):
37-
31+
#### 2.1 No borrowing (weight = 0)
3832
```{r}
39-
# test: within trial
40-
## Data argument + column names (coxph, glm)
4133
method <- ec_ipw(
4234
ps_formula = "S ~ x1 + x2 + x3 + x4 + x5",
4335
weight = 0
4436
)
4537
46-
analysis_primary_obj <- setup_analysis_primary(
38+
analysis <- setup_analysis_primary(
4739
data = SyntheticData,
4840
trial_status_col_name = "S",
4941
treatment_col_name = "A",
@@ -52,16 +44,14 @@ analysis_primary_obj <- setup_analysis_primary(
5244
method_weighting_obj = method
5345
)
5446
55-
res <- run_analysis(analysis_primary_obj)
56-
res
47+
run_analysis(analysis)
5748
```
5849

59-
2) IPW with data-adaptive weight:
50+
#### 2.2 Optimal weight (data-adaptive)
6051
```{r}
61-
# test: within trial
6252
method <- ec_ipw(ps_formula = "S ~ x1 + x2 + x3 + x4 + x5")
6353
64-
analysis_primary_obj <- setup_analysis_primary(
54+
analysis <- setup_analysis_primary(
6555
data = SyntheticData,
6656
trial_status_col_name = "S",
6757
treatment_col_name = "A",
@@ -70,87 +60,69 @@ analysis_primary_obj <- setup_analysis_primary(
7060
method_weighting_obj = method
7161
)
7262
73-
res <- run_analysis(analysis_primary_obj)
74-
75-
res$borrow_weight
76-
res$results
63+
run_analysis(analysis)
7764
```
7865

79-
#### 2.2 Augmented inverse probability weighting (AIPW)
80-
81-
The second approach is AIPW, which also accommodates two external borrowing strategies.
82-
83-
1) AIPW with zero weight (wt = 0):
66+
#### 2.3 Bootstrap inference
8467
```{r}
85-
# test: AIPW with 0 weight, should be same as IPW with 0 weight
86-
method_weighting_obj <- setup_method_weighting(
87-
method_name = "AIPW",
88-
optimal_weight_flag = FALSE,
89-
wt = 0,
90-
model_form_piS = "S ~ x1 + x2 + x3 + x4 + x5",
91-
model_form_mu0_ext = c(
92-
"y1 ~ x1 + x2 + x3 + x4 + x5",
93-
"y2 ~ x1 + x2 + x3 + x4 + x5"
94-
)
68+
method <- ec_ipw(
69+
ps_formula = "S ~ x1 + x2 + x3 + x4 + x5",
70+
bootstrap = 50,
71+
bootstrap_ci_type = "perc"
9572
)
9673
97-
analysis_primary_obj <- setup_analysis_primary(
74+
analysis <- setup_analysis_primary(
9875
data = SyntheticData,
9976
trial_status_col_name = "S",
10077
treatment_col_name = "A",
10178
outcome_col_name = c("y1", "y2"),
10279
covariates_col_name = c("x1", "x2", "x3", "x4", "x5"),
103-
method_weighting_obj = method_weighting_obj
80+
method_weighting_obj = method
10481
)
10582
106-
res <- run_analysis(analysis_primary_obj)
83+
run_analysis(analysis)
10784
```
10885

86+
### 3 EC-AIPW
10987

88+
EC-AIPW augments the IPW estimator with an outcome regression model,
89+
making it doubly robust: consistent if either the propensity score model
90+
or the outcome model is correctly specified.
11091

111-
2) AIPW with data adaptive weight:
92+
#### 3.1 No borrowing (weight = 0)
11293
```{r}
113-
# test: AIPW with given weight
114-
# bootstrap as part of the method and analysis
115-
method_weighting_obj <- setup_method_weighting(
116-
method_name = "AIPW",
117-
optimal_weight_flag = TRUE,
118-
model_form_piS = "S ~ x1 + x2 + x3 + x4 + x5",
119-
model_form_mu0_ext = c(
94+
method <- ec_aipw(
95+
ps_formula = "S ~ x1 + x2 + x3 + x4 + x5",
96+
outcome_formula = c(
12097
"y1 ~ x1 + x2 + x3 + x4 + x5",
12198
"y2 ~ x1 + x2 + x3 + x4 + x5"
122-
)
99+
),
100+
weight = 0
123101
)
124102
125-
analysis_primary_obj <- setup_analysis_primary(
103+
analysis <- setup_analysis_primary(
126104
data = SyntheticData,
127105
trial_status_col_name = "S",
128106
treatment_col_name = "A",
129107
outcome_col_name = c("y1", "y2"),
130108
covariates_col_name = c("x1", "x2", "x3", "x4", "x5"),
131-
method_weighting_obj = method_weighting_obj
109+
method_weighting_obj = method
132110
)
133111
134-
res <- run_analysis(analysis_primary_obj)
112+
run_analysis(analysis)
135113
```
136114

137-
138-
139-
### 3 Bootstrap inference
140-
141-
In this section we present Bootstrap inference results. We report Bootstrap confidence intervals with adjusted quantile ranges.
142-
143-
1) IPW with bootstrap CI
115+
#### 3.2 Optimal weight (data-adaptive)
144116
```{r}
145-
# test: within trial
146-
## bootstrap is now specified directly in the method constructor
147-
method <- ec_ipw(
117+
method <- ec_aipw(
148118
ps_formula = "S ~ x1 + x2 + x3 + x4 + x5",
149-
bootstrap = 50,
150-
bootstrap_ci_type = "perc"
119+
outcome_formula = c(
120+
"y1 ~ x1 + x2 + x3 + x4 + x5",
121+
"y2 ~ x1 + x2 + x3 + x4 + x5"
122+
)
151123
)
152124
153-
analysis_primary_obj <- setup_analysis_primary(
125+
analysis <- setup_analysis_primary(
154126
data = SyntheticData,
155127
trial_status_col_name = "S",
156128
treatment_col_name = "A",
@@ -159,52 +131,36 @@ analysis_primary_obj <- setup_analysis_primary(
159131
method_weighting_obj = method
160132
)
161133
162-
res <- run_analysis(analysis_primary_obj)
163-
res
134+
run_analysis(analysis)
164135
```
165136

166-
2) AIPW with bootstrap CI
167-
137+
#### 3.3 Bootstrap inference
168138
```{r}
169-
# test: with optimal weight
170-
## Data argument + column names (coxph, glm)
171-
bootstrap_obj <- setup_bootstrap(
172-
replicates = 50,
173-
bootstrap_CI_type = "perc"
174-
)
175-
176-
method_weighting_obj <- setup_method_weighting(
177-
method_name = "AIPW",
178-
optimal_weight_flag = TRUE,
179-
wt = 0,
180-
bootstrap_flag = TRUE,
181-
bootstrap_obj = bootstrap_obj,
182-
model_form_piS = "S ~ x1 + x2 + x3 + x4 + x5",
183-
model_form_mu0_ext = c(
139+
method <- ec_aipw(
140+
ps_formula = "S ~ x1 + x2 + x3 + x4 + x5",
141+
outcome_formula = c(
184142
"y1 ~ x1 + x2 + x3 + x4 + x5",
185143
"y2 ~ x1 + x2 + x3 + x4 + x5"
186-
)
144+
),
145+
bootstrap = 50,
146+
bootstrap_ci_type = "perc"
187147
)
188148
189-
analysis_primary_obj <- setup_analysis_primary(
149+
analysis <- setup_analysis_primary(
190150
data = SyntheticData,
191-
trial_status = "S",
192-
treatment = "A",
193-
outcome = c("y1", "y2"),
194-
covariates = c("x1", "x2", "x3", "x4", "x5"),
195-
method_weighting_obj = method_weighting_obj
151+
trial_status_col_name = "S",
152+
treatment_col_name = "A",
153+
outcome_col_name = c("y1", "y2"),
154+
covariates_col_name = c("x1", "x2", "x3", "x4", "x5"),
155+
method_weighting_obj = method
196156
)
197157
198-
199-
res <- run_analysis(analysis_primary_obj)
200-
201-
## best to just do the last one, but also have options
202-
## time dependent way of effects and modeling
158+
run_analysis(analysis)
203159
```
204160

205161
### 4 Notes
206162

207-
1. When there are missing values in the data, the suggestion we have for now is to preprocess the dataset (such as deletion, imputing, etc.) to obtain a dataset without missingness, then apply the package. For general methodology development regarding missing values, we save it for future research work.
163+
1. When there are missing values in the data, preprocess the dataset (deletion, imputation, etc.) to obtain a complete dataset before applying the package.
208164

209165
## References
210166

0 commit comments

Comments
 (0)