a b/R/common.R
1
#Some miscellaneous functions used by both
2
#predict form parameters and predict from data
3
4
##' @importFrom stats uniroot
5
NULL
6
7
##' Return the number of days in the year 
8
##' 
9
##' This allows the user control over
10
##' whether the package uses 365.25, 365 (or any other number) of days
11
##' in a year. 
12
##' 
13
##' The number of days in a month is then set as daysinyear()/12 
14
##' 
15
##' @return The number of days in the year
16
##' @export 
17
standarddaysinyear <- function() {
18
  getOption('eventPrediction.daysinyear',
19
            365.25)
20
}
21
22
23
# Round a number and force trailing zeros to be output
24
# @param number The number to be output
25
# @param dp The number of digits to display after the decimal point
26
# @return A string (e.g. "43.400")
27
roundForceOutputZeros <- function(number,dp){
28
  if(dp < 1) {
29
    stop("Invalid argument dp must be positive")
30
  }
31
  roundnumber <- as.character(round(number,digits=dp))
32
  outputdp <- nchar(strsplit(as.character(roundnumber),"\\.")[[1]][2]) 
33
  zeros <- rep("0",dp-outputdp)
34
  return(paste(roundnumber,paste(zeros,collapse=""),sep=""))
35
  
36
}
37
38
# Calculate the number of subjects on each arm
39
# 
40
# \code{floor((r/(r+1))*N)} are allocated to the experimental arm
41
# the rest are allocated ot the control arm
42
# 
43
# @param singleArm logical TRUE if study is a single arm
44
# @param r Allocation ratio, see Study object
45
# @param N Total number of subjects on the trial
46
# @return A vector (N_control,N_experimental)
47
getNs <- function(singleArm,r,N){
48
  if (singleArm){
49
    return(c(N,0))
50
  }
51
  
52
  is.wholenumber <- function(x, tol = .Machine$double.eps^0.5)  abs(x - round(x)) < tol
53
  
54
  oldres <- (r/(r+1))*N
55
  res <- if(!is.wholenumber(oldres)) floor(oldres) else oldres
56
  res <- c(N-res,res)
57
  
58
  if(any(res==0)){
59
    stop("Too few subjects recruited for given randomization balance
60
         All subjects would be recruited onto the same arm.")
61
  }
62
  
63
  return(res)
64
}
65
66
# Add line breaks to a string
67
# @param title text to add line breaks to
68
# @param text.width Target number of characters per row
69
# @return text with line breaks
70
AddLineBreaks <- function( title, text.width ) {
71
  wrapper <- function(x, ... ) paste(strwrap(x, ... ) ) 
72
  wtext <- wrapper(title, width = text.width )
73
  return(paste(wtext,collapse="\n"))
74
}
75
76
##' Function which attempts to read in a csv file
77
##' picking a delimiter from a given set of options
78
##' 
79
##' The function works by counting the number of occurences 
80
##' of each delimiter on each line and for exactly one 
81
##' of the delimiter options all lines contain the same number
82
##' of occurences (>0) then this delimiter is used
83
##' 
84
##' @param path The path to the csv file to be read
85
##' @param delim_options A vector of characters from which the
86
##' delimiter should be chosen
87
##' @return If successful a data frame containing the csv file
88
##' if unsuccessful an error is thrown 
89
##' @export
90
csvSniffer <- function(path,delim_options=c(',',';','\t')){
91
  #currently reading file twice ought to be improved
92
  vec <- readLines(con=path)
93
  #remove empty lines
94
  vec <- vec[sapply(vec, nchar) > 0] 
95
  ans <- sapply(delim_options,function(x){
96
    a <- unlist(lapply(vec,function(y)sum(unlist(strsplit(y,split=""))==x)))
97
    if(all(a==a[1]) && a[1] != 0) TRUE else FALSE  
98
  })
99
  if(sum(ans) != 1){
100
    stop("Cannot determine delimiter")
101
  }
102
  read.csv(path,sep=names(ans[ans==TRUE])) 
103
}
104
105
106
##' Given trial assumptions describing an exponential model
107
##' Use the method of moments to fit a single Weibull distribution
108
##' 
109
##' See, for example, Lei, Y. Evaluation of three methods for estimating the Weibull distribution 
110
##' parameters of Chinese pine (Pinus tabulaeformis). Journal of Forest Science 54.12 (2008): 566-571.
111
##' 
112
##' @param HR Hazard ratio
113
##' @param r Randomization balance
114
##' @param M control arm median 
115
##' @export
116
FitMixtureModel <- function(HR,r,M){
117
  
118
  lapply(list(HR=HR,r=r,M=M),function(x){
119
    if(!is.numeric(x) || x <= 0 || length(x)!=1){
120
      stop("All arguments to FitMixtureModel must be numeric, positive and of length 1")
121
    }
122
  })
123
  
124
  ml2 <- M/log(2)
125
  mu <- (ml2*(1+r/HR))/(r+1) # = E(control)/(r+1) + rE(active)/r+1
126
  sigma <- (ml2/(1+r)) * sqrt(1+2*r-2*r/HR + (r*(r+2))/(HR*HR))
127
  
128
  return(RootFindRateShape(mu,sigma))
129
}
130
131
132
# Fit shape and rate given mean and sd of Weibull
133
# distribution
134
# 
135
# @param mu mean of Weibull
136
# @param sigma sd of distribution  
137
RootFindRateShape <- function(mu,sigma){
138
  f <- function(x){
139
    sigma/mu - sqrt(gamma(1+2/x) - gamma(1+1/x)^2)/gamma(1+1/x)  
140
  }
141
142
  shape <- tryCatch({ 
143
    uniroot(f,c(0.1,10))$root
144
  },error=function(cond){
145
    warning(paste("Error fitting shape:",cond,"Returning NA"))
146
    return(list(rate=NA,shape=NA))  
147
  }
148
  )
149
150
  rate <- gamma(1+1/shape)/mu
151
  return(list(rate=rate,shape=shape))
152
}