From noreply at r-forge.r-project.org Mon Nov 10 01:07:50 2014 From: noreply at r-forge.r-project.org (noreply at r-forge.r-project.org) Date: Mon, 10 Nov 2014 01:07:50 +0100 (CET) Subject: [Gsdesign-commits] r376 - in pkg/gsDesign: . R man Message-ID: <20141110000751.103C31877FE@r-forge.r-project.org> Author: keaven Date: 2014-11-10 01:07:50 +0100 (Mon, 10 Nov 2014) New Revision: 376 Modified: pkg/gsDesign/DESCRIPTION pkg/gsDesign/NAMESPACE pkg/gsDesign/R/gsMethods.R pkg/gsDesign/R/gsSpending.R pkg/gsDesign/R/gsSurv.R pkg/gsDesign/man/sfLinear.Rd pkg/gsDesign/man/sfTruncated.Rd pkg/gsDesign/man/spendingfunctions.Rd Log: sfTrimmed and sfStep added. Started NEWS file. Rounding inconsistencies for gsSurv summaries fixed. Modified: pkg/gsDesign/DESCRIPTION =================================================================== --- pkg/gsDesign/DESCRIPTION 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/DESCRIPTION 2014-11-10 00:07:50 UTC (rev 376) @@ -1,5 +1,5 @@ Package: gsDesign -Version: 2.9-2 +Version: 2.9-3 Title: Group Sequential Design Author: Keaven Anderson Maintainer: Keaven Anderson Modified: pkg/gsDesign/NAMESPACE =================================================================== --- pkg/gsDesign/NAMESPACE 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/NAMESPACE 2014-11-10 00:07:50 UTC (rev 376) @@ -8,8 +8,9 @@ export(normalGrid,ssrCP,condPower,Power.ssrCP,z2Z,z2Fisher,z2NC,plot.ssrCP) export(plot.gsDesign, plot.gsProbability, print.gsProbability, print.gsDesign) export(print.nSurvival, gsBoundSummary, xtable.gsSurv) -export(summary.gsDesign, xprint, print.gsBoundSummary) -export(sfPower, sfHSD, sfExponential, sfBetaDist, sfLDOF, sfLDPocock, sfPoints, sfLogistic, sfExtremeValue, sfExtremeValue2, sfLinear, sfTruncated) +export(summary.gsDesign, xprint, print.gsBoundSummary,summary.spendfn) +export(sfPower, sfHSD, sfExponential, sfBetaDist, sfLDOF, sfLDPocock, sfPoints) +export(sfLogistic, sfExtremeValue, sfExtremeValue2, sfLinear, sfStep, sfTruncated, sfTrimmed, sfGapped) export(sfCauchy, sfNormal, sfTDist, spendingFunction) export(checkScalar, checkVector, checkRange, checkLengths, isInteger) export(eEvents, print.eEvents, nSurv, gsSurv, print.nSurv, print.gsSurv, tEventsIA, nEventsIA) Modified: pkg/gsDesign/R/gsMethods.R =================================================================== --- pkg/gsDesign/R/gsMethods.R 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/R/gsMethods.R 2014-11-10 00:07:50 UTC (rev 376) @@ -10,6 +10,7 @@ # print.gsBoundSummary # gsBoundSummary # xprint +# summary.spendfn # # Hidden Functions: # @@ -104,8 +105,8 @@ " and ", ceiling(object$n.I[object$k]), " events required, ", sep="") }else if ("gsSurv" %in% class(object)){ out <- paste(out, "time-to-event outcome with sample size ", - ifelse(object$ratio==1,2*ceiling(object$eNE)[object$k,1],ceiling(object$eNE+object$eNC)[object$k,1]), - #ceiling(object$eNC+object$eNE)[object$k], + ifelse(object$ratio==1,2*ceiling(rowSums(object$eNE))[object$k], + (ceiling(rowSums(object$eNE))+ceiling(rowSums(object$eNC)))[object$k]), " and ", ceiling(object$n.I[object$k]), " events required, ", sep="") }else if(information){out <- paste(out," total information ",round(object$n.I[object$k],2),", ",sep="") }else out <- paste(out, "sample size ", ceiling(object$n.I[object$k]), ", ",sep="") @@ -116,32 +117,11 @@ out <- paste(out,". Enrollment and total study durations are assumed to be ",round(sum(object$R),1), " and ",round(max(object$T),1)," ",timeunit,", respectively",sep="") } - if(is.character(object$upper$sf)){ - out <- paste(out, " and ",sep="") - if(object$upper$sf=="WT"){ - out <- paste(out, ". Wang-Tsiatis bounds with Delta=",object$upper$param,sep="") - }else if(object$upper$sf=="Pocock"){ - out <- paste(out, "Pocock bounds") - }else out <- paste(out, "O'Brien-Fleming bounds",sep="") - }else{ - out <- paste(out, ". ",sep="") - if(object$test.type < 3){ - out <- paste(out, "Bounds derived using a ", object$upper$name," spending function",sep="") - if(length(object$upper$param)==1){ - out <- paste(out, " with ", object$upper$parname,"=",object$upper$param,sep="") - } - }else{ - out <- paste(out, "Efficacy bounds derived using a ", object$upper$name," spending function",sep="") - if(length(object$upper$param)==1){ - out <- paste(out, " with ", object$upper$parname,"=",object$upper$param,sep="") - } - out <- paste(out, ". Futility bounds derived using a ", object$lower$name," spending function",sep="") - if(length(object$lower$param)==1){ - out <- paste(out, " with ", object$lower$parname,"=",object$lower$param,sep="") - } - } - } - return(paste(out,".",sep="")) + if(object$test.type==2){out=paste(out,". Bounds derived using a ",sep="") + }else out <- paste(out,". Efficacy bounds derived using a",sep="") + out <- paste(out," ",summary(object$upper),".",sep="") + if (object$test.type>2) out <- paste(out," Futility bounds derived using a ",summary(object$lower),".",sep="") + return(out) } "print.gsDesign" <- function(x, ...) { @@ -251,11 +231,11 @@ if (x$test.type>2) { - sfprint(x$lower) + cat(summary(x$lower),".",sep="") } - cat("++ alpha spending:\n ") - sfprint(x$upper) + cat("\n++ alpha spending:\n ") + cat(summary(x$upper),".\n",sep="") if (x$n.fix==1) { @@ -447,7 +427,7 @@ }else{ nstat <- 4 statframe[statframe$Value==statframe$Value[3],]$Analysis <- paste("Events:",ceiling(rowSums(x$eDC+x$eDE))) - if (x$ratio==1) N <- 2*ceiling(rowSums(x$eNE)) else N <- ceiling(rowSums(x$eNE+x$eNC)) + if (x$ratio==1) N <- 2*ceiling(rowSums(x$eNE)) else N <- ceiling(rowSums(x$eNE))+ceiling(rowSums(x$eNC)) Time <- round(x$T,tdigits) statframe[statframe$Value==statframe$Value[4],]$Analysis <- paste(timename,": ",as.character(Time),sep="") } @@ -502,12 +482,19 @@ cat("Wang-Tsiatis boundary with Delta =", x$param) } else if (x$name == "Truncated") - { cat(x$param$name,"spending function truncated at",x$param$trange) - if (!is.null(x$parname) && !is.null(x$param)) + { cat(x$param$name," spending function compressed to ",x$param$trange[1],", ",x$param$trange[2],sep="") + if (!is.null(x$param$parname)) { - cat("\n with", x$parname, "=", x$param$param) + cat(" with", x$param$parname, "=", x$param$param) } } + else if (x$name == "Trimmed") + { cat(x$param$name," spending function trimmed at ",x$param$trange[1],", ",x$param$trange[2],sep="") + if (!is.null(x$param$parname)) + { + cat(" with", x$param$parname, "=", x$param$param) + } + } else { cat(x$name, "spending function") @@ -518,3 +505,48 @@ } cat("\n") } +"summary.spendfn" <- function(object,...) +{ + # print spending function information + if (object$name == "OF") + { + s <- "O'Brien-Fleming boundary" + } + else if (object$name == "Pocock") + { + s <- "Pocock boundary" + } + else if (object$name == "WT") + { + s <- paste("Wang-Tsiatis boundary with Delta =", object$param) + } + else if (object$name == "Truncated") + { s <- paste(object$param$name," spending function compressed to ",object$param$trange[1],", ",object$param$trange[2],sep="") + if (!is.null(object$param$parname)) + { + s <- paste(s," with", paste(object$param$parname,collapse=" "), "=", paste(object$param$param,collapse=" ")) + } + } + else if (object$name == "Trimmed") + { s <- paste(object$param$name," spending function trimmed at ",object$param$trange[1],", ",object$param$trange[2],sep="") + if (!is.null(object$param$parname)) + { + s <- paste(s," with", paste(object$param$parname, collapse=" "), "=", paste(object$param$param,collapse=" ")) + } + } + else if (object$name == "Gapped") + { s <- paste(object$param$name," spending function no spending in ",object$param$trange[1],", ",object$param$trange[2],sep="") + if (!is.null(object$param$parname)) + { + s <- paste(s," with", paste(object$param$parname, collapse=" "), "=", paste(object$param$param,collapse=" ")) + } + } else + { + s<- paste(object$name, "spending function") + if (!is.null(object$parname) && !is.null(object$param)) + { + s<- paste(s,"with", paste(object$parname,collapse=" "), "=", paste(object$param,collapse=" ")) + } + } + return(s) +} \ No newline at end of file Modified: pkg/gsDesign/R/gsSpending.R =================================================================== --- pkg/gsDesign/R/gsSpending.R 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/R/gsSpending.R 2014-11-10 00:07:50 UTC (rev 376) @@ -12,12 +12,14 @@ # sfLDOF # sfLDPocock # sfLinear +# sfStep # sfLogistic # sfNormal # sfPoints # sfPower # sfTDist # sfTruncated +# sfTrimmed # spendingFunction # # Hidden Functions: @@ -470,6 +472,62 @@ x } +"sfStep" <- function(alpha, t, param) +{ + x <- list(name="Step ", param=param, parname="line points", sf=sfStep, spend=NULL, + bound=NULL, prob=NULL) + + class(x) <- "spendfn" + checkScalar(alpha, "numeric", c(0, Inf), c(FALSE, FALSE)) + checkVector(t, "numeric", c(0, Inf), c(TRUE, FALSE)) + t[t>1] <- 1 + + if (!is.numeric(param)) + { + stop("sfStep parameter param must be numeric") + } + + j <- length(param) + if (floor(j / 2) * 2 != j) + { + stop("sfStep parameter param must have even length") + } + k <- j/2 + + if (max(param) > 1 || min(param) < 0) + { + stop("Timepoints and cumulative proportion of spending must be >= 0 and <= 1 in sfStep") + } + if (k > 1) + { inctime <- x$param[1:k] - c(0, x$param[1:(k-1)]) + incspend <- x$param[(k+1):j]-c(0, x$param[(k+1):(j-1)]) + if ((j > 2) && (min(inctime) <= 0)) + { + stop("Timepoints must be strictly increasing in sfStep") + } + if ((j > 2) && (min(incspend) < 0)) + { + stop("Spending must be non-decreasing in sfStep") + } + + } + s <- t + s[t<=param[1]|t<0]<-0 + s[t>=1] <- 1 + ind <- (0 < t) & (t <= param[1]) + s[ind] <- param[k + 1] + ind <- (1 > t) & (t >= param[k]) + s[ind] <- param[j] + if (k > 1) + { for (i in 2:k) + { ind <- (param[i - 1] < t) & (t <= param[i]) + s[ind] <- param[k + i - 1] + } + } + x$spend <- alpha * s + x +} + "sfPoints" <- function(alpha, t, param) { x <- list(name="User-specified", param=param, parname="Points", sf=sfPoints, spend=NULL, @@ -636,15 +694,85 @@ spend<-as.vector(array(0,length(t))) spend[t>=param$trange[2]]<-alpha indx <- param$trange[1]=1. | param$trange[2]<=param$trange[1] | param$trange[2]<=0) + stop("param$trange must be a vector of length 2 with 0 <= param$trange[1] < param$trange[2]<=1. See help(sfTrimmed)") + if (class(param$sf) != "function") stop("param$sf must be a spending function") + if (!is.numeric(param$param)) stop("param$param must be numeric") + spend<-as.vector(array(0,length(t))) + spend[t>=param$trange[2]]<-alpha + indx <- param$trange[1]=1. | param$trange[2]<=param$trange[1] | param$trange[2]<=0 |param$trange[1]<=0) + stop("param$trange must be a vector of length 2 with 0 < param$trange[1] < param$trange[2]<=1. See help(sfTrimmed)") + if (class(param$sf) != "function") stop("param$sf must be a spending function") + if (!is.numeric(param$param)) stop("param$param must be numeric") + spend<-as.vector(array(0,length(t))) + spend[t>=param$trange[2]]<-alpha + indx <- param$trange[1]>t + if (max(indx)){ + s <- param$sf(alpha=alpha,t=t[indx],param$param) + spend[indx] <- s$spend + } + indx <- (param$trange[1]<=t & param$trange[2]>t) + if (max(indx)){ + spend[indx] <- param$sf(alpha=alpha,t=param$trange[1],param$param)$spend + } + # the following line is awkward, but necessary to get the input spending function name in some cases + param2 <- param$sf(alpha=alpha,t=.5,param=param$param) + param$name <- param2$name + param$parname <- param2$parname + x<-list(name="Gapped", param=param, parname="range", + sf=sfGapped, spend=spend, bound=NULL, prob=NULL) + class(x) <- "spendfn" + x +} + + "spendingFunction" <- function(alpha, t, param) { checkScalar(alpha, "numeric", c(0, Inf), c(FALSE, FALSE)) Modified: pkg/gsDesign/R/gsSurv.R =================================================================== --- pkg/gsDesign/R/gsSurv.R 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/R/gsSurv.R 2014-11-10 00:07:50 UTC (rev 376) @@ -666,8 +666,8 @@ an[5*(0:(k-1))+1]<-c(paste("IA ",as.character(1:(k-1)),": ", as.character(round(100*x$timing[1:(k-1)],1)), "\\%",sep=""), "Final analysis") an[5*(1:(k-1))+1] <- paste("\\hline",an[5*(1:(k-1))+1]) - an[5*(0:(k-1))+2]<- paste("N:",ceiling(x$eNC[1:x$k]+x$eNE[1:x$k])) - an[5*(0:(k-1))+3]<- paste("Events:",ceiling((x$eDC)+(x$eDE))) + an[5*(0:(k-1))+2]<- paste("N:",ceiling(rowSums(x$eNC))+ceiling(rowSums(x$eNE))) + an[5*(0:(k-1))+3]<- paste("Events:",ceiling(rowSums(x$eDC+x$eDE))) an[5*(0:(k-1))+4]<- paste(round(x$T,1),timename,sep=" ") fut[5*(0:(k-1))+1]<- as.character(round(x$lower$bound,2)) eff[5*(0:(k-1))+1]<- as.character(round(x$upper$bound,2)) Modified: pkg/gsDesign/man/sfLinear.Rd =================================================================== --- pkg/gsDesign/man/sfLinear.Rd 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/man/sfLinear.Rd 2014-11-10 00:07:50 UTC (rev 376) @@ -1,17 +1,22 @@ \name{sfLinear} \alias{sfLinear} +\alias{sfStep} -\title{4.6: Piecewise Linear Spending Function} +\title{4.6: Piecewise Linear and Step Function Spending Functions} \description{The function \code{sfLinear()} allows specification of a piecewise linear spending function. -This provides complete flexibility in setting spending at desired timepoints in a group sequential design. -Normally this function will be passed to \code{gsDesign()} in the parameter +The function \code{sfStep()} specifies a step function spending function. +Both functions provide complete flexibility in setting spending at desired timepoints in a group sequential design. +Normally these function will be passed to \code{gsDesign()} in the parameter \code{sfu} for the upper bound or \code{sfl} for the lower bound to specify a spending function family for a design. -When passed to \code{gsDesign()}, the value of \code{param} would be passed to \code{sfLinear} through the \code{gsDesign()} arguments \code{sfupar} for the upper bound and \code{sflpar} for the lower bound. +When passed to \code{gsDesign()}, the value of \code{param} would be passed to \code{sfLinear()} or \code{sfStep()} through the \code{gsDesign()} arguments \code{sfupar} for the upper bound and \code{sflpar} for the lower bound. + +Note that \code{sfStep()} allows setting a particular level of spending when the timing is not strictly known; an example shows how this can inflate Type I error when timing of analyses are changed based on knowing the treatment effect at an interim. } \usage{ sfLinear(alpha, t, param) +sfStep(alpha, t, param) } \arguments{ \item{alpha}{Real value \eqn{> 0} and no more than 1. Normally, @@ -20,16 +25,25 @@ \item{t}{A vector of points with increasing values from 0 to 1, inclusive. Values of the proportion of sample size or information for which the spending function will be computed.} \item{param}{A vector with a positive, even length. Values must range from 0 to 1, inclusive. -Letting \code{m <- length(param/2)}, the first \code{m} points in param specify increasing values strictly between 0 and 1, -where the interim timing (proportion of final total statistical information) where spending is to be specified is given. -The last \code{m} points in param specify non-decreasing values from 0 to 1, inclusive, -with the cumulative proportion of spending at the timepoints in the first part of the vector.} +Letting \code{m <- length(param/2)}, the first \code{m} points in param specify increasing values strictly between 0 and 1 +corresponding to interim timing (proportion of final total statistical information). +The last \code{m} points in \code{param} specify non-decreasing values from 0 to 1, inclusive, +with the cumulative proportion of spending at the specified timepoints.} } \value{An object of type \code{spendfn}. -The cumulative spending returned in \code{sfLinear$spend} is 0 for \code{t=0} and \code{alpha} for \code{t>=1}. +The cumulative spending returned in \code{sfLinear$spend} is 0 for \code{t <= 0} and \code{alpha} for \code{t>=1}. For \code{t} between specified points, linear interpolation is used to determine \code{sfLinear$spend}. -See \code{\link{Spending function overview}} for further details.} +The cumulative spending returned in \code{sfStep$spend} is 0 for \code{t=1}. +Letting \code{m <- length(param/2)}, +for \code{i=1,2,...m-1} and \code{ param[i]<= t < param[i+1]}, the cumulative spending is set at \code{alpha * param[i+m]} (also for \code{param[m]<=t<1}). + +Note that if \code{param[2m]} is 1, then the first time an analysis is performed after the last proportion of final planned information (\code{param[m]}) will be the final analysis, using any remaining error that was not previously spent. + +See \code{\link{Spending function overview}} for further details. +} + + \seealso{\link{Spending function overview}, \code{\link{gsDesign}}, \link{gsDesign package overview}} \note{The manual is not linked to this help file, but is available in library/gsdesign/doc/gsDesignManual.pdf in the directory where R is installed.} @@ -55,5 +69,52 @@ x <- gsDesign(sfu=sfLinear, sfl=sfLinear, sfupar=sfupar, sflpar=sflpar) plot(x, plottype="sf") x + +# now do an example where timing of interims changes slightly, but error spending does not +# also, spend all alpha when at least >=90 percent of final information is in the analysis +sfupar=c(.2,.4,.9,((1:3)/3)^3) +x <- gsDesign(k=3,n.fix=100,sfu=sfStep,sfupar=sfupar,test.type=1) +plot(x,pl="sf") +# original planned sample sizes +ceiling(x$n.I) +# cumulative spending planned at original interims +cumsum(x$upper$spend) +# change timing of analyses; +# note that cumulative spending "P(Cross) if delta=0" does not change from cumsum(x$upper$spend) +# while full alpha is spent, power is reduced by reduced sample size +y <- gsDesign(k=3, sfu=sfStep, sfupar=sfupar, test.type=1, + maxn.IPlan=x$n.I[x$k], n.I=c(30,70,95), + n.fix=x$n.fix) +# note that full alpha is used, but power is reduced due to lowered sample size +gsBoundSummary(y) + +# now show how step function can be abused by 'adapting' stage 2 sample size based on interim result +x <- gsDesign(k=2,delta=.05,sfu=sfStep,sfupar=c(.02,.001),timing=.02,test.type=1) +# spending jumps from miniscule to full alpha at first analysis after interim 1 +plot(x, pl="sf") +# sample sizes at analyses: +ceiling(x$n.I) +# simulate 1 million stage 1 sum of 178 Normal(0,1) random variables +# Normal(0,Variance=178) under null hypothesis +s1 <- rnorm(1000000,0,sqrt(178)) +# compute corresponding z-values +z1 <- s1/sqrt(178) +# set stage 2 sample size to 1 if z1 is over final bound, otherwise full sample size +n2 <- array(1,1000000) +n2[z1<1.96]<- ceiling(x$n.I[2])-ceiling(178) +# now sample n2 observations for second stage +s2 <- rnorm(1000000,0,sqrt(n2)) +# add sum and divide by standard deviation +z2 <- (s1+s2)/(sqrt(178+n2)) +# By allowing full spending when final analysis is either +# early or late depending on observed interim z1, +# Type I error is now almost twice the planned .025 +sum(z1 >= x$upper$bound[1] | z2 >= x$upper$bound[2])/1000000 +# if stage 2 sample size is random and independent of z1 with same frequency, +# this is not a problem +s1alt <- rnorm(1000000,0,sqrt(178)) +z1alt <- s1alt / sqrt(178) +z2alt <- (s1alt+s2)/sqrt(178+n2) +sum(z1alt >= x$upper$bound[1] | z2alt >= x$upper$bound[2])/1000000 } \keyword{design} Modified: pkg/gsDesign/man/sfTruncated.Rd =================================================================== --- pkg/gsDesign/man/sfTruncated.Rd 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/man/sfTruncated.Rd 2014-11-10 00:07:50 UTC (rev 376) @@ -1,16 +1,26 @@ \name{sfTruncated} \alias{sfTruncated} +\alias{sfTrimmed} +\alias{sfGapped} -\title{4.7a: Truncated spending functions} -\description{The function \code{sfTruncated()} applies any other spending function over a restricted range. -This allows eliminating spending for early interim analyses when you desire not to stop for the bound being specified. -The truncation can come late in the trial. +\title{4.7a: Truncated, trimmed and gapped spending functions} +\description{The functions \code{sfTruncated()} and \code{sfTrimmed} apply any other spending function over a restricted range. +This allows eliminating spending for early interim analyses when you desire not to stop for the bound being specified; this is usually applied to eliminate early tests for a positive efficacy finding. +The truncation can come late in the trial if you desire to stop a trial any time after, say, 90 percent of information is available and an analysis is performed. +This allows full Type I error spending if the final analysis occurs early. +Both functions set cumulative spending to 0 below a 'spending interval' in the interval [0,1], and set cumulative spending to 1 above this range. +\code{sfTrimmed()} otherwise does not change an input spending function that is specified; probably the preferred and more intuitive method in most cases. +\code{sfTruncated()} resets the time scale on which the input spending function is computed to the 'spending interval.' + +\code{sfGapped()} allows elimination of analyses after some time point in the trial; see details and examples. } \usage{ sfTruncated(alpha, t, param) +sfTrimmed(alpha, t, param) +sfGapped(alpha, t, param) } \arguments{ \item{alpha}{Real value \eqn{> 0} and no more than 1. Normally, @@ -20,17 +30,25 @@ \item{t}{A vector of points with increasing values from 0 to 1, inclusive. Values of the proportion of sample size or information for which the spending function will be computed.} \item{param}{a list containing the elements sf (a spendfn object such as sfHSD), trange (the range over which the - spending function increases from 0 to 1; 0 <= trange[1] 0), and param (null for a spending function with no parameters or a scalar or vector of parameters needed to fully specify the spending function in sf).} } \value{An object of type \code{spendfn}. See \code{\link{Spending function overview}} for further details.} \details{ +\code{sfTrimmed} simply computes the value of the input spending function and parameters in the sub-range of [0,1], sets spending to 0 below this range and sets spending to 1 above this range. + +\code{sfGapped} spends outside of the range provided in trange. Below trange, the input spending function is used. +Above trange, full spending is used; i.e., the first analysis performed above the interval in trange is the final analysis. +As long as the input spending function is strictly increasing, this means that the first interim in the interval trange is the final interim analysis for the bound being specified. + \code{sfTruncated} compresses spending into a sub-range of [0,1]. The parameter \code{param$trange} specifies the range over which spending is to occur. Within this range, spending is spent according to the spending function specified in \code{param$sf} along with the corresponding spending function parameter(s) in \code{param$param}. See example using \code{sfLinear} that spends uniformly over specified range. + + } \seealso{\link{Spending function overview}, \code{\link{gsDesign}}, \link{gsDesign package overview}} @@ -45,32 +63,67 @@ \examples{ -# Eliminate spending forany interim at or before 20 percent of information. +# Eliminate efficacy spending forany interim at or before 20 percent of information. # Complete spending at first interim at or after 80 percent of information. -s<-sfLinear(alpha=.05,t=(0:100)/100,param=c(.5,.5)) -plot((0:100)/100,s$spend,type="l",main="Accelerating spending with sfTruncated", -xlab="Proportion of information",ylab="Spending") -s<-sfTruncated(alpha=.05,t=(0:100)/100,param=list(sf=sfLinear,trange=c(.2,.8),param=c(.5,.5))) -lines(col=2,(0:100)/100,s$spend) -text("Accelerated (red) spending over interval (.2,.8)",x=.03,y=.045,pos=4) +tx <- (0:100)/100 +s<-sfHSD(alpha=.05,t=tx,param=1)$spend +x <- data.frame(t=tx,Spending=s,sf="Original spending") +param <- list(trange=c(.2,.8),sf=sfHSD,param=1) +s<-sfTruncated(alpha=.05,t=tx,param=param)$spend +x <- rbind(x, data.frame(t=tx,Spending=s,sf="Truncated")) +s<-sfTrimmed(alpha=.05,t=tx,param=param)$spend +x <- rbind(x, data.frame(t=tx,Spending=s,sf="Trimmed")) +s <- sfGapped(alpha=.05,t=tx,param=param)$spend +x <- rbind(x, data.frame(t=tx,Spending=s,sf="Gapped")) +ggplot(x,aes(x=t,y=Spending,col=sf))+geom_line() -# now apply this in gsDesign -# note how sfupar is set up to do as above -# 1st version produces an error next to last interim must be before final spend -# x<-gsDesign(k=5, sfu=sfTruncated, sfupar=list(sf=sfLinear, param=c(.5,.5), -# trange=c(.2,.8))) +# now apply the sfTrimmed version in gsDesign +# initially, eliminate the early efficacy analysis +# note: final spend must occur at > next to last interim +x<-gsDesign(k=4, n.fix=100, sfu=sfTrimmed, + sfupar=list(sf=sfHSD, param=1, trange=c(.3,.9))) -# now final spend occurs at > next to last interim -x<-gsDesign(k=5, sfu=sfTruncated, sfupar=list(sf=sfLinear, param=c(.5,.5), trange=c(.2,.95))) -x +# first upper bound=20 means no testing there +gsBoundSummary(x) +# now, do not eliminate early efficacy analysis +param <- list(sf=sfHSD, param=1, trange=c(0,.9)) +x<-gsDesign(k=4, n.fix=100, sfu=sfTrimmed, sfupar=param) + # The above means if final analysis is done a little early, all spending can occur -# Suppose we skip 4th interim due to fast enrollment and set calendar date -# based on estimated full information, but come up with only 97 pct of plan -xA <- gsDesign(k=x$k-1,n.I=c(x$n.I[1:3],.97*x$n.I[5]),test.type=x$test.type, - maxn.IPlan=x$n.I[x$k],sfu=sfTruncated, - sfupar=list(sf=sfLinear, param=c(.5,.5), trange=c(.2,.95))) -xA +# Suppose we set calendar date for final analysis based on +# estimated full information, but come up with only 97 pct of plan +xA <- gsDesign(k=x$k, n.fix=100, n.I=c(x$n.I[1:3],.97*x$n.I[4]), + test.type=x$test.type, + maxn.IPlan=x$n.I[x$k], + sfu=sfTrimmed, sfupar=param) +# now accelerate without the trimmed spending function +xNT<- gsDesign(k=x$k, n.fix=100, n.I=c(x$n.I[1:3],.97*x$n.I[4]), + test.type=x$test.type, + maxn.IPlan=x$n.I[x$k], + sfu=sfHSD, sfupar=1) +# Check last bound if analysis done at early time +x$upper$bound[4] +# Now look at last bound if done at early time with trimmed spending function +# that allows capture of full alpha +xA$upper$bound[4] +# With original spending function, we don't get full alpha and therefore have +# unnecessarily stringent bound at final analysis +xNT$upper$bound[4] + +# note that if the last analysis is LATE, all 3 approaches should give the same +# final bound that has a little larger z-value +xlate <- gsDesign(k=x$k, n.fix=100, n.I=c(x$n.I[1:3],1.25*x$n.I[4]), + test.type=x$test.type, + maxn.IPlan=x$n.I[x$k], + sfu=sfHSD, sfupar=1) +xlate$upper$bound[4] + +# eliminate futility after the first interim analysis +# note that by setting trange[1] to .2, the spend at t=.2 is used for the first +# interim at or after 20 percent of information +x <- gsDesign(n.fix=100,sfl=sfGapped,sflpar=list(trange=c(.2,.9),sf=sfHSD,param=1)) + } \keyword{design} Modified: pkg/gsDesign/man/spendingfunctions.Rd =================================================================== --- pkg/gsDesign/man/spendingfunctions.Rd 2014-08-22 21:41:56 UTC (rev 375) +++ pkg/gsDesign/man/spendingfunctions.Rd 2014-11-10 00:07:50 UTC (rev 376) @@ -1,16 +1,20 @@ \name{Spending functions} \alias{Spending function overview} \alias{spendingFunction} +\alias{summary.spendfn} \title{4.0: Spending function overview} \description{Spending functions are used to set boundaries for group sequential designs. Using the spending function approach to design offers a natural way to provide interim testing boundaries when unplanned interim analyses are added or when the timing of an interim analysis changes. Many standard and investigational spending functions are provided in the gsDesign package. These offer a great deal of flexibility in setting up stopping boundaries for a design. + +The \code{summary()} function for \code{spendfn} objects provides a brief textual summary of a spending function or boundary used for a design. } \usage{ spendingFunction(alpha, t, param) +\method{summary}{spendfn}(object,...) } \arguments{ \item{alpha}{Real value \eqn{> 0} and no more than 1. Defaults in calls to \code{gsDesign()} are @@ -22,8 +26,10 @@ sample size/information for which the spending function will be computed.} \item{param}{A single real value or a vector of real values specifying the spending function parameter(s); this must be appropriately matched to the spending function specified.} + \item{object}{A spendfn object to be summarized.} + \item{...}{Not currently used.} } -\value{An object of type \code{spendfn}. +\value{\code{spendingFunction} and spending functions in general produce an object of type \code{spendfn}. \item{name}{A character string with the name of the spending function.} \item{param}{any parameters used for the spending function.} \item{parname}{a character string or strings with the name(s) of the parameter(s) in \code{param}.} @@ -71,11 +77,20 @@ # print the design x +# summarize the spending functions +summary(x$upper) +summary(x$lower) # plot the alpha- and beta-spending functions plot(x, plottype=5) -# Example 2: advance example: writing a new spending function +# what happens to summary if we used a boundary function design +x <- gsDesign(test.type=2,sfu="OF") +y <- gsDesign(test.type=1,sfu="WT",sfupar=.25) [TRUNCATED] To get the complete diff run: svnlook diff /svnroot/gsdesign -r 376