---
title: Time-to-Event Endpoint Design
subtitle: Deriving a Design with Integer Event Counts and Sample Size
output: html_document
---

```{r}
library(gsDesign)
library(dplyr)
library(gt)
```

# Overview

This report is based on modifying a design generated by the gsDesign Shiny interface for a time-to-event endpoint.
In particular, we show how to modify the initially-derived design so that it generates and integer sample size and has integer event counts planned for each analysis.
The R Markdown file for this report is at <https://keaven.github.io/gsd-shiny/reports/tte-integer-design.Rmd>.

# Design assumptions

We begin with a design generated by the Shiny interface.
Assumptions are explained in the book for the interface.


```{r}
# Number of analyses
k <- 3
# See gsDesign() help for description of boundary types
test.type <- 4
# 1-sided Type I error
alpha <- 0.025
# Type 2 error (1 - targeted power)
beta <- 0.15
# If test.type = 5 or 6, this sets maximum spending for futility
# under the null hypothesis. Otherwise, this is ignored.
astar <- 0
# Timing (information fraction) at interim analyses
timing <- c(0.4, 0.85)
# Efficacy bound spending function
sfu <- sfLDOF
# Upper bound spending function parameters, if any
sfupar <- c(0)
# Lower bound spending function, if used (test.type > 2)
sfl <- sfCauchy
# Lower bound spending function parameters, if any
sflpar <- c(0.4, 0.85, 0.5, 0.546)
# Assumed hazard ratio under alternate hypothesis
hr <- 0.7
# Null hypothesis hazard ratio
hr0 <- 1
# Dropout rate (exponential rate)
eta <- 0.045
# Enrollment rate (normally will be inflated to achieve power)
gamma <- c(2.5, 5, 7.5, 10)
# Relative enrollment rates by time period
R <- c(2, 2, 2, 18)
# Interval durations for piecewise hazard rate approximation
# (length one less than lambdaC). NULL if length of lambdaC is 1.
S <- NULL
# Calendar time of final analysis
T <- 36
# Minimum follow-up time after enrollment complete
minfup <- 12
# Relative enrollment (experimental/placebo)
ratio <- 1
# Piecewise exponential failure distribution. Column 1 is interval duration
# (last value is ignored and extended indefinitely). Column 2 is median.
obs <- matrix(c(100, 6), ncol = 2)
obs <- obs[(!is.na(obs[, 1])) & (!is.na(obs[, 2])), 2]
# Transform the median time-to-event to exponential event rates
lambdaC <- log(2) / obs
```

Given the above parameters, we now derive a design.

```{r}
x <- gsSurv(
  k = k,
  test.type = test.type,
  alpha = alpha,
  beta = beta,
  astar = astar,
  timing = timing,
  sfu = sfu,
  sfupar = sfupar,
  sfl = sfl,
  sflpar = sflpar,
  lambdaC = lambdaC,
  hr = hr,
  hr0 = hr0,
  eta = eta,
  gamma = gamma,
  R = R,
  S = S,
  T = T,
  minfup = minfup,
  ratio = ratio
)
```

# Default design with continuous sample size and event counts

We can see the planned sample size for the study is a non-integer value as follows:

```{r}
# add experimental and control group sample sizes
# ex
x$eNE + x$eNC
```
In this case, the final sample size is nearly integer but it is an odd-integer which does not yield integer sample sizes planned for each treatment group.
We see that expected sample size at the first interim analysis is about 2/3 of the final sample size.
If the interim can be performed quickly after the targeted analyis is performed, there may be the possibility of saving about 30% in sample size by stopping for futility or efficacy.

The interim and final targeted event counts are also non-integer:

```{r}
# Targeted event counts
x$n.I
```

Now suppose we set targeted event counts by rounding down at interim analyses and up at the final analysis. We derive the corresponding information fraction at interim analyses.

```{r}
targeted_events <- c(129, 274, 323)
targeted_information_fraction <- targeted_events / last(targeted_events)
targeted_information_fraction
```

We now update our design with a new targeted power based on the above event counts

```{r}
x_integer <- gsDesign(
  k = x$k,
  test.type = x$test.type,
  alpha = x$alpha,
  # While we specify beta, this is for spending
  # as final power will be different from 1 - beta
  beta = x$beta,
  # We specify final targeted events
  maxn.IPlan = last(targeted_events),
  n.I = targeted_events,
  # We also specify treatment effect from x
  delta = x$delta,
  delta0 = x$delta0,
  delta1 =x$delta1,
  sfu = x$upper$sf,
  sfupar = x$upper$param,
  sfl = x$lower$sf,
  sflpar = x$lower$param
  )
```

Now we summarize the design.
One additional feature not discussed above is that we provide the conditional power at the futility bound assuming the treatment effect for future observations is as hypothesized under the alternate hypothesis. While this is more optimistic than if computed based on the observed treatment effect, it is worth considering given the sunk costs for a trial at the interim analysis.
The conditional power at the first interim futility bound in this case is 0.6439 assuming HR = 0.7 for the remaining observations.
While the observed HR to reach is bound is ~0.9013, this does not rule out the hypothesized HR = 0.7 based on the Type II error of 0.075 for crossing this bound.

```{r}
# Following needed for HR approximations
x_integer$hr0 <- x$hr0

gsBoundSummary(
  x_integer,
  deltaname = "HR",
  logdelta = TRUE,
  Nname = "Events",
  digits = 4,
  ddigits = 2,
  tdigits = 1,
  exclude = c("B-value", "CP", "PP")
)
```


# Plots

We show the spending function plot, focusing particularly on the futility spending function which has a long plateau for cumulative spending in the middle of the trial to enable primarily spending Type II error at the first interim analysis. 

```{r}
plot(x, plottype = 5, xlab = 'Proportion of final events', ylab = 'Proportion of spending')
```

# Environment information

- Run date: `r Sys.time()`
- R version: `r R.version.string`
- gsDesign version: `r as.character(packageVersion("gsDesign"))`
