diff --git a/R/server.R b/R/server.R index df47ec8..6c1474b 100644 --- a/R/server.R +++ b/R/server.R @@ -32,7 +32,7 @@ app_server <- function(input, output, session) { div( class = "event-row-head", div("Planned look"), - div("Deaths"), + div("Expected deaths"), div() ), div( @@ -113,11 +113,14 @@ app_server <- function(input, output, session) { primary_events <- get_primary_events(rows) validate( - need(length(primary_events) > 0, "Enter at least one interim-analysis event count."), - need(all(!is.na(primary_events)), "Every interim-analysis row needs a numeric death count."), - need(!is.na(input$eventOS), "Final-analysis deaths must be numeric."), - need(all(diff(primary_events) >= 0), "Interim-analysis deaths should increase across planned looks."), - need(input$eventOS >= max(primary_events), "Final-analysis deaths must be greater than or equal to the last interim analysis.") + need(length(primary_events) > 0, "Enter at least one interim-analysis expected death count."), + need(all(!is.na(primary_events)), "Every interim-analysis row needs a numeric expected death count."), + need(!is.na(input$eventOS), "Targeted deaths at final OS analysis must be numeric."), + need(all(diff(primary_events) >= 0), "Interim-analysis expected deaths should increase across planned looks."), + need( + input$eventOS >= max(primary_events), + "Targeted deaths at final OS analysis must be greater than or equal to the last interim analysis." + ) ) primary_rows(Map(function(row, value) { @@ -136,7 +139,12 @@ app_server <- function(input, output, session) { )$summary ci_col <- "Level of 2-sided CI needed to rule out delta null" + alt_col <- "Probability of meeting positivity threshold under delta alt" summary[[ci_col]] <- paste0(summary[[ci_col]], "%") + names(summary)[names(summary) == ci_col] <- + "Level of 2-sided CI needed to rule out the unacceptable detriment" + names(summary)[names(summary) == alt_col] <- + "Probability of meeting positivity threshold under the plausible beneficial effect" summary[, setdiff(seq_along(summary), c(6, 7)), drop = FALSE] }) diff --git a/R/ui.R b/R/ui.R index 58887c1..3576c45 100644 --- a/R/ui.R +++ b/R/ui.R @@ -270,20 +270,36 @@ app_ui <- function(request = NULL) { position: sticky; top: 0; z-index: 5; + padding: 0.95rem 1rem 1rem; border-top-left-radius: 0; border-top-right-radius: 0; } + .results-card .section-kicker { + margin-bottom: 0.35rem; + } + + .results-card h2 { + margin-bottom: 0.35rem; + font-size: 1.35rem; + } + + .results-card .inline-note { + margin-bottom: 0; + font-size: 0.86rem; + line-height: 1.4; + } + .summary-strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0.75rem; - margin-top: 0.9rem; + gap: 0.55rem; + margin-top: 0.65rem; } .summary-item { - padding: 0.8rem 0.9rem; - border-radius: 14px; + padding: 0.55rem 0.7rem; + border-radius: 12px; background: rgba(245, 245, 245, 0.95); border: 1px solid rgba(22, 22, 22, 0.06); } @@ -300,8 +316,8 @@ app_ui <- function(request = NULL) { .summary-item-value { color: var(--monitos-fg); - font-size: 0.96rem; - line-height: 1.45; + font-size: 0.9rem; + line-height: 1.35; font-weight: 600; } @@ -315,8 +331,9 @@ app_ui <- function(request = NULL) { } .results-frame { - margin-top: 1rem; - overflow-x: auto; + margin-top: 0.75rem; + max-height: min(38vh, 18rem); + overflow: auto; border: 1px solid rgba(22, 22, 22, 0.08); border-radius: 16px; background: #fff; @@ -325,24 +342,27 @@ app_ui <- function(request = NULL) { .results-card table { width: 100%; margin: 0; - font-size: 0.95rem; + font-size: 0.88rem; } .results-card thead th { position: sticky; top: 0; z-index: 1; - padding: 0.85rem 0.85rem; + padding: 0.6rem 0.65rem; border: 0; background: rgba(245, 245, 245, 0.98); color: var(--monitos-fg); + font-size: 0.78rem; font-weight: 700; + line-height: 1.3; } .results-card tbody td { - padding: 0.85rem; + padding: 0.6rem 0.65rem; vertical-align: top; border-color: rgba(22, 22, 22, 0.08); + line-height: 1.4; } .results-card tbody tr:nth-child(odd) td { @@ -440,6 +460,10 @@ app_ui <- function(request = NULL) { .results-card { position: static; } + + .results-frame { + max-height: none; + } } ")) ), @@ -495,7 +519,7 @@ app_ui <- function(request = NULL) { h2("Set assumptions, then review the threshold"), p( class = "inline-note", - "1. Set effect sizes. 2. Add interim event counts. 3. Review the final-analysis threshold and interim operating characteristics." + "1. Set effect sizes. 2. Add interim expected death counts. 3. Review the final-analysis threshold and interim operating characteristics." ), p(class = "helper-copy", "HR below 1 indicates benefit.") ), @@ -521,7 +545,7 @@ app_ui <- function(request = NULL) { class = "field-note", tags$strong("Use for: "), "choose the OS hazard ratio that would already be too harmful to accept. ", - tags$em("Example: set delta null to 1.33 if any HR at or above 1.33 would be unacceptable.") + tags$em("Example: set this to 1.33 if any HR at or above 1.33 would be unacceptable.") ) ), div( @@ -540,7 +564,7 @@ app_ui <- function(request = NULL) { class = "field-note", tags$strong("Use for: "), "reflect the benefit you could reasonably expect from prior evidence or mechanism of action. ", - tags$em("Example: use 0.90 if a 10% OS hazard reduction would still be clinically meaningful.") + tags$em("Example: use 0.90 if a 10% OS hazard reduction would still be a plausible beneficial effect.") ) ) ) @@ -556,24 +580,24 @@ app_ui <- function(request = NULL) { div( numericInput( "eventOS", - "Deaths at final analysis", + "Targeted deaths at final OS analysis", value = 70, min = 1 ), p( class = "helper-copy", - "Use the longest feasible follow-up window for the final analysis." + "Use the longest feasible follow-up window when setting the targeted deaths for the final OS analysis." ) ), div( tags$label( `for` = "primary_events_ui", - "Interim analysis schedule" + "Interim OS analysis schedule" ), uiOutput("primary_events_ui"), p( class = "helper-copy", - "Add one row per planned look, ordered by increasing deaths." + "Add one row per planned look, ordered by increasing expected deaths." ) ) ) @@ -601,8 +625,9 @@ app_ui <- function(request = NULL) { class = "field-note", tags$strong("Interpretation: "), paste( - "this controls the final-analysis reassurance threshold when the true OS HR", - "is equal to your unacceptable detriment." + "this controls the final-analysis threshold for positivity.", + "It is the risk of being wrongly reassured when the true OS HR", + "is in fact equal to your unacceptable detriment." ) ) ), @@ -611,7 +636,7 @@ app_ui <- function(request = NULL) { div( sliderInput( "power_int", - "Required power at interim analyses under delta alt", + "Required power at interim analyses under the plausible beneficial effect", min = 0.7, max = 1, value = 0.9, @@ -620,8 +645,16 @@ app_ui <- function(request = NULL) { ), div( class = "field-note", - tags$strong("Typical choice: "), - "0.80 or 0.90, depending on how strict the primary-analysis reassurance should be." + tags$strong("Interpretation: "), + paste( + "this controls the interim analysis threshold for positivity.", + "It is the chance of being correctly reassured when the true OS HR", + "is in fact equal to your plausible beneficial effect." + ), + tags$br(), + tags$em( + "Typical choices include 0.80 or 0.90, depending on the possible impact on the program of wrongly flagging a potential detriment at the interim OS analysis." + ) ) ) ) @@ -647,7 +680,7 @@ app_ui <- function(request = NULL) { div( class = "field-note", tags$strong("When to use: "), - "use this when the drug may still be valuable under a smaller OS benefit than delta alt." + "use this when the drug may still be valuable under a smaller OS benefit than your plausible beneficial effect." ) ), div( @@ -681,9 +714,9 @@ app_ui <- function(request = NULL) { tags$ul( class = "list-tight", tags$li("OS HR threshold for positivity: the observed HR must be at or below this value."), - tags$li("One-sided false positive error rate: the tolerated risk of passing when the true effect equals delta null."), - tags$li("Level of 2-sided CI needed to rule out delta null: the confidence level required to exclude the unacceptable detriment."), - tags$li("Probability of meeting positivity threshold under delta alt: the chance of passing when the treatment effect equals your plausible benefit.") + tags$li("One-sided false positive error rate: the tolerated risk of passing when the true effect equals your unacceptable detriment."), + tags$li("Level of 2-sided CI needed to rule out the unacceptable detriment: the confidence level required to exclude that detriment."), + tags$li("Probability of meeting positivity threshold under the plausible beneficial effect: the chance of passing when the treatment effect equals your plausible beneficial effect.") ) ) ), diff --git a/docs/shinylive-app/app.json b/docs/shinylive-app/app.json index ff87335..e80af87 100644 --- a/docs/shinylive-app/app.json +++ b/docs/shinylive-app/app.json @@ -1 +1 @@ -[{"name":"app.R","content":"# Generated by tools/build_shinylive.R. Do not edit by hand.\n\nlibrary(bslib)\nlibrary(shiny)\n\nsource(\"R/posteriors.R\", local = TRUE)\nsource(\"R/probs.R\", local = TRUE)\nsource(\"R/bounds.R\", local = TRUE)\nsource(\"R/ui.R\", local = TRUE)\nsource(\"R/server.R\", local = TRUE)\n\nui <- app_ui()\nserver <- app_server\n\nshinyApp(ui, server)\n","type":"text"},{"name":"R/server.R","content":"#' Shiny app server function\n#'\n#' @description Server-side logic for the monitOS Shiny application. Computes OS\n#' monitoring boundaries reactively based on user inputs and renders the\n#' summary table of positivity thresholds.\n#'\n#' @param input Shiny input object containing UI widget values.\n#' @param output Shiny output object for rendering results.\n#' @param session Shiny session object for updating UI elements.\n#' @import shiny\n# nocov start\napp_server <- function(input, output, session) {\n primary_rows <- reactiveVal(list(\n list(id = 1L, value = 28),\n list(id = 2L, value = 42)\n ))\n next_primary_id <- reactiveVal(3L)\n\n get_primary_events <- function(rows = primary_rows()) {\n vapply(rows, function(row) {\n input_value <- input[[paste0(\"eventPA_\", row$id)]]\n if (is.null(input_value) || is.na(input_value)) row$value else input_value\n }, numeric(1))\n }\n\n output$primary_events_ui <- renderUI({\n rows <- primary_rows()\n current_values <- get_primary_events(rows)\n\n div(\n class = \"event-editor\",\n div(\n class = \"event-row-head\",\n div(\"Planned look\"),\n div(\"Deaths\"),\n div()\n ),\n div(\n class = \"event-row-list\",\n lapply(seq_along(rows), function(i) {\n row <- rows[[i]]\n div(\n class = \"event-row\",\n div(class = \"event-row-label\", paste(\"Interim analysis\", i)),\n div(\n class = \"event-row-control\",\n numericInput(\n inputId = paste0(\"eventPA_\", row$id),\n label = NULL,\n value = current_values[i],\n min = 1\n )\n ),\n if (length(rows) > 1) {\n actionLink(\n inputId = paste0(\"remove_event_row_\", row$id),\n label = HTML(\"✕\"),\n class = \"event-remove-link\",\n title = \"Remove interim analysis\"\n )\n } else {\n div()\n }\n )\n })\n ),\n div(\n class = \"event-footer\",\n actionLink(\n \"add_event_row\",\n HTML(\"+ Add interim analysis\"),\n class = \"event-add-link\"\n )\n )\n )\n })\n\n observeEvent(input$add_event_row, {\n rows <- primary_rows()\n current_values <- get_primary_events(rows)\n updated_rows <- Map(function(row, value) {\n row$value <- value\n row\n }, rows, current_values)\n new_id <- next_primary_id()\n updated_rows[[length(updated_rows) + 1]] <- list(id = new_id, value = NA_real_)\n primary_rows(updated_rows)\n next_primary_id(new_id + 1L)\n })\n\n observe({\n rows <- primary_rows()\n\n lapply(rows, function(row) {\n observeEvent(input[[paste0(\"remove_event_row_\", row$id)]], {\n current_rows <- primary_rows()\n current_values <- get_primary_events(current_rows)\n current_rows <- Map(function(existing_row, value) {\n existing_row$value <- value\n existing_row\n }, current_rows, current_values)\n\n if (length(current_rows) > 1) {\n keep <- vapply(current_rows, function(existing_row) existing_row$id != row$id, logical(1))\n primary_rows(current_rows[keep])\n }\n }, ignoreInit = TRUE)\n })\n })\n\n summary_data <- reactive({\n rows <- primary_rows()\n primary_events <- get_primary_events(rows)\n\n validate(\n need(length(primary_events) > 0, \"Enter at least one interim-analysis event count.\"),\n need(all(!is.na(primary_events)), \"Every interim-analysis row needs a numeric death count.\"),\n need(!is.na(input$eventOS), \"Final-analysis deaths must be numeric.\"),\n need(all(diff(primary_events) >= 0), \"Interim-analysis deaths should increase across planned looks.\"),\n need(input$eventOS >= max(primary_events), \"Final-analysis deaths must be greater than or equal to the last interim analysis.\")\n )\n\n primary_rows(Map(function(row, value) {\n row$value <- value\n row\n }, rows, primary_events))\n\n summary <- bounds(\n events = c(primary_events, input$eventOS),\n power_int = input$power_int,\n falsepos = input$falsepos,\n hr_null = input$hr_null,\n hr_alt = input$hr_alt,\n rand_ratio = input$rand_ratio,\n hr_marg_benefit = input$hr_marg_benefit\n )$summary\n\n ci_col <- \"Level of 2-sided CI needed to rule out delta null\"\n summary[[ci_col]] <- paste0(summary[[ci_col]], \"%\")\n\n summary[, setdiff(seq_along(summary), c(6, 7)), drop = FALSE]\n })\n\n output$final_threshold_summary <- renderText({\n summary <- summary_data()\n final_row <- summary[nrow(summary), , drop = FALSE]\n paste0(\n \"Observed OS HR must be at or below \",\n sprintf(\"%.3f\", final_row[[\"OS HR threshold for positivity\"]]),\n \".\"\n )\n })\n\n output$analysis_count_summary <- renderText({\n interim_n <- length(primary_rows())\n total_n <- interim_n + 1\n paste0(interim_n, \" interim + 1 final analysis (\", total_n, \" total).\")\n })\n\n output$bounds <- renderUI({\n summary <- summary_data()\n header <- tags$tr(lapply(names(summary), function(name) tags$th(name)))\n body <- lapply(seq_len(nrow(summary)), function(i) {\n row_class <- if (i == nrow(summary)) \"final-analysis-row\" else NULL\n tags$tr(\n class = row_class,\n lapply(summary[i, , drop = TRUE], function(value) tags$td(as.character(value)))\n )\n })\n\n tags$table(\n class = \"table\",\n tags$thead(header),\n tags$tbody(body)\n )\n })\n}\n# nocov end\n","type":"text"},{"name":"R/ui.R","content":"#' Shiny app UI definition\n#'\n#' @description User interface for the monitOS Shiny application. Provides a\n#' branded, guided workflow for setting OS monitoring parameters and\n#' reviewing the resulting positivity thresholds.\n#'\n#' @param request Shiny internal request object for bookmarking support.\n#' @import shiny\n#' @import bslib\n# nocov start\napp_ui <- function(request = NULL) {\n theme <- bslib::bs_theme(\n version = 5,\n bg = \"#FCFCFC\",\n fg = \"#161616\",\n primary = \"#FF4E00\",\n secondary = \"#A5BFF5\"\n )\n\n bslib::page_fluid(\n theme = theme,\n title = \"monitOS\",\n lang = \"en\",\n tags$head(\n tags$style(HTML(\"\n :root {\n --monitos-bg: #FCFCFC;\n --monitos-fg: #161616;\n --monitos-orange: #FF4E00;\n --monitos-grey-1: #DADADA;\n --monitos-grey-2: #F5F5F5;\n --monitos-blue: #A5BFF5;\n --monitos-pink: #EDA8D1;\n --monitos-shadow: 0 16px 36px rgba(22, 22, 22, 0.05);\n --monitos-radius: 22px;\n }\n\n body {\n background:\n radial-gradient(circle at top left, rgba(165, 191, 245, 0.18), transparent 28%),\n linear-gradient(180deg, #ffffff 0%, var(--monitos-bg) 42%, #ffffff 100%);\n color: var(--monitos-fg);\n font-family: 'Avenir Next', 'Segoe UI', 'Helvetica Neue', sans-serif;\n }\n\n h1, h2, h3, h4, .display-title {\n font-family: 'Iowan Old Style', 'Palatino Linotype', 'Book Antiqua', serif;\n letter-spacing: -0.02em;\n }\n\n .page-shell {\n max-width: 1360px;\n margin: 0 auto;\n padding: 1.25rem 0 2.75rem;\n }\n\n .hero-card,\n .panel-card {\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: var(--monitos-radius);\n background: rgba(255, 255, 255, 0.96);\n box-shadow: var(--monitos-shadow);\n }\n\n .hero-card {\n position: relative;\n padding: 1.75rem 1.9rem;\n margin-bottom: 1.5rem;\n background:\n linear-gradient(135deg, rgba(165, 191, 245, 0.22), rgba(255, 255, 255, 0.96) 52%, rgba(237, 168, 209, 0.14) 100%);\n }\n\n .hero-grid {\n position: relative;\n z-index: 1;\n }\n\n .hero-eyebrow {\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n margin-bottom: 0.7rem;\n padding: 0;\n border-radius: 999px;\n color: var(--monitos-orange);\n font-size: 0.78rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .hero-title {\n max-width: 14ch;\n margin: 0 0 0.7rem;\n font-size: clamp(2.1rem, 3.4vw, 3.6rem);\n line-height: 1;\n }\n\n .hero-copy {\n max-width: 42rem;\n margin: 0;\n font-size: 1rem;\n line-height: 1.65;\n color: rgba(22, 22, 22, 0.78);\n }\n\n .app-grid {\n display: grid;\n gap: 1.25rem;\n }\n\n .panel-card {\n padding: 1.2rem 1.2rem 1.3rem;\n }\n\n .panel-card h2,\n .panel-card h3 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n }\n\n .section-kicker {\n display: inline-block;\n margin-bottom: 0.55rem;\n color: rgba(255, 78, 0, 0.82);\n font-size: 0.8rem;\n font-weight: 500;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .lede {\n color: rgba(22, 22, 22, 0.72);\n line-height: 1.65;\n }\n\n .field-grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(220px, 0.72fr);\n gap: 1rem;\n align-items: start;\n }\n\n .field-note {\n padding-top: 0.35rem;\n color: rgba(22, 22, 22, 0.72);\n line-height: 1.55;\n }\n\n .field-note strong {\n color: var(--monitos-fg);\n font-weight: 700;\n }\n\n .field-note em {\n color: rgba(22, 22, 22, 0.64);\n font-style: normal;\n }\n\n .subtle-list {\n margin: 0.4rem 0 0;\n padding-left: 1rem;\n color: var(--monitos-fg);\n line-height: 1.6;\n }\n\n .shiny-input-container {\n width: 100%;\n margin-bottom: 0;\n }\n\n .field-stack {\n display: grid;\n gap: 1rem;\n }\n\n .field-stack .form-control,\n .field-stack .irs,\n .field-stack .selectize-input {\n margin-top: 0.35rem;\n }\n\n .event-editor {\n margin-top: 0.35rem;\n padding: 0.65rem 0.7rem 0.45rem;\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: 14px;\n background: rgba(252, 252, 252, 0.92);\n }\n\n .event-row-head,\n .event-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 120px 32px;\n gap: 0.75rem;\n align-items: center;\n }\n\n .event-row-head {\n padding: 0 0.2rem 0.35rem;\n color: rgba(22, 22, 22, 0.52);\n font-size: 0.72rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n }\n\n .event-row-list {\n display: grid;\n gap: 0.4rem;\n }\n\n .event-row {\n padding: 0.42rem 0.55rem;\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: 10px;\n background: #fff;\n }\n\n .event-row .shiny-input-container {\n margin-bottom: 0;\n }\n\n .event-row-label {\n color: rgba(22, 22, 22, 0.78);\n font-size: 0.9rem;\n font-weight: 500;\n white-space: nowrap;\n }\n\n .event-row-control .form-control {\n max-width: 120px;\n height: 36px;\n padding: 0.3rem 0.55rem;\n }\n\n .event-footer {\n display: flex;\n justify-content: flex-start;\n margin-top: 0.45rem;\n padding: 0.15rem 0.15rem 0;\n }\n\n .event-add-link {\n color: var(--monitos-orange);\n font-size: 0.88rem;\n font-weight: 600;\n text-decoration: none;\n }\n\n .event-add-link:hover,\n .event-add-link:focus {\n color: var(--monitos-fg);\n text-decoration: none;\n }\n\n .helper-copy {\n margin-top: 0.5rem;\n color: rgba(22, 22, 22, 0.68);\n line-height: 1.55;\n }\n\n .inline-note {\n color: rgba(22, 22, 22, 0.62);\n font-size: 0.92rem;\n line-height: 1.55;\n }\n\n .results-card {\n position: sticky;\n top: 0;\n z-index: 5;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n\n .summary-strip {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 0.75rem;\n margin-top: 0.9rem;\n }\n\n .summary-item {\n padding: 0.8rem 0.9rem;\n border-radius: 14px;\n background: rgba(245, 245, 245, 0.95);\n border: 1px solid rgba(22, 22, 22, 0.06);\n }\n\n .summary-item-label {\n display: block;\n margin-bottom: 0.2rem;\n color: rgba(22, 22, 22, 0.56);\n font-size: 0.76rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n }\n\n .summary-item-value {\n color: var(--monitos-fg);\n font-size: 0.96rem;\n line-height: 1.45;\n font-weight: 600;\n }\n\n .form-stack {\n display: grid;\n gap: 1rem;\n }\n\n .form-stack .panel-card {\n width: 100%;\n }\n\n .results-frame {\n margin-top: 1rem;\n overflow-x: auto;\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: 16px;\n background: #fff;\n }\n\n .results-card table {\n width: 100%;\n margin: 0;\n font-size: 0.95rem;\n }\n\n .results-card thead th {\n position: sticky;\n top: 0;\n z-index: 1;\n padding: 0.85rem 0.85rem;\n border: 0;\n background: rgba(245, 245, 245, 0.98);\n color: var(--monitos-fg);\n font-weight: 700;\n }\n\n .results-card tbody td {\n padding: 0.85rem;\n vertical-align: top;\n border-color: rgba(22, 22, 22, 0.08);\n }\n\n .results-card tbody tr:nth-child(odd) td {\n background: rgba(252, 252, 252, 0.95);\n }\n\n .results-card tbody tr.final-analysis-row td {\n background: rgba(255, 78, 0, 0.08);\n font-weight: 600;\n }\n\n .results-card .table {\n margin-bottom: 0;\n }\n\n .event-remove-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: 1px solid rgba(22, 22, 22, 0.1);\n border-radius: 999px;\n color: rgba(22, 22, 22, 0.56);\n font-size: 0.82rem;\n line-height: 1;\n text-decoration: none;\n background: rgba(245, 245, 245, 0.9);\n transition: color 120ms ease, border-color 120ms ease, background 120ms ease;\n }\n\n .event-remove-link:hover,\n .event-remove-link:focus {\n color: var(--monitos-orange);\n border-color: rgba(255, 78, 0, 0.28);\n background: rgba(255, 78, 0, 0.06);\n }\n\n .reference-links {\n display: grid;\n gap: 0.65rem;\n margin-top: 0.9rem;\n }\n\n .reference-links a {\n color: var(--monitos-fg);\n font-weight: 700;\n text-decoration: none;\n }\n\n .reference-links a:hover,\n .reference-links a:focus {\n color: var(--monitos-orange);\n }\n\n .support-card details + details {\n margin-top: 0.75rem;\n }\n\n .support-card summary {\n cursor: pointer;\n color: var(--monitos-fg);\n font-weight: 600;\n list-style: none;\n }\n\n .support-card summary::-webkit-details-marker {\n display: none;\n }\n\n .support-card .details-body {\n margin-top: 0.7rem;\n }\n\n .list-tight {\n margin: 0;\n padding-left: 1.15rem;\n line-height: 1.7;\n }\n\n @media (max-width: 991px) {\n .field-grid {\n grid-template-columns: 1fr;\n }\n\n .event-row {\n grid-template-columns: 1fr;\n gap: 0.55rem;\n }\n\n .event-row-head {\n display: none;\n }\n\n .results-card {\n position: static;\n }\n }\n \"))\n ),\n div(\n class = \"page-shell\",\n div(\n class = \"hero-card\",\n div(\n class = \"hero-grid\",\n div(\n span(class = \"hero-eyebrow\", \"Monitoring overall survival\"),\n h1(class = \"hero-title\", \"Configure OS monitoring thresholds.\"),\n p(\n class = \"hero-copy\",\n paste(\n \"Translate effect-size assumptions and event timing into\",\n \"positivity thresholds for interim and final OS reviews.\"\n )\n )\n )\n )\n ),\n div(\n class = \"app-grid\",\n div(\n class = \"panel-card results-card\",\n span(class = \"section-kicker\", \"Positivity thresholds\"),\n h2(\"Review the monitoring table\"),\n p(\n class = \"inline-note\",\n \"Updates automatically as you change assumptions.\"\n ),\n div(\n class = \"summary-strip\",\n div(\n class = \"summary-item\",\n span(class = \"summary-item-label\", \"Final Analysis\"),\n span(class = \"summary-item-value\", textOutput(\"final_threshold_summary\", container = span))\n ),\n div(\n class = \"summary-item\",\n span(class = \"summary-item-label\", \"Planned Looks\"),\n span(class = \"summary-item-value\", textOutput(\"analysis_count_summary\", container = span))\n )\n ),\n div(class = \"results-frame\", uiOutput(\"bounds\"))\n ),\n div(\n class = \"form-stack\",\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Workflow\"),\n h2(\"Set assumptions, then review the threshold\"),\n p(\n class = \"inline-note\",\n \"1. Set effect sizes. 2. Add interim event counts. 3. Review the final-analysis threshold and interim operating characteristics.\"\n ),\n p(class = \"helper-copy\", \"HR below 1 indicates benefit.\")\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Trial assumptions\"),\n h3(\"Clinically meaningful effect sizes\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"hr_null\",\n \"Unacceptable detrimental effect on OS\",\n min = 1,\n max = 2,\n value = 4 / 3,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Use for: \"),\n \"choose the OS hazard ratio that would already be too harmful to accept. \",\n tags$em(\"Example: set delta null to 1.33 if any HR at or above 1.33 would be unacceptable.\")\n )\n ),\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"hr_alt\",\n \"Plausible beneficial effect on OS\",\n min = 0.05,\n max = 1,\n value = 0.7,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Use for: \"),\n \"reflect the benefit you could reasonably expect from prior evidence or mechanism of action. \",\n tags$em(\"Example: use 0.90 if a 10% OS hazard reduction would still be clinically meaningful.\")\n )\n )\n )\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Design inputs\"),\n h3(\"Event timing\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n numericInput(\n \"eventOS\",\n \"Deaths at final analysis\",\n value = 70,\n min = 1\n ),\n p(\n class = \"helper-copy\",\n \"Use the longest feasible follow-up window for the final analysis.\"\n )\n ),\n div(\n tags$label(\n `for` = \"primary_events_ui\",\n \"Interim analysis schedule\"\n ),\n uiOutput(\"primary_events_ui\"),\n p(\n class = \"helper-copy\",\n \"Add one row per planned look, ordered by increasing deaths.\"\n )\n )\n )\n )\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Decision thresholds\"),\n h3(\"Statistical threshold settings\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"falsepos\",\n \"One-sided false positive rate at final analysis\",\n min = 0,\n max = 0.3,\n value = 0.1,\n step = 0.005\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Interpretation: \"),\n paste(\n \"this controls the final-analysis reassurance threshold when the true OS HR\",\n \"is equal to your unacceptable detriment.\"\n )\n )\n ),\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"power_int\",\n \"Required power at interim analyses under delta alt\",\n min = 0.7,\n max = 1,\n value = 0.9,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Typical choice: \"),\n \"0.80 or 0.90, depending on how strict the primary-analysis reassurance should be.\"\n )\n )\n )\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Optional sensitivity inputs\"),\n h3(\"Stress-test the plan\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"hr_marg_benefit\",\n \"Marginal-benefit OS HR\",\n min = 0.7,\n max = 1.1,\n value = 0.95,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"When to use: \"),\n \"use this when the drug may still be valuable under a smaller OS benefit than delta alt.\"\n )\n ),\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"rand_ratio\",\n \"Randomization ratio (experimental : control)\",\n min = 1,\n max = 3,\n value = 1,\n step = 1\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Example: \"),\n \"use 1 for 1:1 randomization and 2 for 2:1 randomization.\"\n )\n )\n )\n ),\n div(\n class = \"panel-card support-card\",\n span(class = \"section-kicker\", \"Support\"),\n h3(\"Method notes\"),\n tags$details(\n tags$summary(\"How to read the table\"),\n div(\n class = \"details-body\",\n tags$ul(\n class = \"list-tight\",\n tags$li(\"OS HR threshold for positivity: the observed HR must be at or below this value.\"),\n tags$li(\"One-sided false positive error rate: the tolerated risk of passing when the true effect equals delta null.\"),\n tags$li(\"Level of 2-sided CI needed to rule out delta null: the confidence level required to exclude the unacceptable detriment.\"),\n tags$li(\"Probability of meeting positivity threshold under delta alt: the chance of passing when the treatment effect equals your plausible benefit.\")\n )\n )\n ),\n tags$details(\n tags$summary(\"Paper and package resources\"),\n div(\n class = \"details-body reference-links\",\n tags$a(href = \"https://www.tandfonline.com/doi/full/10.1080/19466315.2024.2365648\", target = \"_blank\", rel = \"noopener noreferrer\", \"Read the monitOS paper\"),\n tags$a(href = \"https://opensource.nibr.com/monitOS/articles/monitOS.html\", target = \"_blank\", rel = \"noopener noreferrer\", \"Open the package vignette\"),\n tags$a(href = \"https://github.com/Novartis/monitOS/issues\", target = \"_blank\", rel = \"noopener noreferrer\", \"Report an issue or request help\")\n )\n )\n )\n )\n )\n )\n )\n}\n# nocov end\n","type":"text"},{"name":"R/bounds.R","content":"#' @title Bounds\n#'\n#' @description OS monitoring guidelines as proposed in manuscript \"Monitoring Overall Survival in Pivotal Trials in\n#' Indolent Cancers\". Calculate thresholds for positivity that can be used at an analysis to judge whether emerging\n#' evidence about the effect of treatment on OS is concerning or not. The threshold for positivity at any given analysis\n#' is the value below which the observed hazard ratio must be in order to provide sufficient reassurance that the effect\n#' on OS does not reach the selected unacceptable level of detriment (the margin hr_null).\n#' Terminology follows the manuscript \"Monitoring Overall Survival in Pivotal Trials in Indolent Cancers\"\n#' @details Monitoring guidelines assume that the hazard ratio (HR) can adequately summarize the size of the benefits\n#' and harms of the experimental\n#' intervention vs control on overall survival (OS). Furthermore, guidelines assume that an OS HR < 1 is consistent\n#' with a beneficial effect of the intervention on OS (and smaller OS HRs <1 indicate increased efficacy).\n#' @param events Vector. Target number of deaths at each analysis\n#' @param power_int Scalar. Marginal power required at the Primary Analysis when true hazard ratio (HR) is hr_alt.\n#' @param falsepos Scalar. Marginal one-sided false positive error rate we are prepared to tolerate at the Final Analysis.\n#' Determines the positivity threshold at Final Analysis\n#' @param hr_null Scalar. The unacceptably large detrimental effect of treatment on OS we want to rule out (on HR scale)\n#' @param hr_alt Scalar. Plausible clinically relevant beneficial effect of treatment on OS (on HR scale)\n#' @param rand_ratio Integer. If patients are randomized k:1 between experimental intervention and control, rand_ratio\n#' should be inputted as k.\n#' Example: if patients are randomized 1:1 between experimental and control, k=1. If patients are randomized 2:1\n#' between experimental and control, k=2.\n#' @param hr_marg_benefit Scalar. We may be uncertain about what a plausible beneficial effect of treatment on OS is.\n#' User can enter a (on HR scale) and function will evaluate the probability we meet the positivity threshold\n#' at each analysis under\n#' this HR. This second OS benefit will\n#' usually be closer to 1 than hr_alt.\n#' @importFrom stats pnorm qnorm\n#' @return List that contains:\n#' * `lhr_null`: Scalar, unacceptable OS log-HR,\n#' * `lhr_alt`: Scalar, plausible clinically relevant log-HR,\n#' * `lhr_pos`: Scalar, positivity thresholds for log-HR estimates,\n#' * `summary`: Dataframe, which contains:\n#' * `OS HR threshold for positivity`,\n#' * `One sided false positive error rate`,\n#' * `Level of 2 sided CI needed to rule out hr_null`,\n#' * `Probability of meeting positivity threshold under hr_alt`,\n#' * `Positivity_Thres_Posterior`: Pr(true OS HR >= minimum unacceptable OS HR | current data),\n#' * `Positivity_Thres_PredProb`: Pr(OS HR estimate at Final Analysis <= Final Analysis positivity threshold | current data)\n#' @export\n#' @examples\n#' # Example 01: OS monitoring guideline retrospectively applied to Motivating Example 1\n#' # with delta null = 1.3, delta alt = 0.80, gamma_FA = 0.025 and beta_PA = 0.10.\n#' bounds(\n#' events = c(60, 89, 110, 131, 178),\n#' power_int = 0.9, # beta_PA\n#' falsepos = 0.025, # gamma_FA\n#' hr_null = 1.3, # delta_null\n#' hr_alt = 0.8, # delta_alt\n#' rand_ratio = 1, # rand_ratio\n#' hr_marg_benefit = NULL\n#' )\n#' # Example 02: OS monitoring guideline applied to Motivating Example 2\n#' # with delta null = 4/3, delta alt = 0.7, gamma_FA = 0.20 and beta_PA = 0.1.\n#' bounds(\n#' events = c(60, 89, 110, 131, 178),\n#' power_int = 0.9, # beta_PA\n#' falsepos = 0.025, # gamma_FA\n#' hr_null = 1.3, # delta_null\n#' hr_alt = 0.8, # delta_alt\n#' rand_ratio = 1, # rand_ratio\n#' hr_marg_benefit = 0.95\n#' )\nbounds <- function(\n events,\n # OS events at each analysis\n power_int = 0.9,\n # 1-Beta PA, what power do we want to not flag a safety concern at an interim analysis if the true\n # OS HR equals our target alternative?\n falsepos = 0.025,\n # Gamme FA, What is the (one-sided) type I error rate that we will accept at the final analysis?\n hr_null = 1.3,\n # Delta null, what is the minimum unacceptable OS HR?\n hr_alt = 0.9,\n # Delta alt, what is a plausible alternative OS HR consistent with OS benefit?\n rand_ratio = 1,\n # for every patient randomized to control, rand_ratio patients are allocated to experimental intervention\n hr_marg_benefit = NULL\n # evaluate probability of meeting positivity thresholds under a second plausible beneficial effect of\n # treatment on OS (HR = hr_marg_benefit)\n) {\n # Log scale\n lhr_null <- log(hr_null)\n lhr_alt <- log(hr_alt)\n\n # Init variables\n nstage <- length(events) # total number of analyses planned\n info <-\n rand_ratio * events / ((rand_ratio + 1)^2) # Fisher's information for log-HR at each analysis\n se <-\n sqrt(1 / info) # asymptotic standard error for log-HR at each analysis\n\n # Calculate the attained power when true HR = hr_alt at Final Analysis\n power_final <-\n pnorm(\n (lhr_null - qnorm(1 - falsepos) * se[nstage] - lhr_alt) / se[nstage]\n )\n\n # calculate the levels of the two-sided CIs used to monitor the OS log-HR\n # at each interim analysis and the corresponding one-sided false positive error rate\n # assuming we want marginal power = power_int to 'rule out' hr_Lnull at required\n # evidentiary level when true OS HR = hr_alt\n gamma <-\n 2 *\n (1 - pnorm(((lhr_null - lhr_alt) / se[1:(nstage - 1)]) - qnorm(power_int)))\n falsepos_all <- c(gamma / 2, falsepos)\n ci_level_monit_null <- 100 * (1 - 2 * falsepos_all)\n power_all <-\n c(rep(power_int, times = (nstage - 1)), power_final)\n\n lhr_pos <- lhr_null - qnorm(1 - falsepos_all) * se\n\n # Given the positivity thresholds, re-express these via Bayesian metrics\n post_pos <- calc_posterior(lhr_pos, lhr_null, events)\n pred_pos <- calc_predictive(lhr_pos, events)\n\n summary <- data.frame(\"Deaths\" = events)\n\n # OS HR thresholds for positivity\n summary$\"OS HR threshold for positivity\" <- round(exp(lhr_pos), 3)\n\n # One sided false positive error_rate at each analysis\n summary$\"One-sided false positive error rate\" <- round(falsepos_all, 3)\n\n # Level of 2-sided CI needed to rule out δnull at given analysis (%)\n summary$\"Level of 2-sided CI needed to rule out delta null\" <- round(\n pmax(0, ci_level_monit_null),\n 0\n )\n\n # Probability of meeting positivity threshold under plausible OS benefit\n summary$\"Probability of meeting positivity threshold under delta alt\" <- round(\n power_all,\n 3\n )\n\n # Pr(true OS HR >= detrimental OS HR | current data)\n summary$\"Posterior probability the true OS HR exceeds delta null given the data\" <- round(\n post_pos,\n 3\n )\n summary$\"Predictive probability the OS HR estimate at Final Analysis does not exceed the positivity threshold\" <- c(\n round(pred_pos * 100, 3),\n NA\n )\n\n if (!is.null(hr_marg_benefit)) {\n # calculate the probability of meeting positivity thresholds under lhr_marg_benefit\n summary$\"Probability of meeting positivity threshold under incremental benefit\" <-\n round(\n meeting_probs(\n summary = summary,\n lhr_pos = lhr_pos,\n lhr_target = log(hr_marg_benefit),\n rand_ratio = rand_ratio\n ),\n 3\n )\n }\n\n return(list(\n lhr_null = lhr_null,\n lhr_alt = lhr_alt,\n lhr_pos = lhr_pos,\n summary = summary\n ))\n}\n","type":"text"},{"name":"R/logo_data.R","content":"# Generated by tools/build_shinylive.R. Do not edit by hand.\nlogo_src <- \"data:image/png;base64,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\"\n","type":"text"},{"name":"R/posteriors.R","content":"#' @title Function which calculates for k=1, ..., K, Pr(log-HR >= lhr_null | theta.hat.k = lhr_con.k)\n#' @name calc_posterior\n#' @description i.e. the posterior probability the true OS log-hr exceeds the minimum unacceptable\n#' OS log-HR given the estimate of the log-hr at analysis k equals lhr_con.k (i.e. the estimate\n#' is equal to the stage k 'continuation threshold').\n#' @param lhr_con vector of length K (# number of looks at OS data) containing 'continuation' thresholds on log-HR scale\n#' @param lhr_null scalar - minimum unacceptable OS log-HR\n#' @param events vector length K - number of OS events at each look at the data\n#' @importFrom stats pnorm\n#' @export\n#' @return vector of length K - continuation thresholds expressed on posterior probability scale\n#' @examples\n#' lhr_con <- c(0.2, 0.15, 0.1)\n#' lhr_null <- 0.25\n#' events <- c(100, 200, 300)\n#' calc_posterior(lhr_con, lhr_null, events)\ncalc_posterior <- function(lhr_con, lhr_null, events) {\n info <-\n events / 4 # Fisher's information for log-HR at each analysis\n se <-\n sqrt(1 / info) # asymptotic standard error for log-HR at each analysis\n\n # calculating Pr(log-hr >= lhr_null | theta.hat.k = lk)\n # where lk is the threshold (for the partial likelihood estimate of the OS log-HR) for 'continuation'\n 1 - pnorm((lhr_null - lhr_con) / se)\n}\n\n#' @title Calculate posterior predictive probability of ruling out lhr_null at final OS analysis\n#' @name calc_predictive\n#' @description Calculates the posterior predictive probability of 'ruling out' lhr_null at final OS analysis\n#' given current estimate of OS log-HR is lhr_cont_k, for k=1, ..., K-1\n#' @param lhr_con vector of length K (# number of looks at OS data) containing 'continuation' thresholds on log-HR scale\n#' @param events vector length K - number of OS events at each look at the data\n#' @return vector of length K-1: continuation thresholds at analyses k=1, ..., K-1 expressed on scale of\n#' posterior predictive probability of ruling out lhr_null at final OS analysis\n#' @importFrom stats pnorm\n#' @export\n#' @examples\n#' lhr_con <- c(0.2, 0.15, 0.1)\n#' events <- c(100, 200, 300)\n#' calc_predictive(lhr_con, events)\ncalc_predictive <- function(lhr_con, events) {\n nstage <- length(events)\n\n info <-\n events / 4 # Fisher's information for log-HR at each analysis\n\n # calculating Pr(ZK <= lK*sqrt(info.K) | Z.k = sqrt(info.k)*lk)\n # where lk is the OS log-HR threshold for 'continuation' at analysis k\n pred_pos <- vapply(1:(nstage - 1), function(i) {\n pnorm(\n lhr_con[nstage] * sqrt(info[nstage]),\n mean = lhr_con[i] * sqrt(info[i]) * sqrt(info[nstage] / info[i]),\n sd = sqrt((info[nstage] - info[i]) / info[i])\n )\n }, numeric(1))\n\n return(pred_pos)\n}\n","type":"text"},{"name":"R/probs.R","content":"#' Probabilities of meeting positivity threshold under target HR\n#'\n#' @param lhr_pos List. Log HRs for positive threshold\n#' @param summary DataFrame. Summary dataframe from bounds.R\n#' @param lhr_target Scalar. Target log HR to calculate the probability of meeting positivity thresholds\n#' @param rand_ratio Integer. If patients are randomized k:1 between experimental intervention and control,\n#' rand_ratio should be inputted as k.\n#' Example: if patients are randomized 1:1 between experimental and control, k=1. If patients are randomized 2:1\n#' between experimental and control, k=2.\n#'\n#' @return Array. Probabilities of meeting positivity threshold under target HR\n#' @export\nmeeting_probs <-\n function(summary, lhr_pos, lhr_target = 1, rand_ratio = 1) {\n events <- summary$Deaths\n info <-\n rand_ratio * events / ((rand_ratio + 1)^2) # Fisher's information for log-HR at each analysis\n se <-\n sqrt(1 / info) # asymptotic standard error for log-HR at each analysis\n prob <- vapply(seq_along(events), function(i) {\n pnorm(lhr_pos[i], mean = lhr_target, sd = se[i], lower.tail = TRUE)\n }, numeric(1))\n return(prob)\n }\n","type":"text"},{"name":"logo.png","content":"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","type":"binary"}] +[{"name":"app.R","content":"# Generated by tools/build_shinylive.R. Do not edit by hand.\n\nlibrary(bslib)\nlibrary(shiny)\n\nsource(\"R/posteriors.R\", local = TRUE)\nsource(\"R/probs.R\", local = TRUE)\nsource(\"R/bounds.R\", local = TRUE)\nsource(\"R/ui.R\", local = TRUE)\nsource(\"R/server.R\", local = TRUE)\n\nui <- app_ui()\nserver <- app_server\n\nshinyApp(ui, server)\n","type":"text"},{"name":"R/server.R","content":"#' Shiny app server function\n#'\n#' @description Server-side logic for the monitOS Shiny application. Computes OS\n#' monitoring boundaries reactively based on user inputs and renders the\n#' summary table of positivity thresholds.\n#'\n#' @param input Shiny input object containing UI widget values.\n#' @param output Shiny output object for rendering results.\n#' @param session Shiny session object for updating UI elements.\n#' @import shiny\n# nocov start\napp_server <- function(input, output, session) {\n primary_rows <- reactiveVal(list(\n list(id = 1L, value = 28),\n list(id = 2L, value = 42)\n ))\n next_primary_id <- reactiveVal(3L)\n\n get_primary_events <- function(rows = primary_rows()) {\n vapply(rows, function(row) {\n input_value <- input[[paste0(\"eventPA_\", row$id)]]\n if (is.null(input_value) || is.na(input_value)) row$value else input_value\n }, numeric(1))\n }\n\n output$primary_events_ui <- renderUI({\n rows <- primary_rows()\n current_values <- get_primary_events(rows)\n\n div(\n class = \"event-editor\",\n div(\n class = \"event-row-head\",\n div(\"Planned look\"),\n div(\"Expected deaths\"),\n div()\n ),\n div(\n class = \"event-row-list\",\n lapply(seq_along(rows), function(i) {\n row <- rows[[i]]\n div(\n class = \"event-row\",\n div(class = \"event-row-label\", paste(\"Interim analysis\", i)),\n div(\n class = \"event-row-control\",\n numericInput(\n inputId = paste0(\"eventPA_\", row$id),\n label = NULL,\n value = current_values[i],\n min = 1\n )\n ),\n if (length(rows) > 1) {\n actionLink(\n inputId = paste0(\"remove_event_row_\", row$id),\n label = HTML(\"✕\"),\n class = \"event-remove-link\",\n title = \"Remove interim analysis\"\n )\n } else {\n div()\n }\n )\n })\n ),\n div(\n class = \"event-footer\",\n actionLink(\n \"add_event_row\",\n HTML(\"+ Add interim analysis\"),\n class = \"event-add-link\"\n )\n )\n )\n })\n\n observeEvent(input$add_event_row, {\n rows <- primary_rows()\n current_values <- get_primary_events(rows)\n updated_rows <- Map(function(row, value) {\n row$value <- value\n row\n }, rows, current_values)\n new_id <- next_primary_id()\n updated_rows[[length(updated_rows) + 1]] <- list(id = new_id, value = NA_real_)\n primary_rows(updated_rows)\n next_primary_id(new_id + 1L)\n })\n\n observe({\n rows <- primary_rows()\n\n lapply(rows, function(row) {\n observeEvent(input[[paste0(\"remove_event_row_\", row$id)]], {\n current_rows <- primary_rows()\n current_values <- get_primary_events(current_rows)\n current_rows <- Map(function(existing_row, value) {\n existing_row$value <- value\n existing_row\n }, current_rows, current_values)\n\n if (length(current_rows) > 1) {\n keep <- vapply(current_rows, function(existing_row) existing_row$id != row$id, logical(1))\n primary_rows(current_rows[keep])\n }\n }, ignoreInit = TRUE)\n })\n })\n\n summary_data <- reactive({\n rows <- primary_rows()\n primary_events <- get_primary_events(rows)\n\n validate(\n need(length(primary_events) > 0, \"Enter at least one interim-analysis expected death count.\"),\n need(all(!is.na(primary_events)), \"Every interim-analysis row needs a numeric expected death count.\"),\n need(!is.na(input$eventOS), \"Targeted deaths at final OS analysis must be numeric.\"),\n need(all(diff(primary_events) >= 0), \"Interim-analysis expected deaths should increase across planned looks.\"),\n need(\n input$eventOS >= max(primary_events),\n \"Targeted deaths at final OS analysis must be greater than or equal to the last interim analysis.\"\n )\n )\n\n primary_rows(Map(function(row, value) {\n row$value <- value\n row\n }, rows, primary_events))\n\n summary <- bounds(\n events = c(primary_events, input$eventOS),\n power_int = input$power_int,\n falsepos = input$falsepos,\n hr_null = input$hr_null,\n hr_alt = input$hr_alt,\n rand_ratio = input$rand_ratio,\n hr_marg_benefit = input$hr_marg_benefit\n )$summary\n\n ci_col <- \"Level of 2-sided CI needed to rule out delta null\"\n alt_col <- \"Probability of meeting positivity threshold under delta alt\"\n summary[[ci_col]] <- paste0(summary[[ci_col]], \"%\")\n names(summary)[names(summary) == ci_col] <-\n \"Level of 2-sided CI needed to rule out the unacceptable detriment\"\n names(summary)[names(summary) == alt_col] <-\n \"Probability of meeting positivity threshold under the plausible beneficial effect\"\n\n summary[, setdiff(seq_along(summary), c(6, 7)), drop = FALSE]\n })\n\n output$final_threshold_summary <- renderText({\n summary <- summary_data()\n final_row <- summary[nrow(summary), , drop = FALSE]\n paste0(\n \"Observed OS HR must be at or below \",\n sprintf(\"%.3f\", final_row[[\"OS HR threshold for positivity\"]]),\n \".\"\n )\n })\n\n output$analysis_count_summary <- renderText({\n interim_n <- length(primary_rows())\n total_n <- interim_n + 1\n paste0(interim_n, \" interim + 1 final analysis (\", total_n, \" total).\")\n })\n\n output$bounds <- renderUI({\n summary <- summary_data()\n header <- tags$tr(lapply(names(summary), function(name) tags$th(name)))\n body <- lapply(seq_len(nrow(summary)), function(i) {\n row_class <- if (i == nrow(summary)) \"final-analysis-row\" else NULL\n tags$tr(\n class = row_class,\n lapply(summary[i, , drop = TRUE], function(value) tags$td(as.character(value)))\n )\n })\n\n tags$table(\n class = \"table\",\n tags$thead(header),\n tags$tbody(body)\n )\n })\n}\n# nocov end\n","type":"text"},{"name":"R/ui.R","content":"#' Shiny app UI definition\n#'\n#' @description User interface for the monitOS Shiny application. Provides a\n#' branded, guided workflow for setting OS monitoring parameters and\n#' reviewing the resulting positivity thresholds.\n#'\n#' @param request Shiny internal request object for bookmarking support.\n#' @import shiny\n#' @import bslib\n# nocov start\napp_ui <- function(request = NULL) {\n theme <- bslib::bs_theme(\n version = 5,\n bg = \"#FCFCFC\",\n fg = \"#161616\",\n primary = \"#FF4E00\",\n secondary = \"#A5BFF5\"\n )\n\n bslib::page_fluid(\n theme = theme,\n title = \"monitOS\",\n lang = \"en\",\n tags$head(\n tags$style(HTML(\"\n :root {\n --monitos-bg: #FCFCFC;\n --monitos-fg: #161616;\n --monitos-orange: #FF4E00;\n --monitos-grey-1: #DADADA;\n --monitos-grey-2: #F5F5F5;\n --monitos-blue: #A5BFF5;\n --monitos-pink: #EDA8D1;\n --monitos-shadow: 0 16px 36px rgba(22, 22, 22, 0.05);\n --monitos-radius: 22px;\n }\n\n body {\n background:\n radial-gradient(circle at top left, rgba(165, 191, 245, 0.18), transparent 28%),\n linear-gradient(180deg, #ffffff 0%, var(--monitos-bg) 42%, #ffffff 100%);\n color: var(--monitos-fg);\n font-family: 'Avenir Next', 'Segoe UI', 'Helvetica Neue', sans-serif;\n }\n\n h1, h2, h3, h4, .display-title {\n font-family: 'Iowan Old Style', 'Palatino Linotype', 'Book Antiqua', serif;\n letter-spacing: -0.02em;\n }\n\n .page-shell {\n max-width: 1360px;\n margin: 0 auto;\n padding: 1.25rem 0 2.75rem;\n }\n\n .hero-card,\n .panel-card {\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: var(--monitos-radius);\n background: rgba(255, 255, 255, 0.96);\n box-shadow: var(--monitos-shadow);\n }\n\n .hero-card {\n position: relative;\n padding: 1.75rem 1.9rem;\n margin-bottom: 1.5rem;\n background:\n linear-gradient(135deg, rgba(165, 191, 245, 0.22), rgba(255, 255, 255, 0.96) 52%, rgba(237, 168, 209, 0.14) 100%);\n }\n\n .hero-grid {\n position: relative;\n z-index: 1;\n }\n\n .hero-eyebrow {\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n margin-bottom: 0.7rem;\n padding: 0;\n border-radius: 999px;\n color: var(--monitos-orange);\n font-size: 0.78rem;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .hero-title {\n max-width: 14ch;\n margin: 0 0 0.7rem;\n font-size: clamp(2.1rem, 3.4vw, 3.6rem);\n line-height: 1;\n }\n\n .hero-copy {\n max-width: 42rem;\n margin: 0;\n font-size: 1rem;\n line-height: 1.65;\n color: rgba(22, 22, 22, 0.78);\n }\n\n .app-grid {\n display: grid;\n gap: 1.25rem;\n }\n\n .panel-card {\n padding: 1.2rem 1.2rem 1.3rem;\n }\n\n .panel-card h2,\n .panel-card h3 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n }\n\n .section-kicker {\n display: inline-block;\n margin-bottom: 0.55rem;\n color: rgba(255, 78, 0, 0.82);\n font-size: 0.8rem;\n font-weight: 500;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n }\n\n .lede {\n color: rgba(22, 22, 22, 0.72);\n line-height: 1.65;\n }\n\n .field-grid {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(220px, 0.72fr);\n gap: 1rem;\n align-items: start;\n }\n\n .field-note {\n padding-top: 0.35rem;\n color: rgba(22, 22, 22, 0.72);\n line-height: 1.55;\n }\n\n .field-note strong {\n color: var(--monitos-fg);\n font-weight: 700;\n }\n\n .field-note em {\n color: rgba(22, 22, 22, 0.64);\n font-style: normal;\n }\n\n .subtle-list {\n margin: 0.4rem 0 0;\n padding-left: 1rem;\n color: var(--monitos-fg);\n line-height: 1.6;\n }\n\n .shiny-input-container {\n width: 100%;\n margin-bottom: 0;\n }\n\n .field-stack {\n display: grid;\n gap: 1rem;\n }\n\n .field-stack .form-control,\n .field-stack .irs,\n .field-stack .selectize-input {\n margin-top: 0.35rem;\n }\n\n .event-editor {\n margin-top: 0.35rem;\n padding: 0.65rem 0.7rem 0.45rem;\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: 14px;\n background: rgba(252, 252, 252, 0.92);\n }\n\n .event-row-head,\n .event-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 120px 32px;\n gap: 0.75rem;\n align-items: center;\n }\n\n .event-row-head {\n padding: 0 0.2rem 0.35rem;\n color: rgba(22, 22, 22, 0.52);\n font-size: 0.72rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n }\n\n .event-row-list {\n display: grid;\n gap: 0.4rem;\n }\n\n .event-row {\n padding: 0.42rem 0.55rem;\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: 10px;\n background: #fff;\n }\n\n .event-row .shiny-input-container {\n margin-bottom: 0;\n }\n\n .event-row-label {\n color: rgba(22, 22, 22, 0.78);\n font-size: 0.9rem;\n font-weight: 500;\n white-space: nowrap;\n }\n\n .event-row-control .form-control {\n max-width: 120px;\n height: 36px;\n padding: 0.3rem 0.55rem;\n }\n\n .event-footer {\n display: flex;\n justify-content: flex-start;\n margin-top: 0.45rem;\n padding: 0.15rem 0.15rem 0;\n }\n\n .event-add-link {\n color: var(--monitos-orange);\n font-size: 0.88rem;\n font-weight: 600;\n text-decoration: none;\n }\n\n .event-add-link:hover,\n .event-add-link:focus {\n color: var(--monitos-fg);\n text-decoration: none;\n }\n\n .helper-copy {\n margin-top: 0.5rem;\n color: rgba(22, 22, 22, 0.68);\n line-height: 1.55;\n }\n\n .inline-note {\n color: rgba(22, 22, 22, 0.62);\n font-size: 0.92rem;\n line-height: 1.55;\n }\n\n .results-card {\n position: sticky;\n top: 0;\n z-index: 5;\n padding: 0.95rem 1rem 1rem;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n\n .results-card .section-kicker {\n margin-bottom: 0.35rem;\n }\n\n .results-card h2 {\n margin-bottom: 0.35rem;\n font-size: 1.35rem;\n }\n\n .results-card .inline-note {\n margin-bottom: 0;\n font-size: 0.86rem;\n line-height: 1.4;\n }\n\n .summary-strip {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 0.55rem;\n margin-top: 0.65rem;\n }\n\n .summary-item {\n padding: 0.55rem 0.7rem;\n border-radius: 12px;\n background: rgba(245, 245, 245, 0.95);\n border: 1px solid rgba(22, 22, 22, 0.06);\n }\n\n .summary-item-label {\n display: block;\n margin-bottom: 0.2rem;\n color: rgba(22, 22, 22, 0.56);\n font-size: 0.76rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n }\n\n .summary-item-value {\n color: var(--monitos-fg);\n font-size: 0.9rem;\n line-height: 1.35;\n font-weight: 600;\n }\n\n .form-stack {\n display: grid;\n gap: 1rem;\n }\n\n .form-stack .panel-card {\n width: 100%;\n }\n\n .results-frame {\n margin-top: 0.75rem;\n max-height: min(38vh, 18rem);\n overflow: auto;\n border: 1px solid rgba(22, 22, 22, 0.08);\n border-radius: 16px;\n background: #fff;\n }\n\n .results-card table {\n width: 100%;\n margin: 0;\n font-size: 0.88rem;\n }\n\n .results-card thead th {\n position: sticky;\n top: 0;\n z-index: 1;\n padding: 0.6rem 0.65rem;\n border: 0;\n background: rgba(245, 245, 245, 0.98);\n color: var(--monitos-fg);\n font-size: 0.78rem;\n font-weight: 700;\n line-height: 1.3;\n }\n\n .results-card tbody td {\n padding: 0.6rem 0.65rem;\n vertical-align: top;\n border-color: rgba(22, 22, 22, 0.08);\n line-height: 1.4;\n }\n\n .results-card tbody tr:nth-child(odd) td {\n background: rgba(252, 252, 252, 0.95);\n }\n\n .results-card tbody tr.final-analysis-row td {\n background: rgba(255, 78, 0, 0.08);\n font-weight: 600;\n }\n\n .results-card .table {\n margin-bottom: 0;\n }\n\n .event-remove-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: 1px solid rgba(22, 22, 22, 0.1);\n border-radius: 999px;\n color: rgba(22, 22, 22, 0.56);\n font-size: 0.82rem;\n line-height: 1;\n text-decoration: none;\n background: rgba(245, 245, 245, 0.9);\n transition: color 120ms ease, border-color 120ms ease, background 120ms ease;\n }\n\n .event-remove-link:hover,\n .event-remove-link:focus {\n color: var(--monitos-orange);\n border-color: rgba(255, 78, 0, 0.28);\n background: rgba(255, 78, 0, 0.06);\n }\n\n .reference-links {\n display: grid;\n gap: 0.65rem;\n margin-top: 0.9rem;\n }\n\n .reference-links a {\n color: var(--monitos-fg);\n font-weight: 700;\n text-decoration: none;\n }\n\n .reference-links a:hover,\n .reference-links a:focus {\n color: var(--monitos-orange);\n }\n\n .support-card details + details {\n margin-top: 0.75rem;\n }\n\n .support-card summary {\n cursor: pointer;\n color: var(--monitos-fg);\n font-weight: 600;\n list-style: none;\n }\n\n .support-card summary::-webkit-details-marker {\n display: none;\n }\n\n .support-card .details-body {\n margin-top: 0.7rem;\n }\n\n .list-tight {\n margin: 0;\n padding-left: 1.15rem;\n line-height: 1.7;\n }\n\n @media (max-width: 991px) {\n .field-grid {\n grid-template-columns: 1fr;\n }\n\n .event-row {\n grid-template-columns: 1fr;\n gap: 0.55rem;\n }\n\n .event-row-head {\n display: none;\n }\n\n .results-card {\n position: static;\n }\n\n .results-frame {\n max-height: none;\n }\n }\n \"))\n ),\n div(\n class = \"page-shell\",\n div(\n class = \"hero-card\",\n div(\n class = \"hero-grid\",\n div(\n span(class = \"hero-eyebrow\", \"Monitoring overall survival\"),\n h1(class = \"hero-title\", \"Configure OS monitoring thresholds.\"),\n p(\n class = \"hero-copy\",\n paste(\n \"Translate effect-size assumptions and event timing into\",\n \"positivity thresholds for interim and final OS reviews.\"\n )\n )\n )\n )\n ),\n div(\n class = \"app-grid\",\n div(\n class = \"panel-card results-card\",\n span(class = \"section-kicker\", \"Positivity thresholds\"),\n h2(\"Review the monitoring table\"),\n p(\n class = \"inline-note\",\n \"Updates automatically as you change assumptions.\"\n ),\n div(\n class = \"summary-strip\",\n div(\n class = \"summary-item\",\n span(class = \"summary-item-label\", \"Final Analysis\"),\n span(class = \"summary-item-value\", textOutput(\"final_threshold_summary\", container = span))\n ),\n div(\n class = \"summary-item\",\n span(class = \"summary-item-label\", \"Planned Looks\"),\n span(class = \"summary-item-value\", textOutput(\"analysis_count_summary\", container = span))\n )\n ),\n div(class = \"results-frame\", uiOutput(\"bounds\"))\n ),\n div(\n class = \"form-stack\",\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Workflow\"),\n h2(\"Set assumptions, then review the threshold\"),\n p(\n class = \"inline-note\",\n \"1. Set effect sizes. 2. Add interim expected death counts. 3. Review the final-analysis threshold and interim operating characteristics.\"\n ),\n p(class = \"helper-copy\", \"HR below 1 indicates benefit.\")\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Trial assumptions\"),\n h3(\"Clinically meaningful effect sizes\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"hr_null\",\n \"Unacceptable detrimental effect on OS\",\n min = 1,\n max = 2,\n value = 4 / 3,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Use for: \"),\n \"choose the OS hazard ratio that would already be too harmful to accept. \",\n tags$em(\"Example: set this to 1.33 if any HR at or above 1.33 would be unacceptable.\")\n )\n ),\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"hr_alt\",\n \"Plausible beneficial effect on OS\",\n min = 0.05,\n max = 1,\n value = 0.7,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Use for: \"),\n \"reflect the benefit you could reasonably expect from prior evidence or mechanism of action. \",\n tags$em(\"Example: use 0.90 if a 10% OS hazard reduction would still be a plausible beneficial effect.\")\n )\n )\n )\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Design inputs\"),\n h3(\"Event timing\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n numericInput(\n \"eventOS\",\n \"Targeted deaths at final OS analysis\",\n value = 70,\n min = 1\n ),\n p(\n class = \"helper-copy\",\n \"Use the longest feasible follow-up window when setting the targeted deaths for the final OS analysis.\"\n )\n ),\n div(\n tags$label(\n `for` = \"primary_events_ui\",\n \"Interim OS analysis schedule\"\n ),\n uiOutput(\"primary_events_ui\"),\n p(\n class = \"helper-copy\",\n \"Add one row per planned look, ordered by increasing expected deaths.\"\n )\n )\n )\n )\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Decision thresholds\"),\n h3(\"Statistical threshold settings\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"falsepos\",\n \"One-sided false positive rate at final analysis\",\n min = 0,\n max = 0.3,\n value = 0.1,\n step = 0.005\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Interpretation: \"),\n paste(\n \"this controls the final-analysis threshold for positivity.\",\n \"It is the risk of being wrongly reassured when the true OS HR\",\n \"is in fact equal to your unacceptable detriment.\"\n )\n )\n ),\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"power_int\",\n \"Required power at interim analyses under the plausible beneficial effect\",\n min = 0.7,\n max = 1,\n value = 0.9,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Interpretation: \"),\n paste(\n \"this controls the interim analysis threshold for positivity.\",\n \"It is the chance of being correctly reassured when the true OS HR\",\n \"is in fact equal to your plausible beneficial effect.\"\n ),\n tags$br(),\n tags$em(\n \"Typical choices include 0.80 or 0.90, depending on the possible impact on the program of wrongly flagging a potential detriment at the interim OS analysis.\"\n )\n )\n )\n )\n ),\n div(\n class = \"panel-card\",\n span(class = \"section-kicker\", \"Optional sensitivity inputs\"),\n h3(\"Stress-test the plan\"),\n div(\n class = \"field-stack\",\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"hr_marg_benefit\",\n \"Marginal-benefit OS HR\",\n min = 0.7,\n max = 1.1,\n value = 0.95,\n step = 0.01\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"When to use: \"),\n \"use this when the drug may still be valuable under a smaller OS benefit than your plausible beneficial effect.\"\n )\n ),\n div(\n class = \"field-grid\",\n div(\n sliderInput(\n \"rand_ratio\",\n \"Randomization ratio (experimental : control)\",\n min = 1,\n max = 3,\n value = 1,\n step = 1\n )\n ),\n div(\n class = \"field-note\",\n tags$strong(\"Example: \"),\n \"use 1 for 1:1 randomization and 2 for 2:1 randomization.\"\n )\n )\n )\n ),\n div(\n class = \"panel-card support-card\",\n span(class = \"section-kicker\", \"Support\"),\n h3(\"Method notes\"),\n tags$details(\n tags$summary(\"How to read the table\"),\n div(\n class = \"details-body\",\n tags$ul(\n class = \"list-tight\",\n tags$li(\"OS HR threshold for positivity: the observed HR must be at or below this value.\"),\n tags$li(\"One-sided false positive error rate: the tolerated risk of passing when the true effect equals your unacceptable detriment.\"),\n tags$li(\"Level of 2-sided CI needed to rule out the unacceptable detriment: the confidence level required to exclude that detriment.\"),\n tags$li(\"Probability of meeting positivity threshold under the plausible beneficial effect: the chance of passing when the treatment effect equals your plausible beneficial effect.\")\n )\n )\n ),\n tags$details(\n tags$summary(\"Paper and package resources\"),\n div(\n class = \"details-body reference-links\",\n tags$a(href = \"https://www.tandfonline.com/doi/full/10.1080/19466315.2024.2365648\", target = \"_blank\", rel = \"noopener noreferrer\", \"Read the monitOS paper\"),\n tags$a(href = \"https://opensource.nibr.com/monitOS/articles/monitOS.html\", target = \"_blank\", rel = \"noopener noreferrer\", \"Open the package vignette\"),\n tags$a(href = \"https://github.com/Novartis/monitOS/issues\", target = \"_blank\", rel = \"noopener noreferrer\", \"Report an issue or request help\")\n )\n )\n )\n )\n )\n )\n )\n}\n# nocov end\n","type":"text"},{"name":"R/bounds.R","content":"#' @title Bounds\n#'\n#' @description OS monitoring guidelines as proposed in manuscript \"Monitoring Overall Survival in Pivotal Trials in\n#' Indolent Cancers\". Calculate thresholds for positivity that can be used at an analysis to judge whether emerging\n#' evidence about the effect of treatment on OS is concerning or not. The threshold for positivity at any given analysis\n#' is the value below which the observed hazard ratio must be in order to provide sufficient reassurance that the effect\n#' on OS does not reach the selected unacceptable level of detriment (the margin hr_null).\n#' Terminology follows the manuscript \"Monitoring Overall Survival in Pivotal Trials in Indolent Cancers\"\n#' @details Monitoring guidelines assume that the hazard ratio (HR) can adequately summarize the size of the benefits\n#' and harms of the experimental\n#' intervention vs control on overall survival (OS). Furthermore, guidelines assume that an OS HR < 1 is consistent\n#' with a beneficial effect of the intervention on OS (and smaller OS HRs <1 indicate increased efficacy).\n#' @param events Vector. Target number of deaths at each analysis\n#' @param power_int Scalar. Marginal power required at the Primary Analysis when true hazard ratio (HR) is hr_alt.\n#' @param falsepos Scalar. Marginal one-sided false positive error rate we are prepared to tolerate at the Final Analysis.\n#' Determines the positivity threshold at Final Analysis\n#' @param hr_null Scalar. The unacceptably large detrimental effect of treatment on OS we want to rule out (on HR scale)\n#' @param hr_alt Scalar. Plausible clinically relevant beneficial effect of treatment on OS (on HR scale)\n#' @param rand_ratio Integer. If patients are randomized k:1 between experimental intervention and control, rand_ratio\n#' should be inputted as k.\n#' Example: if patients are randomized 1:1 between experimental and control, k=1. If patients are randomized 2:1\n#' between experimental and control, k=2.\n#' @param hr_marg_benefit Scalar. We may be uncertain about what a plausible beneficial effect of treatment on OS is.\n#' User can enter a (on HR scale) and function will evaluate the probability we meet the positivity threshold\n#' at each analysis under\n#' this HR. This second OS benefit will\n#' usually be closer to 1 than hr_alt.\n#' @importFrom stats pnorm qnorm\n#' @return List that contains:\n#' * `lhr_null`: Scalar, unacceptable OS log-HR,\n#' * `lhr_alt`: Scalar, plausible clinically relevant log-HR,\n#' * `lhr_pos`: Scalar, positivity thresholds for log-HR estimates,\n#' * `summary`: Dataframe, which contains:\n#' * `OS HR threshold for positivity`,\n#' * `One sided false positive error rate`,\n#' * `Level of 2 sided CI needed to rule out hr_null`,\n#' * `Probability of meeting positivity threshold under hr_alt`,\n#' * `Positivity_Thres_Posterior`: Pr(true OS HR >= minimum unacceptable OS HR | current data),\n#' * `Positivity_Thres_PredProb`: Pr(OS HR estimate at Final Analysis <= Final Analysis positivity threshold | current data)\n#' @export\n#' @examples\n#' # Example 01: OS monitoring guideline retrospectively applied to Motivating Example 1\n#' # with delta null = 1.3, delta alt = 0.80, gamma_FA = 0.025 and beta_PA = 0.10.\n#' bounds(\n#' events = c(60, 89, 110, 131, 178),\n#' power_int = 0.9, # beta_PA\n#' falsepos = 0.025, # gamma_FA\n#' hr_null = 1.3, # delta_null\n#' hr_alt = 0.8, # delta_alt\n#' rand_ratio = 1, # rand_ratio\n#' hr_marg_benefit = NULL\n#' )\n#' # Example 02: OS monitoring guideline applied to Motivating Example 2\n#' # with delta null = 4/3, delta alt = 0.7, gamma_FA = 0.20 and beta_PA = 0.1.\n#' bounds(\n#' events = c(60, 89, 110, 131, 178),\n#' power_int = 0.9, # beta_PA\n#' falsepos = 0.025, # gamma_FA\n#' hr_null = 1.3, # delta_null\n#' hr_alt = 0.8, # delta_alt\n#' rand_ratio = 1, # rand_ratio\n#' hr_marg_benefit = 0.95\n#' )\nbounds <- function(\n events,\n # OS events at each analysis\n power_int = 0.9,\n # 1-Beta PA, what power do we want to not flag a safety concern at an interim analysis if the true\n # OS HR equals our target alternative?\n falsepos = 0.025,\n # Gamme FA, What is the (one-sided) type I error rate that we will accept at the final analysis?\n hr_null = 1.3,\n # Delta null, what is the minimum unacceptable OS HR?\n hr_alt = 0.9,\n # Delta alt, what is a plausible alternative OS HR consistent with OS benefit?\n rand_ratio = 1,\n # for every patient randomized to control, rand_ratio patients are allocated to experimental intervention\n hr_marg_benefit = NULL\n # evaluate probability of meeting positivity thresholds under a second plausible beneficial effect of\n # treatment on OS (HR = hr_marg_benefit)\n) {\n # Log scale\n lhr_null <- log(hr_null)\n lhr_alt <- log(hr_alt)\n\n # Init variables\n nstage <- length(events) # total number of analyses planned\n info <-\n rand_ratio * events / ((rand_ratio + 1)^2) # Fisher's information for log-HR at each analysis\n se <-\n sqrt(1 / info) # asymptotic standard error for log-HR at each analysis\n\n # Calculate the attained power when true HR = hr_alt at Final Analysis\n power_final <-\n pnorm(\n (lhr_null - qnorm(1 - falsepos) * se[nstage] - lhr_alt) / se[nstage]\n )\n\n # calculate the levels of the two-sided CIs used to monitor the OS log-HR\n # at each interim analysis and the corresponding one-sided false positive error rate\n # assuming we want marginal power = power_int to 'rule out' hr_Lnull at required\n # evidentiary level when true OS HR = hr_alt\n gamma <-\n 2 *\n (1 - pnorm(((lhr_null - lhr_alt) / se[1:(nstage - 1)]) - qnorm(power_int)))\n falsepos_all <- c(gamma / 2, falsepos)\n ci_level_monit_null <- 100 * (1 - 2 * falsepos_all)\n power_all <-\n c(rep(power_int, times = (nstage - 1)), power_final)\n\n lhr_pos <- lhr_null - qnorm(1 - falsepos_all) * se\n\n # Given the positivity thresholds, re-express these via Bayesian metrics\n post_pos <- calc_posterior(lhr_pos, lhr_null, events)\n pred_pos <- calc_predictive(lhr_pos, events)\n\n summary <- data.frame(\"Deaths\" = events)\n\n # OS HR thresholds for positivity\n summary$\"OS HR threshold for positivity\" <- round(exp(lhr_pos), 3)\n\n # One sided false positive error_rate at each analysis\n summary$\"One-sided false positive error rate\" <- round(falsepos_all, 3)\n\n # Level of 2-sided CI needed to rule out δnull at given analysis (%)\n summary$\"Level of 2-sided CI needed to rule out delta null\" <- round(\n pmax(0, ci_level_monit_null),\n 0\n )\n\n # Probability of meeting positivity threshold under plausible OS benefit\n summary$\"Probability of meeting positivity threshold under delta alt\" <- round(\n power_all,\n 3\n )\n\n # Pr(true OS HR >= detrimental OS HR | current data)\n summary$\"Posterior probability the true OS HR exceeds delta null given the data\" <- round(\n post_pos,\n 3\n )\n summary$\"Predictive probability the OS HR estimate at Final Analysis does not exceed the positivity threshold\" <- c(\n round(pred_pos * 100, 3),\n NA\n )\n\n if (!is.null(hr_marg_benefit)) {\n # calculate the probability of meeting positivity thresholds under lhr_marg_benefit\n summary$\"Probability of meeting positivity threshold under incremental benefit\" <-\n round(\n meeting_probs(\n summary = summary,\n lhr_pos = lhr_pos,\n lhr_target = log(hr_marg_benefit),\n rand_ratio = rand_ratio\n ),\n 3\n )\n }\n\n return(list(\n lhr_null = lhr_null,\n lhr_alt = lhr_alt,\n lhr_pos = lhr_pos,\n summary = summary\n ))\n}\n","type":"text"},{"name":"R/logo_data.R","content":"# Generated by tools/build_shinylive.R. Do not edit by hand.\nlogo_src <- \"data:image/png;base64,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\"\n","type":"text"},{"name":"R/posteriors.R","content":"#' @title Function which calculates for k=1, ..., K, Pr(log-HR >= lhr_null | theta.hat.k = lhr_con.k)\n#' @name calc_posterior\n#' @description i.e. the posterior probability the true OS log-hr exceeds the minimum unacceptable\n#' OS log-HR given the estimate of the log-hr at analysis k equals lhr_con.k (i.e. the estimate\n#' is equal to the stage k 'continuation threshold').\n#' @param lhr_con vector of length K (# number of looks at OS data) containing 'continuation' thresholds on log-HR scale\n#' @param lhr_null scalar - minimum unacceptable OS log-HR\n#' @param events vector length K - number of OS events at each look at the data\n#' @importFrom stats pnorm\n#' @export\n#' @return vector of length K - continuation thresholds expressed on posterior probability scale\n#' @examples\n#' lhr_con <- c(0.2, 0.15, 0.1)\n#' lhr_null <- 0.25\n#' events <- c(100, 200, 300)\n#' calc_posterior(lhr_con, lhr_null, events)\ncalc_posterior <- function(lhr_con, lhr_null, events) {\n info <-\n events / 4 # Fisher's information for log-HR at each analysis\n se <-\n sqrt(1 / info) # asymptotic standard error for log-HR at each analysis\n\n # calculating Pr(log-hr >= lhr_null | theta.hat.k = lk)\n # where lk is the threshold (for the partial likelihood estimate of the OS log-HR) for 'continuation'\n 1 - pnorm((lhr_null - lhr_con) / se)\n}\n\n#' @title Calculate posterior predictive probability of ruling out lhr_null at final OS analysis\n#' @name calc_predictive\n#' @description Calculates the posterior predictive probability of 'ruling out' lhr_null at final OS analysis\n#' given current estimate of OS log-HR is lhr_cont_k, for k=1, ..., K-1\n#' @param lhr_con vector of length K (# number of looks at OS data) containing 'continuation' thresholds on log-HR scale\n#' @param events vector length K - number of OS events at each look at the data\n#' @return vector of length K-1: continuation thresholds at analyses k=1, ..., K-1 expressed on scale of\n#' posterior predictive probability of ruling out lhr_null at final OS analysis\n#' @importFrom stats pnorm\n#' @export\n#' @examples\n#' lhr_con <- c(0.2, 0.15, 0.1)\n#' events <- c(100, 200, 300)\n#' calc_predictive(lhr_con, events)\ncalc_predictive <- function(lhr_con, events) {\n nstage <- length(events)\n\n info <-\n events / 4 # Fisher's information for log-HR at each analysis\n\n # calculating Pr(ZK <= lK*sqrt(info.K) | Z.k = sqrt(info.k)*lk)\n # where lk is the OS log-HR threshold for 'continuation' at analysis k\n pred_pos <- vapply(1:(nstage - 1), function(i) {\n pnorm(\n lhr_con[nstage] * sqrt(info[nstage]),\n mean = lhr_con[i] * sqrt(info[i]) * sqrt(info[nstage] / info[i]),\n sd = sqrt((info[nstage] - info[i]) / info[i])\n )\n }, numeric(1))\n\n return(pred_pos)\n}\n","type":"text"},{"name":"R/probs.R","content":"#' Probabilities of meeting positivity threshold under target HR\n#'\n#' @param lhr_pos List. Log HRs for positive threshold\n#' @param summary DataFrame. Summary dataframe from bounds.R\n#' @param lhr_target Scalar. Target log HR to calculate the probability of meeting positivity thresholds\n#' @param rand_ratio Integer. If patients are randomized k:1 between experimental intervention and control,\n#' rand_ratio should be inputted as k.\n#' Example: if patients are randomized 1:1 between experimental and control, k=1. If patients are randomized 2:1\n#' between experimental and control, k=2.\n#'\n#' @return Array. Probabilities of meeting positivity threshold under target HR\n#' @export\nmeeting_probs <-\n function(summary, lhr_pos, lhr_target = 1, rand_ratio = 1) {\n events <- summary$Deaths\n info <-\n rand_ratio * events / ((rand_ratio + 1)^2) # Fisher's information for log-HR at each analysis\n se <-\n sqrt(1 / info) # asymptotic standard error for log-HR at each analysis\n prob <- vapply(seq_along(events), function(i) {\n pnorm(lhr_pos[i], mean = lhr_target, sd = se[i], lower.tail = TRUE)\n }, numeric(1))\n return(prob)\n }\n","type":"text"},{"name":"logo.png","content":"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","type":"binary"}] diff --git a/inst/shinylive-app/R/server.R b/inst/shinylive-app/R/server.R index df47ec8..6c1474b 100644 --- a/inst/shinylive-app/R/server.R +++ b/inst/shinylive-app/R/server.R @@ -32,7 +32,7 @@ app_server <- function(input, output, session) { div( class = "event-row-head", div("Planned look"), - div("Deaths"), + div("Expected deaths"), div() ), div( @@ -113,11 +113,14 @@ app_server <- function(input, output, session) { primary_events <- get_primary_events(rows) validate( - need(length(primary_events) > 0, "Enter at least one interim-analysis event count."), - need(all(!is.na(primary_events)), "Every interim-analysis row needs a numeric death count."), - need(!is.na(input$eventOS), "Final-analysis deaths must be numeric."), - need(all(diff(primary_events) >= 0), "Interim-analysis deaths should increase across planned looks."), - need(input$eventOS >= max(primary_events), "Final-analysis deaths must be greater than or equal to the last interim analysis.") + need(length(primary_events) > 0, "Enter at least one interim-analysis expected death count."), + need(all(!is.na(primary_events)), "Every interim-analysis row needs a numeric expected death count."), + need(!is.na(input$eventOS), "Targeted deaths at final OS analysis must be numeric."), + need(all(diff(primary_events) >= 0), "Interim-analysis expected deaths should increase across planned looks."), + need( + input$eventOS >= max(primary_events), + "Targeted deaths at final OS analysis must be greater than or equal to the last interim analysis." + ) ) primary_rows(Map(function(row, value) { @@ -136,7 +139,12 @@ app_server <- function(input, output, session) { )$summary ci_col <- "Level of 2-sided CI needed to rule out delta null" + alt_col <- "Probability of meeting positivity threshold under delta alt" summary[[ci_col]] <- paste0(summary[[ci_col]], "%") + names(summary)[names(summary) == ci_col] <- + "Level of 2-sided CI needed to rule out the unacceptable detriment" + names(summary)[names(summary) == alt_col] <- + "Probability of meeting positivity threshold under the plausible beneficial effect" summary[, setdiff(seq_along(summary), c(6, 7)), drop = FALSE] }) diff --git a/inst/shinylive-app/R/ui.R b/inst/shinylive-app/R/ui.R index 58887c1..3576c45 100644 --- a/inst/shinylive-app/R/ui.R +++ b/inst/shinylive-app/R/ui.R @@ -270,20 +270,36 @@ app_ui <- function(request = NULL) { position: sticky; top: 0; z-index: 5; + padding: 0.95rem 1rem 1rem; border-top-left-radius: 0; border-top-right-radius: 0; } + .results-card .section-kicker { + margin-bottom: 0.35rem; + } + + .results-card h2 { + margin-bottom: 0.35rem; + font-size: 1.35rem; + } + + .results-card .inline-note { + margin-bottom: 0; + font-size: 0.86rem; + line-height: 1.4; + } + .summary-strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 0.75rem; - margin-top: 0.9rem; + gap: 0.55rem; + margin-top: 0.65rem; } .summary-item { - padding: 0.8rem 0.9rem; - border-radius: 14px; + padding: 0.55rem 0.7rem; + border-radius: 12px; background: rgba(245, 245, 245, 0.95); border: 1px solid rgba(22, 22, 22, 0.06); } @@ -300,8 +316,8 @@ app_ui <- function(request = NULL) { .summary-item-value { color: var(--monitos-fg); - font-size: 0.96rem; - line-height: 1.45; + font-size: 0.9rem; + line-height: 1.35; font-weight: 600; } @@ -315,8 +331,9 @@ app_ui <- function(request = NULL) { } .results-frame { - margin-top: 1rem; - overflow-x: auto; + margin-top: 0.75rem; + max-height: min(38vh, 18rem); + overflow: auto; border: 1px solid rgba(22, 22, 22, 0.08); border-radius: 16px; background: #fff; @@ -325,24 +342,27 @@ app_ui <- function(request = NULL) { .results-card table { width: 100%; margin: 0; - font-size: 0.95rem; + font-size: 0.88rem; } .results-card thead th { position: sticky; top: 0; z-index: 1; - padding: 0.85rem 0.85rem; + padding: 0.6rem 0.65rem; border: 0; background: rgba(245, 245, 245, 0.98); color: var(--monitos-fg); + font-size: 0.78rem; font-weight: 700; + line-height: 1.3; } .results-card tbody td { - padding: 0.85rem; + padding: 0.6rem 0.65rem; vertical-align: top; border-color: rgba(22, 22, 22, 0.08); + line-height: 1.4; } .results-card tbody tr:nth-child(odd) td { @@ -440,6 +460,10 @@ app_ui <- function(request = NULL) { .results-card { position: static; } + + .results-frame { + max-height: none; + } } ")) ), @@ -495,7 +519,7 @@ app_ui <- function(request = NULL) { h2("Set assumptions, then review the threshold"), p( class = "inline-note", - "1. Set effect sizes. 2. Add interim event counts. 3. Review the final-analysis threshold and interim operating characteristics." + "1. Set effect sizes. 2. Add interim expected death counts. 3. Review the final-analysis threshold and interim operating characteristics." ), p(class = "helper-copy", "HR below 1 indicates benefit.") ), @@ -521,7 +545,7 @@ app_ui <- function(request = NULL) { class = "field-note", tags$strong("Use for: "), "choose the OS hazard ratio that would already be too harmful to accept. ", - tags$em("Example: set delta null to 1.33 if any HR at or above 1.33 would be unacceptable.") + tags$em("Example: set this to 1.33 if any HR at or above 1.33 would be unacceptable.") ) ), div( @@ -540,7 +564,7 @@ app_ui <- function(request = NULL) { class = "field-note", tags$strong("Use for: "), "reflect the benefit you could reasonably expect from prior evidence or mechanism of action. ", - tags$em("Example: use 0.90 if a 10% OS hazard reduction would still be clinically meaningful.") + tags$em("Example: use 0.90 if a 10% OS hazard reduction would still be a plausible beneficial effect.") ) ) ) @@ -556,24 +580,24 @@ app_ui <- function(request = NULL) { div( numericInput( "eventOS", - "Deaths at final analysis", + "Targeted deaths at final OS analysis", value = 70, min = 1 ), p( class = "helper-copy", - "Use the longest feasible follow-up window for the final analysis." + "Use the longest feasible follow-up window when setting the targeted deaths for the final OS analysis." ) ), div( tags$label( `for` = "primary_events_ui", - "Interim analysis schedule" + "Interim OS analysis schedule" ), uiOutput("primary_events_ui"), p( class = "helper-copy", - "Add one row per planned look, ordered by increasing deaths." + "Add one row per planned look, ordered by increasing expected deaths." ) ) ) @@ -601,8 +625,9 @@ app_ui <- function(request = NULL) { class = "field-note", tags$strong("Interpretation: "), paste( - "this controls the final-analysis reassurance threshold when the true OS HR", - "is equal to your unacceptable detriment." + "this controls the final-analysis threshold for positivity.", + "It is the risk of being wrongly reassured when the true OS HR", + "is in fact equal to your unacceptable detriment." ) ) ), @@ -611,7 +636,7 @@ app_ui <- function(request = NULL) { div( sliderInput( "power_int", - "Required power at interim analyses under delta alt", + "Required power at interim analyses under the plausible beneficial effect", min = 0.7, max = 1, value = 0.9, @@ -620,8 +645,16 @@ app_ui <- function(request = NULL) { ), div( class = "field-note", - tags$strong("Typical choice: "), - "0.80 or 0.90, depending on how strict the primary-analysis reassurance should be." + tags$strong("Interpretation: "), + paste( + "this controls the interim analysis threshold for positivity.", + "It is the chance of being correctly reassured when the true OS HR", + "is in fact equal to your plausible beneficial effect." + ), + tags$br(), + tags$em( + "Typical choices include 0.80 or 0.90, depending on the possible impact on the program of wrongly flagging a potential detriment at the interim OS analysis." + ) ) ) ) @@ -647,7 +680,7 @@ app_ui <- function(request = NULL) { div( class = "field-note", tags$strong("When to use: "), - "use this when the drug may still be valuable under a smaller OS benefit than delta alt." + "use this when the drug may still be valuable under a smaller OS benefit than your plausible beneficial effect." ) ), div( @@ -681,9 +714,9 @@ app_ui <- function(request = NULL) { tags$ul( class = "list-tight", tags$li("OS HR threshold for positivity: the observed HR must be at or below this value."), - tags$li("One-sided false positive error rate: the tolerated risk of passing when the true effect equals delta null."), - tags$li("Level of 2-sided CI needed to rule out delta null: the confidence level required to exclude the unacceptable detriment."), - tags$li("Probability of meeting positivity threshold under delta alt: the chance of passing when the treatment effect equals your plausible benefit.") + tags$li("One-sided false positive error rate: the tolerated risk of passing when the true effect equals your unacceptable detriment."), + tags$li("Level of 2-sided CI needed to rule out the unacceptable detriment: the confidence level required to exclude that detriment."), + tags$li("Probability of meeting positivity threshold under the plausible beneficial effect: the chance of passing when the treatment effect equals your plausible beneficial effect.") ) ) ),