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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
   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? If given as charcater "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 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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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 )
    

Want to suggest features or report bugs for rdrr.io? Use the GitHub issue tracker.