R/mean_diff_weighted.R

Defines functions nabla.weighted power.weighted.asymp n.weighted power.weighted.nabla power.weighted apriori.density

Documented in apriori.density nabla.weighted n.weighted power.weighted power.weighted.asymp power.weighted.nabla

#' Returns a standard a-priori density function
#' 
#' Let U denote the uniform distribution on \eqn{[0,1]}, T the triangle distribution 
#' with center point \eqn{0.5} and full support on \eqn{[0,1]} and HT the half-triangle distribution
#' with center point \eqn{0.5} and support on \eqn{[0.25, 0.75]}. This function returns one of
#' these a-priori densities in dependence of its input parameter. These three
#' a-priori distributions are deemed as helpful in experimenting with
#' the Schuirmann constant. 
#' 
#' @param density.id 'U', 'T' or 'HT', depending on which a-priori density should
#' be returned
#' @return Uniform (U), triangle (T) or half triangle (HT) a-priori density function
#' @examples  
#' rho <- apriori.density('T')
#' r <- seq(0, 1, length = 100)
#' plot(r, rho(r), type = 'l')
#' @export
#' 
apriori.density <- function(density.id) {
  # Uniform density
  density.U <- function(r) {
    rho.U <- ((r >= 0) & (r <= 1))*1.
    return(rho.U)
  }
  # Triangle density
  density.T <- function(r) {
    rho.T <- ((r >= 0) & (r <= 1))*(2-4*abs(r-0.5))
    return(rho.T)
  }
  # Half-triangle density
  density.HT <- function(r) {
    rho.HT <- ((r >= 0.25) & (r <= 0.75))*(4-16*abs(r-0.5))
    return(rho.HT)
  }  
  
  density.id <- tolower(density.id)
  if (density.id == 'u') {
    return (density.U)
  } else if (density.id == 't') {
    return (density.T)
  } else if (density.id == 'ht') {
    return (density.HT)
  } else {
    stop('Invalid input! density.id must be \'U\', \'T\' or \'HT\'')
  }
}


#' Exact weighted power; standard parameterization
#' 
#' Calculates the exact power of an unpaired, balanced TOST with 
#' n samples in each group, where the true mean-difference is modeled with an
#' a-priori density rather than a fixed mean-difference.
#' 
#' @param alpha type I error
#' @param theta1 lower limit of equivalence interval
#' @param theta2 upper limit of equivalence interval
#' @param density a-priori density for the mean-difference
#' @param sigma standard deviation of the measurements
#' @param n sample size in each group
#'
#' @return Exact power of the TOST for a weighted true mean-difference
#' @examples
#' rho <- apriori.density('U')
#' pwr <- power.weighted(alpha = 0.05, theta1 = -2, theta2 = 2, density = rho, sigma = 1, n = 22)
#' n.weighted(alpha = 0.05, theta1 = -2, theta2 = 2, density = rho, sigma = 1, pwr = pwr)
#' @export
#' 
power.weighted <- function(alpha, theta1, theta2, density, sigma, n) {
  check.input.parameter(as.list(match.call())[-1])
  integrand <- function(r) {
    theta0 <- (1-r)*theta1 + r*theta2
    power.point(alpha, theta1, theta2, theta0, sigma, n) * density(r)
  }
  pwr <- integrate(integrand, lower = 0, upper = 1)$value
  return(pwr)
}



#' Exact weighted power; sensitivity parameterization
#' 
#' Calculates the exact power of an unpaired, balanced TOST with 
#' n samples in each group, where the true mean-difference is modeled with an
#' a-priori density rather than a fixed mean-difference. The calculation is performed 
#' in the TOST sensitivity parameterization.
#' 
#' @param alpha type I error
#' @param density a-priori density for the mean-difference
#' @param nabla TOST sensitivity index
#' @param n sample size in each group
#'
#' @return Exact power of the TOST for a weighted mean-difference
#' @examples
#' rho <- apriori.density('U')
#' power.weighted.nabla(alpha = 0.05, density = rho, nabla = 8, n = 22)
#' ## Since we are performing a weighted power calculation, the r respectively 
#' ## theta0 value is not needed. Thus, the following calculation does not depend
#' ## on r.
#' l <- nabla.to.standard(r = 0.99, nabla = 8, theta1 = -1, theta2 = 4, n = 22)
#' power.weighted(alpha = 0.05, theta1 = -1, theta2 = 4, density = rho, sigma = l$sigma, n = 22)
#' @export
#' 
power.weighted.nabla <- function(alpha, density, nabla, n) {
  check.input.parameter(as.list(match.call())[-1])
  integrand <- function(r) {
    power.point.nabla(alpha, r, nabla, n) * density(r)
  }
  pwr <- integrate(integrand, lower = 0, upper = 1)$value
  return(pwr)
}


#' Exact sample size; weighted mean-difference
#' 
#' Exact sample size calculation of a balanced, unpaired TOST, where the true
#' mean-difference follows a prescribed a-priori distribution. The lowest sample 
#' size n that exceeds the target power pwd is calculated using a standard binary 
#' search algorithm. The result n is the sample size in one group.
#'
#' @param alpha type I error
#' @param theta1 lower limit of equivalence interval
#' @param theta2 upper limit of equivalence interval
#' @param density a-priori density for the mean-difference
#' @param sigma standard deviation of the measurements
#' @param pwr Exact power of the TOST
#'
#' @return sample size in each group
#' @examples
#' rho <- apriori.density('U')
#' pwr <- power.weighted(alpha = 0.05, theta1 = -2, theta2 = 2, density = rho, sigma = 1, n = 22)
#' n.weighted(alpha = 0.05, theta1 = -2, theta2 = 2, density = rho, sigma = 1, pwr = pwr)
#' @export
#' 
n.weighted <- function(alpha, theta1, theta2, density, sigma, pwr) {
  check.input.parameter(as.list(match.call())[-1])
  
  n.low <- 2
  n.high <- 64
  
  # Check whether two samples are enough. If not, then we have
  # power(n.low) < pwr
  if (power.weighted(alpha, theta1, theta2, density, sigma, n.low) >= pwr) {
    return(n.low)
  }
  
  # Find n.high, such that
  # power(n.low) < pwr <= power(n.high)
  pwr.high <- power.weighted(alpha, theta1, theta2, density, sigma, n.high)
  while (pwr.high < pwr) {
    n.low <- n.high
    n.high <- 2*n.high
    pwr.high <- power.weighted(alpha, theta1, theta2, density, sigma, n.high)
  }
  
  # Repeat until n.high = n.low + 1 and
  # power(n.heigh-1) < pwr <= power(n.heigh)
  while (n.high - n.low > 1) {
    n.inter <- floor((n.low + n.high)/2)
    pwr.inter <- power.weighted(alpha, theta1, theta2, density, sigma, n.inter)
    if (pwr.inter < pwr) {
      n.low <- n.inter  
    } else {
      n.high <- n.inter
    }
  }
  
  return(n.high)
}


#' Asymptotical weighted power
#' 
#' Calculates the asymptotical power of an unpaired, balanced TOST 
#' for a weighted a-priori mean-difference. 
#' 
#' @param alpha type I error
#' @param density a-priori density for the mean-difference
#' @param nabla TOST sensitivity index
#'
#' @return Asymptotic power of the TOST for a weighted mean-difference
#' @examples 
#' rho <- apriori.density('HT')
#' power.weighted.nabla(alpha = 0.05, density = rho, nabla = 7, n = 10)
#' power.weighted.nabla(alpha = 0.05, density = rho, nabla = 7, n = 50)
#' power.weighted.asymp(alpha = 0.05, density = rho, nabla = 7)
#' @export
#' 
power.weighted.asymp <- function(alpha, density, nabla) {
  check.input.parameter(as.list(match.call())[-1])
  integrand <- function(r) { 
    (pnorm(nabla*r - qnorm(1-alpha)) - pnorm(qnorm(1-alpha)-nabla*(1-r))) * density(r)  
  }
  if (nabla > 2*qnorm(1-alpha)) {
    pwr <- integrate(integrand, lower = 0, upper = 1)$value
  } else {
    pwr <- 0
  }
  return(pwr)
}

#' nabla; weighted mean-difference
#'
#' The equality 
#' \deqn{power_{asymp, density}(\nabla, \alpha) = pwr}{
#' power_asymp_density(nabla, alpha) = pwr}
#' is solved for \eqn{\nabla}{nabla} in dependence of \eqn{\alpha}{alpha}, 
#' \eqn{pwr}{pwr} and \eqn{density}{density}.
#' 
#' @param alpha type I error
#' @param pwr asymptotic weighted power of the TOST
#' @param density a-priori density for the mean-difference
#'
#' @return \eqn{\nabla}{nabla} for a weighted mean-difference
#' @examples
#' rho <- apriori.density('U')
#' nabla <- nabla.weighted(alpha = 0.05, pwr = 0.8, density = rho)
#' power.weighted.asymp(alpha = 0.05, density = rho, nabla = nabla)
#' @export
#' 
nabla.weighted <- function(alpha, pwr, density) {
  check.input.parameter(as.list(match.call())[-1])
  fct <- function(x) {
    power.weighted.asymp(alpha, density, x) - pwr
  }
  nabla <- uniroot2(fct, lower = 2*qnorm(1-alpha), upper = 10^5, to.one = F)$root
  return(nabla)
}
cpalmes/schuirmann.constant documentation built on Dec. 31, 2020, 10:07 p.m.