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

boxes.MS {Epi} | R 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.20,
                         hmult = 1.20 + 0.85*(!show.Y),
                           cex = 1.40,
                        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 = show & 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 box borders.
`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. These must be numbers between 0 and 100 indicating percentages of the display in the two directions.
`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? If given as character "nz" or "noz" the numbers will be shown, but zeros omitted.
`BE.sep` | Character vector of length 4, used for annotation of the number of persons beginning and ending in each state: 1st element 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` | Synonymous with `scale.R`, retained for compatibility.
`digits.D` | Synonymous with `digits.R`, retained for compatibility.
`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 widths 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, diagonal elements taken as person-years, and off-diagonal 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 data frame 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 sub-setting 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 )

[Package Epi version 2.42 Index]