R/common.R

#Some miscellaneous functions used by both
#predict form parameters and predict from data

##' @importFrom stats uniroot
NULL

##' Return the number of days in the year 
##' 
##' This allows the user control over
##' whether the package uses 365.25, 365 (or any other number) of days
##' in a year. 
##' 
##' The number of days in a month is then set as daysinyear()/12 
##' 
##' @return The number of days in the year
##' @export 
standarddaysinyear <- function() {
  getOption('eventPrediction.daysinyear',
            365.25)
}


# Round a number and force trailing zeros to be output
# @param number The number to be output
# @param dp The number of digits to display after the decimal point
# @return A string (e.g. "43.400")
roundForceOutputZeros <- function(number,dp){
  if(dp < 1) {
    stop("Invalid argument dp must be positive")
  }
  roundnumber <- as.character(round(number,digits=dp))
  outputdp <- nchar(strsplit(as.character(roundnumber),"\\.")[[1]][2]) 
  zeros <- rep("0",dp-outputdp)
  return(paste(roundnumber,paste(zeros,collapse=""),sep=""))
  
}

# Calculate the number of subjects on each arm
# 
# \code{floor((r/(r+1))*N)} are allocated to the experimental arm
# the rest are allocated ot the control arm
# 
# @param singleArm logical TRUE if study is a single arm
# @param r Allocation ratio, see Study object
# @param N Total number of subjects on the trial
# @return A vector (N_control,N_experimental)
getNs <- function(singleArm,r,N){
  if (singleArm){
    return(c(N,0))
  }
  
  is.wholenumber <- function(x, tol = .Machine$double.eps^0.5)  abs(x - round(x)) < tol
  
  oldres <- (r/(r+1))*N
  res <- if(!is.wholenumber(oldres)) floor(oldres) else oldres
  res <- c(N-res,res)
  
  if(any(res==0)){
    stop("Too few subjects recruited for given randomization balance
         All subjects would be recruited onto the same arm.")
  }
  
  return(res)
}

# Add line breaks to a string
# @param title text to add line breaks to
# @param text.width Target number of characters per row
# @return text with line breaks
AddLineBreaks <- function( title, text.width ) {
  wrapper <- function(x, ... ) paste(strwrap(x, ... ) ) 
  wtext <- wrapper(title, width = text.width )
  return(paste(wtext,collapse="\n"))
}

##' Function which attempts to read in a csv file
##' picking a delimiter from a given set of options
##' 
##' The function works by counting the number of occurences 
##' of each delimiter on each line and for exactly one 
##' of the delimiter options all lines contain the same number
##' of occurences (>0) then this delimiter is used
##' 
##' @param path The path to the csv file to be read
##' @param delim_options A vector of characters from which the
##' delimiter should be chosen
##' @return If successful a data frame containing the csv file
##' if unsuccessful an error is thrown 
##' @export
csvSniffer <- function(path,delim_options=c(',',';','\t')){
  #currently reading file twice ought to be improved
  vec <- readLines(con=path)
  #remove empty lines
  vec <- vec[sapply(vec, nchar) > 0] 
  ans <- sapply(delim_options,function(x){
    a <- unlist(lapply(vec,function(y)sum(unlist(strsplit(y,split=""))==x)))
    if(all(a==a[1]) && a[1] != 0) TRUE else FALSE  
  })
  if(sum(ans) != 1){
    stop("Cannot determine delimiter")
  }
  read.csv(path,sep=names(ans[ans==TRUE])) 
}


##' Given trial assumptions describing an exponential model
##' Use the method of moments to fit a single Weibull distribution
##' 
##' See, for example, Lei, Y. Evaluation of three methods for estimating the Weibull distribution 
##' parameters of Chinese pine (Pinus tabulaeformis). Journal of Forest Science 54.12 (2008): 566-571.
##' 
##' @param HR Hazard ratio
##' @param r Randomization balance
##' @param M control arm median 
##' @export
FitMixtureModel <- function(HR,r,M){
  
  lapply(list(HR=HR,r=r,M=M),function(x){
    if(!is.numeric(x) || x <= 0 || length(x)!=1){
      stop("All arguments to FitMixtureModel must be numeric, positive and of length 1")
    }
  })
  
  ml2 <- M/log(2)
  mu <- (ml2*(1+r/HR))/(r+1) # = E(control)/(r+1) + rE(active)/r+1
  sigma <- (ml2/(1+r)) * sqrt(1+2*r-2*r/HR + (r*(r+2))/(HR*HR))
  
  return(RootFindRateShape(mu,sigma))
}


# Fit shape and rate given mean and sd of Weibull
# distribution
# 
# @param mu mean of Weibull
# @param sigma sd of distribution  
RootFindRateShape <- function(mu,sigma){
  f <- function(x){
    sigma/mu - sqrt(gamma(1+2/x) - gamma(1+1/x)^2)/gamma(1+1/x)  
  }

  shape <- tryCatch({ 
    uniroot(f,c(0.1,10))$root
  },error=function(cond){
    warning(paste("Error fitting shape:",cond,"Returning NA"))
    return(list(rate=NA,shape=NA))  
  }
  )

  rate <- gamma(1+1/shape)/mu
  return(list(rate=rate,shape=shape))
}
scientific-computing-solutions/eventPrediction documentation built on May 29, 2019, 3:44 p.m.