Design
in effect.
The nomogram does not have lines representing sums, but it has a reference
line for reading scoring points (default range 0-100). Once the reader
manually totals the points, the predicted values can be read at the bottom.
Non-monotonic transformations of continuous variables are handled (scales
wrap around), as
are transformations which have flat sections (tick marks are labeled
with ranges). If interactions are in the model, one variable
is picked as the "axis variable", and separate axes are constructed for
each level of the interacting factors (preference is given automatically
to using any discrete factors to construct separate axes) and
levels of factors which are indirectly related to interacting
factors (see DETAILS). Thus the nomogram is designed so that only
one axis is actually read for each variable, since the variable
combinations are disjoint. For
categorical interacting factors, the default is to construct axes for
all levels.
The user may specify
coordinates of each predictor to label on its axis, or use default values.
If a factor interacts with other factors, settings for one or more of
the interacting factors may be specified separately (this is mandatory
for continuous variables). Optional confidence intervals will be
drawn for individual scores as well as for the linear predictor.
If more than one confidence level is chosen, multiple levels may be
displayed using different colors or gray scales. Functions of the
linear predictors may be added to the nomogram.
print.nomogram
prints axis information stored in an object returned
by
nomogram
. This is useful in producing tables of point assignments
by levels of predictors. It also prints how many linear predictor
units there are per point and the number of points per unit change in
the linear predictor.
legend.nomabbrev
draws legends describing abbreviations used for
labeling tick marks for levels of categorical predictors.
nomogram(fit, ...) ## S3 method for class 'Design': nomogram(fit, ..., adj.to, lp=TRUE, lp.at, lplabel="Linear Predictor", fun, fun.at, fun.lp.at, funlabel="Predicted Value", fun.side, interact=NULL, intercept=1, conf.int=FALSE, col.conf=c(1, if(under.unix).3 else 12), conf.space=c(.08,.2), conf.lp=c("representative","all", "none"), est.all=TRUE, abbrev=FALSE, minlength=4, maxscale=100, nint=10, label.every=1, force.label=FALSE, xfrac=0.35, cex.axis=0.85, cex.var=1, col.grid=FALSE, vnames=c("labels","names"), varname.label=TRUE, varname.label.sep="=", ia.space=.7, tck=-.009, lmgp=.4, omit=NULL, naxes, points.label='Points', total.points.label='Total Points', total.sep.page=FALSE, total.fun, verbose=FALSE) ## S3 method for class 'nomogram': print(x, dec=0, ...) legend.nomabbrev(object, which, x, y, ncol=3, ...)
library(Design)
in
effect, and (usually) with
options(datadist="object.name")
in effect.
nomogram
legend
function. This is the upper left
corner of the legend box. You can omit
y
if
x
is a list with
named elements
x
and
y
. To use the mouse to locate the legend,
specify
locator(1)
for
x
. For
print
,
x
is
the result of
nomogram
.
datadist
was in effect, the default is to use
pretty(total range, nint)
for continuous variables, and the class levels for discrete ones.
For
legend.nomabbrev
,
...
specifies optional parameters to pass
to
legend
. Common ones are
bty="n"
to suppress drawing the
box. You may want to specify a non-proportionally spaced font
(e.g., courier) number if abbreviations are more than one letter long.
This will make the abbreviation definitions line up (e.g., specify
font=2
, the default for courier). Ignored for
print
.
datadist
for all predictors, you will have to
define adjustment settings for the undefined ones, e.g.
adj.to=list(age=50, sex="female")
.
interact
.
For discrete interacting factors, you may specify levels to use in
constructing the multiple axes. For continuous interacting factors,
you must do this. Examples:
interact=list(age=seq(10,70,by=10),
treat=c("A","B","D"))
.
FALSE
to suppress creation of an axis for scoring
X beta.
lp=TRUE
,
lp.at
may specify a vector of settings of
X beta.
Default is to use
pretty(range of linear predictors, nint)
.
"Linear Predictor"
.
list(function(x)x/2, function(x)2*x)
.
Any function values equal to NA will be ignored.
fun
evaluated
at
lp.at
. If more than one
fun
was specified, using a vector
for
fun.at
will cause all functions to be evaluated at the same
argument values. To use different values, specify a list of vectors for
fun.at
, with elements corresponding to the different functions
(lists of vectors also applies to
fun.lp.at
and
fun.side
).
nomogram
to compute numerical approximations of
the inverse of the function. It also allows the user-supplied function
to return
factor
objects, which is useful when e.g. a single tick
mark position actually represents a range.
If the
fun.lp.at
parameter is present, the
fun.at
vector for that function is ignored.
side
parameters for the
axis
function
for labeling function values.
Values may be 1 to position a tick mark label below the axis (the default),
or 3 for above the axis. If for example an axis has 5 tick mark labels
and the second and third will run into each other, specify
fun.side=c(1,1,3,1,1)
(assuming only one function is specified as
fun
).
fun
axis. If more than one function was given but
funlabel is of length one, it will be duplicated as needed. If
fun
is
a list of functions for which you specified names (see the final example
below), these names will be used as labels.
FALSE
to display
no confidence limits. Setting
conf.int
to
TRUE
is the same as
setting it to
c(0.7, 0.9)
,
with the line segment between the 0.7 and 0.9 levels shaded using
gray scale.
conf.int
. Use fractions for gray scale
(for UNIX S-PLUS).
"representative"
to group all linear predictors evaluated
into deciles, and to show, for the linear predictor confidence intervals,
only the mean linear predictor within the deciles along with the median
standard error within the deciles. Set
conf.lp="none"
to suppress
confidence limits for the linear predictors, and to
"all"
to show
all confidence limits.
...
, set
est.all=FALSE
. Note: This option only works when zero has a special
meaning for the variables that are omitted from the graph.
TRUE
to use the
abbreviate
function to abbreviate levels of
categorical factors, both for labeling tick marks and for axis titles.
If you only want to abbreviate certain predictor variables, set
abbrev
to a vector of character strings containing their names.
abbrev=TRUE
. Is the minimum abbreviation length passed to the
abbreviate
function. If you set
minlength=1
, the letters of the
alphabet are used to label tick marks for categorical predictors, and
all letters are drawn no matter how close together they are. For
labeling axes (interaction settings),
minlength=1
causes
minlength=4
to be used.
pretty
.
label.every=i
to label on every
i
th tick mark.
TRUE
to force every tick mark intended to be labeled to have
a label plotted (whether the labels run into each other or not)
col.grid=1
, no gray scale is used, but an ordinary line is drawn.
If
0<col.grid<1
,
a
col
(gray scale) of
col.grid
is used to draw vertical reference
lines for major axis divisions and
col.grid/2
for minor divisions.
The default is
col.grid=FALSE
, i.e., reference lines are omitted.
Specifying
col.grid=TRUE
is the same as specifying a gray scale level
of
col.grid=.2
(5 for Windows S-PLUS).
vnames="names"
to instead use variable names.
"(interacting.varname=level)
on the right. Specify
varname.label=FALSE
to instead use
"(level)"
.
varname.label=TRUE
, you can change the separator to something other than
=
by specifying this parameter.
tck
under
par
par
for
mgp
)
TRUE
to force the total points and later axes to be placed on a
separate page
total.sep.page=TRUE
and you wish to use
locator
to find the
coordinates for positioning an abbreviation legend before it's too late
and a new page is started (i.e.,
total.fun=function()print(locator(1))
).
TRUE
to get printed output detailing how tick marks are chosen
and labeled for function axes. This is useful in seeing how certain
linear predictor values cannot be solved for using inverse linear
interpolation on the (requested linear predictor values, function values at
these lp values). When this happens you will see
NA
s in the
verbose
output, and the corresponding tick marks will not appear in the nomogram.
print.nomogram
. Default is to round to the nearest
whole number of points.
A variable is considered to be discrete if it is categorical or ordered
or if
datadist
stored
values
for it (meaning it had
<11
unique
values).
A variable is said to be indirectly related to another variable if
the two are related by some interaction. For example, if a model
has variables a, b, c, d, and the interactions are a:c and c:d,
variable d is indirectly related to variable a. The complete list
of variables related to a is c, d. If an axis is made for variable a,
several axes will actually be drawn, one for each combination of c
and d specified in
interact
.
Note that with a caliper, it is easy to continually add point scores
for individual predictors, and then to place the caliper on the upper
Points
axis (with extrapolation if needed). Then transfer these
points to the
Total Points
axis. In this way, points can be added without
without writing them down.
Confidence limits for an individual predictor score are really confidence
limits for the entire linear predictor, with other predictors set to
adjustment values. If
lp=TRUE
, all confidence bars for all linear
predictor values evaluated are drawn. The extent to which multiple
confidence bars of differing widths appear at the same linear predictor
value means that precision depended on how the linear predictor was
arrived at (e.g., a certain value may be realized from a setting of
a certain predictor that was associated with a large standard error
on the regression coefficients for that predictor).
On occasion, you may want to reverse the regression coefficients of a model
to make the "points" scales reverse direction. For parametric survival
models, which are stated in terms of increasing regression effects meaning
longer survival (the opposite of a Cox model), just do something like
fit$coefficients <- -fit$coefficients
before invoking
nomogram
,
and if you add function axes, negate the function
arguments. For the Cox model, you also need to negate
fit$center
.
If you omit
lp.at
, also negate
fit$linear.predictors
.
"nomogram"
that contains information used in plotting
the axes. If you specified
abbrev=TRUE
, a list called
abbrev
is also
returned that gives the abbreviations used for tick mark labels, if any.
This list is useful for
making legends and is used by
legend.nomabbrev
(see the last example).
The returned list also has components called
total.points
,
lp
,
and the function axis names. These components have components
x
(
at
argument vector given to
axis
),
y
(
pos
for
axis
),
and
x.real
, the x-coordinates appearing on tick mark labels.
An often useful result is stored in the list of data for each axis variable,
namely the exact number of points that correspond to each tick mark on
that variable's axis.
Frank Harrell
Department of Biostatistics
Vanderbilt University
f.harrell@vanderbilt.edu
Banks J: Nomograms. Encylopedia of Statistical Sciences, Vol 6. Editors: S Kotz and NL Johnson. New York: Wiley; 1985.
Lubsen J, Pool J, van der Does, E: A practical device for the application of a diagnostic or prognostic function. Meth. Inform. Med. 17:127–129; 1978.
n <- 1000 # define sample size set.seed(17) # so can reproduce the results age <- rnorm(n, 50, 10) blood.pressure <- rnorm(n, 120, 15) cholesterol <- rnorm(n, 200, 25) sex <- factor(sample(c('female','male'), n,TRUE)) # Specify population model for log odds that Y=1 L <- .4*(sex=='male') + .045*(age-50) + (log(cholesterol - 10)-5.2)*(-2*(sex=='female') + 2*(sex=='male')) # Simulate binary y to have Prob(y=1) = 1/[1+exp(-L)] y <- ifelse(runif(n) < plogis(L), 1, 0) ddist <- datadist(age, blood.pressure, cholesterol, sex) options(datadist='ddist') f <- lrm(y ~ lsp(age,50)+sex*rcs(cholesterol,4)+blood.pressure) nomogram(f, fun=function(x)1/(1+exp(-x)), # or fun=plogis fun.at=c(.001,.01,.05,seq(.1,.9,by=.1),.95,.99,.999), funlabel="Risk of Death", xfrac=.45) #Instead of fun.at, could have specified fun.lp.at=logit of #sequence above - faster and slightly more accurate nomogram(f, age=seq(10,90,by=10), xfrac=.45) g <- lrm(y ~ sex + rcs(age,3)*rcs(cholesterol,3)) nomogram(g, interact=list(age=c(20,40,60)), conf.int=c(.7,.9,.95), col.conf=c(1,.5,.2)) cens <- 15*runif(n) h <- .02*exp(.04*(age-50)+.8*(sex=='Female')) d.time <- -log(runif(n))/h death <- ifelse(d.time <= cens,1,0) d.time <- pmin(d.time, cens) f <- psm(Surv(d.time,death) ~ sex*age, dist=if(.R.)'lognormal' else 'gaussian') med <- Quantile(f) surv <- Survival(f) # This would also work if f was from cph nomogram(f, fun=function(x) med(lp=x), funlabel="Median Survival Time") nomogram(f, fun=list(function(x) surv(3, x), function(x) surv(6, x)), funlabel=c("3-Month Survival Probability", "6-month Survival Probability"), xfrac=.5) ## Not run: nom <- nomogram(fit.with.categorical.predictors, abbrev=TRUE, minlength=1) nom$x1$points # print points assigned to each level of x1 for its axis #Add legend for abbreviations for category levels abb <- nom$abbrev$treatment legend(locator(1), abb$full, pch=paste(abb$abbrev,collapse=''), ncol=2, bty='n') # this only works for 1-letter abbreviations #Or use the legend.nomabbrev function: legend.nomabbrev(nom, 'treatment', locator(1), ncol=2, bty='n') ## End(Not run) #Make a nomogram with axes predicting probabilities Y>=j for all j=1-3 #in an ordinal logistic model, where Y=0,1,2,3 Y <- ifelse(y==0, 0, sample(1:3, length(y), TRUE)) g <- lrm(Y ~ age+rcs(cholesterol,4)*sex) fun2 <- function(x) plogis(x-g$coef[1]+g$coef[2]) fun3 <- function(x) plogis(x-g$coef[1]+g$coef[3]) f <- Newlabels(g, c(age='Age in Years')) #see Design.Misc, which also has Newlevels to change #labels for levels of categorical variables nomogram(f, fun=list('Prob Y>=1'=plogis, 'Prob Y>=2'=fun2, 'Prob Y=3'=fun3), fun.at=c(.01,.05,seq(.1,.9,by=.1),.95,.99), lmgp=.2, cex.axis=.6) options(datadist=NULL)