--- title: "Using winratiosim" author: "Se Yoon Lee" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Using winratiosim} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5 ) ``` ## Overview `winratiosim` simulates operating characteristics for two-arm clinical trials with a hierarchical win ratio endpoint. A simulated trial includes three prioritized outcome layers: 1. time to death, 2. annualized recurrent event count, and 3. a continuous quality-of-life change score. The package was created for the simulation workflow used in Lee (2025), which compares Finkelstein-Schoenfeld permutation variance calculations with the large-sample variance formula discussed by Yu and Ganju. ## Quick start The main function is `winratiosim()`. The example below uses only two simulated trials and a small sample size so that the vignette builds quickly. Increase `nsim` and `N` for a real operating-characteristic study. ```{r quick-start} library(winratiosim) quick_res <- winratiosim( nsim = 2, N = 20, Randomization.ratio = c(1, 1), alpha.JFM = 0, theta.JFM = 1, lambda_trt = 0.13, lambda_ctl = 0.15, ann.icr_trt = 0.32, ann.icr_ctl = 0.55, xbase_trt = 45, xfinal_trt = 52.5, xbase_ctl = 45, xfinal_ctl = 45, sd.delta.x_trt = 20, sd.delta.x_ctl = 20, censorrate_trt = 0.2, censorrate_ctl = 0.2, nc = 1, seed = 20250518 ) quick_res$df_WR.analysis.summary quick_res$df_sample.size.summary ``` The returned object is a named list: ```{r result-names} names(quick_res) ``` The most commonly used elements are: * `df_FS.analysis.summary`: Finkelstein-Schoenfeld statistic, variance, z-score, and p-value for each simulated trial. * `df_WR.analysis.summary`: win ratio estimates, confidence limits, variance estimates, and p-values for each simulated trial. * `df_Total_probability`: treatment win, tie, and control win probabilities. * `df_sample.size.summary`: treatment and control sample sizes generated under the requested randomization ratio. ## Estimating power For a one-sided superiority analysis at level 0.025, one common summary is the proportion of simulated trials with a significant result. Exact binomial confidence intervals can be calculated with `binom.conf.exact()`. ```{r power-summary} fs_success <- quick_res$df_FS.analysis.summary$p_value_FS < 0.025 wr_success <- quick_res$df_WR.analysis.summary$LB_R_w > 1 data.frame( Method = c("FS test", "YG win ratio test"), Estimated_power = c( mean(fs_success, na.rm = TRUE), mean(wr_success, na.rm = TRUE) ) ) binom.conf.exact( x = sum(wr_success, na.rm = TRUE), n = sum(!is.na(wr_success)) ) ``` This small example is intended only to show the workflow. Power estimates from two simulations are not scientifically meaningful. ## Paper-style simulation workflow The following code mirrors the larger simulation workflow used for the paper. It is not evaluated when this vignette is built because `nsim = 10000` can take substantial time. ```{r paper-workflow, eval = FALSE} library(winratiosim) power.design_parameters <- list( nsim = 10000, N = 400, Randomization.ratio = c(1, 1), alpha.JFM = 0, theta.JFM = 1, lambda_trt = 0.13, lambda_ctl = 0.15, ann.icr_trt = 0.32, ann.icr_ctl = 0.55, xbase_trt = 45, xfinal_trt = 45 + 7.5, sd.delta.x_trt = 20, xbase_ctl = 45, xfinal_ctl = 45, sd.delta.x_ctl = 20, censorrate_trt = 0.2, censorrate_ctl = 0.2, nc = 10, seed = 20250518 ) power.sim_res <- do.call(winratiosim, power.design_parameters) Power_binom_CI_one_sided_FS_Permutation <- binom.conf.exact( x = sum(power.sim_res$df_FS.analysis.summary$p_value_FS < 0.025, na.rm = TRUE), n = sum(!is.na(power.sim_res$df_FS.analysis.summary$p_value_FS)) ) Power_binom_CI_one_sided_WR_Ron_Yu <- binom.conf.exact( x = sum(power.sim_res$df_WR.analysis.summary$LB_R_w > 1, na.rm = TRUE), n = sum(!is.na(power.sim_res$df_WR.analysis.summary$LB_R_w)) ) t1e.design_parameters <- list( nsim = power.design_parameters$nsim, N = power.design_parameters$N, Randomization.ratio = power.design_parameters$Randomization.ratio, alpha.JFM = power.design_parameters$alpha.JFM, theta.JFM = power.design_parameters$theta.JFM, lambda_trt = power.design_parameters$lambda_ctl, lambda_ctl = power.design_parameters$lambda_ctl, ann.icr_trt = power.design_parameters$ann.icr_ctl, ann.icr_ctl = power.design_parameters$ann.icr_ctl, xbase_trt = power.design_parameters$xbase_ctl, xfinal_trt = power.design_parameters$xfinal_ctl, sd.delta.x_trt = power.design_parameters$sd.delta.x_trt, xbase_ctl = power.design_parameters$xbase_ctl, xfinal_ctl = power.design_parameters$xfinal_ctl, sd.delta.x_ctl = power.design_parameters$sd.delta.x_ctl, censorrate_trt = power.design_parameters$censorrate_trt, censorrate_ctl = power.design_parameters$censorrate_ctl, nc = power.design_parameters$nc, seed = 20250518 ) t1e.sim_res <- do.call(winratiosim, t1e.design_parameters) t1e_binom_CI_one_sided_FS_Permutation <- binom.conf.exact( x = sum(t1e.sim_res$df_FS.analysis.summary$p_value_FS < 0.025, na.rm = TRUE), n = sum(!is.na(t1e.sim_res$df_FS.analysis.summary$p_value_FS)) ) t1e_binom_CI_one_sided_WR_Ron_Yu <- binom.conf.exact( x = sum(t1e.sim_res$df_WR.analysis.summary$LB_R_w > 1, na.rm = TRUE), n = sum(!is.na(t1e.sim_res$df_WR.analysis.summary$LB_R_w)) ) df.power.type1 <- data.frame( Method = c("FS test", "YG test"), Power = paste( round(c(Power_binom_CI_one_sided_FS_Permutation[1], Power_binom_CI_one_sided_WR_Ron_Yu[1]), 3), "(", round(c(Power_binom_CI_one_sided_FS_Permutation[2], Power_binom_CI_one_sided_WR_Ron_Yu[2]), 3), ", ", round(c(Power_binom_CI_one_sided_FS_Permutation[3], Power_binom_CI_one_sided_WR_Ron_Yu[3]), 3), ")", sep = "" ), Type_I_Error = paste( round(c(t1e_binom_CI_one_sided_FS_Permutation[1], t1e_binom_CI_one_sided_WR_Ron_Yu[1]), 3), "(", round(c(t1e_binom_CI_one_sided_FS_Permutation[2], t1e_binom_CI_one_sided_WR_Ron_Yu[2]), 3), ", ", round(c(t1e_binom_CI_one_sided_FS_Permutation[3], t1e_binom_CI_one_sided_WR_Ron_Yu[3]), 3), ")", sep = "" ) ) df.variance <- data.frame( Median_Variance_under_Power = c( median(power.sim_res$df_WR.analysis.summary$variance_log_R_w_permutation, na.rm = TRUE), median(power.sim_res$df_WR.analysis.summary$Var_logR_w, na.rm = TRUE) ), Median_Variance_under_Type_I_Error = c( median(t1e.sim_res$df_WR.analysis.summary$variance_log_R_w_permutation, na.rm = TRUE), median(t1e.sim_res$df_WR.analysis.summary$Var_logR_w, na.rm = TRUE) ) ) df.combined <- cbind(df.power.type1, round(df.variance, 4)) df.combined median(power.sim_res$df_WR.analysis.summary$R_w, na.rm = TRUE) median(power.sim_res$df_Total_probability[, "Prob_of_tie"], na.rm = TRUE) ``` ## Output from the 10,000-simulation run When the paper-style workflow above is run with `nsim = 10000`, `N = 400`, `nc = 10`, and `seed = 20250518`, the final summary commands produce the following output. The full simulation is not rerun during vignette building. ```r df.power.type1 #> Method Power Type_I_Error #> 1 FS test 0.86(0.853, 0.866) 0.024(0.021, 0.028) #> 2 YG test 0.811(0.803, 0.819) 0.018(0.015, 0.021) df.variance #> Median_Variance_under_Power Median_Variance_under_Type_I_Error #> 1 0.01677787 0.01607165 #> 2 0.01969007 0.01882680 median(power.sim_res$df_WR.analysis.summary$R_w, na.rm = TRUE) #> [1] 1.474161 median(power.sim_res$df_Total_probability[, "Prob_of_tie"], na.rm = TRUE) #> [1] 0.191 ``` ## Parameter notes * `lambda_trt` and `lambda_ctl` are annual mortality probabilities. * `ann.icr_trt` and `ann.icr_ctl` are annual recurrent event incidence rates. * `xbase_*` and `xfinal_*` define the mean continuous outcome change in each arm. * `censorrate_*` gives the annual censoring probability. * `nc` controls the number of worker processes. Use `nc = 1` when debugging. * `seed` makes the simulation reproducible. ## References Lee, S. Y. (2025). A note on the sample size formula for a win ratio endpoint. *Statistics in Medicine*, 44, e70165. Finkelstein, D. M., and Schoenfeld, D. A. (1999). Combining mortality and longitudinal measures in clinical trials. *Statistics in Medicine*, 18(11), 1341-1354. Pocock, S. J., Ariti, C. A., Collier, T. J., and Wang, D. (2012). The win ratio: a new approach to the analysis of composite endpoints in clinical trials based on clinical priorities. *European Heart Journal*, 33(2), 176-182. Yu, R. X., and Ganju, J. (2022). Sample size formula for a win ratio endpoint. *Statistics in Medicine*, 41(6), 950-963.