The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
This vignette validates the BinaryPower()
and
BinarySampleSize()
functions in the bbssr
package by comparing results with established R packages:
Exact
and exact2x2
. We also compare
computational performance to demonstrate the efficiency improvements in
bbssr
.
The validation covers all five statistical tests implemented in
bbssr
: 1. Pearson chi-squared test ('Chisq'
)
2. Fisher exact test ('Fisher'
) 3. Fisher mid-p test
('Fisher-midP'
) 4. Z-pooled exact unconditional test
('Z-pool'
) 5. Boschloo exact unconditional test
('Boschloo'
)
We use the following parameters for validation:
For users who want to see immediate results without running external packages:
# Quick verification using bbssr only
cat("=== Quick Verification Results ===\n")
#> === Quick Verification Results ===
cat("Test Parameters:\n")
#> Test Parameters:
cat("p1 =", paste(p1, collapse = ", "), "\n")
#> p1 = 0.5, 0.6, 0.7, 0.8
cat("p2 =", paste(p2, collapse = ", "), "\n")
#> p2 = 0.2, 0.2, 0.2, 0.2
cat("N1 =", N1, ", N2 =", N2, ", alpha =", alpha, "\n\n")
#> N1 = 10 , N2 = 40 , alpha = 0.025
# Show all test results
tests <- c('Chisq', 'Fisher', 'Fisher-midP', 'Z-pool', 'Boschloo')
for(test in tests) {
powers <- BinaryPower(p1, p2, N1, N2, alpha, Test = test)
cat(sprintf("%-12s: %s\n", test, paste(round(powers, 6), collapse = " ")))
}
#> Chisq : 0.484712 0.697121 0.866421 0.963872
#> Fisher : 0.328839 0.547489 0.761936 0.917285
#> Fisher-midP : 0.40576 0.62751 0.823033 0.947556
#> Z-pool : 0.40649 0.627702 0.822931 0.947353
#> Boschloo : 0.427797 0.654462 0.844536 0.95702
# bbssr results
bbssr_chisq <- BinaryPower(p1, p2, N1, N2, alpha, Test = 'Chisq')
print("bbssr results:")
#> [1] "bbssr results:"
print(round(bbssr_chisq, 7))
#> [1] 0.4847117 0.6971209 0.8664207 0.9638721
# Exact package results for comparison
exact_chisq <- sapply(seq(p1), function(i) {
Exact::power.exact.test(p1[i], p2[i], N1, N2, method = 'pearson chisq', 'greater', alpha)$power
})
print("Exact package results:")
#> [1] "Exact package results:"
print(round(exact_chisq, 7))
#> [1] 0.4847117 0.6971209 0.8664207 0.9638721
# Create comparison table
chisq_comparison <- data.frame(
p1 = p1,
p2 = p2,
bbssr = round(bbssr_chisq, 7),
Exact = round(exact_chisq, 7),
Difference = round(abs(bbssr_chisq - exact_chisq), 10)
)
knitr::kable(chisq_comparison, caption = "Pearson Chi-squared Test: bbssr vs Exact Package")
p1 | p2 | bbssr | Exact | Difference |
---|---|---|---|---|
0.5 | 0.2 | 0.4847117 | 0.4847117 | 0 |
0.6 | 0.2 | 0.6971209 | 0.6971209 | 0 |
0.7 | 0.2 | 0.8664207 | 0.8664207 | 0 |
0.8 | 0.2 | 0.9638721 | 0.9638721 | 0 |
# bbssr results
bbssr_fisher <- BinaryPower(p1, p2, N1, N2, alpha, Test = 'Fisher')
# Exact package results for comparison
exact_fisher <- sapply(seq(p1), function(i) {
Exact::power.exact.test(p1[i], p2[i], N1, N2, method = 'fisher', 'greater', alpha)$power
})
# Create comparison table
fisher_comparison <- data.frame(
p1 = p1,
p2 = p2,
bbssr = round(bbssr_fisher, 7),
Exact = round(exact_fisher, 7),
Difference = round(abs(bbssr_fisher - exact_fisher), 10)
)
knitr::kable(fisher_comparison, caption = "Fisher Exact Test: bbssr vs Exact Package")
p1 | p2 | bbssr | Exact | Difference |
---|---|---|---|---|
0.5 | 0.2 | 0.3288391 | 0.3288391 | 0 |
0.6 | 0.2 | 0.5474891 | 0.5474891 | 0 |
0.7 | 0.2 | 0.7619361 | 0.7619361 | 0 |
0.8 | 0.2 | 0.9172846 | 0.9172846 | 0 |
# bbssr results
bbssr_midp <- BinaryPower(p1, p2, N1, N2, alpha, Test = 'Fisher-midP')
# exact2x2 package results for comparison
exact2x2_midp <- sapply(seq(p1), function(i) {
exact2x2::Power2x2(N1, N2, p1[i], p2[i], alpha, pvalFunc =
function(x1, n1, x2, n2) {
fisher.exact(matrix(c(x1, n1 - x1, x2, n2 - x2), 2),
alt = 'greater', midp = TRUE)$p.value
}
)
})
# Create comparison table
midp_comparison <- data.frame(
p1 = p1,
p2 = p2,
bbssr = round(bbssr_midp, 7),
exact2x2 = round(exact2x2_midp, 7),
Difference = round(abs(bbssr_midp - exact2x2_midp), 10)
)
knitr::kable(midp_comparison, caption = "Fisher Mid-p Test: bbssr vs exact2x2 Package")
p1 | p2 | bbssr | exact2x2 | Difference |
---|---|---|---|---|
0.5 | 0.2 | 0.4057605 | 0.4057605 | 0 |
0.6 | 0.2 | 0.6275103 | 0.6275103 | 0 |
0.7 | 0.2 | 0.8230326 | 0.8230326 | 0 |
0.8 | 0.2 | 0.9475556 | 0.9475556 | 0 |
# bbssr results
bbssr_zpool <- BinaryPower(p1, p2, N1, N2, alpha, Test = 'Z-pool')
# Exact package results for comparison
exact_zpool <- sapply(seq(p1), function(i) {
Exact::power.exact.test(p1[i], p2[i], N1, N2, method = 'z-pooled', 'greater', alpha)$power
})
# Create comparison table
zpool_comparison <- data.frame(
p1 = p1,
p2 = p2,
bbssr = round(bbssr_zpool, 7),
Exact = round(exact_zpool, 7),
Difference = round(abs(bbssr_zpool - exact_zpool), 10)
)
knitr::kable(zpool_comparison, caption = "Z-pooled Test: bbssr vs Exact Package")
p1 | p2 | bbssr | Exact | Difference |
---|---|---|---|---|
0.5 | 0.2 | 0.4064897 | 0.4064897 | 0 |
0.6 | 0.2 | 0.6277024 | 0.6277024 | 0 |
0.7 | 0.2 | 0.8229305 | 0.8229305 | 0 |
0.8 | 0.2 | 0.9473531 | 0.9473531 | 0 |
# bbssr results
bbssr_boschloo <- BinaryPower(p1, p2, N1, N2, alpha, Test = 'Boschloo')
# Exact package results for comparison
exact_boschloo <- sapply(seq(p1), function(i) {
Exact::power.exact.test(p1[i], p2[i], N1, N2, method = 'boschloo', 'greater', alpha)$power
})
# Create comparison table
boschloo_comparison <- data.frame(
p1 = p1,
p2 = p2,
bbssr = round(bbssr_boschloo, 7),
Exact = round(exact_boschloo, 7),
Difference = round(abs(bbssr_boschloo - exact_boschloo), 10)
)
knitr::kable(boschloo_comparison, caption = "Boschloo Test: bbssr vs Exact Package")
p1 | p2 | bbssr | Exact | Difference |
---|---|---|---|---|
0.5 | 0.2 | 0.4277969 | 0.4277969 | 0 |
0.6 | 0.2 | 0.6544621 | 0.6544621 | 0 |
0.7 | 0.2 | 0.8445363 | 0.8445363 | 0 |
0.8 | 0.2 | 0.9570202 | 0.9570202 | 0 |
We compare the computational speed using more demanding scenarios to highlight bbssr’s performance advantages.
# Enhanced parameters for performance comparison
perf_scenarios <- expand.grid(
p1 = c(0.5, 0.6, 0.7, 0.8),
p2 = c(0.2, 0.3, 0.4),
N1 = c(15, 20, 25),
N2 = c(15, 20, 25)
) %>%
filter(p1 > p2) %>% # Only meaningful comparisons
slice_head(n = 8) # Select representative scenarios
perf_alpha <- 0.025
# Display scenarios for transparency
knitr::kable(perf_scenarios, caption = "Performance Benchmark Scenarios")
p1 | p2 | N1 | N2 |
---|---|---|---|
0.5 | 0.2 | 15 | 15 |
0.6 | 0.2 | 15 | 15 |
0.7 | 0.2 | 15 | 15 |
0.8 | 0.2 | 15 | 15 |
0.5 | 0.3 | 15 | 15 |
0.6 | 0.3 | 15 | 15 |
0.7 | 0.3 | 15 | 15 |
0.8 | 0.3 | 15 | 15 |
# Function to benchmark a single scenario
benchmark_scenario <- function(p1, p2, N1, N2) {
microbenchmark::microbenchmark(
bbssr_chisq = BinaryPower(p1, p2, N1, N2, perf_alpha, Test = 'Chisq'),
exact_chisq = Exact::power.exact.test(p1, p2, N1, N2,
method = 'pearson chisq', 'greater', perf_alpha),
bbssr_fisher = BinaryPower(p1, p2, N1, N2, perf_alpha, Test = 'Fisher'),
exact_fisher = Exact::power.exact.test(p1, p2, N1, N2,
method = 'fisher', 'greater', perf_alpha),
bbssr_zpool = BinaryPower(p1, p2, N1, N2, perf_alpha, Test = 'Z-pool'),
exact_zpool = Exact::power.exact.test(p1, p2, N1, N2,
method = 'z-pooled', 'greater', perf_alpha),
bbssr_boschloo = BinaryPower(p1, p2, N1, N2, perf_alpha, Test = 'Boschloo'),
exact_boschloo = Exact::power.exact.test(p1, p2, N1, N2,
method = 'boschloo', 'greater', perf_alpha),
times = 20
)
}
# Run benchmarks for multiple scenarios
benchmark_results <- list()
for(i in 1:nrow(perf_scenarios)) {
scenario <- perf_scenarios[i, ]
cat(sprintf("Benchmarking scenario %d: p1=%.1f, p2=%.1f, N1=%d, N2=%d\n",
i, scenario$p1, scenario$p2, scenario$N1, scenario$N2))
benchmark_results[[i]] <- benchmark_scenario(
scenario$p1, scenario$p2, scenario$N1, scenario$N2
) %>%
summary() %>%
mutate(
scenario_id = i,
p1 = scenario$p1,
p2 = scenario$p2,
N1 = scenario$N1,
N2 = scenario$N2,
total_n = scenario$N1 + scenario$N2
)
}
#> Benchmarking scenario 1: p1=0.5, p2=0.2, N1=15, N2=15
#> Benchmarking scenario 2: p1=0.6, p2=0.2, N1=15, N2=15
#> Benchmarking scenario 3: p1=0.7, p2=0.2, N1=15, N2=15
#> Benchmarking scenario 4: p1=0.8, p2=0.2, N1=15, N2=15
#> Benchmarking scenario 5: p1=0.5, p2=0.3, N1=15, N2=15
#> Benchmarking scenario 6: p1=0.6, p2=0.3, N1=15, N2=15
#> Benchmarking scenario 7: p1=0.7, p2=0.3, N1=15, N2=15
#> Benchmarking scenario 8: p1=0.8, p2=0.3, N1=15, N2=15
# Combine all benchmark results
all_benchmarks <- do.call(rbind, benchmark_results)
# Calculate speed improvements
speed_comparison <- all_benchmarks %>%
mutate(
test_name = case_when(
grepl("chisq", expr) ~ "Chi-squared",
grepl("fisher", expr) & !grepl("zpool|boschloo", expr) ~ "Fisher",
grepl("zpool", expr) ~ "Z-pooled",
grepl("boschloo", expr) ~ "Boschloo"
),
package = if_else(grepl("bbssr", expr), "bbssr", "Exact")
) %>%
group_by(scenario_id, test_name, package, p1, p2, N1, N2, total_n) %>%
summarise(median_time = median(median), .groups = 'drop') %>%
tidyr::pivot_wider(names_from = package, values_from = median_time) %>%
mutate(
speed_improvement = round(Exact / bbssr, 1),
bbssr_ms = round(bbssr / 1000, 2),
exact_ms = round(Exact / 1000, 2)
)
# Display summary
speed_summary <- speed_comparison %>%
group_by(test_name) %>%
summarise(
avg_improvement = round(mean(speed_improvement), 1),
max_improvement = round(max(speed_improvement), 1),
min_improvement = round(min(speed_improvement), 1),
.groups = 'drop'
)
knitr::kable(speed_summary,
col.names = c("Test", "Avg Speed-up", "Max Speed-up", "Min Speed-up"),
caption = "Speed Improvement Summary: bbssr vs Exact Package (times faster)")
Test | Avg Speed-up | Max Speed-up | Min Speed-up |
---|---|---|---|
Boschloo | 1.3 | 1.6 | 1.0 |
Chi-squared | 14.1 | 18.7 | 10.3 |
Fisher | 3.0 | 3.4 | 2.9 |
Z-pooled | 1.0 | 1.1 | 0.8 |
# Create speed improvement comparison (how many times faster bbssr is)
speed_plot_data <- speed_comparison %>%
mutate(
test_name = factor(test_name, levels = c("Chi-squared", "Fisher", "Z-pooled", "Boschloo")),
scenario_label = paste0("Scenario ", scenario_id, "\n(N=", total_n, ")")
)
# Speed improvement plot
speed_plot <- ggplot(speed_plot_data, aes(x = scenario_label, y = speed_improvement)) +
geom_col(fill = "#4CAF50", alpha = 0.8, width = 0.6) +
geom_text(aes(label = paste0(speed_improvement, "x")),
vjust = -0.3, size = 3, fontface = "bold") +
facet_wrap(~test_name, ncol = 1, scales = "free_y") +
geom_hline(yintercept = 1, linetype = "dashed", color = "red", linewidth = 1) +
scale_y_continuous(expand = expansion(mult = c(0, 0.15))) + # Add 15% padding at top
labs(
title = "Speed Improvement: bbssr vs Exact Package",
subtitle = "How many times faster bbssr is compared to Exact package",
x = "Benchmark Scenario",
y = "Speed Improvement Factor (times faster)",
caption = "Red dashed line shows no improvement (factor = 1). Higher bars = better performance."
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, hjust = 0.5, margin = margin(b = 5)),
plot.subtitle = element_text(size = 11, hjust = 0.5, margin = margin(b = 15)),
strip.text = element_text(size = 12, face = "bold", margin = margin(t = 8, b = 8)),
strip.background = element_rect(fill = "gray95", color = "gray80"),
axis.title.x = element_text(size = 11, margin = margin(t = 10)),
axis.title.y = element_text(size = 11, margin = margin(r = 10)),
axis.text.x = element_text(size = 8, angle = 45, hjust = 1),
axis.text.y = element_text(size = 9),
panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
plot.margin = margin(t = 10, r = 10, b = 15, l = 10)
)
print(speed_plot)
# Detailed execution time comparison
time_comparison_data <- speed_comparison %>%
select(scenario_id, test_name, bbssr_ms, exact_ms, total_n) %>%
tidyr::pivot_longer(
cols = c(bbssr_ms, exact_ms),
names_to = "package",
values_to = "time_ms"
) %>%
mutate(
package = case_when(
package == "bbssr_ms" ~ "bbssr",
package == "exact_ms" ~ "Exact"
),
test_name = factor(test_name, levels = c("Chi-squared", "Fisher", "Z-pooled", "Boschloo")),
scenario_label = paste0("Scenario ", scenario_id, "\n(N=", total_n, ")")
)
# Execution time plot
time_plot <- ggplot(time_comparison_data, aes(x = scenario_label, y = time_ms, fill = package)) +
geom_col(position = "dodge", width = 0.7) +
facet_wrap(~test_name, ncol = 1, scales = "free_y") +
scale_fill_manual(
values = c("bbssr" = "#2E8B57", "Exact" = "#CD5C5C"),
name = "Package"
) +
labs(
title = "Execution Time Comparison: bbssr vs Exact Package",
subtitle = "Actual execution times in milliseconds",
x = "Benchmark Scenario",
y = "Execution Time (milliseconds)",
caption = "Lower bars indicate better performance"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 14, hjust = 0.5, margin = margin(b = 5)),
plot.subtitle = element_text(size = 11, hjust = 0.5, margin = margin(b = 15)),
strip.text = element_text(size = 12, face = "bold", margin = margin(t = 8, b = 8)),
strip.background = element_rect(fill = "gray95", color = "gray80"),
legend.position = "bottom",
legend.title = element_text(size = 11),
legend.text = element_text(size = 10),
axis.title.x = element_text(size = 11, margin = margin(t = 10)),
axis.title.y = element_text(size = 11, margin = margin(r = 10)),
axis.text.x = element_text(size = 8, angle = 45, hjust = 1),
axis.text.y = element_text(size = 9),
panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
plot.margin = margin(t = 10, r = 10, b = 15, l = 10)
)
print(time_plot)
# Overall performance summary
overall_summary <- speed_comparison %>%
summarise(
total_scenarios = n(),
overall_avg_improvement = round(mean(speed_improvement), 1),
overall_max_improvement = round(max(speed_improvement), 1),
overall_min_improvement = round(min(speed_improvement), 1),
scenarios_over_2x = sum(speed_improvement >= 2),
scenarios_over_5x = sum(speed_improvement >= 5),
scenarios_over_10x = sum(speed_improvement >= 10)
)
cat("Performance Summary:\n")
#> Performance Summary:
cat(sprintf("- Total benchmark scenarios: %d\n", overall_summary$total_scenarios))
#> - Total benchmark scenarios: 32
cat(sprintf("- Average speed improvement: %.1fx faster\n", overall_summary$overall_avg_improvement))
#> - Average speed improvement: 4.8x faster
cat(sprintf("- Maximum speed improvement: %.1fx faster\n", overall_summary$overall_max_improvement))
#> - Maximum speed improvement: 18.7x faster
cat(sprintf("- Minimum speed improvement: %.1fx faster\n", overall_summary$overall_min_improvement))
#> - Minimum speed improvement: 0.8x faster
cat(sprintf("- Scenarios where bbssr is >2x faster: %d (%.1f%%)\n",
overall_summary$scenarios_over_2x,
100 * overall_summary$scenarios_over_2x / overall_summary$total_scenarios))
#> - Scenarios where bbssr is >2x faster: 16 (50.0%)
cat(sprintf("- Scenarios where bbssr is >5x faster: %d (%.1f%%)\n",
overall_summary$scenarios_over_5x,
100 * overall_summary$scenarios_over_5x / overall_summary$total_scenarios))
#> - Scenarios where bbssr is >5x faster: 8 (25.0%)
cat(sprintf("- Scenarios where bbssr is >10x faster: %d (%.1f%%)\n",
overall_summary$scenarios_over_10x,
100 * overall_summary$scenarios_over_10x / overall_summary$total_scenarios))
#> - Scenarios where bbssr is >10x faster: 8 (25.0%)
The validation results demonstrate that bbssr
functions
produce identical results to established packages:
Exact
and exact2x2
packagesThe bbssr
package demonstrates significant computational
efficiency across all statistical tests.
The validation results confirm that bbssr
provides:
The package successfully combines statistical rigor with computational efficiency, making it an excellent choice for implementing blinded sample size re-estimation in clinical trials with binary endpoints.
sessionInfo()
#> R version 4.5.0 (2025-04-11 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26100)
#>
#> Matrix products: default
#> LAPACK version 3.12.1
#>
#> locale:
#> [1] LC_COLLATE=C LC_CTYPE=Japanese_Japan.utf8
#> [3] LC_MONETARY=Japanese_Japan.utf8 LC_NUMERIC=C
#> [5] LC_TIME=Japanese_Japan.utf8
#>
#> time zone: Asia/Tokyo
#> tzcode source: internal
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] exact2x2_1.6.9 exactci_1.4-4 testthat_3.2.3
#> [4] ssanv_1.1 Exact_3.3 microbenchmark_1.5.0
#> [7] tidyr_1.3.1 ggplot2_3.5.2 dplyr_1.1.4
#> [10] bbssr_1.0.2
#>
#> loaded via a namespace (and not attached):
#> [1] gtable_0.3.6 jsonlite_2.0.0 compiler_4.5.0 brio_1.1.5
#> [5] tidyselect_1.2.1 jquerylib_0.1.4 scales_1.4.0 yaml_2.3.10
#> [9] fastmap_1.2.0 R6_2.6.1 labeling_0.4.3 generics_0.1.4
#> [13] knitr_1.50 tibble_3.2.1 bslib_0.9.0 pillar_1.10.2
#> [17] RColorBrewer_1.1-3 rlang_1.1.6 utf8_1.2.5 cachem_1.1.0
#> [21] xfun_0.52 sass_0.4.10 cli_3.6.5 withr_3.0.2
#> [25] magrittr_2.0.3 digest_0.6.37 grid_4.5.0 rootSolve_1.8.2.4
#> [29] rstudioapi_0.17.1 lifecycle_1.0.4 vctrs_0.6.5 fpCompare_0.2.4
#> [33] evaluate_1.0.3 glue_1.8.0 farver_2.1.2 rmarkdown_2.29
#> [37] purrr_1.0.4 tools_4.5.0 pkgconfig_2.0.3 htmltools_0.5.8.1
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.