Diff of /R/study.R [000000] .. [f2e496]

Switch to unified view

a b/R/study.R
1
#The Study class definition (used in predict from parameters)
2
#together with the exported methods. Also see study_constructors.R
3
4
#' @include lag.R common.R ctrlSpec.R sfn.R eventPrediction_package.R
5
NULL
6
7
##' Class defining the Study 
8
##' @slot HR Hazard ratio to be detected
9
##' @slot alpha Significance level [0,1] (see also two-sided indicator)
10
##' @slot power Power [0,1]
11
##' @slot two.sided If TRUE, two sided test will be used (i.e. alpha/2).
12
##' @slot r Control:Experimental subject balance (1:r), i.e. nE/nC=r. r=1 corresponds to equally 
13
##' many subjects in both arms. 2 means we have twice the number of subjects in the experimental arm.
14
##' Specifically \code{floor(r*N/(r+1))} subjects are 
15
##' allocated to the experimental arm and all other subjects are allocated to the control arm.
16
##' @slot N Number of subjects to be recruited (integer)
17
##' @slot study.duration Number of months the study will be going.
18
##' @slot ctrlSpec A CtrlSpec object which calculates the control group median. This object will be created automatically
19
##' when calling a constructor for the Study class.
20
##' @slot dropout A list of CtrlSpec object which calculates the median drop out rate for the control arm (index 1) and 
21
##' active arm (index 2). 
22
##' This object will be created automatically when calling a constructor for the study class
23
##' @slot dropout.shape The Weibull shape parameter of the dropout hazard function
24
##' @slot k non-uniformity of accrual (integer, 1=uniform). Non-uniform accrual is allowed for 
25
##' using the following distribution for the probability of a patient entering the trial at time \eqn{b} 
26
##' within the accrual period \eqn{[0,B]}: \eqn{F(b)=b_k/B_k}; \eqn{f(b)=k b_{k-1}/B_k} where \eqn{k} is the 
27
##' measure of non-uniformity (\eqn{k>0}). \eqn{k=1} indicates uniform accrual. This implies that during 
28
##' the first half of the accrual period, \eqn{1/2^k} of the patients will be recruited. Half of the patients 
29
##' will be recruited by time \eqn{B/2^{1/k}}. 
30
##' @slot acc.period Accrual time in months
31
##' @slot shape The Weibull shape parameter
32
##' @slot followup The time a subject is followed after randomization, if Inf then there is no fixed time period
33
##' @slot type Character: The study type, either "Oncology" or "CRGI"
34
##' @slot lag.settings The \code{LaggedEffect} object describing any lag effect for the study 
35
##' @export 
36
setClass( "Study", 
37
          slots= list( HR = "numeric",  # Hazard ratio
38
                       alpha = "numeric",
39
                       power = "numeric",
40
                       two.sided = "logical",
41
                       r = "numeric",  
42
                       N = "numeric",  # Patients to be recruited
43
                       study.duration = "numeric",  # Length of study (In months)
44
                       ctrlSpec = "CtrlSpec",  # Median of control arm
45
                       dropout = "list",
46
                       dropout.shape ="numeric",
47
                       k = "numeric",  
48
                       acc.period = "numeric",
49
                       shape="numeric",
50
                       followup="numeric",
51
                       type="character",
52
                       lag.settings = "LagEffect"),
53
          
54
          validity = function(object){
55
            is.wholenumber <- function(x, tol = .Machine$double.eps^0.5)  abs(x - round(x)) < tol          
56
            ans <- ""
57
            if(object@shape <= 0) ans <- paste(ans,"Invalid shape.")
58
            if(object@acc.period <= 0) ans <- paste(ans,"Invalid acc.period")
59
            if(object@k <= 0) ans <- paste(ans,"Invalid k.")
60
            if(object@N <= 0 || !is.wholenumber(object@N) || length(object@N)>1 ) ans <- paste(ans,"Invalid N.")
61
            if(object@study.duration <= 0 ) ans <- paste(ans,"Invalid study.duration.")
62
            if(object@r < 0 ) ans <- paste(ans,"Invalid r.")
63
                        
64
            if(object@study.duration <= object@acc.period) 
65
              ans <- paste(ans,"acc.period must be < study.duration")
66
            
67
            if(object@r == 0){
68
              if(!is.na(object@HR)) ans <- paste(ans,"HR must be NA if r = 0")
69
              if(!is.na(object@power)) ans <- paste(ans,"power must be NA if r = 0")
70
              if(!is.na(object@alpha)) ans <- paste(ans,"alpha must be NA if r = 0")
71
            }
72
            else{
73
              if(is.na(object@HR) || object@HR >= 1 || object@HR <= 0)  ans <- paste(ans,"Invalid HR.") 
74
              if(object@alpha >= 1 || object@alpha <= 0 ) ans <- paste(ans,"Invalid alpha.")
75
              if(object@power >= 1 || object@power <= 0 ) ans <- paste(ans,"Invalid power.")
76
            }
77
            
78
            if(!object@type %in% c("Oncology","CRGI")) ans <- paste(ans,"Invalid type.") 
79
            
80
            if(length(object@followup) > 1 || object@followup <= 0 ) ans <- paste(ans,"Invalid followup.")
81
            
82
            if(class(object@lag.settings)!="LagEffect")ans <- paste(ans,"Invalid lag.settings")
83
            
84
            if(!isNullLag(object@lag.settings)){
85
              if(isSingleArm(object) && !is.na(object@lag.settings@L.HazardRatio)){
86
                ans <- paste(ans,"lag.settings@L.HazardRatio must be as.numeric(NA) if study has one arm")
87
              }
88
              if(!isSingleArm(object) && is.na(object@lag.settings@L.HazardRatio)){
89
                ans <- paste(ans,"lag.settings@L.HazardRatio cannot be NA if study has more than one arm")
90
              }
91
            }
92
                        
93
            if(!is.infinite(object@followup) && !isNullLag(object@lag.settings)){
94
              ans <- paste(ans, "Cannot use lagged settings with a study which has a finite follow up time")
95
            }
96
            
97
            if(ans=="") return(TRUE)
98
            ans
99
          }
100
          
101
)
102
103
104
105
106
##' @name show
107
##' @rdname show-methods
108
##' @aliases show,Study-method
109
##' @export
110
setMethod("show", signature(object="Study"), 
111
    function(object) {
112
      cat("Study definition:\n")
113
      cat(paste("Number of Patients (N):",object@N,"\n"))
114
      cat(paste("Study duration:",object@study.duration,"months\n"))
115
      cat(paste("Accrual period:",object@acc.period,"months\n"))
116
      cat(paste("Accrual uniformity (k):",object@k,"\n"))
117
      if(!isSingleArm(object)){
118
        cat(paste("Control arm survival:"),object@ctrlSpec@text,"\n")
119
        cat(paste("Hazard Ratio:",object@HR,"\n"))
120
        cat(paste("Ratio of control to experimental 1:",object@r,"\n",sep=""))
121
        cat(paste("alpha:",object@alpha))
122
        if(object@two.sided){
123
          cat("(two sided)\n")
124
        }
125
        else{
126
          cat("(one sided)\n")
127
        }
128
        cat(paste("Power:",object@power,"\n"))
129
      }
130
    else{
131
      cat(paste("Survival:"),object@ctrlSpec@text,"\n")
132
      cat("Single Arm trial\n")
133
    } 
134
      
135
    if(object@shape==1){
136
      cat("Exponential survival\n")
137
    }
138
    else{
139
      cat(paste("Weibull survival with shape parameter",object@shape,"\n"))    
140
    }
141
      
142
    if(!is.infinite(object@followup)){
143
      cat("Subject follow up period:",object@followup,"months\n")
144
    } 
145
      
146
    if(isSingleArm(object)){
147
      outputdropouttext("Subject",object@dropout[[1]],object@dropout.shape)
148
    }  
149
    else{
150
      outputdropouttext("Control Arm",object@dropout[[1]])
151
      outputdropouttext("Active Arm",object@dropout[[2]],object@dropout.shape)
152
    }
153
    show(object@lag.settings) 
154
})
155
156
157
158
##' Is the \code{Study} a single arm study
159
##' @rdname isSingleArm-methods
160
##' @name isSingleArm
161
##' @param study A \code{Study} object
162
##' @return TRUE if study is single arm, FALSE otherwise
163
##' @export
164
setGeneric("isSingleArm",
165
           def=function(study)
166
           standardGeneric("isSingleArm"))
167
168
169
170
171
##' @rdname isSingleArm-methods
172
##' @aliases isSingleArm,Study-method
173
##' @name isSingleArm
174
##' @export
175
setMethod("isSingleArm",representation(study="Study"),
176
          function(study){
177
            study@r==0
178
          })
179
180
181
##' @name predict
182
##' @rdname predict-methods
183
##' @aliases predict,Study-method
184
##' @param step.size The resolution of the grid to be used to calculate the time v 
185
##' expected events curves 
186
##' @export
187
setMethod( "predict", representation( object="Study" ), 
188
  function( object, time.pred=NULL,
189
            event.pred=NULL,step.size=0.5 ) {
190
    
191
    study <- object
192
    lagged <- study@lag.settings
193
    validatePredictArguments(time.pred,event.pred,step.size,study@study.duration)
194
        
195
    #times for the plotting function
196
    grid.times <- seq( 0, study@study.duration, step.size )
197
    
198
    #Next calculate the values of rate parameters
199
    lambda    <- lambda.calc( study@ctrlSpec@median, study@HR, study@shape )
200
    if(isNullLag(lagged)){
201
      lambdaot <- as.numeric(NA)
202
    }
203
    else{
204
      lambdaot  <- lambda.calc( lagged@ctrlSpec@median,  lagged@L.HazardRatio,study@shape )  
205
    }
206
    
207
    dropout.lambda <- GetDropoutLambda(study)
208
        
209
    #Create the survival functions
210
    sfns <- GetSurvivalFunctions(lambda,lambdaot,lagged@Lag.T,isSingleArm(study),study@shape,
211
                                 study@followup,study@dropout.shape,dropout.lambda)
212
       
213
    #Calculate event details for plot 
214
    grid <- CalculateEventsGivenTimes(grid.times,study,sfns,calc.at.risk=FALSE)  
215
    
216
    #Calculate average HR
217
    av.HR <-if(isNullLag(lagged) || isSingleArm(study)) study@HR 
218
            else calculateAverageHR(sfns,study,lagged@Lag.T,lagged@L.HazardRatio,
219
                                    lambdaot,lambda,study@shape)
220
   
221
    #Calculate the critical value
222
    critical.data <- CalculateCriticalValue(study,sfns,grid,av.HR)
223
    
224
    #Calculate events at given times
225
    predict.data <- if(!is.null(time.pred)) CalculateEventsGivenTimes(time.pred,study,sfns) 
226
                    else predict.data <- data.frame()
227
    
228
    
229
    #Calculate times for given number of events
230
    if(!is.null(event.pred)){
231
      predict.data <- rbind(predict.data,CalculateTimesGivenEvents(event.pred,study,sfns,grid))
232
    }
233
    
234
    return(AnalysisResults( 
235
           critical.HR = critical.data[["chr"]], 
236
           critical.data = critical.data[["critical.data"]], 
237
           critical.events.req= critical.data[["critical.events.req"]],
238
           av.hr=av.HR,
239
           grid=grid,
240
           predict.data=predict.data,
241
           study=study,
242
           sfns=sfns))
243
})
244