Simulation results 1
Fixed design with single look at data
Simulation 1 is a fixed sized trial with decision criteria for superiority, and non-inferiority and also for futility with respect to both superiority and non-inferiority.
We provide summaries of each simulation scenario and the results that were obtained.
Configuration used for each simulated scenario
# cfg used in each scenario
d_cfg <- rbindlist(lapply(seq_along(l), function(i){
tok <- toks[[i]]
m <- data.table(do.call(cbind, l[[i]]$cfg))
data.table(cbind(sc = tok[2], v = tok[3], analys = 1:nrow(m), m))
}))
# conversion to numeric
d_cfg[, `:=`(
nsim = as.numeric(nsim),
N_pt = as.numeric(N_pt),
b_r1 = as.numeric(b_r1),
b_r2 = as.numeric(b_r2),
w_srp2 = as.numeric(w_srp2),
b_r1d = as.numeric(b_r1d),
b_r2d = as.numeric(b_r2d),
b_f = as.numeric(b_f),
d_sup = as.numeric(thresh_sup),
d_ni = as.numeric(thresh_non_inf),
d_fut_sup = as.numeric(thresh_fut_sup),
d_fut_ni = as.numeric(thresh_fut_ni)
)]
d_cfg[, `:=`(w_srp2 = NULL)]
Process simulation results for variables of interest
# Decisions
i <- 1
d_sup <- rbindlist(lapply(seq_along(l), function(i){
cbind(sc = d_cfg[i, sc], v = d_cfg[i, v], l[[i]]$d_sup[, lapply(.SD, mean)])
}))
d_sup <- melt(d_sup, id.vars = c("sc", "v"), value.name = "p")
d_sup[, type := "sup"]
d_trt_ni_ref <- rbindlist(lapply(seq_along(l), function(i){
cbind(sc = d_cfg[i, sc], v = d_cfg[i, v], l[[i]]$d_pr_trt_ni_ref[, lapply(.SD, mean)])
}))
d_trt_ni_ref <- melt(d_trt_ni_ref, id.vars = c("sc", "v"), value.name = "p")
d_trt_ni_ref[, type := "trt_ni_ref"]
d_fut_sup <- rbindlist(lapply(seq_along(l), function(i){
cbind(sc = d_cfg[i, sc], v = d_cfg[i, v], l[[i]]$d_fut_sup[, lapply(.SD, mean)])
}))
d_fut_sup <- melt(d_fut_sup, id.vars = c("sc", "v"), value.name = "p")
d_fut_sup[, type := "fut_sup"]
d_fut_ni <- rbindlist(lapply(seq_along(l), function(i){
cbind(sc = d_cfg[i, sc], v = d_cfg[i, v], l[[i]]$d_fut_trt_ni_ref[, lapply(.SD, mean)])
}))
d_fut_ni <- melt(d_fut_ni, id.vars = c("sc", "v"), value.name = "p")
d_fut_ni[, type := "fut_ni"]
d_dec <- rbind(
d_sup, d_trt_ni_ref, d_fut_sup, d_fut_ni
)
n_sims <- d_cfg$nsim[1]
N_pt <- d_cfg$N_pt[1]
# Posterior summaries on effects of interest
d_post_smry_2 <- rbindlist(lapply(seq_along(l), function(i){
tok <- toks[[i]]
m <- l[[i]]$d_post_smry_2
cbind(sc = tok[2], v = tok[3], m)
}))
# Participant data from trial (grouped)
d_all <- rbindlist(lapply(seq_along(l), function(i){
tok <- toks[[i]]
m <- l[[i]]$d_grp
cbind(sc = tok[2], v = tok[3], m)
}))
Table 1 summarises the configurations used in each simulated scenario. Each treatment effect parameter is set to have the same magnitude of effect. The effects range from \(\log(1/2)\) in scenario 1 to \(\log(2)\) in scenario 7. Decision rules and thresholds remain constant over the entire enrolment period.
Revision effects are computed as a weighted combination of the log-odds ratios for the one-stage and two-stage revision effects. The weights are the sample proportion receiving one-stage and two-stage surgery in those patients receiving randomised surgical treatment and randomised to revision.
Code
d_tbl <- d_cfg[, .(v, N_pt, b_r1, b_r2, b_r1d, b_r2d, b_f,
delta_sup = delta_sup,
delta_sup_fut = delta_sup_fut,
delta_ni = 1/delta_ni,
thresh_sup, thresh_non_inf, thresh_fut_sup, thresh_fut_ni)]
g_tbl <- d_tbl |> gt() |>
cols_align(
columns = everything(),
align = "center"
) |>
fmt_number(
columns = c(b_r1, b_r2, b_r1d, b_r2d, b_f,
delta_ni
),
decimals = 3
) |>
tab_spanner(
label = html("Surgical (D<sub>a</sub>)"),
columns = c(b_r1, b_r2)
) |>
tab_spanner(
label = html("Duration (D<sub>b</sub>)"),
columns = c(b_r1d, b_r2d)
) |>
tab_spanner(
label = html("Type (D<sub>c</sub>)"),
columns = c(b_f)
) |>
tab_spanner(
label = html("Decision setup"),
columns = c(delta_sup, thresh_sup,
delta_sup_fut, thresh_fut_sup,
delta_ni, thresh_non_inf, thresh_fut_ni)
) |>
cols_label(
v = html("Configuration"),
b_r1 = html("rev<br>(one-stage)"),
b_r2 = html("rev<br>(two-stage)"),
b_r1d = html("short<br>(one-stage)"),
b_r2d = html("short<br>(two-stage)"),
b_f = html("rif"),
delta_sup = html("delta<sub>sup</sub>"),
thresh_sup = html("p<sub>sup</sub>"),
delta_sup_fut = html("delta<sub>fut-sup</sub>"),
thresh_fut_sup = html("p<sub>fut-sup</sub>"),
delta_ni = html("delta<sub>ni</sub>"),
thresh_non_inf = html("p<sub>ni</sub>"),
thresh_fut_ni = html("p<sub>fut-ni</sub>")
) |>
tab_style(
style = list(
cell_borders(
sides = c("bottom"), color = "black", weight = px(1), style = "solid"
)),
locations = list(
cells_body(
columns = everything(),
rows = N_pt == 2500
)
)
) |>
tab_options(
table.font.size = "70%"
) |>
tab_footnote(
footnote = "Surgical effects only applies to late silo, effect is relative to response under DAIR.",
locations = cells_column_labels(columns = c(b_r1, b_r2))
) |>
tab_footnote(
footnote = "Applies to all silos, effect is relative to response under long duration.",
locations = cells_column_labels(columns = b_r1d)
) |>
tab_footnote(
footnote = "Applies to all silos, effect is relative to response under short duration.",
locations = cells_column_labels(columns = b_r2d)
) |>
tab_footnote(
footnote = "Applies to all silos, effect is relative to response under no-rifampicin",
locations = cells_column_labels(columns = b_f)
) |>
tab_footnote(
footnote = "Reference OR for evaluating superiority",
locations = cells_column_labels(columns = delta_sup)
) |>
tab_footnote(
footnote = "Probability threshold above which superiority is concluded",
locations = cells_column_labels(columns = thresh_sup)
) |>
tab_footnote(
footnote = "Reference OR for evaluating futility wrt the superiority decision",
locations = cells_column_labels(columns = delta_sup_fut)
) |>
tab_footnote(
footnote = "Probability threshold below which futility is concluded",
locations = cells_column_labels(columns = thresh_fut_sup)
) |>
tab_footnote(
footnote = "Reference OR for evaluating non-inferiority",
locations = cells_column_labels(columns = delta_ni)
) |>
tab_footnote(
footnote = "Probability threshold above which non-inferiority is concluded",
locations = cells_column_labels(columns = thresh_non_inf)
) |>
tab_footnote(
footnote = "Probability threshold below which non-inferiority decision is deemed futile",
locations = cells_column_labels(columns = thresh_fut_ni)
)
g_tbl
Configuration | N_pt | Surgical (Da) | Duration (Db) | Type (Dc) | Decision setup | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
rev (one-stage)1 |
rev (two-stage)1 |
short (one-stage)2 |
short (two-stage)3 |
rif4 | deltasup5 | psup6 | deltafut-sup7 | pfut-sup8 | deltani9 | pni10 | pfut-ni11 | ||
v01 | 2500 | −0.693 | −0.693 | −0.693 | −0.693 | −0.693 | 1 | 0.975 | 1.2 | 0.05 | 0.833 | 0.975 | 0.05 |
v02 | 2500 | −0.405 | −0.405 | −0.405 | −0.405 | −0.405 | 1 | 0.975 | 1.2 | 0.05 | 0.833 | 0.975 | 0.05 |
v03 | 2500 | −0.182 | −0.182 | −0.182 | −0.182 | −0.182 | 1 | 0.975 | 1.2 | 0.05 | 0.833 | 0.975 | 0.05 |
v04 | 2500 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 1 | 0.975 | 1.2 | 0.05 | 0.833 | 0.975 | 0.05 |
v05 | 2500 | 0.182 | 0.182 | 0.182 | 0.182 | 0.182 | 1 | 0.975 | 1.2 | 0.05 | 0.833 | 0.975 | 0.05 |
v06 | 2500 | 0.405 | 0.405 | 0.405 | 0.405 | 0.405 | 1 | 0.975 | 1.2 | 0.05 | 0.833 | 0.975 | 0.05 |
v07 | 2500 | 0.693 | 0.693 | 0.693 | 0.693 | 0.693 | 1 | 0.975 | 1.2 | 0.05 | 0.833 | 0.975 | 0.05 |
1 Surgical effects only applies to late silo, effect is relative to response under DAIR. | |||||||||||||
2 Applies to all silos, effect is relative to response under long duration. | |||||||||||||
3 Applies to all silos, effect is relative to response under short duration. | |||||||||||||
4 Applies to all silos, effect is relative to response under no-rifampicin | |||||||||||||
5 Reference OR for evaluating superiority | |||||||||||||
6 Probability threshold above which superiority is concluded | |||||||||||||
7 Reference OR for evaluating futility wrt the superiority decision | |||||||||||||
8 Probability threshold below which futility is concluded | |||||||||||||
9 Reference OR for evaluating non-inferiority | |||||||||||||
10 Probability threshold above which non-inferiority is concluded | |||||||||||||
11 Probability threshold below which non-inferiority decision is deemed futile |
Figure 1 summarises the variation in the probability of declaring each decision type on each parameter with increasing effects size (odds ratios). The results based on 5000 simulations for a cohort sample size of 2500.
Code
d_fig <- copy(d_dec)
d_fig[, or := g_or_num[v]]
d_fig[, type := factor(
type, levels = c("sup", "fut_sup", "trt_ni_ref", "fut_ni"),
labels = c("sup", "fut_sup", "ni", "fut_ni"))]
ggplot(d_fig, aes(x = or, y = p, group = type, col = type)) +
geom_point(size = 0.5) +
geom_line(lwd = 0.4) +
geom_hline(yintercept = 0.05, lwd = 0.4) +
ggthemes::scale_colour_tableau(
"", palette = "Tableau 10",
type = "regular",
direction = 1) +
scale_x_continuous("Odds-ratio", breaks = seq(0, 2, by = 0.2)) +
scale_y_continuous("Proportion sims with decision", breaks = seq(0, 1, by = 0.1)) +
facet_wrap(~variable, ncol = 2, scales = "free")
Table 2 shows the same detail as the above figure, but makes it easier to see what the magnitudes of the probabilities are.
Code
# Widen data so that power is shown by col with each col corresponding to an
# analysis
d_tbl <- copy(d_fig)
d_tbl <- dcast(d_tbl, variable + or ~ type, value.var = "p")
names(d_tbl) <- gsub("superiority", "sup", names(d_tbl))
names(d_tbl) <- gsub("futility", "fut", names(d_tbl))
names(d_tbl) <- gsub("trt ni ref", "trt_ni_ref", names(d_tbl))
d_tbl <- d_tbl[order(variable, or)]
g_tbl <- d_tbl |> gt(groupname_col = "variable") |>
cols_label(
or = html("Odds ratio<br>(true)"),
sup = html("Superiority"),
fut_sup = html("Futility (sup)"),
ni = html("NI (trt ni ref)"),
fut_ni = html("Futility (ni)"),
) |>
fmt_number(
columns = everything(),
decimals = 3
) |>
tab_style(
style = list(
cell_borders(
sides = c("top", "bottom"), color = "red", weight = px(1), style = "solid"
)),
locations = list(
cells_body(
columns = everything(),
rows = or == "1"
)
)
) |>
tab_options(
table.font.size = "80%"
)
g_tbl
Odds ratio (true) |
Superiority | Futility (sup) | NI (trt ni ref) | Futility (ni) |
---|---|---|---|---|
b_r | ||||
0.500 | 0.000 | 1.000 | 0.003 | 0.988 |
0.667 | 0.000 | 0.998 | 0.113 | 0.522 |
0.833 | 0.000 | 0.883 | 0.508 | 0.046 |
1.000 | 0.024 | 0.406 | 0.847 | 0.002 |
1.200 | 0.294 | 0.043 | 0.979 | 0.000 |
1.500 | 0.864 | 0.000 | 0.999 | 0.000 |
2.000 | 1.000 | 0.000 | 1.000 | 0.000 |
b_r1d | ||||
0.500 | 0.000 | 0.991 | 0.053 | 0.735 |
0.667 | 0.000 | 0.877 | 0.226 | 0.267 |
0.833 | 0.002 | 0.554 | 0.496 | 0.043 |
1.000 | 0.021 | 0.204 | 0.737 | 0.005 |
1.200 | 0.135 | 0.041 | 0.891 | 0.000 |
1.500 | 0.437 | 0.002 | 0.971 | 0.000 |
2.000 | 0.809 | 0.000 | 0.995 | 0.000 |
b_r2d | ||||
0.500 | 0.000 | 1.000 | 0.011 | 0.945 |
0.667 | 0.000 | 0.990 | 0.147 | 0.429 |
0.833 | 0.001 | 0.795 | 0.505 | 0.043 |
1.000 | 0.022 | 0.337 | 0.812 | 0.001 |
1.200 | 0.209 | 0.049 | 0.956 | 0.000 |
1.500 | 0.711 | 0.001 | 0.996 | 0.000 |
2.000 | 0.977 | 0.000 | 1.000 | 0.000 |
b_f | ||||
0.500 | 0.000 | 1.000 | 0.000 | 0.999 |
0.667 | 0.000 | 1.000 | 0.072 | 0.663 |
0.833 | 0.000 | 0.967 | 0.507 | 0.047 |
1.000 | 0.030 | 0.518 | 0.894 | 0.000 |
1.200 | 0.397 | 0.053 | 0.991 | 0.000 |
1.500 | 0.954 | 0.000 | 1.000 | 0.000 |
2.000 | 1.000 | 0.000 | 1.000 | 0.000 |
Figure 2 shows the distribution of estimated posterior means by simulated effect size, parameter and scenario.