--- a +++ b/R/common.R @@ -0,0 +1,152 @@ +#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)) +}