R/accrual.R

#This file contains the generator functions for recruiting extra subjects 
#into a predict from data simulation. It also contains the code for
#estimating k in the accrual cdf G(t) t^k/B^k from recruitment data.

##' @include eventData.R fromDataInternal.R
NULL

##' A class to allow additional subjects 
##' to be recruited onto the study
##' @slot f A function with 1 argument (the number of subjects to be recruited)
##' which returns a vector of recruitment times (of the required length)
##' @slot model A string, name of the accrual procedure used  
##' @slot text The summary text to be output when printing the accrual procedure
##' for example "a poisson process with rate..."
##' @seealso \code{\link{show,AccrualGenerator-method}}
##' @export
setClass("AccrualGenerator",
         slots=list(f="function",
                    model="character",
                    text="character"
         )
)

##' @name show
##' @rdname show-methods
##' @aliases show,AccrualGenerator-method
##' @export
setMethod("show","AccrualGenerator",function(object){
  cat(paste("Accrual model:",object@model,"\n"))
  cat(paste("Model Description:\n"))
  cat(object@text)  
})

##' Create A Poisson process accrual generator
##' @param start.date The start date for the recruitment process
##' @param rate The rate of the required Poisson process.
##' @return An \code{AccrualGenerator} object which can 
##' be used when recruiting subjects with a Possion process
##' with given \code{start.date} and \code{rate}
##' @export 
Generate.PoissonAccrual <- function(start.date,rate){
  if(rate <= 0) stop("rate must be positive")
  start.date <- FixDates(start.date)
  f <- function(N){
    start.date + ceiling( cumsum( rexp( N, rate=rate ) ) )
  }
  
  r <- if(rate < 0.01) r else round(rate,2)
  
  text <- paste("a poisson process with rate=", 
                 r, " and start date=", start.date,".",sep="")
  
  new("AccrualGenerator",f=f,
      model="Poisson process",text=text)    
    
}

##' Create An AccrualGenerator using a power law recruitment
##' 
##' Subjects are accrued according to the c.d.f 
##' \code{G(t)=t^k/B^k} where \code{k} is a parameter, 
##' \code{t} is the time and \code{B} is the recruitment period. 
##'  
##' See the predict from data vignette for further details  
##' @param start.date The start of the subject accrual period
##' @param end.date The date the last subject is accrued so \code{B} = 
##' \code{end.date} - \code{start.date} unless \code{rec.start.date} is used see 
##' below
##' @param k The non-uniformity accrual parameter
##' @param deterministic Logical, if FALSE then the recruitment times 
##' are non-stochastically chosen so that their cumulative distribution function is \code{G(t)}
##' otherwise they are generated by sampling random variables with a cdf \code{G(t)}
##' @param rec.start.date If this argument is used the subjects are still recruited between 
##' start.date and end.date but they follow the cdf \code{G(t)=(t^k-L^k)/(B^k-L^k)} where
##' \code{t} is in \code{[L,B]} and \code{B = end.date - rec.start.date} and \code{L = start.date- rec.start.date}.
##' @return An AccrualGenerator object which generates the required subject accruals
##' @export
Generate.Accrual <- function(start.date,end.date,k,deterministic=FALSE,rec.start.date=NULL){
  start.date <- FixDates(start.date)
  end.date <- FixDates(end.date)
  rec.start.date <- if(is.null(rec.start.date)) start.date else FixDates(rec.start.date)
  
  # Length of recruitment period
  B <- as.integer(end.date - rec.start.date)
  L <- as.integer(start.date - rec.start.date)
  
  if(L < 0 || k <= 0|| B <= L){
    stop("Invalid arguments")
  }
  
  if(deterministic){
    f <- function(N){
      as.Date(((1:N/N)*(B^k-L^k) + L^k)^(1/k)-L,origin=start.date)
    }
    stext <- "non-stochastic"
  }
  else{
    f <- function(N){
      ans <- ((B^k-L^k)*runif(N) + L^k)^(1/k)-L
      ans[order(ans)]
      as.Date(ans,origin=start.date)
    }
    stext <- "stochastic"
  }  
  
  text <- paste("a ",stext  ," allocation following G(t)=t^k/B^k, where k=", 
  round(k,2), " and B=",B," days is the recruitment period [", rec.start.date, ", ", end.date, "].",sep="")
  
  if(L!=0){
    text <- paste(text," New subjects will be recruited after ",start.date,".",sep="") 
  }
  
  new("AccrualGenerator",f=f,
      model="Power law allocation",text=text) 
  
} 

##' Generic function to estimate non-uniformity accrual 
##' parameter \code{k}
##' 
##' Note The estimate produced assumes recruitment has completed.
##' @docType methods
##' @param object An \code{EventData} object
##' @param ... Additional arguments to be passed to specific method
##' @rdname estimateAccrualParameter-methods  
##' @export
setGeneric("estimateAccrualParameter",function(object,...) standardGeneric("estimateAccrualParameter"))



##' @param start.date The startdate for recruitment, by default the date 
##' the first subject is recruited. If subjects are recruited on start.date, it is
##' assumed they are recruited on the following day to avoid log(0) in the 
##' maximum likelihood estimate.
##' @param end.date The end date for recruitment, by default the date 
##' the last subject is recruited. 
##' @return A maximum likelihood estimate for k, see vignette 
##' for further details    
##' @rdname estimateAccrualParameter-methods 
##' @aliases estimateAccrualParameter,EventData-method
##' @export
setMethod("estimateAccrualParameter", "EventData",
  function(object, start.date=min(object@subject.data$rand.date),
           end.date=max(object@subject.data$rand.date)){
    message("Note this estimate assumes recruitment has completed")          
    
    start.date <- FixDates(start.date)
    end.date <- FixDates(end.date)
    
    B <- as.numeric(end.date-start.date)
    if(B <= 0) stop("end.date must be after start.date")
    
    recs <- (object@subject.data$rand.date-start.date)
    recs <- ifelse(recs==0,0.5,recs)
    
    if(any(recs< 0 | recs > B)) stop("Subjects recruited outside of recruitment period")
    
    return(MLestimateK(B,as.numeric(recs)))
    
  }
)


               
scientific-computing-solutions/eventPrediction documentation built on May 29, 2019, 3:44 p.m.