\section{Microvascular complications data} <<>>= options( keep.source=TRUE, width=90 ) library( Epi ) sessionInfo() load( "../data/ndm.Rda" ) lls() head( ndm ) @ We make a Lexis object from entry to exit: <<>>= L0 <- Lexis( entry = list( age = doEnt-doBth, per = doEnt, dur = doEnt-doDM ), exit = list( per = doX ), exit.status = factor( !is.na(doDth) & abs(doDth-doX) < 1/1000, labels=c("None","Dead") ), data = ndm ) @ % Note that there are deaths occurring \emph{after} \texttt{doX}, hence we need to only use deaths occurring at \texttt{doX}. Further note that by default the factor levels from a logical refer to (\texttt{FALSE},\texttt{TRUE}) in that order, and since \texttt{Lexis} by default assigns all entry states (unless specified by \texttt{entry.status}) to the \emph{first} level of the status factor the specification must be as ``Death'' and not as ``Survival''. In this example we use ``None'' to indicate that persons start without complications. We want to use the dates of complications occurrence, \texttt{doK}, \texttt{doN} and \texttt{doR}, to cut the follow-up in epochs with different complications status. \section{Splitting at complications events} \subsection{Naive approach} The following naive approach by just cutting at each complications date obviously does not work to get us what we want --- namely getting all possible sequences of events as states, even is we use the argument \texttt{split}: <<>>= LK <- cutLexis( L0, cut = L0$doK, time = "per", new.state = "K", pre = "None", split = TRUE ) summary( LK ) LKN <- cutLexis( LK, cut = LK$doN, time = "per", new.state = "N", pre = c("None","K"), split = TRUE ) summary( LKN ) LN <- cutLexis( L0, cut = L0$doN, time = "per", new.state = "N", pre = "None", split = TRUE ) summary( LN ) LNK <- cutLexis( LN, cut = LN$doK, time = "per", new.state = "K", pre = c("None","N"), split = TRUE ) summary( LNK ) summary( Relevel(LNK,c(1,3,2,4,7,6,5)) ) with( L0, table( doNL0$doK] ( idx <- c( ii[!is.na(ii)][1], jj[!is.na(jj)][1] ) ) subset( L0, lex.id %in% idx )[,c(2,4:7,12,17:20)] subset( LK, lex.id %in% idx )[,c(2,4:7,12,17:20)] subset( LN, lex.id %in% idx )[,c(2,4:7,12,17:20)] subset( LKN, lex.id %in% idx )[,c(2,4:7,12,17:20)] subset( LNK, lex.id %in% idx )[,c(2,4:7,12,17:20)] @ % \subsection{Restriction to a known ordering of events} But let us now do it in reverse order, where we split on the latest occurring event first: <<>>= S3 <- subset( L0, doK < doN & doN < doR ) summary( S3 ) @ % <<>>= LR <- cutLexis( S3, cut = S3$doR, time = "per", new.state = "R", pre = "None", split = TRUE ) summary( LR ) LNR <- cutLexis( LR, cut = LR$doN, time = "per", new.state = "N", pre = "None", split = TRUE ) summary( LNR ) LKNR <- cutLexis( LNR, cut = LNR$doK, time = "per", new.state = "K", pre = "None", split = TRUE ) summary( LKNR ) table(tt <- table(LKNR$lex.id) ) idx <- names( tt[tt==4][1:2] ) subset( LKNR, lex.id %in% idx )[,c(2,4:7,12,17,19,20,18)] @ % This is --- sort of --- ok if we make sure states appear in a well defined order, but the \texttt{split=} argument does not work properly; it does not update the \texttt{lex.Cst}. A clear bug in \texttt{cutLexis}. \subsection{Splitting explicitly for a known sequence of events} But here is a workaround, using hand-updating of states: <<>>= LK <- cutLexis( S3, cut = S3$doK, time = "per", new.state = "K", pre = "None" ) summary( LK ) LKN <- cutLexis( LK, cut = LK$doN, time = "per", new.state = "K-N", pre = c("None","K") ) summary( LKN ) LKNR <- cutLexis( LKN, cut = LKN$doR, time = "per", new.state = "K-N-R", pre = c("None","K","K-N") ) summary( LKNR ) table(tt <- table(LKNR$lex.id) ) idx <- names( tt[tt==4][1:2] ) subset( LKNR, lex.id %in% idx )[,c(2,4:7,12,17,19,20,18)] @ % This apparently works so we just need to hammer it out it in general terms, that is split the original dataset by the actually occurring sequences of events in the dataset --- note that we must expand the number of precursor states as we go along too. \subsection{Generalizing} For more generality we need an order function that gives the ordering but excludes NAs: <<>>= NAorder <- function( x ) { oo <- order( x, na.last=T ) oo[is.na(x)] <- NA paste( oo[!is.na(oo)], collapse="-" ) } @ % We can use this to extract the sequences of the dates in \texttt{L0}, and define the names of the states <<>>= wh <- 18:20 ( stnam <- gsub( "do", "", names(L0)[wh] ) ) whseq <- apply( L0[,wh], 1, NAorder ) table( whseq ) unique( whseq ) @ % Once we have these unique sequences, we can run through them, for each select the relevant set of persons, and cut their follow-up time according to the order in which the events (complications) occur. \section{The \texttt{mcutLexis} function} These tricks are all implemented in the \texttt{mcutLexis} function: which has the following help page: <>= library( tools ) Rd2latex( "/home/bendix/stat/R/lib.src/Epi/pkg/man/mcutLexis.Rd", out="mcutLexis.tex" ) @ % \footnotesize \input{mcutLexis} \normalsize --- and the definition of the function is: <>= source( "/home/bendix/stat/R/lib.src/Epi/pkg/R/mcutLexis.R", keep.source=TRUE ) @ <<>>= mcutLexis @ <>= rm( mcutLexis ) @ \subsection{Illustration} So now we have \texttt{mcutLexis} to cut the initial Lexis object, showing how the arguments \texttt{wh}, \texttt{new.states} and \textsc{new.states} are parallel. <<>>= Lx <- mcutLexis( L0, timescale = "per", wh = c("doR","doK","doN"), new.states = c( "R", "K", "N"), new.scales = c( "tR", "tK", "tN"), precursor.states = "None", seq.states = FALSE ) @ We should reorder the levels in a sensible way: <<>>= levels( Lx )[c(1,2,6,9,7,3,8,4,5)] Lx <- Relevel( Lx, c(1,2,6,9,7,3,8,4,5) ) summary( L0 ) summary( Lx, t=T ) @ We want to plot the transitions too, including the number of persons starting and ending in each state: <>= # pdf("bb.pdf",width=15, height=10) boxes( Lx, boxpos=list(x= c(10,36,36,36,64,64,64,92 ,50), y=c(c(50,80,50,20,80,50,20,50)+15, 5) ), show.BE=TRUE, scale.R=100, col.arr=c("black","gray")[c(1,1,1,2, 1,1,2, 1,1,2, 1,1,2, 1,2, 1,2, 1,2, 2)]) par( new=TRUE ) boxes( Lx, boxpos=list(x= c(10,36,36,36,64,64,64,92 , 5), y=c(c(50,80,50,20,80,50,20,50)+15,50) ), show.BE=TRUE, scale.R=100, subset=1:8, col.bg="white" ) # dev.off() @ \insfig{bb}{1.0}{Transitions between complications states. For clarity, the Dead state should really be split in two one for persons with 0 or 1 complications and one with 2 or 3.} We could expand the ``Dead'' state according to whether (0 or 1) or (2 or 3) complications have occurred, to make the plot more legible in terms of the mortality rates. <>= Lxx <- transform( Lx, lex.Xst = ifelse( lex.Xst=="Dead", paste( lex.Cst, as.character(lex.Xst) ), as.character(lex.Xst) ) ) table( Lxx$lex.Xst, Lx$lex.Xst ) print( table( Lxx$lex.Xst, nchar(Lxx$lex.Xst) ), zero="." ) Lxx$lex.Xst <- ifelse( nchar(Lxx$lex.Xst)%in%c(6, 9), "Dead(0-1)", Lxx$lex.Xst ) Lxx$lex.Xst <- ifelse( nchar(Lxx$lex.Xst)%in%c(8,10), "Dead(2-3)", Lxx$lex.Xst ) Lxx <- Relevel( Lxx ) summary( Lxx ) # pdf("bbx.pdf",width=15, height=10) zz <- boxes( Lxx, boxpos=list(x= seq( 6,94,,4)[c(1,2,2,2,3,3,3,4,1,4)], y=c(seq(15,95,,3)[c(2,3:1,3:1,2)],5,5) ), show.BE=TRUE, scale.R=100, col.bg=rep(c("white","gray"),c(8,2)), col.arr=c("black","gray")[c(1,1,1,2, 1,1,2, 1,1,2, 1,1,2, 1,2, 1,2, 1,2, 2)]) par( new=TRUE ) boxes( Lxx, boxpos=list(x=seq(6,94,,4)[c(1,2,2,2,3,3,3,4,1,4)], y=c(seq(15,95,,3)[c(2,3:1,3:1,2)],5,5) ), show.BE=TRUE, scale.R=100, subset=1:8, col.bg="white" ) zz # dev.off() @ \insfig{bbx}{1.0}{Transitions between complications states. For clarity, the Dead state has been split.}