# S-plus/R functions to determine the NPMLE of the distribution for interval-
# censored event time.
# Copyright 1998-2000 Alain Vandal and Robert Gentleman
# University of Auckland
# These functions should work, but their intent is to illustrate the
# concepts involved. Functions are provided as is, with no guarantee.
# Redistribute freely, without undocumented modification & without charge.
# Queries to vandal@stat.auckland.ac.nz or rgentlem@stat.auckland.ac.nz.
# Returns a list of maximal antichains from a list of real valued intervals
# Arguments: intvls: n x 2 matrix;first column contains left endpoints
# second column contains right endpoints
# Returned value: list of length m (magnitude of underlying interval order)
# - each entry corresponds to one maximal antichain
# - each entry contains the row numbers of all intervals
# belonging to the maximal antichains
# - maximal antichains occur in the list in their natural
# linear ordering
# Known bugs: In R, will issue some ignorable warnings if there is
#right-censored data (with an "Inf" right endpoint).
Maclist <- function(intvls, Lopen=TRUE, Ropen=FALSE)
{
m <- dim(intvls)[1]
id <- 1:m
or <- order(intvls[,1])
maclist <- NULL
curmac <- id[or[1]]
minend <- intvls[curmac,2]
for (i in 2:m) {
curintvl <- id[or[i]]
if( intvls[curintvl,1]>minend ||
((Lopen || Ropen) && intvls[curintvl,1]==minend ) ) {
# New maximal antichain
maclist <- c(maclist,list(curmac))
oldmac <- curmac
curmac <- NULL
for (j in 1:length(oldmac))
if ( intvls[curintvl,1]<intvls[oldmac[j],2] ||
(!Lopen && !Ropen &&
intvls[curintvl,1]==intvls[oldmac[j],2]) )
curmac <- c(curmac,oldmac[j])
curmac <- c(curmac,curintvl)
minend <- min(intvls[curmac,2])
} else {
curmac <- c(curmac,curintvl)
minend <- min(minend,intvls[curintvl,2]) }
}
c(maclist,list(curmac))
}
# Returns the clique matrix and Petrie pairs of an interval order
# given its list of maximal antichains Arguments: ml: list of maximal
# antichains as returned by Maclist Returned value: object containing
# # - pmat: clique matrix of the underlying interval order, # rows are
# ordered according to the linear ordering of # the maximal antichains
# # - ppairs: Petrie pairs indicate the first and last # maximal
# antichains to which each elements belongs
Macmat <- function(ml)
{
temp <- NULL
m <- length(ml)
for (i in 1:m)
temp <- c(temp,ml[[i]])
temp <- sort(unique(temp))
n <- length(temp)
ppairs <- matrix(0,2,n)
retmat <- matrix(0,m,n)
for (i in 1:m) {
for (j in ml[[i]]) {
if (ppairs[1, j]==0)
ppairs[1, j] <- i
ppairs[2, j] <- i
}
retmat[i, ml[[i]]] <- 1
}
dimnames(ppairs) <- list(c("Start","End"),temp)
dimnames(retmat) <- list(NULL,temp)
ret <- list(pmat = retmat, ppairs = ppairs)
class(ret) <- "petrie"
return(ret)
}
# Produce the mapping of the maximal antichains to their real interval
# representation for an interval order given by real-valued intervals.
# Arguments: intvls: see Maclist
# ml: list of maximal antichains for the intervals as
# returned by Maclist
# Returned values: matrix m x 2 containing the mapping row-wise
# (1rst row corresponds to 1rst maximal antichains, etc.)
# m is the number of maximal antichains,
# 1rst column contains left endpoints of the mapping
# 2nd column contains right endpoints of the mapping
MLEintvl <- function(intvls, ml=Maclist(intvls))
{
if( ncol(intvls) != 2 || any(intvls[,2] < intvls[,1]) )
stop("only one dimensional intervals can be handled")
m <- length(ml)
ret <- matrix(0, m, 2)
for(i in 1:m) {
LL <- min(intvls)
RR <- max(intvls)
for(j in 1:length(ml[[i]])) {
LL <- max(LL, intvls[ml[[i]][j], 1])
RR <- min(RR, intvls[ml[[i]][j], 2])
}
ret[i, ] <- c(LL, RR)
}
ret
}
# Pool monotone groups algorithm
# Adapted from Y.L. Zhang & M.A. Newton (1997)
# (http://www.stat.wisc.edu/~newton/newton.html)
# Isotonizes a weighted and ordered set of values
# Arguments: est: the list of values
# ww: their weights
# Returned values: object containing
# - est: isotonized estimates
# - ww: weights of the isotonized estimates
# - poolnum: number of values pooled in the current
# estimate
# - passes: number of passes which were required to
# isotonize the list
PMGA<-function(est,ww=rep(1,length(est)))
{
curm<-length(est)
poolnum<-rep(1,curm)
passes<-0
iso<-FALSE
while (!iso) {
iso<-TRUE
poolind<-1
curind<-1
while (curind<=curm) {
groupstart<-curind
while (curind<curm && est[curind+1]<est[curind])
curind<-curind+1
iso<-poolind == curind
est[poolind]<-sum(ww[groupstart:curind]*est[groupstart:curind])/sum(ww[groupstart:curind])
ww[poolind]<-sum(ww[groupstart:curind])
poolnum[poolind]<-sum(poolnum[groupstart:curind])
poolind<-poolind+1
curind<-curind+1
}
curm<-poolind-1
passes<-passes+1
}
return(list(est = est[1:curm], ww = ww[1:curm], poolnum = poolnum[1:curm],
passes = passes))
}
# Returns the (unsmoothed) NPMLE of the distribution function on the maximal
# antichains of interval censored survival data.
# The algorithm is adapted from Wellner & Zhan (1997).
# Arguments:
# - A: clique matrix of the data (only necessary argument)
# - EMstep: boolean determining whether an EM-step will be taken
# at each iteration
# - ICMstep: boolean determining whether an ICM step will be taken
# at each iteration
# - checkbnds: make sure that isotonization step does not wash out
# essential maximal antichains by using self-consistent bounds
# - keepiter: boolean determining whether to keep the iteration
# states
# - eps: maximal L1 distance between successive estimates before
# stopping iteration
# - maxiter: maximal number of iterations to perform before
# stopping
# Returned values: object containing
# - sigma: NPMLE of the survival function on the maximal
# antichains
# - weights: diagonal of the likelihood function's second
# derivative
# - lastchange: vector of differences between the last two
# iterations
# - numiter: total number of iterations performed
# - iter: (only present if keepiter is true) states of sigma during
# the iteration
EMICMmac <- function(A, EMstep=TRUE, ICMstep=TRUE, keepiter=FALSE, tol=1e-7,
tolbis=1e-7,maxiter=1000)
{
if (!EMstep && !ICMstep) {
print("One of EMstep or ICMstep must be true.")
return(NULL)
}
Meps<-.Machine$double.eps
m<-dim(A)[1]
n<-dim(A)[2]
tA<-t(A)
if (m==1) {
ret<-NULL
ret$sigma<-1
ret$weights<-n
ret$lastchange<-0
ret$numiter<-0
return(ret)
}
WW<-matrix(0,m,n)
for (i in 1:(m-1))
WW[i,]<-A[i,]-A[i+1,]
WW[m,]<-A[m,]
sigma<-cumsum(apply(A,1,sum)/sum(A))
numiter<-0
oldsigma<-rep(-1,m)
if (keepiter) iter<-sigma
while (max(abs(oldsigma-sigma))>tol && numiter<=maxiter) {
oldsigma<-sigma
if (EMstep) {
pvec<-diff(c(0,sigma))
temp<-sweep(A,1,pvec,FUN="*")
if (sum(apply(temp,2,sum)==0)==0) {
pvec<-apply(sweep(temp,2,apply(temp,2,sum),
FUN="/"),1,sum)
sigma<-cumsum(pvec)/sum(pvec)
}
if (keepiter) iter<-rbind(iter,sigma)
}
if (ICMstep) {
Wps<-1/(t(WW)%*%sigma)
weights<-(abs(WW)%*%Wps^2)
increment<-as.vector((WW%*%Wps)/weights)
sigma<-sigma+increment
sigma[m]<-1
if (keepiter) iter<-rbind(iter,sigma)
temp<-PMGA(sigma[-m],weights[-m])
poolnum<-c(0,cumsum(temp$poolnum))
for (i in 2:length(poolnum))
for (j in (poolnum[i-1]+1):poolnum[i])
sigma[j]<-temp$est[i-1]
if (keepiter) iter<-rbind(iter,sigma)
# Implementing Jongbloed's correction through bisection
temp<-c(0,sigma)
pvec<-diff(c(0,oldsigma))
ndir<-diff(c(0,temp[2:(m+1)]))-pvec
pvec<-Bisect(tA,pvec,ndir,Meps,tolbis=1e-7)
sigma<-cumsum(pvec)
if (keepiter) iter<-rbind(iter,sigma)
}
numiter<-numiter+1
}
if (numiter == maxiter)
warning("EM/ICM may have failed to converge.")
pf<-diff(c(0,sigma))
ret<-list(sigma=sigma,pf=pf,llk=sum(log(t(A)%*%pf)),
weights=as.vector(weights),lastchange=sigma-oldsigma,
numiter=numiter,eps=tol)
if (keepiter) {
if (EMstep && ICMstep)
dimnames(iter)<-list(c("Seed",rep(c("EM","Fisher","PMGA","Bisect"),
numiter)),NULL)
else if (EMstep)
dimnames(iter)<-list(rep("EM",numiter+1),NULL)
else
dimnames(iter)<-list(c("Seed",rep(c("Fisher","PMGA"),
numiter)),NULL)
ret$iter<-iter
}
ret
}
# Returns the distribution function NPMLE for interval censored data, with
# information regarding its real-line support
# Arguments: - intvls: list of intervals as per Maclist
# - all other arguments: see EMICMmac
# Returned values: object containing
# - all information as per returned value of EMICMmac
# - pf: probability function on the maximal antichains
# - intmap: real interval mapping for the mass of the
# NPMLE; the Groeneboom-Wellner estimate is derived
# by assigning all the mass of the NPMLE on the maximal
# antichain to the right endpoint of the corresponding
# interval.
# - class: value "icsurv"
EMICM <- function(A, EMstep=TRUE, ICMstep=TRUE, keepiter=FALSE, tol=1e-7,
maxiter=1000)
{
if( ncol(A) == 2 && all(A[,2]>=A[,1]) ) {
ml<-Maclist(A)
intmap <- t(MLEintvl(A, ml))
A <- Macmat(ml)$pmat
}
else
intmap <- NULL
temp<-EMICMmac(A, EMstep=EMstep, ICMstep=ICMstep,
keepiter=keepiter,tol=tol,maxiter=maxiter)
if (is.null(temp)) return(NULL)
class(temp) <- "icsurv"
temp$intmap <- intmap
temp
}
# Plotting an icsurv class object.
# Arguments:
# - x: an object returned by EMICM
# - type: "eq" equivalence class
# "gw" Groeneboom-Wellner estimate
# "lc" left-continuous estimate
# Returned value: none; a plot is produced on the current graphics device.
plot.icsurv <- function(x, type="eq", surv=FALSE, bounds=FALSE, shade=3,
density=30, angle=45, lty=1, new=TRUE, xlab="Time",
ylab="Probability", main="GMLE",ltybnds=2,...)
{
if(!inherits(x, "icsurv" ) ) {
stop("plot.icsurv only works for icsurv objects")
}
if( is.null(x$intmap) )
stop("the object does not contain the real representation")
m<-dim(x$intmap)[[2]]
maxx<-1.1*max(x$intmap[!is.infinite(x$intmap)])
effsupp<-x$intmap[,x$pf>0]
effsupp<-cbind(c(0,0),effsupp)
sigma <- x$sigma
if( is.null(sigma) )
sigma <- cumsum(x$pf)
sigma<-c(0,sigma[x$pf>0])
if (surv) sigma<-1-sigma
if (new) {
if (missing(main)) {
if (surv) {
if (type=="gw")
main<-"Survival GWMLE function"
else if (type=="lc")
main<-"Left-continuous survival GMLE function"
else
main<-"GMLE survival equivalence class"
} else {
if (type=="gw")
main<-"Distribution GWMLE function"
else if (type=="lc")
main<-"Left-continuous distribution GMLE function"
else
main<-"GMLE distribution equivalence class"
}
}
plot(c(0,maxx),c(0,1),xlab=xlab,ylab=ylab,main=main,type="n",...)
}
effm<-length(sigma)-1
if((type=="gw" && !surv) || (type=="lc" && surv)) {
for (i in 1:effm) {
segments(effsupp[2,i],sigma[i],effsupp[2,i+1],sigma[i],lty=lty)
segments(effsupp[2,i+1],sigma[i],effsupp[2,i+1],sigma[i+1],lty=lty)
}
if (sum(is.infinite(effsupp))==0)
segments(effsupp[2,effm+1],1,maxx,1)
}
else if ((type=="lc" && !surv) ||( type=="gw" && surv)) {
for (i in 1:effm) {
segments(effsupp[1,i],sigma[i],effsupp[1,i+1],sigma[i],lty=lty)
segments(effsupp[1,i+1],sigma[i],effsupp[1,i+1],sigma[i+1],lty=lty)
}
if (sum(is.infinite(effsupp))==0)
segments(effsupp[1,effm+1],1,maxx,1)
}
else {
if (shade==1) {
for (i in 1:effm) {
segments(effsupp[2,i],sigma[i],effsupp[1,i+1],sigma[i],lty=lty)
}
} else if (shade==2) {
for (i in 1:effm) {
segments(effsupp[2,i],sigma[i],effsupp[1,i+1],sigma[i],lty=lty)
polygon(c(effsupp[2,i+1],effsupp[2,i+1],effsupp[1,i+1],effsupp[1,i+1]),
c(sigma[i],sigma[i+1],sigma[i+1],sigma[i]),
border=TRUE,density=0,lty=2)
}
} else {
for (i in 1:effm) {
segments(effsupp[2,i],sigma[i],effsupp[1,i+1],sigma[i],lty=lty)
#dirty check on R vs. S, since density not applied in R
if (is.null(version$language))
polygon(c(effsupp[2,i+1],effsupp[2,i+1],effsupp[1,i+1],effsupp[1,i+1]),
c(sigma[i],sigma[i+1],sigma[i+1],sigma[i]),
border=FALSE,density=density,angle=angle)
else
polygon(c(effsupp[2,i+1],effsupp[2,i+1],effsupp[1,i+1],effsupp[1,i+1]),
c(sigma[i],sigma[i+1],sigma[i+1],sigma[i]),
border=FALSE,col="green")
}
if (bounds) {
bndhi<-cbind(c(0,x$intmap[1,]),c(x$intmap[1,],maxx))
bndlo<-cbind(c(0,x$intmap[2,]),c(x$intmap[2,],maxx))
if (surv) bndval<-1-bndval
segments(bndlo[,1],x$bounds[1,],bndlo[,2],x$bounds[1,],lty=ltybnds)
segments(x$intmap[2,],x$bounds[1,-(m+1)],x$intmap[2,],x$bounds[1,-1],lty=ltybnds)
segments(bndhi[,1],x$bounds[2,],bndhi[,2],x$bounds[2,],lty=ltybnds)
segments(x$intmap[1,],x$bounds[2,-(m+1)],x$intmap[1,],x$bounds[2,-1],lty=ltybnds)
}
}
if (sum(is.infinite(effsupp))==0)
if (!surv)
segments(effsupp[2,effm+1],1,maxx,1,lty=lty)
else
segments(effsupp[2,effm+1],0,maxx,0,lty=lty)
}
}
####################################
# MIXTURE METHODS for interval censored data survival estimation
# VEM, ISDM, PGM
# All require argument A, the clique matrix of the data, so that a typical call would be
# VEM(Macmat(Maclist(brcm))$pmat)
##################################
Bisect <- function(tA, pvec, ndir, Meps, tolbis=1e-7)
{
etainv<-1/(tA%*%pvec)
bot<-0
top<-1
mult<-tA%*%ndir
dbot<-sum(etainv*mult)
ptop<-rescaleP(pvec+top*ndir, Meps)
pbot<-pvec
dtop<-sum(mult/(tA%*%ptop))
done<-FALSE
while( !done ) {
if( sign(dbot)*sign(dtop) > 0 || top-bot<tolbis ) {
ltop<-sum(log(tA%*%ptop))
lbot<-sum(log(tA%*%pbot))
if( lbot > ltop )
pnew<-rescaleP(pvec+bot*ndir, Meps)
else
pnew<-rescaleP(pvec+top*ndir, Meps)
done<-TRUE
}
else {
mid<-(bot+top)/2
pmid<-rescaleP(pvec+mid*ndir, Meps)
dmid<-sum(mult/(tA%*%pmid))
if( dmid*dtop < 0 ) {
bot<-mid
dbot<-dmid
pbot<-pmid
}
else {
top<-mid
dtop<-dmid
ptop<-pmid
}
}
}
pnew
}
##################################
# Bohning's Vertex Exchange Method
##################################
VEM <- function(A, pvec, maxiter = 500, tol = 1e-7, tolbis = 1e-7,
keepiter=FALSE)
{
if( ncol(A) == 2 && all(A[,2]-A[,1] >= 0) ) {
ml <- Maclist(A)
intmap <- t(MLEintvl(A, ml))
A <- Macmat(ml)$pmat
}
else
intmap <- NULL
m<-dim(A)[1]
n<-dim(A)[2]
tA<-t(A)
Meps<-.Machine$double.eps
if(missing(pvec))
pvec <- apply(A, 1, sum)/sum(A)
pvec<-rescaleP(pvec, Meps)
if (keepiter)
iter<-pvec
m<-length(pvec)
Linv<-1/(tA%*%pvec)
lnew<--sum(log(Linv))
finished<-FALSE
i<-0
while( i<maxiter && !finished ) {
i<-i+1
dvec<-A%*%Linv
mind<-min(dvec[pvec>0])
minj<-match(mind,dvec)
maxd<-max(dvec)
maxj<-match(maxd,dvec)
ndir<-rep(0,m)
ndir[minj]<- -1
ndir[maxj]<-1
ndir<-ndir*pvec[minj]
pold<-pvec
pvec<-Bisect(tA,pvec,ndir,Meps,tolbis=tolbis)
lold<-lnew
Linv<-1/(tA%*%pvec)
lnew<--sum(log(Linv))
if (keepiter)
iter<-cbind(iter,pvec)
finished<-(sum(abs(pvec-pold))<tol && abs(lnew-lold)<tol )
}
if( !finished )
warning("VEM may have failed to converge")
ret<-list(pf=pvec, lval=lnew, numiter=i, intmap=intmap,
converge=finished )
if (keepiter)
ret$iter<-iter
class(ret) <- "icsurv"
ret
}
#################################
# Lesperance & Kalbfleisch ISDM
#################################
ISDM <- function(A, pvec, maxiter = 500, tol = 1e-07, tolbis = 1e-08,
verbose = FALSE )
{
if( ncol(A) == 2 && all( A[,2]-A[,1] >= 0 ) ) {
ml <- Maclist(A)
intmap <- t(MLEintvl(A, ml))
A <- Macmat(ml)$pmat
}
else
intmap <- NULL
n <- dim(A)[2]
m <- dim(A)[1]
Meps<-.Machine$double.eps
tA<-t(A)
if(missing(pvec))
pvec <- apply(A, 1, sum)/sum(A)
pvec<-rescaleP(pvec, Meps)
LL <- as.vector(tA %*% pvec)
dd <- A %*% (1/LL) - n
ind <- dd > 0
numiter <- 0
finished <- FALSE
eps0 <- 0.5 #or some other number
while(!finished && (numiter < maxiter) ) {
numiter <- numiter + 1
k <- sum(ind)
eps0 <- 1-k/m #or some other number
aug <- rbind(LL, A[ind, ])
taug <- t(aug)
p1 <- c(eps0, (1-eps0)*(pvec[ind]/sum(pvec[ind])))
rval <- VEM(aug, p1, tol=1e-5/numiter, tolbis=1e-5/numiter)
epsilon <- rval$pf
curlike <- prod(LL)
LL <- as.vector(taug%*% epsilon)
if(verbose) {
print(paste("iteration: ",numiter))
print(paste("VEM iterations: ", rval$numiter))
print(epsilon)
print(round(pvec,4))
print(sum(log(LL)))
}
pold <- pvec
pvec <- as.vector(solve(A %*% tA, A %*% LL))
pvec <- rescaleP(pvec, Meps)
finished <- sum(abs(pvec - pold)) < tol
dd <- A %*% (1/LL) - n
ind <- dd >= 0
}
if( !finished )
warning("ISDM may have failed to converge")
ret <- list(pf=pvec, numiter=numiter, intmap=intmap,
converge=finished)
class(ret) <- "icsurv"
return(ret)
}
#############
#rescaleP is a function that rescales a prob vector so that elements
# that are negative or less than machine epsilon are set to zero.
###########
rescaleP <- function(pvec, tiny)
{
pvec<-ifelse(pvec<tiny,0,pvec)
pvec<-pvec/sum(pvec)
return(pvec)
}
########################
# Projected gradient method
# Search direction is based on all directional derivatives on the
# caveat that support points with 0 mass and negative directional
# derivatives are excluded from the direction.
# Alain: told does not seem to be used???
########################
PGM <- function(A, pvec, maxiter = 500, tol = 1e-07, told=2e-5,
tolbis = 1e-08, keepiter=FALSE)
{
if( ncol(A) == 2 && all(A[,2]>=A[,1]) ) {
ml <- Maclist(A)
intmap <- t(MLEintvl(A, ml))
A <- Macmat(ml)$pmat
}
else
intmap <- NULL
m<-dim(A)[1]
n<-dim(A)[2]
tA<-t(A)
Meps<-.Machine$double.eps
if(missing(pvec))
pvec <- apply(A, 1, sum)/sum(A)
Linv<-1/(tA%*%pvec)
lnew<-sum(-log(Linv))
dd <- A %*% Linv
if (keepiter)
iter<-pvec
i <- 0
finished <- FALSE
while (i<maxiter && !finished) {
i <- i + 1
pvec<-rescaleP(pvec, Meps)
# Limit directional derivative vector to possible
# directions of motion (!zero)
zero<-dd<n & pvec<=0
# Get projected gradient
# Lam can be replaced with another pd matrix to get
# another member of Wu's class of search directions
dlam<-dd[!zero]
Lam<-diag(rep(1,m-sum(zero)))
dir<-rep(0,m)
dir[!zero]<-(m-sum(zero))*dlam-sum(dlam)
# Renormalize for numerical stability
dir<-dir/max(abs(dir))
# Obtain maximum distance from current vector
# in direction of dir
pos<-dir>0
neg<-dir<0
pma<-min(c(-pvec[neg]/dir[neg],(1-pvec[pos])/dir[pos]))
dir<-pma*dir
# Setup the bisection
pold<-pvec
pvec<-Bisect(tA,pvec,dir,Meps,tolbis=tolbis)
if (keepiter)
iter<-cbind(iter,pvec)
lold<-lnew
Linv<-1/(tA%*%pvec)
lnew<-sum(-log(Linv))
dd <- A %*% (1/(tA%*%pvec))
finished <- (sum(abs(pvec - pold)) < tol && abs(lnew-lold)<tol)
}
if ( !finished )
warning("PGM may have failed to converge.")
eps<-c(tol,told,tolbis)
names(eps)<-c("tol","told","tolbis")
ret<-list(pf=pvec,sigma=cumsum(pvec),lval=lnew,clmat=A,
method="MPGM", lastchange=pvec-pold, numiter=i,
eps=eps, converge=finished, intmap=intmap)
if (keepiter)
ret$iter<-iter
class(ret) <- "icsurv"
ret
}
# Returns the (unsmoothed) NPMLE of the distribution function on the maximal
# antichains of interval censored survival data.
# The algorithm is adapted from Wellner & Zhan (1997).
# Arguments:
# - A: clique matrix of the data (only necessary argument)
# - EMstep: boolean determining whether an EM-step will be taken
# at each iteration
# - ICMstep: boolean determining whether an ICM step will be taken
# at each iteration
# - checkbnds: make sure that isotonization step does not wash out
# essential maximal antichains by using self-consistent bounds
# - keepiter: boolean determining whether to keep the iteration
# states
# - eps: maximal L1 distance between successive estimates before
# stopping iteration
# - maxiter: maximal number of iterations to perform before
# stopping
# Returned values: object containing
# - sigma: NPMLE of the survival function on the maximal
# antichains
# - weights: diagonal of the likelihood function's second
# derivative
# - lastchange: vector of differences between the last two
# iterations
# - numiter: total number of iterations performed
# - iter: (only present if keepiter is true) states of sigma during
# the iteration
VEMICMmac <- function(A, VEMstep=TRUE, ICMstep=TRUE, keepiter=FALSE, tol=1e-7,
tolbis=1e-7,maxiter=1000)
{
if (!VEMstep && !ICMstep) {
print("One of EMstep or ICMstep must be true.")
return(NULL)
}
Meps<-.Machine$double.eps
m<-dim(A)[1]
n<-dim(A)[2]
tA<-t(A)
if (m==1) {
ret<-NULL
ret$sigma<-1
ret$weights<-n
ret$lastchange<-0
ret$numiter<-0
return(ret)
}
WW<-matrix(0,m,n)
for (i in 1:(m-1))
WW[i,]<-A[i,]-A[i+1,]
WW[m,]<-A[m,]
sigma<-cumsum(apply(A,1,sum)/sum(A))
numiter<-0
oldsigma<-rep(-1,m)
if (keepiter) iter<-sigma
while (max(abs(oldsigma-sigma))>tol && numiter<=maxiter) {
oldsigma<-sigma
if (VEMstep) {
pvec<-diff(c(0,sigma))
Linv<-1/(tA%*%pvec)
dvec<-A%*%Linv
mind<-min(dvec[pvec>0])
minj<-match(mind,dvec)
maxd<-max(dvec)
maxj<-match(maxd,dvec)
ndir<-rep(0,m)
ndir[minj]<- -1
ndir[maxj]<-1
ndir<-ndir*pvec[minj]
pold<-pvec
pvec<-Bisect(tA,pvec,ndir,Meps,tolbis=tolbis)
sigma<-cumsum(pvec)
if (keepiter) iter<-rbind(iter,sigma)
}
if (ICMstep) {
Wps<-1/(t(WW)%*%sigma)
weights<-(abs(WW)%*%Wps^2)
increment<-as.vector((WW%*%Wps)/weights)
sigma<-sigma+increment
sigma[m]<-1
if (keepiter) iter<-rbind(iter,sigma)
temp<-PMGA(sigma[-m],weights[-m])
poolnum<-c(0,cumsum(temp$poolnum))
for (i in 2:length(poolnum))
for (j in (poolnum[i-1]+1):poolnum[i])
sigma[j]<-temp$est[i-1]
if (keepiter) iter<-rbind(iter,sigma)
# Implementing Jongbloed's correction through bisection
temp<-c(0,sigma)
pvec<-diff(c(0,oldsigma))
ndir<-diff(c(0,temp[2:(m+1)]))-pvec
pvec<-Bisect(tA,pvec,ndir,Meps,tolbis=tolbis)
sigma<-cumsum(pvec)
if (keepiter) iter<-rbind(iter,sigma)
}
numiter<-numiter+1
}
if (i==maxiter)
warning("VEM/ICM may have failed to converge.")
pf<-diff(c(0,sigma))
ret<-list(sigma=sigma,pf=pf,lval=sum(log(t(A)%*%pf)),
weights=as.vector(weights),lastchange=sigma-oldsigma,
numiter=i,eps=tol)
if (keepiter) {
if (VEMstep && ICMstep)
dimnames(iter)<-list(c("Seed",rep(c("VEM","Fisher",
"PMGA","Bisect"),
numiter)),NULL)
else if (EMstep)
dimnames(iter)<-list(rep("VEM",numiter+1),NULL)
else
dimnames(iter)<-list(c("Seed",rep(c("Fisher","PMGA"),
numiter)),NULL)
ret$iter<-iter
}
ret
}
#The EM algorithm for interval censored data
EM<-function(A, pvec, maxiter = 500, tol = 1e-12)
{
if( ncol(A)==2 && all(A[,2]>=A[,1]) ) {
ml <- Maclist(A)
intmap <- t(MLEintvl(A, ml))
A <- Macmat(ml)$pmat
}
else
intmap <- NULL
i<-0
notdone<-TRUE
n<-ncol(A)
Meps<-.Machine$double.eps
if(missing(pvec))
pvec <- apply(A, 1, sum)/sum(A)
pvec<-rescaleP(pvec, Meps)
while(i<maxiter && notdone) {
i<-i+1
dmat<-diag(pvec)
t1<-dmat%*%A
t2<-1/(t(A)%*%pvec)
np<-rescaleP(as.vector(t1%*%t2)/n, Meps)
if( sum(abs(np-pvec)) < tol )
notdone<-FALSE
pvec<-np
}
if( notdone )
warning("EM may have failed to converge")
ret <- list(pf=pvec, numiter=i,
converge=!notdone, intmap=intmap)
class(ret) <- "icsurv"
return(ret)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.