Merck / Merck/gsDesign2

Consider adding functionality to transform `theta` for gs_cp functions between cumulative and incremental drift

Open
#668 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
R
Stars
34
Forks
11
Avg merge
2d 14h
Merged PRs (30d)
7

Description

For users, it may be useful to know both the expected drift at the jth analysis and the expected drift between analyses to get there.
This could be helpful to clarify in the vignette on CP as well so that users are not including a future drift parameter that ends up requiring nearly all information between analyses to come from essentially a single group.

I provide sample functions below for ease of reference at a later date.

#' Computation of non-constant incremental natural parameter based on cumulative natural parameter.
#' 
#' @details 
#' We assume that \eqn{\theta_i, i = 1, ..., K} are the cumulative natural parameters
#' and \eqn{\delta_i, i = 1, ..., K} are the incremental natural parameters.
#' We assume that the analysis occur at information fractions \eqn{t_i, i = 1, ..., K}.
#' We assume that the information available at \eqn{t_i} is given by \eqn{I_i}.
#' 
#' Then the B-value at time \eqn{t_j} is given by \eqn{B_j = \theta_j I_j^{1/2} }.
#' We assume the relationship between analyses for sequential B-values is linear. 
#' Thus, the B-value at time \eqn{t_j} can be expressed in terms of the information differences
#' and the incremental natural parameters \delta_i as 
#' \deqn{E(B_j) = \delta_1 I_1^{1/2} + \sum_{k=i+1}^j \delta_k (I_{k} - I_{k-1})^{1/2})}
#' The value of $\delta_j, j > i$ can be computed sequentially after $\delta_1 = \theta_1$ as
#' \deqn{\delta_j = \frac{ \theta_j I_j^{1/2} - \theta_1 I_1^{1/2} +
#'      \sum_{k=i+1}^{j-1} \delta_k (I_{k} - I_{k-1})^{1/2} }{(I_j - I_{j-1})^{1/2}}} 
#' where the summand from $k=i+1$ to $j-1$ is 0 if $j=i+1$.
#' 
#' @param theta A vector of j-i+1, which specifies the natural parameter for treatment effect.
#'              The first element of `theta` is the treatment effect of an interim analysis i.
#'              The second element of `theta` is the treatment effect of an interim analysis i+1.
#'              ...
#'              The last element of `theta` is the treatment effect of a future analysis j.
#' @param t A vector of j-i+1, which specifies the information fraction under the treatment effect `theta`.
#' @param info A vector of j-i+1, which specifies the statistical information under the treatment effect `theta`.
#' @return A vector of j-i+1, which specifies the incremental natural parameter for treatment effect.
#' @noRd 
#' 
#' @examples 
#' library(gsDesign2)
#' library(dplyr)
#' library(mvtnorm)
#' # Example 1 ----
#' # Calculate the incremental natural parameters when the cumulative drift is the same at all analyses
#' # assuming that the cumulative drift is -log(0.7) based on a survival outcome with hazard ratio 0.7
#' # The incremental drift should decrease at each subsequent analysis. 
#' # We provide both the delta and the transformed hazard rate between analyses. 
#' delta <- theta_cum_to_inc(theta = c(-log(.7), -log(.7), -log(.7)), 
#'                t = c(0.5, 0.75, 1), 
#'                info = c(50, 75, 100))
#' delta
#' exp(-delta)
#' 
#' # Example 2 ----
#' # Calculate the incremental natural parameters when the cumulative drift 
#' # is improving from a detrimental effect, to a positive effect, to a very positive effect.
#' # The delta starts negative and grows more positive. 
#' delta <- theta_cum_to_inc(theta = c(-log(1.1), -log(.8), -log(.6)), 
#'                t = c(0.5, 0.75, 1), 
#'                info = c(50, 75, 100))
#' delta
#' exp(-delta)


theta_cum_to_inc <- function(theta, t, info){
  # check that theta, t, and info are all the same length
  if(length(theta) != length(t) | length(theta) != length(info)){
    stop("The inputs theta, t, and info must all be the same length")
  }
  # check that t is increasing and between 0 and 1
  if(any(diff(t) <= 0) | any(t < 0) | any(t > 1)){
    stop("The input t must be increasing and between 0 and 1")
  }
  # Compute the incremental drift parameters delta
  delta <- numeric(length(theta))
  delta[1] <- theta[1]
  if (length(theta) > 1) {
    delta[2] <- (theta[2] * sqrt(info[2]) - theta[1] * sqrt(info[1])) / sqrt(diff(info[1:2]))
    if (length(theta) > 2) {
      for (j in 3:length(theta)) {
        delta[j] <- (theta[j] * sqrt(info[j]) - theta[1] * sqrt(info[1]) - 
          sum(delta[2:(j-1)] * sqrt(diff(info[2:j])))) / sqrt(diff(info[(j-1):j]))
      }
    }
  }
  return(delta)
}


#' Computation of cumulative natural parameter based on incremental natural parameter.
#' 
#' @details 
#' This is the reverse transformation of \code{theta_cum_to_inc}.
#' We assume that \eqn{\delta_i, i = 1, ..., K} are the incremental natural parameters
#' and \eqn{\theta_i, i = 1, ..., K} are the cumulative natural parameters.
#' We assume that the analyses occur at information fractions \eqn{t_i, i = 1, ..., K}.
#' We assume that the information available at \eqn{t_i} is given by \eqn{I_i}.
#' 
#' The B-value at time \eqn{t_j} is:
#' \deqn{E(B_j) = \delta_1 I_1^{1/2} + \sum_{k=2}^j \delta_k (I_{k} - I_{k-1})^{1/2}}
#' 
#' Since \eqn{E(B_j) = \theta_j I_j^{1/2}}, the cumulative natural parameter is recovered as:
#' \deqn{\theta_j = \frac{\delta_1 I_1^{1/2} + \sum_{k=2}^j \delta_k (I_{k} - I_{k-1})^{1/2}}{I_j^{1/2}}}
#' 
#' @param delta A vector of j-i+1, which specifies the incremental natural parameter for treatment effect.
#'              The first element of `delta` is the treatment effect at the first analysis.
#'              Subsequent elements are the incremental treatment effects over each interval.
#' @param t A vector of j-i+1, which specifies the information fraction under the treatment effect.
#' @param info A vector of j-i+1, which specifies the statistical information.
#' @return A vector of j-i+1, which specifies the cumulative natural parameter for treatment effect.
#' @noRd 
#' 
#' @examples 
#' library(gsDesign2)
#' library(dplyr)
#' # Example 1 ----
#' # Round-trip: cumulative -> incremental -> cumulative
#' theta_orig <- c(-log(.7), -log(.7), -log(.7))
#' info <- c(50, 75, 100)
#' t <- c(0.5, 0.75, 1)
#' delta <- theta_cum_to_inc(theta = theta_orig, t = t, info = info)
#' theta_recovered <- theta_inc_to_cum(delta = delta, t = t, info = info)
#' (all.equal(theta_orig, theta_recovered))
#' 
#' # Example 2 ----
#' # Given incremental HRs, compute cumulative theta
#' delta <- c(-log(1.0), -log(0.7), -log(0.65))
#' theta <- theta_inc_to_cum(delta = delta, t = c(0.5, 0.75, 1), info = c(50, 75, 100))
#' theta
#' exp(-theta)  # cumulative HR at each analysis


theta_inc_to_cum <- function(delta, t, info){
  # check that delta, t, and info are all the same length
  if(length(delta) != length(t) | length(delta) != length(info)){
    stop("The inputs delta, t, and info must all be the same length")
  }
  # check that t is increasing and between 0 and 1
  if(any(diff(t) <= 0) | any(t < 0) | any(t > 1)){
    stop("The input t must be increasing and between 0 and 1")
  }
  # Compute the cumulative drift parameters theta
  theta <- numeric(length(delta))
  theta[1] <- delta[1]
  if (length(delta) > 1) {
    for (j in 2:length(delta)) {
      B_j <- delta[1] * sqrt(info[1]) + sum(delta[2:j] * sqrt(diff(info[1:j])))
      theta[j] <- B_j / sqrt(info[j])
    }
  }
  return(theta)
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the gs_cp functions and the CP vignette, then compare their current theta handling with the supplied theta_cum_to_inc and theta_inc_to_cum examples. Determine how cumulative and incremental drift should be exposed and documented. Done means the transformations are integrated consistently and the vignette explains when each representation is appropriate.

Written by the indexing model from the issue text.

Assessment

Tech stack
r
Domain
tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.