[f2e496]: / R / fromParameterInternal.R

Download this file

329 lines (271 with data), 11.8 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# The predict from parameters non-exported functions
# Calculate the expected number of event occuring at the given
# times
#
# @param times A vector of times, see the time.pred argument
# to the S4 predict method for the Study object
# @param study A \code{Study} object
# @param sfns The survival functions the output from \code{GetSurvivalFunctions}
# @param calc.at.risk Logical, if true calculate the time at risk
# @return A data frame containing the required data. See AnalysisResults
# slot grid for more details
CalculateEventsGivenTimes <- function(times,study,sfns,calc.at.risk=TRUE){
#Sort out recruitment
m <- pmin( times, study@acc.period )
rec.rate <- m^study@k/study@acc.period^study@k
Ns <- getNs(isSingleArm(study),study@r,study@N)
recruit <- rec.rate %*% t( Ns )
recruit.tot <- apply( recruit, 1, sum )
#Calculate events
eventscdf <- lapply(sfns,function(x){events.integ(x,study@acc.period, study@k, times)})
events <- cbind( eventscdf[[1]]*Ns[ 1 ], eventscdf[[2]]*Ns[ 2 ] )
events.tot <- apply( events, 1, sum )
rounded.events.tot <- floor(events[,1])+floor(events[,2])
df <- data.frame(time=times,events1=events[,1],events2=events[,2],events.tot=events.tot,recruit.tot=recruit.tot,
rounded.events.tot,time.pred=rep(TRUE,length(times)))
if(calc.at.risk){
atriskcdf <- lapply(sfns,function(x){atrisk.integ(x,study@acc.period, study@k, times)})
atrisk <- cbind( atriskcdf[[1]]*Ns[ 1 ], atriskcdf[[2]]*Ns[ 2 ] )
atrisk.tot <- apply(atrisk,1,sum)
df <- cbind(df,at.risk1=atrisk[,1],at.risk2=atrisk[,2],atrisk.tot=atrisk.tot)
}
return(df)
}
# Calculate the expected time a given number of events
# will have occured
#
# @param event.pred A vector of numbers of events, see the event.pred argument
# to the S4 predict method for the Study object
# @param study A \code{Study} object
# @param sfns The survival functions the output from \code{GetSurvivalFunctions}
# @param grid The output to \code{CalculateEventsGivenTimes} which is used
# to find good starting points for the search for thr required times
# @param supress.warning logical, should the warning about too many events be supressed
# @return A data frame containing the required data. See AnalysisResults
# slot grid for more details
CalculateTimesGivenEvents <- function(event.pred,study,sfns,grid,supress.warning=FALSE){
roundedEvents <- grid$rounded.events.tot
startIndex <- sapply(event.pred,function(x) sum(roundedEvents < x))
ans.times <- unlist(lapply(1:length(event.pred),function(x){
if(startIndex[x]==nrow(grid)){
if(!supress.warning) warning("event.pred argument too large -- too many events for study.duration")
NA
}
else{
exact_time <- subdivide(grid[startIndex[x],"time"],
grid[startIndex[x]+1,"time"],
event.pred[x],study,sfns)
}
}))
if(all(is.na(ans.times))){
return(data.frame())
}
retVal <- CalculateEventsGivenTimes(ans.times[!is.na(ans.times)],study,sfns)
retVal$time.pred<-rep(FALSE,nrow(retVal))
retVal
}
# Find the exact time at which a requested number
# of events occurs
#
# @param low A time at which the target number of events
# has not yet occured
# @param high A time at which the target number of events
# has already occured
# @param aim The target number of events
# @param study The study
# @param sfns list of Survival functions
# @return The time when the requested number of events occurs
subdivide <- function(low,high,aim,study,sfns){
while(!isTRUE(all.equal(low,high))){
mid <- (low+high)/2
ans <- CalculateEventsGivenTimes(mid,study,sfns,calc.at.risk=FALSE)$rounded.events.tot
if(ans >= aim){
high <- mid
}
else{
low <- mid
}
}
return(high)
}
# Output a list of survival functions to be in the intergration
# to calculate the number of events -- one surival function per arm
#
# If the study has a single arm then the second entry in the list is
# the logical 'FALSE'
#
# @param lambda A vector of rate parameters
# for the arms of the study
# @param lambdaot A vector of rate parameters for time < T
# for studies with a lag
# @param lag.T The amount of lag for the study (=0 if no lag)
# @param isSingleArm Logical, True is trial has a single arm
# @param shape Weibull shape parameter of the study
# @param followup The time subjects are followed from randomization (=\code{Inf} if followed
# until end of study/event occurs) - this is only used if \code{lag.T} is zero.
# @param dropout.shape The Weibull shape parameter for the drop out hazard function
# @param dropout.lambda The rate parameter for the drop out hazard function = 0 if no dropout
# if no drop out
# @return The survival functions
GetSurvivalFunctions <- function(lambda,lambdaot,lag.T,isSingleArm,shape,followup,dropout.shape,dropout.lambda){
.range <- if(isSingleArm) 1 else 1:2
ans <- lapply(.range,function(x){
Sfn(lambda[x],lambdaot[x],lag.T,shape,followup,dropout.shape=dropout.shape,dropout.lambda=dropout.lambda[x])
})
if(isSingleArm){
ans <- c(ans,NullSfn())
}
ans
}
# Calculate the required number of events to reject
# hypothesis H0: ln(HR)=0
#
# An error is produced if more events are required than
# there are subjects on the trial.
# See the vignette for details of the formula used.
#
# @param r Allocation ratio 1:r, control:experiment
# @param alpha Statistical significance (assuming if a two sided
# test then alpha has already been divided by 2)
# @param power Power of test
# @param HR Hazard ratio
# @param N total number of subjects recruited on trial
# @return The required number of events
RequiredEvents <- function(r,alpha,power,HR,N){
events.req <- ((r + 1)*(qnorm(1 - alpha) + qnorm(power))) /
(sqrt(r)*log(HR))
events.req <- events.req*events.req
if(events.req > N){
warning(paste0("Given these settings, the (rounded) required number of events is ", round(events.req),
" which is more than there are patients, please increase trial size!"))
}
events.req
}
# Calculate the events required for the study parameters
# the expected time for this to occur
# and the critical HR at this number of events
#
# @param study A \code{Study} object
# @param sfns The survival functions the output from \code{GetSurvivalFunctions}
# @param grid The output to \code{CalculateEventsGivenTimes} which is used
# to find good starting points for the search for thr required times
# @param av.HR The HR used to determine the number of events required
# @return A list containing \code{chr} the critical hazard ratio,
# \code{critical.events.req}, the critical number of events required and
# \code{critical.data}, a data frame for the critical number of events (see grid
# slot for AnalysisResults). If single arm study then an empty data frame and
# NA's for the other elements of the list is returned
CalculateCriticalValue <- function(study,sfns,grid,av.HR){
if(isSingleArm(study)){
chr<-as.numeric(NA)
critical.data<-data.frame()
events.req <- as.numeric(NA)
}
else{
#Calculate the number of events required
alpha <-if(study@two.sided) study@alpha/2 else study@alpha
events.req <- RequiredEvents(study@r,alpha,study@power,av.HR, study@N)
####### calculation of critical value - HR with 50% power ######
chr <- exp(-1*((study@r + 1)*(qnorm(1 - alpha)+qnorm(0.5)))/sqrt(study@r*events.req))
critical.data <- CalculateTimesGivenEvents(events.req,study,sfns,grid,supress.warning=TRUE)
}
return(list(chr=chr,critical.data=critical.data,
critical.events.req=events.req))
}
# Function which calculates the average HR for a lagged study
#
# See the vignette for details as to how this average is calculated
#
# @param sfns A list of the survival functions
# @param study A Study object
# @param Lagt The lag for the study
# @param LagHR The HR for time < t
# @param lambdaot The lambda for time < lag
# @param lambdatts The lambda for time > lag
# @param shape The Weibull shape parameter for the study
# @return The average HR
calculateAverageHR <- function(sfns,study,Lagt,LagHR,lambdaot,lambdatts,shape){
sfnsLagt <- GetSurvivalFunctions(lambda=lambdaot,lambdaot=0,lag.T=0,
isSingleArm=isSingleArm(study),
shape=shape,followup=Lagt,study@dropout.shape,
GetDropoutLambda(study))
pot <- unlist(lapply(sfnsLagt,function(x)
events.integ(x,study@acc.period, study@k,study@study.duration)))
ptts <- if(Lagt < study@study.duration){
unlist(lapply(sfns,function(x)
events.integ(x,study@acc.period, study@k, study@study.duration))) - pot}
else
c(0,0)
Ns <- getNs(isSingleArm(study),study@r,study@N)
w1 <- sum(pot*Ns)
w2 <- sum(ptts*Ns)
return(exp((w1*log(LagHR) + w2*log(study@HR))/(w1+w2)))
}
# Function that converts HR and control median into lambda
# @param median Control median
# @param hr Hazard ratio
# @param shape A Weibull Shape parameter
# @return A vector of hazard ratios
lambda.calc <- function( median, hr,shape){
if(length(median)!=1 || length(shape)!=1 || (!is.na(hr) && length(hr) != 1)){
stop("Invalid arguments to lambda.calc")
}
log(2)^(1/shape)/(median/c(1, hr^(1/shape)))
}
# Check the arguments to predict are valid
#
# @param time.pred A vector of times for which the number events are to be predicted
# @param event.pred A vector of events for which the times they are expected
# to occur are output
# @param step.size The time between consecutive time points used for plotting the graphs
# @param study.duration the duration of the study
# @return Function throws an exception if any of the arguments are invalid
validatePredictArguments <- function(time.pred,event.pred,step.size,study.duration){
if(!is.null(time.pred)){
if(!is.numeric(time.pred) || any(time.pred < 0) || any(time.pred > study.duration) ){
stop("Invalid time.pred argument")
}
}
if(!is.null(event.pred)){
is.wholenumber <- function(x, tol = .Machine$double.eps^0.5) abs(x - round(x)) < tol
if(!is.numeric(event.pred) || any(event.pred < 0) || any(!is.wholenumber(event.pred))){
stop("Invalid event.pred argument")
}
}
if(!is.numeric(step.size) || length(step.size) > 1 || step.size <= 0 || step.size > study.duration ){
stop("Invalid step.size argument")
}
}
# Return the drop out rates vector from a Study object
# @param study A Study object
# @return A vector of dropout rates - control then active for 2 arm studies, just control
# arm for single arm studies
GetDropoutLambda <- function(study){
.range <- if(isSingleArm(study)) 1 else 1:2
unlist(lapply(.range,function(x){
lambda.calc(study@dropout[[x]]@median,1,study@dropout.shape)[1]
}))
}
# Output the text concerning dropouts for the show method
# for the subject object
#
# @param subject.text string denonting who the dropouts concern
# (e.g. "Subject" or "Control arm")
# @param dropoutspec The CtrlSpec object representing the dropout data to be output
# @param dropoutshape If not Null then output the dropout shape
outputdropouttext <- function(subject.text,dropoutspec,dropoutshape=NULL){
if(!is.infinite(dropoutspec@median)){
cat(paste(subject.text,"drop out:",dropoutspec@text,"\n"))
if(!is.null(dropoutshape)){
if(dropoutshape==1){
cat("Exponential drop out\n")
}
else{
cat(paste("Weibull drop out with shape parameter",dropoutshape,"\n"))
}
}
}
else{
cat(paste(subject.text,"do not drop out","\n"))
}
}