From 7f9a4e8dcaf79011e124ea9e7a89d93c34d19a29 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Fri, 10 Jul 2026 00:56:39 -0400 Subject: [PATCH] Replace gt with lt for table output Replace the gt package dependency with the lightweight lt package for all table output, mirroring Merck/gsDesign2#629. - Add lt.gsBinomialExactTable() S3 method (R/as_lt.R) and deprecate as_gt() in favor of lt::lt(). - Swap gt for lt in DESCRIPTION Imports; add Remotes: yihui/lt pending the next lt release on CRAN. - Migrate all table-bearing vignettes to lt, dropping the bundled html_vignette CSS in favor of a minimal vignettes/vignette.css. - Update tests, docs, and _pkgdown.yml accordingly. Co-Authored-By: Claude Opus 4.8 --- DESCRIPTION | 4 +- NAMESPACE | 9 +- NEWS.md | 9 ++ R/as_gt.R | 83 ++----------- R/as_lt.R | 72 +++++++++++ R/as_table.R | 2 +- R/globals.R | 2 +- _pkgdown.yml | 2 +- man/as_gt.Rd | 66 +--------- man/as_table.Rd | 2 +- man/lt-methods.Rd | 76 ++++++++++++ .../testthat/_snaps/independent-test-as_gt.md | 26 ---- tests/testthat/test-independent-test-as_gt.R | 76 ------------ tests/testthat/test-independent-test-lt.R | 48 ++++++++ vignettes/ConditionalErrorSpending.Rmd | 75 +++++------ vignettes/MultiSeasonRareEvents.Rmd | 90 +++++++------- vignettes/PoissonMixtureModel.Rmd | 10 +- vignettes/VaccineEfficacy.Rmd | 116 +++++++++--------- vignettes/binomialSPRTExample.Rmd | 10 +- vignettes/binomialTwoSample.Rmd | 66 +++++----- vignettes/gsSurvBasicExamples.Rmd | 46 +++---- vignettes/vignette.css | 20 +++ 22 files changed, 461 insertions(+), 449 deletions(-) create mode 100644 R/as_lt.R create mode 100644 man/lt-methods.Rd delete mode 100644 tests/testthat/_snaps/independent-test-as_gt.md delete mode 100644 tests/testthat/test-independent-test-as_gt.R create mode 100644 tests/testthat/test-independent-test-lt.R create mode 100644 vignettes/vignette.css diff --git a/DESCRIPTION b/DESCRIPTION index c96d6bdb..29d52db4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,7 +22,7 @@ Imports: dplyr (>= 1.1.0), ggplot2 (>= 3.1.1), graphics, - gt, + lt, methods, r2rtf, rlang, @@ -44,6 +44,8 @@ Suggests: testthat, utils, vdiffr +Remotes: + yihui/lt VignetteBuilder: knitr Config/roxygen2/version: 8.0.0 diff --git a/NAMESPACE b/NAMESPACE index ff9aba52..38cac007 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,9 +1,9 @@ # Generated by roxygen2: do not edit by hand -S3method(as_gt,gsBinomialExactTable) S3method(as_rtf,gsBinomialExactTable) S3method(as_rtf,gsBoundSummary) S3method(as_table,gsBinomialExact) +S3method(lt::lt,gsBinomialExactTable) S3method(plot,binomialSPRT) S3method(plot,gsBinomialExact) S3method(plot,gsDesign) @@ -140,13 +140,6 @@ importFrom(graphics,plot) importFrom(graphics,points) importFrom(graphics,strwidth) importFrom(graphics,text) -importFrom(gt,cols_label) -importFrom(gt,fmt_number) -importFrom(gt,fmt_percent) -importFrom(gt,gt) -importFrom(gt,html) -importFrom(gt,tab_header) -importFrom(gt,tab_spanner) importFrom(methods,is) importFrom(r2rtf,rtf_body) importFrom(r2rtf,rtf_colheader) diff --git a/NEWS.md b/NEWS.md index 24f1e0ed..16b8662d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,14 @@ # gsDesign (development version) +## Major changes + +- The (heavy) **gt** dependency has been replaced with the lightweight **lt** + package. The `as_gt()` function is deprecated in favor of `lt::lt()`. An S3 + method for `lt()` is provided for `gsBinomialExactTable` objects, and all + vignettes now use lt functions (`lt()`, `lt_header()`, `lt_spanner()`, + `lt_format()`, `lt_footnote()`, `lt_note()`, `lt_label()`, `lt_align()`, + `lt_group()`). There are no significant visual changes in the HTML tables. + # gsDesign 3.10.0 ## New features diff --git a/R/as_gt.R b/R/as_gt.R index 08113cf6..6c6cad02 100644 --- a/R/as_gt.R +++ b/R/as_gt.R @@ -1,82 +1,17 @@ -#' Convert a summary table object to a gt object +#' Deprecated: use lt() instead #' -#' Convert a summary table object created with \code{\link{as_table}} -#' to a \code{gt_tbl} object; currently only implemented for -#' \code{\link{gsBinomialExact}}. +#' \code{as_gt()} is deprecated; use \code{\link[lt]{lt}()} instead. #' #' @param x Object to be converted. -#' @param ... Other parameters that may be specific to the object. +#' @param ... Additional arguments passed to \code{\link[lt]{lt}()}. #' -#' @return A \code{gt_tbl} object that may be extended by overloaded versions of -#' \code{\link{as_gt}}. +#' @return An \code{lt_tbl} object. #' -#' @seealso \code{vignette("binomialSPRTExample")} -#' -#' @details -#' Currently only implemented for \code{\link{gsBinomialExact}} objects. -#' Creates a table to summarize an object. -#' For \code{\link{gsBinomialExact}}, this summarized operating characteristics -#' across a range of effect sizes. -#' -#' @export -#' -#' @examples -#' safety_design <- binomialSPRT( -#' p0 = .04, p1 = .1, alpha = .04, beta = .2, minn = 4, maxn = 75 -#' ) -#' safety_power <- gsBinomialExact( -#' k = length(safety_design$n.I), -#' theta = seq(.02, .16, .02), -#' n.I = safety_design$n.I, -#' a = safety_design$lower$bound, -#' b = safety_design$upper$bound -#' ) -#' safety_power |> -#' as_table() |> -#' as_gt( -#' theta_label = gt::html("Underlying
AE rate"), -#' prob_decimals = 3, -#' bound_label = c("low rate", "high rate") -#' ) -as_gt <- function(x, ...) UseMethod("as_gt") - -#' @rdname as_gt -#' -#' @param title Table title. -#' @param subtitle Table subtitle. -#' @param theta_label Label for theta. -#' @param bound_label Label for bounds. -#' @param prob_decimals Number of decimal places for probability of crossing. -#' @param en_decimals Number of decimal places for expected number of -#' observations when bound is crossed or when trial ends without crossing. -#' @param rr_decimals Number of decimal places for response rates. -#' -#' @importFrom gt gt tab_spanner cols_label html fmt_number fmt_percent tab_header +#' @seealso \code{\link{lt-methods}} #' #' @export -as_gt.gsBinomialExactTable <- function( - x, - ..., - title = "Operating Characteristics for the Truncated SPRT Design", - subtitle = "Assumes trial evaluated sequentially after each response", - theta_label = html("Underlying
response rate"), - bound_label = c("Futility bound", "Efficacy bound"), - prob_decimals = 2, - en_decimals = 1, - rr_decimals = 0) { - out_gt <- x |> - gt() |> - tab_spanner(label = "Probability of crossing", columns = c(Lower, Upper)) |> - cols_label( - theta = theta_label, - Lower = bound_label[1], - Upper = bound_label[2], - en = html("Average
sample size") - ) |> - fmt_number(columns = c(Lower, Upper), decimals = prob_decimals) |> - fmt_number(columns = en, decimals = en_decimals) |> - fmt_percent(columns = theta, decimals = rr_decimals) |> - tab_header(title = title, subtitle = subtitle) - - out_gt +as_gt <- function(x, ...) { + .Deprecated("lt", package = "lt", + msg = "as_gt() is deprecated; please use lt::lt() instead.") + lt::lt(x, ...) } diff --git a/R/as_lt.R b/R/as_lt.R new file mode 100644 index 00000000..b5c393de --- /dev/null +++ b/R/as_lt.R @@ -0,0 +1,72 @@ +#' Convert a summary table object to an lt table +#' +#' Convert a summary table object created with \code{\link{as_table}} +#' to an \code{lt_tbl} object; currently only implemented for +#' \code{\link{gsBinomialExact}}. +#' +#' @param data Object to be converted. +#' @param ... Other parameters that may be specific to the object. +#' @param title Table title. +#' @param subtitle Table subtitle. +#' @param theta_label Label for theta. +#' @param bound_label Label for bounds. +#' @param prob_decimals Number of decimal places for probability of crossing. +#' @param en_decimals Number of decimal places for expected number of +#' observations when bound is crossed or when trial ends without crossing. +#' @param rr_decimals Number of decimal places for response rates. +#' +#' @return An \code{lt_tbl} object. +#' +#' @seealso \code{vignette("binomialSPRTExample")} +#' +#' @details +#' Currently only implemented for \code{\link{gsBinomialExact}} objects. +#' Creates a table to summarize an object. +#' For \code{\link{gsBinomialExact}}, this summarized operating characteristics +#' across a range of effect sizes. +#' +#' @name lt-methods +#' +#' @examples +#' safety_design <- binomialSPRT( +#' p0 = .04, p1 = .1, alpha = .04, beta = .2, minn = 4, maxn = 75 +#' ) +#' safety_power <- gsBinomialExact( +#' k = length(safety_design$n.I), +#' theta = seq(.02, .16, .02), +#' n.I = safety_design$n.I, +#' a = safety_design$lower$bound, +#' b = safety_design$upper$bound +#' ) +#' safety_power |> +#' as_table() |> +#' lt::lt( +#' theta_label = I("Underlying
AE rate"), +#' prob_decimals = 3, +#' bound_label = c("low rate", "high rate") +#' ) +#' +#' @exportS3Method lt::lt +lt.gsBinomialExactTable <- function( + data, + ..., + title = "Operating Characteristics for the Truncated SPRT Design", + subtitle = "Assumes trial evaluated sequentially after each response", + theta_label = I("Underlying
response rate"), + bound_label = c("Futility bound", "Efficacy bound"), + prob_decimals = 2, + en_decimals = 1, + rr_decimals = 0) { + lt::lt(as.data.frame(data), ...) |> + lt::lt_spanner(label = "Probability of crossing", columns = c("Lower", "Upper")) |> + lt::lt_label( + theta = theta_label, + Lower = bound_label[1], + Upper = bound_label[2], + en = I("Average
sample size") + ) |> + lt::lt_format(columns = c("Lower", "Upper"), decimals = prob_decimals) |> + lt::lt_format(columns = "en", decimals = en_decimals) |> + lt::lt_format(columns = "theta", decimals = rr_decimals, percent = TRUE) |> + lt::lt_header(title = title, subtitle = subtitle) +} diff --git a/R/as_table.R b/R/as_table.R index d37b94b2..3b48a379 100644 --- a/R/as_table.R +++ b/R/as_table.R @@ -30,7 +30,7 @@ #' ) #' b_power |> #' as_table() |> -#' as_gt() +#' lt::lt() as_table <- function(x, ...) UseMethod("as_table") #' @rdname as_table diff --git a/R/globals.R b/R/globals.R index 08605416..6df05d46 100644 --- a/R/globals.R +++ b/R/globals.R @@ -1,7 +1,7 @@ utils::globalVariables( unique( c( - # From `as_gt.gsBinomialExactTable()` + # From `lt.gsBinomialExactTable()` c("Lower", "Upper", "en", "theta"), # From `binomialPowerTable()` c("pE") diff --git a/_pkgdown.yml b/_pkgdown.yml index e798df10..71cfdb6b 100755 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -131,8 +131,8 @@ reference: contents: - as_table - as_table.gsBinomialExact + - lt-methods - as_gt - - as_gt.gsBinomialExactTable - as_rtf - as_rtf.gsBinomialExactTable diff --git a/man/as_gt.Rd b/man/as_gt.Rd index 09e13150..00b1b2ee 100644 --- a/man/as_gt.Rd +++ b/man/as_gt.Rd @@ -2,77 +2,21 @@ % Please edit documentation in R/as_gt.R \name{as_gt} \alias{as_gt} -\alias{as_gt.gsBinomialExactTable} -\title{Convert a summary table object to a gt object} +\title{Deprecated: use lt() instead} \usage{ as_gt(x, ...) - -\method{as_gt}{gsBinomialExactTable}( - x, - ..., - title = "Operating Characteristics for the Truncated SPRT Design", - subtitle = "Assumes trial evaluated sequentially after each response", - theta_label = html("Underlying
response rate"), - bound_label = c("Futility bound", "Efficacy bound"), - prob_decimals = 2, - en_decimals = 1, - rr_decimals = 0 -) } \arguments{ \item{x}{Object to be converted.} -\item{...}{Other parameters that may be specific to the object.} - -\item{title}{Table title.} - -\item{subtitle}{Table subtitle.} - -\item{theta_label}{Label for theta.} - -\item{bound_label}{Label for bounds.} - -\item{prob_decimals}{Number of decimal places for probability of crossing.} - -\item{en_decimals}{Number of decimal places for expected number of -observations when bound is crossed or when trial ends without crossing.} - -\item{rr_decimals}{Number of decimal places for response rates.} +\item{...}{Additional arguments passed to \code{\link[lt]{lt}()}.} } \value{ -A \code{gt_tbl} object that may be extended by overloaded versions of - \code{\link{as_gt}}. +An \code{lt_tbl} object. } \description{ -Convert a summary table object created with \code{\link{as_table}} -to a \code{gt_tbl} object; currently only implemented for -\code{\link{gsBinomialExact}}. -} -\details{ -Currently only implemented for \code{\link{gsBinomialExact}} objects. -Creates a table to summarize an object. -For \code{\link{gsBinomialExact}}, this summarized operating characteristics -across a range of effect sizes. -} -\examples{ -safety_design <- binomialSPRT( - p0 = .04, p1 = .1, alpha = .04, beta = .2, minn = 4, maxn = 75 -) -safety_power <- gsBinomialExact( - k = length(safety_design$n.I), - theta = seq(.02, .16, .02), - n.I = safety_design$n.I, - a = safety_design$lower$bound, - b = safety_design$upper$bound -) -safety_power |> - as_table() |> - as_gt( - theta_label = gt::html("Underlying
AE rate"), - prob_decimals = 3, - bound_label = c("low rate", "high rate") - ) +\code{as_gt()} is deprecated; use \code{\link[lt]{lt}()} instead. } \seealso{ -\code{vignette("binomialSPRTExample")} +\code{\link{lt-methods}} } diff --git a/man/as_table.Rd b/man/as_table.Rd index 57a8564f..c486ebe5 100644 --- a/man/as_table.Rd +++ b/man/as_table.Rd @@ -35,7 +35,7 @@ b_power <- gsBinomialExact( ) b_power |> as_table() |> - as_gt() + lt::lt() } \seealso{ \code{vignette("binomialSPRTExample")} diff --git a/man/lt-methods.Rd b/man/lt-methods.Rd new file mode 100644 index 00000000..c0f25fe4 --- /dev/null +++ b/man/lt-methods.Rd @@ -0,0 +1,76 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/as_lt.R +\name{lt-methods} +\alias{lt-methods} +\alias{lt.gsBinomialExactTable} +\title{Convert a summary table object to an lt table} +\usage{ +\method{lt}{gsBinomialExactTable}( + data, + ..., + title = "Operating Characteristics for the Truncated SPRT Design", + subtitle = "Assumes trial evaluated sequentially after each response", + theta_label = I("Underlying
response rate"), + bound_label = c("Futility bound", "Efficacy bound"), + prob_decimals = 2, + en_decimals = 1, + rr_decimals = 0 +) +} +\arguments{ +\item{data}{Object to be converted.} + +\item{...}{Other parameters that may be specific to the object.} + +\item{title}{Table title.} + +\item{subtitle}{Table subtitle.} + +\item{theta_label}{Label for theta.} + +\item{bound_label}{Label for bounds.} + +\item{prob_decimals}{Number of decimal places for probability of crossing.} + +\item{en_decimals}{Number of decimal places for expected number of +observations when bound is crossed or when trial ends without crossing.} + +\item{rr_decimals}{Number of decimal places for response rates.} +} +\value{ +An \code{lt_tbl} object. +} +\description{ +Convert a summary table object created with \code{\link{as_table}} +to an \code{lt_tbl} object; currently only implemented for +\code{\link{gsBinomialExact}}. +} +\details{ +Currently only implemented for \code{\link{gsBinomialExact}} objects. +Creates a table to summarize an object. +For \code{\link{gsBinomialExact}}, this summarized operating characteristics +across a range of effect sizes. +} +\examples{ +safety_design <- binomialSPRT( + p0 = .04, p1 = .1, alpha = .04, beta = .2, minn = 4, maxn = 75 +) +safety_power <- gsBinomialExact( + k = length(safety_design$n.I), + theta = seq(.02, .16, .02), + n.I = safety_design$n.I, + a = safety_design$lower$bound, + b = safety_design$upper$bound +) +safety_power |> + as_table() |> + lt::lt( + theta_label = I("Underlying
AE rate"), + prob_decimals = 3, + bound_label = c("low rate", "high rate") + ) + +} +\seealso{ +\code{vignette("binomialSPRTExample")} +} diff --git a/tests/testthat/_snaps/independent-test-as_gt.md b/tests/testthat/_snaps/independent-test-as_gt.md deleted file mode 100644 index a9dfc5d7..00000000 --- a/tests/testthat/_snaps/independent-test-as_gt.md +++ /dev/null @@ -1,26 +0,0 @@ -# Snapshot test for gsBinomialExact safety table - - \begin{table}[t] - \caption*{ - {\fontsize{20}{25}\selectfont Operating Characteristics for the Truncated SPRT Design\fontsize{12}{15}\selectfont } \\ - {\fontsize{14}{17}\selectfont Assumes trial evaluated sequentially after each response\fontsize{12}{15}\selectfont } - } - \fontsize{12.0pt}{14.0pt}\selectfont - \begin{tabular*}{\linewidth}{@{\extracolsep{\fill}}rrrr} - \toprule - & \multicolumn{2}{c}{{Probability of crossing}} & \\ - \cmidrule(lr){2-3} - Underlying
AE rate & low rate & high rate & Average
sample size \\ - \midrule\addlinespace[2.5pt] - 2\% & 0.964 & 0.001 & 34.8 \\ - 4\% & 0.769 & 0.019 & 46.4 \\ - 6\% & 0.506 & 0.108 & 54.3 \\ - 8\% & 0.291 & 0.290 & 56.1 \\ - 10\% & 0.155 & 0.516 & 52.8 \\ - 12\% & 0.079 & 0.714 & 46.8 \\ - 14\% & 0.039 & 0.851 & 40.2 \\ - 16\% & 0.020 & 0.930 & 34.2 \\ - \bottomrule - \end{tabular*} - \end{table} - diff --git a/tests/testthat/test-independent-test-as_gt.R b/tests/testthat/test-independent-test-as_gt.R deleted file mode 100644 index 0aa53165..00000000 --- a/tests/testthat/test-independent-test-as_gt.R +++ /dev/null @@ -1,76 +0,0 @@ -test_that("Snapshot test for gsBinomialExact safety table", { - skip_on_cran() - - gt_to_latex <- function(data) cat(as.character(gt::as_latex(data))) - - safety_design <- binomialSPRT(p0 = 0.04, p1 = 0.1, alpha = 0.04, beta = 0.2, minn = 4, maxn = 75) - safety_power <- gsBinomialExact( - k = length(safety_design$n.I), - theta = seq(0.02, 0.16, 0.02), - n.I = safety_design$n.I, - a = safety_design$lower$bound, - b = safety_design$upper$bound - ) - output <- safety_power |> - as_table() |> - as_gt( - theta_label = gt::html("Underlying
AE rate"), - prob_decimals = 3, - bound_label = c("low rate", "high rate") - ) - - expect_s3_class(output, "gt_tbl") - - local_edition(3) - expect_snapshot_output(gt_to_latex(output)) -}) - -test_that("Number of set decimals for `Probability of crossing` are respected", { - safety_design <- binomialSPRT(p0 = 0.04, p1 = 0.1, alpha = 0.04, beta = 0.2, minn = 4, maxn = 75) - safety_power <- gsBinomialExact( - k = length(safety_design$n.I), - theta = seq(0.02, 0.16, 0.02), - n.I = safety_design$n.I, - a = safety_design$lower$bound, - b = safety_design$upper$bound - ) - output <- safety_power |> - as_table() |> - as_gt( - theta_label = gt::html("Underlying
AE rate"), - prob_decimals = 3, - bound_label = c("low rate", "high rate") - ) - - # Get formats where columns include "Lower" or "Upper" - prob_format <- Filter(function(x) any(x$columns %in% c("Lower", "Upper")), output[["_formats"]]) - - # Get formats where columns include "theta" - rr_format <- Filter(function(x) any(x$columns == "theta"), output[["_formats"]]) - - expect_true(all(sapply(prob_format, function(x) x$decimals == 3))) - expect_true(all(sapply(rr_format, function(x) x$decimals == 1))) -}) - -test_that("We can set custom title and subtitles", { - safety_design <- binomialSPRT(p0 = 0.04, p1 = 0.1, alpha = 0.04, beta = 0.2, minn = 4, maxn = 75) - safety_power <- gsBinomialExact( - k = length(safety_design$n.I), - theta = seq(0.02, 0.16, 0.02), - n.I = safety_design$n.I, - a = safety_design$lower$bound, - b = safety_design$upper$bound - ) - output <- safety_power |> - as_table() |> - as_gt( - title = "Custom Title", - subtitle = "Custom Subtitle", - theta_label = gt::html("Underlying
AE rate"), - prob_decimals = 3, - bound_label = c("low rate", "high rate") - ) - - expect_equal(output[["_heading"]]$title, "Custom Title") - expect_equal(output[["_heading"]]$subtitle, "Custom Subtitle") -}) diff --git a/tests/testthat/test-independent-test-lt.R b/tests/testthat/test-independent-test-lt.R new file mode 100644 index 00000000..862fe255 --- /dev/null +++ b/tests/testthat/test-independent-test-lt.R @@ -0,0 +1,48 @@ +safety_lt <- function(...) { + safety_design <- binomialSPRT(p0 = 0.04, p1 = 0.1, alpha = 0.04, beta = 0.2, minn = 4, maxn = 75) + safety_power <- gsBinomialExact( + k = length(safety_design$n.I), + theta = seq(0.02, 0.16, 0.02), + n.I = safety_design$n.I, + a = safety_design$lower$bound, + b = safety_design$upper$bound + ) + safety_power |> + as_table() |> + lt::lt(...) +} + +# Number formatting operations recorded on an lt_tbl for the given columns. +fmt_for <- function(x, cols) { + Filter( + function(op) op$type == "fmt_number" && any(op$columns %in% cols), + x$ops + ) +} + +test_that("Number of set decimals for `Probability of crossing` are respected", { + output <- safety_lt( + theta_label = I("Underlying
AE rate"), + prob_decimals = 3, + bound_label = c("low rate", "high rate") + ) + + prob_format <- fmt_for(output, c("Lower", "Upper")) + rr_format <- fmt_for(output, "theta") + + expect_true(all(vapply(prob_format, function(x) x$decimals == 3, logical(1)))) + expect_true(all(vapply(rr_format, function(x) x$decimals == 0, logical(1)))) +}) + +test_that("We can set custom title and subtitles", { + output <- safety_lt( + title = "Custom Title", + subtitle = "Custom Subtitle", + theta_label = I("Underlying
AE rate"), + prob_decimals = 3, + bound_label = c("low rate", "high rate") + ) + + expect_equal(output$header$title, "Custom Title") + expect_equal(output$header$subtitle, "Custom Subtitle") +}) diff --git a/vignettes/ConditionalErrorSpending.Rmd b/vignettes/ConditionalErrorSpending.Rmd index eef6875b..65b20a43 100755 --- a/vignettes/ConditionalErrorSpending.Rmd +++ b/vignettes/ConditionalErrorSpending.Rmd @@ -1,6 +1,8 @@ --- title: "Conditional error spending functions" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + css: vignette.css bibliography: gsDesign.bib vignette: > %\VignetteIndexEntry{Conditional error spending functions} @@ -53,7 +55,7 @@ We also compare results to other commonly used spending functions. library(gsDesign) library(tibble) library(dplyr) -library(gt) +library(lt) ``` ### Method 1 @@ -247,21 +249,22 @@ xx <- rbind( transpose_df(ce(x1.8)) |> mutate(gamma = "gamma = 0.8") ) xx |> - gt(groupname_col = "gamma") |> - tab_spanner(label = "Analysis", columns = 2:5) |> - fmt_number(columns = 2:5, decimals = 3) |> - tab_options(data_row.padding = px(1)) |> - tab_header( + lt() |> + lt_group(~ gamma, sep = TRUE, sort = FALSE) |> + lt_spanner(label = "Analysis", columns = 2:5) |> + lt_format(columns = 2:5, decimals = 3) |> + lt_label(rowname = "") |> + lt_header( title = "Xi-Gallo, Method 1 Spending Function", subtitle = "Conditional Error Spending Functions" ) |> - tab_footnote( - footnote = "Conditional Error not accounting for future interim bounds.", - locations = cells_stub(rows = seq(2, 20, 3)) + lt_footnote( + "Conditional Error not accounting for future interim bounds.", + where = "body", columns = "rowname", rows = seq(2, 20, 3) ) |> - tab_footnote( - footnote = "CE = Conditional Error accounting for all analyses.", - locations = cells_stub(rows = seq(3, 21, 3)) + lt_footnote( + "CE = Conditional Error accounting for all analyses.", + where = "body", columns = "rowname", rows = seq(3, 21, 3) ) ``` @@ -288,19 +291,20 @@ xx <- rbind( transpose_df(ce(x1.8)) |> mutate(gamma = "gamma = 0.8") ) xx |> - gt(groupname_col = "gamma") |> - tab_spanner(label = "Analysis", columns = 2:5) |> - fmt_number(columns = 2:5, decimals = 3) |> - tab_options(data_row.padding = px(1)) |> - tab_footnote( - footnote = "Conditional Error not accounting for future interim bounds.", - locations = cells_stub(rows = seq(2, 20, 3)) + lt() |> + lt_group(~ gamma, sep = TRUE, sort = FALSE) |> + lt_spanner(label = "Analysis", columns = 2:5) |> + lt_format(columns = 2:5, decimals = 3) |> + lt_label(rowname = "") |> + lt_footnote( + "Conditional Error not accounting for future interim bounds.", + where = "body", columns = "rowname", rows = seq(2, 20, 3) ) |> - tab_footnote( - footnote = "CE = Conditional Error accounting for all analyses.", - locations = cells_stub(rows = seq(3, 21, 3)) + lt_footnote( + "CE = Conditional Error accounting for all analyses.", + where = "body", columns = "rowname", rows = seq(3, 21, 3) ) |> - tab_header( + lt_header( title = "Xi-Gallo, Method 2 Spending Function", subtitle = "Conditional Error Spending Functions" ) @@ -353,19 +357,20 @@ xx <- rbind( transpose_df(ce(x3.05)) |> mutate(gamma = "gamma = 0.05 ") ) xx |> - gt(groupname_col = "gamma") |> - tab_spanner(label = "Analysis", columns = 2:5) |> - fmt_number(columns = 2:5, decimals = 3) |> - tab_options(data_row.padding = px(1)) |> - tab_footnote( - footnote = "Conditional Error not accounting for future interim bounds.", - locations = cells_stub(rows = seq(2, 11, 3)) + lt() |> + lt_group(~ gamma, sep = TRUE, sort = FALSE) |> + lt_spanner(label = "Analysis", columns = 2:5) |> + lt_format(columns = 2:5, decimals = 3) |> + lt_label(rowname = "") |> + lt_footnote( + "Conditional Error not accounting for future interim bounds.", + where = "body", columns = "rowname", rows = seq(2, 11, 3) ) |> - tab_footnote( - footnote = "CE = Conditional Error accounting for all analyses.", - locations = cells_stub(rows = seq(3, 12, 3)) + lt_footnote( + "CE = Conditional Error accounting for all analyses.", + where = "body", columns = "rowname", rows = seq(3, 12, 3) ) |> - tab_header( + lt_header( title = "Xi-Gallo, Method 3 Spending Function", subtitle = "Conditional Error Spending Functions" ) diff --git a/vignettes/MultiSeasonRareEvents.Rmd b/vignettes/MultiSeasonRareEvents.Rmd index 2e05ea04..897a0ce2 100644 --- a/vignettes/MultiSeasonRareEvents.Rmd +++ b/vignettes/MultiSeasonRareEvents.Rmd @@ -1,6 +1,8 @@ --- title: "Multi-season studies for rare events" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + css: vignette.css bibliography: gsDesign.bib vignette: > %\VignetteIndexEntry{Multi-season studies for rare events} @@ -25,7 +27,7 @@ options(width = 58) ```{r, message=FALSE, warning=FALSE} library(gsDesign) -library(gt) +library(lt) library(tibble) ``` @@ -249,46 +251,46 @@ tibble( `Achieved cumulative alpha spend` = achieved_alpha_spend, `Cumulative power under H1` = achieved_power_h1 ) |> - gt() |> - fmt_number(columns = 2, decimals = 3) |> - fmt_percent(columns = c(`VE at bound (efficacy)`, `VE at bound (futility)`), decimals = 1) |> - fmt_number( + lt() |> + lt_format(columns = 2, decimals = 3) |> + lt_format(columns = c("VE at bound (efficacy)", "VE at bound (futility)"), decimals = 1, percent = TRUE) |> + lt_format( columns = c( - `Nominal 1-sided p at bound (efficacy)`, - `Nominal 1-sided p at bound (futility)`, - `Target cumulative alpha spend`, - `Achieved cumulative alpha spend`, - `Cumulative power under H1` + "Nominal 1-sided p at bound (efficacy)", + "Nominal 1-sided p at bound (futility)", + "Target cumulative alpha spend", + "Achieved cumulative alpha spend", + "Cumulative power under H1" ), decimals = 4 ) |> - tab_spanner( + lt_spanner( label = "Efficacy", columns = c( - `Exact efficacy bound (x <= a)`, - `VE at bound (efficacy)`, - `Nominal 1-sided p at bound (efficacy)` + "Exact efficacy bound (x <= a)", + "VE at bound (efficacy)", + "Nominal 1-sided p at bound (efficacy)" ) ) |> - tab_spanner( + lt_spanner( label = "Futility", columns = c( - `Exact futility bound (x >= b)`, - `VE at bound (futility)`, - `Nominal 1-sided p at bound (futility)` + "Exact futility bound (x >= b)", + "VE at bound (futility)", + "Nominal 1-sided p at bound (futility)" ) ) |> - tab_header( + lt_header( title = "Planned exact binomial seasonal monitoring", subtitle = "Super-superiority example with Pocock-like efficacy spending" ) |> - tab_footnote( - footnote = "x denotes cumulative observed events in the experimental arm; efficacy is established when x is at or below the listed efficacy bound.", - locations = cells_column_labels(columns = `Exact efficacy bound (x <= a)`) + lt_footnote( + "x denotes cumulative observed events in the experimental arm; efficacy is established when x is at or below the listed efficacy bound.", + where = "column", columns = "Exact efficacy bound (x <= a)" ) |> - tab_footnote( - footnote = "Blank futility entries indicate no futility stopping boundary at that analysis.", - locations = cells_column_labels(columns = `Exact futility bound (x >= b)`) + lt_footnote( + "Blank futility entries indicate no futility stopping boundary at that analysis.", + where = "column", columns = "Exact futility bound (x >= b)" ) ``` @@ -315,8 +317,8 @@ dplyr::bind_rows( `Cumulative planned enrollment` = sum(enrollment_table$`Total planned enrollment`) ) ) |> - gt() |> - tab_header(title = "Planned enrollment by season and overall") + lt() |> + lt_header(title = "Planned enrollment by season and overall") ``` ## Example repeated and sequential p-values @@ -390,8 +392,8 @@ tibble( `Updated futility bound, default spending` = update_exact$upper$bound, `Updated futility bound, maxSpend=TRUE` = update_exact_full$upper$bound ) |> - gt() |> - tab_header(title = "Updated exact bounds using observedEvents") + lt() |> + lt_header(title = "Updated exact bounds using observedEvents") ``` ## Lightweight runnable simulation @@ -439,16 +441,16 @@ oc <- sim_light$summary |> ) oc |> - gt() |> - fmt_number(columns = 2:5, decimals = 4) |> - fmt_number(columns = 6:8, decimals = 2) |> - tab_header( + lt() |> + lt_format(columns = 2:5, decimals = 4) |> + lt_format(columns = 6:8, decimals = 2) |> + lt_header( title = "Lightweight simulation results", subtitle = "Exact-binomial monitoring with seasonal analyses" ) |> - tab_footnote( - footnote = "For VE=30% scenarios, efficacy crossing probability is Type I error under the non-binding futility convention (futility crossings do not block later efficacy crossings).", - locations = cells_column_labels(columns = `Efficacy crossing probability`) + lt_footnote( + "For VE=30% scenarios, efficacy crossing probability is Type I error under the non-binding futility convention (futility crossings do not block later efficacy crossings).", + where = "column", columns = "Efficacy crossing probability" ) ``` @@ -516,16 +518,16 @@ tibble( low$mean_looks[low$adaptive & low$scenario == "H1 (VE=80%)"] ) ) |> - gt() |> - fmt_number(columns = 2:3, decimals = 4) |> - fmt_number(columns = 4:6, decimals = 2) |> - tab_header( + lt() |> + lt_format(columns = 2:3, decimals = 4) |> + lt_format(columns = 4:6, decimals = 2) |> + lt_header( title = "Lower-than-planned event rate illustration", subtitle = "Adaptive approach increases enrollment to recover information" ) |> - tab_footnote( - footnote = "Type I error rows use non-binding futility for efficacy crossing probability; futility stopping probability is shown separately.", - locations = cells_column_labels(columns = `Efficacy crossing probability`) + lt_footnote( + "Type I error rows use non-binding futility for efficacy crossing probability; futility stopping probability is shown separately.", + where = "column", columns = "Efficacy crossing probability" ) ``` diff --git a/vignettes/PoissonMixtureModel.Rmd b/vignettes/PoissonMixtureModel.Rmd index d3c3e926..f1f39f02 100644 --- a/vignettes/PoissonMixtureModel.Rmd +++ b/vignettes/PoissonMixtureModel.Rmd @@ -1,6 +1,8 @@ --- title: "A cure model calendar-based design" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + css: vignette.css bibliography: gsDesign.bib vignette: > %\VignetteIndexEntry{A cure model calendar-based design} @@ -26,7 +28,7 @@ library(gsDesign) library(dplyr) library(tibble) library(ggplot2) -library(gt) +library(lt) ``` ## Introduction @@ -335,8 +337,8 @@ design_calendar <- ) design_calendar |> gsBoundSummary(exclude = c("B-value", "CP", "CP H1", "PP")) |> - gt() |> - tab_header( + lt() |> + lt_header( title = "Calendar-Based Design", subtitle = "Calendar Spending" ) diff --git a/vignettes/VaccineEfficacy.Rmd b/vignettes/VaccineEfficacy.Rmd index beb7ae99..df81ffa4 100644 --- a/vignettes/VaccineEfficacy.Rmd +++ b/vignettes/VaccineEfficacy.Rmd @@ -1,6 +1,8 @@ --- title: "Vaccine efficacy trial design" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + css: vignette.css bibliography: gsDesign.bib vignette: > %\VignetteIndexEntry{Vaccine efficacy trial design} @@ -25,7 +27,7 @@ options(width = 58) ```{r, message=FALSE, warning=FALSE} library(gsDesign) -library(gt) +library(lt) ``` ## Introduction @@ -95,9 +97,8 @@ We also translate several vaccine efficacy values to proportion of events in the ve <- c(.5, .6, .65, .7, .75, .8) prob_experimental <- ratio / (ratio + 1 / (1 - ve)) tibble::tibble(VE = ve, "P(Experimental)" = prob_experimental) |> - gt() |> - tab_options(data_row.padding = px(1)) |> - fmt_number(columns = 2, decimals = 3) + lt() |> + lt_format(columns = 2, decimals = 3) ``` @@ -178,12 +179,11 @@ gsBoundSummary(xx, tdigits = 1, logdelta = TRUE, deltaname = "HR", Nname = "Events", exclude = c("B-value", "CP", "CP H1", "PP") ) |> - gt() |> - tab_header( + lt() |> + lt_header( title = "Initial group sequential approximation", subtitle = "Integer event counts at analyses" - ) |> - tab_options(data_row.padding = px(1)) + ) ``` A textual summary for the design is: @@ -247,39 +247,39 @@ veTable <- function(xbDesign, tteDesign, ve) { ```{r, echo=FALSE} veTable(xb, x, ve) |> - gt() |> - fmt_number(columns = 2, decimals = 1) |> - fmt_number(columns = c(7:8, 11:16), decimals = 2) |> - fmt_number(columns = 9:10, decimals = 4) |> - tab_spanner(label = "Experimental Cases at Bound", columns = 5:6, id = "cases") |> - tab_spanner(label = "Power by Vaccine Efficacy", columns = 11:16, id = "power") |> - tab_spanner(label = "Error Spending", columns = 9:10, id = "spend") |> - tab_spanner(label = "Vaccine Efficacy at Bound", columns = 7:8, id = "vebound") |> - cols_label( + lt() |> + lt_format(columns = 2, decimals = 1) |> + lt_format(columns = c(7:8, 11:16), decimals = 2) |> + lt_format(columns = 9:10, decimals = 4) |> + lt_spanner(label = "Experimental Cases at Bound", columns = c("Success", "Futility")) |> + lt_spanner(label = "Power by Vaccine Efficacy", columns = paste0(ve * 100, "%")) |> + lt_spanner(label = "Error Spending", columns = c("alpha", "beta")) |> + lt_spanner(label = "Vaccine Efficacy at Bound", columns = c("ve_efficacy", "ve_futility")) |> + lt_label( ve_efficacy = "Efficacy", ve_futility = "Futility" ) |> - tab_footnote( - footnote = "Cumulative spending at each analysis", - locations = cells_column_spanners(spanners = "spend") + lt_footnote( + "Cumulative spending at each analysis", + where = "spanner", columns = "Error Spending" ) |> - tab_footnote( - footnote = "Experimental case counts to cross between success and futility counts do not stop trial", - locations = cells_column_spanners(spanners = "cases") + lt_footnote( + "Experimental case counts to cross between success and futility counts do not stop trial", + where = "spanner", columns = "Experimental Cases at Bound" ) |> - tab_footnote( - footnote = "Exact vaccine efficacy required to cross bound", - locations = cells_column_spanners(spanners = "vebound") + lt_footnote( + "Exact vaccine efficacy required to cross bound", + where = "spanner", columns = "Vaccine Efficacy at Bound" ) |> - tab_footnote( - footnote = "Cumulative power at each analysis by underlying vaccine efficacy", - locations = cells_column_spanners(spanners = "power") + lt_footnote( + "Cumulative power at each analysis by underlying vaccine efficacy", + where = "spanner", columns = "Power by Vaccine Efficacy" ) |> - tab_footnote( - footnote = "alpha-spending for efficacy ignores non-binding futility bound", - location = cells_column_labels(columns = alpha) + lt_footnote( + "alpha-spending for efficacy ignores non-binding futility bound", + where = "column", columns = "alpha" ) |> - tab_header("Design Bounds and Operating Characteristics") + lt_header("Design Bounds and Operating Characteristics") ``` The initial approximation of bounds for the exact binomial design was generated from the time-to-event design as follows. First, we computed nominal p-value 1-sided bounds under the null hypothesis for the efficacy bounds using the normal approximation that the time-to-event design used: @@ -423,38 +423,38 @@ We hide the code to produce the table; this is available in package vignette cod beta = as.vector(cumsum(ebUpdate$upper$prob[, 2])) ) out_tab <- cbind(out_tab, power_table) - out_tab |> gt() |> - fmt_number(columns = c(5:6, 9:11), decimals = 2) |> - fmt_number(columns = 7:8, decimals = 4) |> - tab_spanner(label = "Cases at Bound", columns = 3:4, id = "cases") |> - tab_spanner(label = "Power by VE", columns = 9:11, id = "power") |> - tab_spanner(label = "Error Spending", columns = 7:8, id = "spend") |> - tab_spanner(label = "VE at Bound", columns = 5:6, id = "vebound") |> - cols_label( + out_tab |> lt() |> + lt_format(columns = c(5:6, 9:11), decimals = 2) |> + lt_format(columns = 7:8, decimals = 4) |> + lt_spanner(label = "Cases at Bound", columns = c("Success", "Futility")) |> + lt_spanner(label = "Power by VE", columns = paste0(ve * 100, "%")) |> + lt_spanner(label = "Error Spending", columns = c("alpha", "beta")) |> + lt_spanner(label = "VE at Bound", columns = c("ve_efficacy", "ve_futility")) |> + lt_label( ve_efficacy = "Efficacy", ve_futility = "Futility" ) |> - tab_footnote( - footnote = "Cumulative spending at each analysis", - locations = cells_column_spanners(spanners = "spend") + lt_footnote( + "Cumulative spending at each analysis", + where = "spanner", columns = "Error Spending" ) |> - tab_footnote( - footnote = "Experimental case counts; counts between success and futility bounds do not stop trial", - locations = cells_column_spanners(spanners = "cases") + lt_footnote( + "Experimental case counts; counts between success and futility bounds do not stop trial", + where = "spanner", columns = "Cases at Bound" ) |> - tab_footnote( - footnote = "Exact vaccine efficacy required to cross bound", - locations = cells_column_spanners(spanners = "vebound") + lt_footnote( + "Exact vaccine efficacy required to cross bound", + where = "spanner", columns = "VE at Bound" ) |> - tab_footnote( - footnote = "Cumulative power at each analysis by underlying vaccine efficacy", - locations = cells_column_spanners(spanners = "power") + lt_footnote( + "Cumulative power at each analysis by underlying vaccine efficacy", + where = "spanner", columns = "Power by VE" ) |> - tab_footnote( - footnote = "Efficacy spending ignores non-binding futility bound", - location = cells_column_labels(columns = alpha) + lt_footnote( + "Efficacy spending ignores non-binding futility bound", + where = "column", columns = "alpha" ) |> - tab_header(title = "Updated Bounds for Actual Analyses from SPUTNIK trial") + lt_header(title = "Updated Bounds for Actual Analyses from SPUTNIK trial") ``` ## Summary diff --git a/vignettes/binomialSPRTExample.Rmd b/vignettes/binomialSPRTExample.Rmd index deed99a7..72037d47 100644 --- a/vignettes/binomialSPRTExample.Rmd +++ b/vignettes/binomialSPRTExample.Rmd @@ -1,6 +1,8 @@ --- title: "Binomial SPRT" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + css: vignette.css bibliography: "gsDesign.bib" vignette: > %\VignetteIndexEntry{Binomial SPRT} @@ -91,7 +93,7 @@ b_power <- gsBinomialExact( ```{r} b_power |> as_table() |> - as_gt() + lt::lt() ``` ## Safety monitoring example @@ -131,8 +133,8 @@ safety_power <- gsBinomialExact( ) safety_power |> as_table() |> - as_gt( - theta_label = gt::html("Underlying
AE rate"), + lt::lt( + theta_label = I("Underlying
AE rate"), prob_decimals = 3, bound_label = c("low rate", "high rate") ) diff --git a/vignettes/binomialTwoSample.Rmd b/vignettes/binomialTwoSample.Rmd index 9a87124b..1c284ab8 100644 --- a/vignettes/binomialTwoSample.Rmd +++ b/vignettes/binomialTwoSample.Rmd @@ -1,6 +1,8 @@ --- title: "Binomial two arm trial design and analysis" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + css: vignette.css bibliography: "gsDesign.bib" vignette: > %\VignetteIndexEntry{Binomial two arm trial design and analysis} @@ -41,7 +43,7 @@ The R packages we use are: library(gsDesign) library(ggplot2) library(tidyr) -library(gt) +library(lt) library(dplyr) ``` @@ -72,8 +74,8 @@ tibble(scale, "Sample size" = c( nBinomial(p1 = 0.2, p2 = 0.1, ratio = 0.5, alpha = 0.025, beta = 0.15, scale = scale[2]) |> ceiling(), nBinomial(p1 = 0.2, p2 = 0.1, ratio = 0.5, alpha = 0.025, beta = 0.15, scale = scale[3]) |> ceiling() )) |> - gt() |> - tab_header("Sample size by scale for a superiority design", + lt() |> + lt_header("Sample size by scale for a superiority design", subtitle = "alpha = 0.025, beta = 0.15, pE = 0.2, pC = 0.1" ) ``` @@ -131,11 +133,11 @@ rbind( scale = c("Risk difference", "Risk-ratio", "Odds-ratio"), Effect = c(rd, rr, orr) ) |> - gt() |> - tab_header("Confidence intervals for a binomial effect size", + lt() |> + lt_header("Confidence intervals for a binomial effect size", subtitle = "x1 = 20, n1 = 30, x2 = 10, n2 = 30" ) |> - fmt_number(columns = c(lower, upper, Effect), n_sigfig = 3) + lt_format(columns = c("lower", "upper", "Effect"), decimals = 3) ``` Again, how treatment groups are assigned makes a difference. @@ -166,19 +168,19 @@ tibble( ceiling(nBinomial(p1 = 0.2, p2 = 0.1, alpha = 0.025, beta = 0.15, ratio = 0.5, delta0 = 0.02)) ) ) |> - gt() |> - tab_header("Sample size for binomial two arm trial design", + lt() |> + lt_header("Sample size for binomial two arm trial design", subtitle = "alpha = 0.025, beta = 0.15" ) |> - fmt_number(columns = c(`p1 (pE)`, `p2 (pC)`), decimals = 2) |> - cols_label( + lt_format(columns = c("p1 (pE)", "p2 (pC)"), decimals = 2) |> + lt_label( Design = "Design", `p1 (pE)` = "Experimental group rate", `p2 (pC)` = "Control group rate", delta0 = "Null hypothesis value of rate difference (delta0)", `Sample size` = "Sample size" ) |> - tab_footnote("Randomization ratio is 2:1 (Experimental:Control) with assumed control failure rate p1 = 0.2 and experimental rate 0.1.") + lt_note("Randomization ratio is 2:1 (Experimental:Control) with assumed control failure rate p1 = 0.2 and experimental rate 0.1.") ``` Testing for non-inferiority and super-superiority is equivalent to whether or not the confidence intervals contain the margin `delta0`. @@ -224,12 +226,12 @@ In any case, this produces a slightly conservative Type I error rate. ```{r} zcut <- quantile(z, 0.975) tibble("Z cutoff" = zcut, "p cutoff" = pnorm(zcut, lower.tail = FALSE)) |> - gt() |> - fmt_number(columns = c("Z cutoff", "p cutoff"), n_sigfig = 3) |> - tab_header("Exact cutoff for Type I error rate", + lt() |> + lt_format(columns = c("Z cutoff", "p cutoff"), decimals = 3) |> + lt_header("Exact cutoff for Type I error rate", subtitle = "Based on 1 million simulations" ) |> - tab_footnote("The Z cutoff is the quantile of the simulated Z-values at 0.975 using p1 = p2 = 0.15.") + lt_note("The Z cutoff is the quantile of the simulated Z-values at 0.975 using p1 = p2 = 0.15.") ``` Now we examine power with the asymptotic and exact cutoffs. @@ -254,14 +256,14 @@ ptab <- tibble( ) ) ptab |> - gt() |> - tab_header("Simulation power for sample size based on risk-difference and odds-ratio", + lt() |> + lt_header("Simulation power for sample size based on risk-difference and odds-ratio", subtitle = "pE = 0.2, pC = 0.1, alpha = 0.025, beta = 0.15" ) |> - fmt_number(columns = c(n, Power), n_sigfig = 3) |> - cols_label(Scale = "Scale", n = "Sample size", Power = "Power") |> - tab_footnote("Power based on 100,000 simulated trials and nominal alpha = 0.025 test; 2 x simulation error = 0.002") |> - tab_footnote("Power based on Z-test for risk-difference with no continuity correction.", location = cells_column_labels("Power")) + lt_format(columns = c("n", "Power"), decimals = 3) |> + lt_label(Scale = "Scale", n = "Sample size", Power = "Power") |> + lt_footnote("Power based on Z-test for risk-difference with no continuity correction.", where = "column", columns = "Power") |> + lt_note("Power based on 100,000 simulated trials and nominal alpha = 0.025 test; 2 x simulation error = 0.002") ``` ## Power table @@ -280,9 +282,9 @@ binomialPowerTable( ratio = 1, alpha = 0.025, simulation = TRUE, nsim = 1e6, adj = 0 ) |> rename("Type I error" = "Power") |> - gt() |> - fmt_number(columns = "Type I error", n_sigfig = 3) |> - tab_header("Type I error is not controlled with nominal p = 0.025 cutoff") + lt() |> + lt_format(columns = "Type I error", decimals = 3) |> + lt_header("Type I error is not controlled with nominal p = 0.025 cutoff") ``` Adding the continuity correction (`adj = 1`) helps a small amount at better controlling Type I error in this case. @@ -295,9 +297,9 @@ binomialPowerTable( ratio = 1, alpha = 0.023, simulation = TRUE, nsim = 1e6, adj = 0 ) |> rename("Type I error" = "Power") |> - gt() |> - fmt_number(columns = "Type I error", n_sigfig = 3) |> - tab_header("Type I error is controlled at 0.025 with nominal p = 0.023 cutoff") + lt() |> + lt_format(columns = "Type I error", decimals = 3) |> + lt_header("Type I error is controlled at 0.025 with nominal p = 0.023 cutoff") ``` Now we look at power for a range of control rates and treatment effects. @@ -367,13 +369,13 @@ power_table_simulation |> values_from = Power ) |> dplyr::rename(`Control group rate` = pC) |> - gt::gt() |> - gt::tab_spanner( + lt::lt() |> + lt::lt_spanner( label = "Treatment effect (delta)", columns = 2:7 ) |> - gt::fmt_percent(decimals = 1) |> - gt::tab_header("Power by Control Group Rate and Treatment Effect") + lt::lt_format(columns = 1:7, decimals = 1, percent = TRUE) |> + lt::lt_header("Power by Control Group Rate and Treatment Effect") ``` ## Summary diff --git a/vignettes/gsSurvBasicExamples.Rmd b/vignettes/gsSurvBasicExamples.Rmd index f0b841f9..9dad6b0f 100644 --- a/vignettes/gsSurvBasicExamples.Rmd +++ b/vignettes/gsSurvBasicExamples.Rmd @@ -1,6 +1,8 @@ --- title: "Basic time-to-event group sequential design using gsSurv" -output: rmarkdown::html_vignette +output: + rmarkdown::html_vignette: + css: vignette.css bibliography: gsDesign.bib vignette: > %\VignetteIndexEntry{Basic time-to-event group sequential design using gsSurv} @@ -204,15 +206,15 @@ An important addition not provided above is that the median time-to-event is ass Following are the enrollment rates required to power the trial. ```{r, warning=FALSE} -library(gt) +library(lt) library(tibble) tibble( Period = paste("Month", rownames(x$gamma)), Rate = as.numeric(x$gamma) ) |> - gt() |> - tab_header(title = "Enrollment rate requirements") + lt() |> + lt_header(title = "Enrollment rate requirements") ``` Next we provide a tabular summary of bounds for the design. @@ -246,15 +248,15 @@ caption <- paste( ```{r, echo=TRUE, message=FALSE} gsBoundSummary(x) |> - gt() |> - tab_header(title = "Time-to-event group sequential design") |> - cols_align("left") |> - tab_footnote(footnoteUS, locations = cells_column_labels(columns = 3)) |> - tab_footnote(footnoteLS, locations = cells_column_labels(columns = 4)) |> - tab_footnote(footnoteHR, locations = cells_body(columns = 2, rows = c(3, 8, 13))) |> - tab_footnote(footnoteM, locations = cells_body(columns = 1, rows = c(4, 9, 14))) |> - tab_footnote(footnote1, locations = cells_body(columns = 2, rows = c(4, 5, 9, 10, 14, 15))) |> - tab_footnote(footnote2, locations = cells_body(columns = 2, rows = c(4, 9, 14))) + lt() |> + lt_header(title = "Time-to-event group sequential design") |> + lt_align(columns = c("Analysis", "Value", "Efficacy", "Futility"), align = "left") |> + lt_footnote(footnoteUS, where = "column", columns = "Efficacy") |> + lt_footnote(footnoteLS, where = "column", columns = "Futility") |> + lt_footnote(footnoteHR, where = "body", columns = "Value", rows = c(3, 8, 13)) |> + lt_footnote(footnoteM, where = "body", columns = "Analysis", rows = c(4, 9, 14)) |> + lt_footnote(footnote1, where = "body", columns = "Value", rows = c(4, 5, 9, 10, 14, 15)) |> + lt_footnote(footnote2, where = "body", columns = "Value", rows = c(4, 9, 14)) ``` ### Summary plots @@ -317,23 +319,23 @@ gsBoundSummary( "PP", "P(Cross) if HR=1", "P(Cross) if HR=0.75" ) ) |> - gt() |> - cols_align("left") |> - tab_header( + lt() |> + lt_align(columns = c("Analysis", "Value", "Efficacy", "Futility"), align = "left") |> + lt_header( title = "Time-to-event group sequential bound guidance", subtitle = "Bounds updated based on event counts through IA2" ) |> - tab_footnote( + lt_footnote( "Nominal p-value required to establish statistical significance.", - locations = cells_body(columns = 3, rows = c(2, 5, 8)) + where = "body", columns = "Efficacy", rows = c(2, 5, 8) ) |> - tab_footnote( + lt_footnote( "Interim futility guidance based on observed HR is non-binding.", - locations = cells_body(columns = 4, rows = c(3, 6)) + where = "body", columns = "Futility", rows = c(3, 6) ) |> - tab_footnote( + lt_footnote( "HR bounds are approximations; decisions on crossing are based solely on p-values.", - locations = cells_body(column = 2, rows = c(3, 6, 9)) + where = "body", columns = "Value", rows = c(3, 6, 9) ) ``` diff --git a/vignettes/vignette.css b/vignettes/vignette.css new file mode 100644 index 00000000..232206e6 --- /dev/null +++ b/vignettes/vignette.css @@ -0,0 +1,20 @@ +body { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.35; + max-width: 700px; + margin: 1.5em auto; + padding: 0 1em; +} +pre, code { + background-color: #f7f7f7; + border-radius: 3px; + font-size: 90%; +} +pre { + padding: 10px; + white-space: pre-wrap; +} +.lt-table { + min-width: 70%; +}