Last data update: 2014.03.03

R: Draw boxes and arrows for illustration of multistate models.
boxes.MSR Documentation

Draw boxes and arrows for illustration of multistate models.

Description

Boxes can be drawn with text (tbox) or a cross (dbox), and arrows pointing between the boxes (boxarr) can be drawn automatically not overlapping the boxes. The boxes method for Lexis objects generates displays of states with person-years and transitions with events or rates.

Usage

   tbox( txt, x, y, wd, ht,
         font=2, lwd=2,
         col.txt=par("fg"),
         col.border=par("fg"),
         col.bg="transparent" )
   dbox( x, y, wd, ht=wd,
         font=2, lwd=2, cwd=5,
         col.cross=par("fg"),
         col.border=par("fg"),
         col.bg="transparent"  )
   boxarr( b1, b2, offset=FALSE, pos=0.45, ... )
## S3 method for class 'Lexis'
boxes( obj,
                    boxpos = FALSE,
                     wmult = 1.15,
                     hmult = 1.15,
                       cex = 1.45,
                    show   = inherits( obj, "Lexis" ),
                    show.Y = show,
                   scale.Y = 1,
                  digits.Y = 1,
                   show.BE = FALSE,
                    BE.sep = c("","","          ",""),
                    show.D = show,
                   scale.D = FALSE,
                  digits.D = as.numeric(as.logical(scale.D)),
                    show.R = is.numeric(scale.R),
                   scale.R = 1,
                  digits.R = as.numeric(as.logical(scale.R)),
                    DR.sep = if( show.D ) c("\n(",")") else c("",""),
                     eq.wd = TRUE,
                     eq.ht = TRUE,
                        wd,
                        ht,
                    subset = NULL,
                   exclude = NULL,
                      font = 2,
                       lwd = 2,
                   col.txt = par("fg"),
                col.border = col.txt,
                    col.bg = "transparent",
                   col.arr = par("fg"),
                   lwd.arr = 2,
                  font.arr = 2,
                   pos.arr = 0.45,
                   txt.arr = NULL,
               col.txt.arr = col.arr,
                offset.arr = 2,
                             ... )
## S3 method for class 'matrix'
boxes( obj, ... )
## S3 method for class 'MS'
boxes( obj, sub.st, sub.tr, cex=1.5, ... )
   fillarr( x1, y1, x2, y2, gap=2, fr=0.8,
            angle=17, lwd=2, length=par("pin")[1]/30, ... )
   

Arguments

txt

Text to be placed inside the box.

x

x-coordinate of center of box.

y

y-coordinate of center of box.

wd

width of boxes in percentage of the plot width.

ht

height of boxes in percentage of the plot height.

font

Font for the text. Defaults to 2 (=bold).

lwd

Line width of the boxborders.

col.txt

Color for the text in boxes.

col.border

Color of the box border.

col.bg

Background color for the interior of the box.

...

Arguments to be passed on to the call of other functions.

cwd

Width of the lines in the cross.

col.cross

Color of the cross.

b1

Coordinates of the "from" box. A vector with 4 components, x, y, w, h.

b2

Coordinates of the "to" box; like b1.

offset

Logical. Should the arrow be offset a bit to the left.

pos

Numerical between 0 and 1, determines the position of the point on the arrow which is returned.

obj

A Lexis object or a transition matrix; that is a square matrix indexed by state in both dimensions, and the (i,j)th entry different from NA if a transition i to j can occur. If show.D=TRUE, the arrows between states are annotated by these numbers. If show.Y=TRUE, the boxes representing states are annotated by the numbers in the diagonal of obj.

For boxes.matrix obj is a matrix and for boxes.MS, obj is an MS.boxes object (see below).

boxpos

If TRUE the boxes are positioned equidistantly on a circle, if FALSE (the default) you are queried to click on the screen for the positions. This argument can also be a named list with elements x and y, both numerical vectors, giving the centers of the boxes.

wmult

Multiplier for the width of the box relative to the width of the text in the box.

hmult

Multiplier for the height of the box relative to the height of the text in the box.

cex

Character expansion for text in the box.

show

Should person-years and transitions be put in the plot. Ignored if obj is not a Lexis object.

show.Y

If logical: Should person-years be put in the boxes. If numeric: Numbers to put in boxes.

scale.Y

What scale should be used for annotation of person-years.

digits.Y

How many digits after the decimal point should be used for the person-years.

show.BE

Logical. Should number of persons beginning resp. ending follow up in each state be shown?

BE.sep

Character vector of length 4, used for annotation of the number of persons beginning and ending in each state: 1st elemet precedes no. beginning, 2nd trails it, 3rd precedes the no. ending (defaults to 8 spaces), and the 4th trails the no. ending.

show.D

Should no. transitions be put alongside the arrows. Ignored if obj is not a Lexis object.

scale.D

Synonumous with scale.R, retained for compatability.

digits.D

Synonumous with digits.R, retained for compatability.

show.R

Should the transition rates be shown on the arrows?

scale.R

If this a scalar, rates instead of no. transitions are printed at the arrows, scaled by scale.R.

digits.R

How many digits after the decimal point should be used for the rates.

DR.sep

Character vector of length 2. If rates are shown, the first element is inserted before and the second after the rate.

eq.wd

Should boxes all have the same width?

eq.ht

Should boxes all have the same height?

subset

Draw only boxes and arrows for a subset of the states. Can be given either as a numerical vector or character vector state names.

exclude

Exclude states from the plot. The complementary of subset. Ignored if subset is given.

col.arr

Color of the arrows between boxes. A vector of character strings, the arrows are referred to as the row-wise sequence of non-NA elements of the transition matrix. Thus the first ones refer to the transitions out of state 1, in order of states.

lwd.arr

Line withs of the arrows.

font.arr

Font of the text annotation the arrows.

pos.arr

Numerical between 0 and 1, determines the position on the arrows where the text is written.

txt.arr

Text put on the arrows.

col.txt.arr

Colors for text on the arrows.

offset.arr

The amount offset between arrows representing two-way transitions, that is where there are arrows both ways between two boxes.

sub.st

Subset of the states to be drawn.

sub.tr

Subset of the transitions to be drawn.

x1

x-coordinate of the starting point.

y1

y-coordinate of the starting point.

x2

x-coordinate of the end point.

y2

y-coordinate of the end point.

gap

Length of the gap between the box and the ends of the arrows.

fr

Length of the arrow as the fraction of the distance between the boxes. Ignored unless given explicitly, in which case any value given for gap is ignored.

angle

What angle should the arrow-head have?

length

Length of the arrow head in inches. Defaults to 1/30 of the physical width of the plot.

Details

These functions are designed to facilitate the drawing of multistate models, mainly by automatic calculation of the arrows between boxes.

tbox draws a box with centered text, and returns a vector of location, height and width of the box. This is used when drawing arrows between boxes. dbox draws a box with a cross, symbolizing a death state. boxarr draws an arrow between two boxes, making sure it does not intersect the boxes. Only straight lines are drawn.

boxes.Lexis takes as input a Lexis object sets up an empty plot area (with axes 0 to 100 in both directions) and if boxpos=FALSE (the default) prompts you to click on the locations for the state boxes, and then draws arrows implied by the actual transitions in the Lexis object. The default is to annotate the transitions with the number of transitions.

A transition matrix can also be supplied, in which case the row/column names are used as state names, diagnonal elements taken as person-years, and off-diagnonal elements as number of transitions. This also works for boxes.matrix.

Optionally returns the R-code reproducing the plot in a file, which can be useful if you want to produce exactly the same plot with differing arrow colors etc.

boxarr draws an arrow between two boxes, on the line connecting the two box centers. The offset argument is used to offset the arrow a bit to the left (as seen in the direction of the arrow) on order to accommodate arrows both ways between boxes. boxarr returns a named list with elements x, y and d, where the two former give the location of a point on the arrow used for printing (see argument pos) and the latter is a unit vector in the direction of the arrow, which is used by boxes.Lexis to position the annotation of arrows with the number of transitions.

boxes.MS re-draws what boxes.Lexis has done based on the object of class MS produced by boxes.Lexis. The point being that the MS object is easily modifiable, and thus it is a machinery to make variations of the plot with different color annotations etc.

fill.arr is just a utility drawing nicer arrows than the default arrows command, basically by using filled arrow-heads; called by boxarr.

Value

The functions tbox and dbox return the location and dimension of the boxes, c(x,y,w,h), which are designed to be used as input to the boxarr function.

The boxarr function returns the coordinates (as a named list with names x and y) of a point on the arrow, designated to be used for annotation of the arrow.

The function boxes.Lexis returns an MS object, a list with five elements: 1) Boxes - a dataframe with one row per box and columns xx, yy, wd, ht, font, lwd, col.txt, col.border and col.bg, 2) an object State.names with names of states (possibly an expression, hence not possible to include as a column in Boxes), 3) a matrix Tmat, the transition matrix, 4) a data frame, Arrows with one row per transition and columns: lwd.arr, col.arr, pos.arr, col.txt.arr, font.arr and offset.arr and 5) an object Arrowtext with names of states (possibly an expression, hence not possible to include as a column in Arrows)

An MS object is used as input to boxes.MS, the primary use is to modify selected entries in the MS object first, e.g. colors, or supply subsetting arguments in order to produce displays that have the same structure, but with different colors etc.

Author(s)

Bendix Carstensen

See Also

tmat.Lexis

Examples

par( mar=c(0,0,0,0), cex=1.5 )
plot( NA,
      bty="n",
      xlim=0:1*100, ylim=0:1*100, xaxt="n", yaxt="n", xlab="", ylab="" )
bw  <- tbox( "Well"    , 10, 60, 22, 10, col.txt="blue" )
bo  <- tbox( "other Ca", 45, 80, 22, 10, col.txt="gray" )
bc  <- tbox( "Ca"      , 45, 60, 22, 10, col.txt="red" )
bd  <- tbox( "DM"      , 45, 40, 22, 10, col.txt="blue" )
bcd <- tbox( "Ca + DM" , 80, 60, 22, 10, col.txt="gray" )
bdc <- tbox( "DM + Ca" , 80, 40, 22, 10, col.txt="red" )
      boxarr( bw, bo , col=gray(0.7), lwd=3 )
# Note the argument adj= can takes values outside (0,1)
text( boxarr( bw, bc , col="blue", lwd=3 ),
      expression( lambda[Well] ), col="blue", adj=c(1,-0.2), cex=0.8 )
      boxarr( bw, bd , col=gray(0.7) , lwd=3 )
      boxarr( bc, bcd, col=gray(0.7) , lwd=3 )
text( boxarr( bd, bdc, col="blue", lwd=3 ),
      expression( lambda[DM] ), col="blue", adj=c(1.1,-0.2), cex=0.8 )

# Set up a transition matrix allowing recovery
tm <- rbind( c(NA,1,1), c(1,NA,1), c(NA,NA,NA) )
rownames(tm) <- colnames(tm) <- c("Cancer","Recurrence","Dead")
tm
boxes.matrix( tm, boxpos=TRUE )

# Illustrate texting of arrows
boxes.Lexis( tm, boxpos=TRUE, txt.arr=c("en","to","tre","fire") )
zz <- boxes( tm, boxpos=TRUE, txt.arr=c(expression(lambda[C]),
                                        expression(mu[C]),
                                        "recovery",
                                        expression(mu[R]) ) )

# Change color of a box
zz$Boxes[3,c("col.bg","col.border")] <- "green"
boxes( zz )

# Set up a Lexis object
data(DMlate)
str(DMlate)
dml <- Lexis( entry=list(Per=dodm, Age=dodm-dobth, DMdur=0 ),
               exit=list(Per=dox),
        exit.status=factor(!is.na(dodth),labels=c("DM","Dead")),
               data=DMlate[1:1000,] )

# Cut follow-up at Insulin
dmi <- cutLexis( dml, cut=dml$doins, new.state="Ins", pre="DM" )
summary( dmi )
boxes( dmi, boxpos=TRUE )
boxes( dmi, boxpos=TRUE, show.BE=TRUE )
boxes( dmi, boxpos=TRUE, show.BE="nz" )
boxes( dmi, boxpos=TRUE, show.BE="nz", BE.sep=c("In:","      Out:","") )

# Set up a bogus recovery date just to illustrate two-way transitions
dmi$dorec <- dmi$doins + runif(nrow(dmi),0.5,10)
dmi$dorec[dmi$dorec>dmi$dox] <- NA
dmR <- cutLexis( dmi, cut=dmi$dorec, new.state="DM", pre="Ins" )
summary( dmR )
boxes( dmR, boxpos=TRUE )
boxes( dmR, boxpos=TRUE, show.D=FALSE )
boxes( dmR, boxpos=TRUE, show.D=FALSE, show.Y=FALSE )
boxes( dmR, boxpos=TRUE, scale.R=1000 )
MSobj <- boxes( dmR, boxpos=TRUE, scale.R=1000, show.D=FALSE )
MSobj <- boxes( dmR, boxpos=TRUE, scale.R=1000, DR.sep=c(" (",")") )
class( MSobj )
boxes( MSobj )
MSobj$Boxes[1,c("col.txt","col.border")] <- "red"
MSobj$Arrows[1:2,"col.arr"] <- "red"
boxes( MSobj )
    

Results


R version 3.3.1 (2016-06-21) -- "Bug in Your Hair"
Copyright (C) 2016 The R Foundation for Statistical Computing
Platform: x86_64-pc-linux-gnu (64-bit)

R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.

Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.

> library(Epi)

Attaching package: 'Epi'

The following object is masked from 'package:base':

    merge.data.frame

> png(filename="/home/ddbj/snapshot/RGM3/R_CC/result/Epi/boxes.MS.Rd_%03d_medium.png", width=480, height=480)
> ### Name: boxes.MS
> ### Title: Draw boxes and arrows for illustration of multistate models.
> ### Aliases: tbox dbox fillarr boxarr boxes boxes.Lexis boxes.matrix
> ###   boxes.MS
> ### Keywords: survival hplot iplot
> 
> ### ** Examples
> 
> par( mar=c(0,0,0,0), cex=1.5 )
> plot( NA,
+       bty="n",
+       xlim=0:1*100, ylim=0:1*100, xaxt="n", yaxt="n", xlab="", ylab="" )
> bw  <- tbox( "Well"    , 10, 60, 22, 10, col.txt="blue" )
> bo  <- tbox( "other Ca", 45, 80, 22, 10, col.txt="gray" )
> bc  <- tbox( "Ca"      , 45, 60, 22, 10, col.txt="red" )
> bd  <- tbox( "DM"      , 45, 40, 22, 10, col.txt="blue" )
> bcd <- tbox( "Ca + DM" , 80, 60, 22, 10, col.txt="gray" )
> bdc <- tbox( "DM + Ca" , 80, 40, 22, 10, col.txt="red" )
>       boxarr( bw, bo , col=gray(0.7), lwd=3 )
> # Note the argument adj= can takes values outside (0,1)
> text( boxarr( bw, bc , col="blue", lwd=3 ),
+       expression( lambda[Well] ), col="blue", adj=c(1,-0.2), cex=0.8 )
>       boxarr( bw, bd , col=gray(0.7) , lwd=3 )
>       boxarr( bc, bcd, col=gray(0.7) , lwd=3 )
> text( boxarr( bd, bdc, col="blue", lwd=3 ),
+       expression( lambda[DM] ), col="blue", adj=c(1.1,-0.2), cex=0.8 )
> 
> # Set up a transition matrix allowing recovery
> tm <- rbind( c(NA,1,1), c(1,NA,1), c(NA,NA,NA) )
> rownames(tm) <- colnames(tm) <- c("Cancer","Recurrence","Dead")
> tm
           Cancer Recurrence Dead
Cancer         NA          1    1
Recurrence      1         NA    1
Dead           NA         NA   NA
> boxes.matrix( tm, boxpos=TRUE )
> 
> # Illustrate texting of arrows
> boxes.Lexis( tm, boxpos=TRUE, txt.arr=c("en","to","tre","fire") )
> zz <- boxes( tm, boxpos=TRUE, txt.arr=c(expression(lambda[C]),
+                                         expression(mu[C]),
+                                         "recovery",
+                                         expression(mu[R]) ) )
> 
> # Change color of a box
> zz$Boxes[3,c("col.bg","col.border")] <- "green"
> boxes( zz )
> 
> # Set up a Lexis object
> data(DMlate)
> str(DMlate)
'data.frame':	10000 obs. of  7 variables:
 $ sex  : Factor w/ 2 levels "M","F": 2 1 2 2 1 2 1 1 2 1 ...
 $ dobth: num  1940 1939 1918 1965 1933 ...
 $ dodm : num  1999 2003 2005 2009 2009 ...
 $ dodth: num  NA NA NA NA NA ...
 $ dooad: num  NA 2007 NA NA NA ...
 $ doins: num  NA NA NA NA NA NA NA NA NA NA ...
 $ dox  : num  2010 2010 2010 2010 2010 ...
> dml <- Lexis( entry=list(Per=dodm, Age=dodm-dobth, DMdur=0 ),
+                exit=list(Per=dox),
+         exit.status=factor(!is.na(dodth),labels=c("DM","Dead")),
+                data=DMlate[1:1000,] )
NOTE: entry.status has been set to "DM" for all.
> 
> # Cut follow-up at Insulin
> dmi <- cutLexis( dml, cut=dml$doins, new.state="Ins", pre="DM" )
> summary( dmi )
     
Transitions:
     To
From   DM Ins Dead  Records:  Events: Risk time:  Persons:
  DM  616 178  195       989      373    4454.40       989
  Ins   0 142   47       189       47     854.16       189
  Sum 616 320  242      1178      420    5308.56      1000
> boxes( dmi, boxpos=TRUE )
> boxes( dmi, boxpos=TRUE, show.BE=TRUE )
Warning messages:
1: In formatC(Beg, format = "f", digits = 0, big.mark = ",") :
  class of 'x' was discarded
2: In formatC(End, format = "f", digits = 0, big.mark = ",") :
  class of 'x' was discarded
> boxes( dmi, boxpos=TRUE, show.BE="nz" )
Warning messages:
1: In formatC(Beg, format = "f", digits = 0, big.mark = ",") :
  class of 'x' was discarded
2: In formatC(End, format = "f", digits = 0, big.mark = ",") :
  class of 'x' was discarded
> boxes( dmi, boxpos=TRUE, show.BE="nz", BE.sep=c("In:","      Out:","") )
Warning messages:
1: In formatC(Beg, format = "f", digits = 0, big.mark = ",") :
  class of 'x' was discarded
2: In formatC(End, format = "f", digits = 0, big.mark = ",") :
  class of 'x' was discarded
> 
> # Set up a bogus recovery date just to illustrate two-way transitions
> dmi$dorec <- dmi$doins + runif(nrow(dmi),0.5,10)
> dmi$dorec[dmi$dorec>dmi$dox] <- NA
> dmR <- cutLexis( dmi, cut=dmi$dorec, new.state="DM", pre="Ins" )
> summary( dmR )
     
Transitions:
     To
From   DM Ins Dead  Records:  Events: Risk time:  Persons:
  DM  685 178  201      1064      379    4758.67       991
  Ins  75  73   41       189      116     549.89       189
  Sum 760 251  242      1253      495    5308.56      1000
> boxes( dmR, boxpos=TRUE )
> boxes( dmR, boxpos=TRUE, show.D=FALSE )
> boxes( dmR, boxpos=TRUE, show.D=FALSE, show.Y=FALSE )
> boxes( dmR, boxpos=TRUE, scale.R=1000 )
> MSobj <- boxes( dmR, boxpos=TRUE, scale.R=1000, show.D=FALSE )
> MSobj <- boxes( dmR, boxpos=TRUE, scale.R=1000, DR.sep=c(" (",")") )
> class( MSobj )
[1] "MS"
> boxes( MSobj )
> MSobj$Boxes[1,c("col.txt","col.border")] <- "red"
> MSobj$Arrows[1:2,"col.arr"] <- "red"
> boxes( MSobj )
>     
> 
> 
> 
> 
> dev.off()
null device 
          1 
>