library(survival)
library(Epi)
library(popEpi)
##
## Attaching package: 'popEpi'
## The following object is masked from 'package:survival':
##
## Surv
# popEpi::splitMulti returns a data.frame rather than a data.table
options("popEpi.datatable" = FALSE)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2
## ──
## ✔ ggplot2 3.4.1 ✔ purrr 1.0.1
## ✔ tibble 3.1.8 ✔ dplyr 1.1.0
## ✔ tidyr 1.3.0 ✔ stringr 1.5.0
## ✔ readr 2.1.4 ✔ forcats 1.0.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
clear()
data(steno2)
steno2 <- cal.yr(steno2)
steno2 <- transform(steno2,
doEnd = pmin(doEnd, doDth, na.rm = TRUE))
str(steno2)
## 'data.frame': 160 obs. of 14 variables:
## $ id : num 1 2 3 4 5 6 7 8 9 10 ...
## $ allo : Factor w/ 2 levels "Int","Conv": 1 1 2 2 2 2 2 1 1 1 ...
## $ sex : Factor w/ 2 levels "F","M": 2 2 2 2 2 2 1 2 2 2 ...
## $ baseCVD : num 0 0 0 0 0 1 0 0 0 0 ...
## $ deathCVD: num 0 0 0 0 1 0 0 0 1 0 ...
## $ doBth : 'cal.yr' num 1932 1947 1943 1945 1936 ...
## $ doDM : 'cal.yr' num 1991 1982 1983 1977 1986 ...
## $ doBase : 'cal.yr' num 1993 1993 1993 1993 1993 ...
## $ doCVD1 : 'cal.yr' num 2014 2009 2002 1995 1994 ...
## $ doCVD2 : 'cal.yr' num NA 2009 NA 1997 1995 ...
## $ doCVD3 : 'cal.yr' num NA 2010 NA 2003 1998 ...
## $ doESRD : 'cal.yr' num NaN NaN NaN NaN 1998 ...
## $ doEnd : 'cal.yr' num 2015 2015 2002 2003 1998 ...
## $ doDth : 'cal.yr' num NA NA 2002 2003 1998 ...
Start by setting up a Lexis data frame for the entire observation time for each person; from entry (doBase, date of baseline) to exit, doEnd. Note that we call the initial state Mic(roalbuminuria), because all patients in the Steno2 study had this status at entry—it was one of the inclusion criteria:
L2 <- Lexis(entry = list(per = doBase,
age = doBase - doBth,
tfi = 0),
exit = list(per = doEnd),
exit.status = factor(deathCVD + !is.na(doDth),
labels=c("Mic","D(oth)","D(CVD)")),
id = id,
data = steno2)
## NOTE: entry.status has been set to "Mic" for all.
summary(L2, t = TRUE)
##
## Transitions:
## To
## From Mic D(oth) D(CVD) Records: Events: Risk time: Persons:
## Mic 67 55 38 160 93 2416.59 160
##
## Timescales:
## per age tfi
## "" "" ""
boxes(L2, boxpos = TRUE, show.BE = TRUE, scale.R = 100)
How many deaths are there in the cohort?
Explain the coding of exit.status.
How many person-years are in the follow-up?
What are the time scales defined?
In this set-up we can study the CVD and the non-CVD mortality rates, a classical competing risks problem, but we want in particular to see how the mortality rates depend on albuminuria status.
In order to allocate follow-up (person-time and events) to current albuminuria status we need to know when the persons change status; this is recorded in the data frame st2alb.
We will cut the follow-up at each date of albuminuria measurement allowing the patients to change between states Normoalbuminuria, Microalbuminuria and Macroalbuminuria at each of these dates, possibly several times per person. To this end we use the function rcutLexis (recurrent cuts), which requires a data frame of transitions with columns lex.id, cut and new.state — see ?rcutLexis.
We change the scale of the date of transition to year by cal.yr (to align with the per variable in L2), and in order to comply with the requirements of rcutLexis rename the id variable id to lex.id, the date variable doTr to cut and the state variable state to new.state:
data(st2alb)
cut2 <- cal.yr(st2alb)
names(cut2)
## [1] "id" "doTr" "state"
names(cut2) <- c("lex.id", "cut", "new.state")
str(cut2)
## 'data.frame': 563 obs. of 3 variables:
## $ lex.id : num 1 1 1 1 1 2 2 2 2 2 ...
## $ cut : 'cal.yr' num 1993 1995 2000 2002 2007 ...
## $ new.state: Factor w/ 3 levels "Norm","Mic","Mac": 2 1 2 1 2 1 2 3 2 2 ...
head(cut2)
## lex.id cut new.state
## 1 1 1993.444 Mic
## 2 1 1995.361 Norm
## 3 1 2000.067 Mic
## 4 1 2001.984 Norm
## 5 1 2007.317 Mic
## 6 2 1993.786 Norm
How many persons are in the cut2 data frame? We can do this in two different ways, illustrating the tidyverse philosophy
addmargins(table(table(cut2$lex.id)))
##
## 1 2 3 4 5 Sum
## 4 25 40 46 41 156
cut2$lex.id %>% table %>% table %>% addmargins
## .
## 1 2 3 4 5 Sum
## 4 25 40 46 41 156
Explain the entries in this table.
Now cut the follow-up at intermediate transition times (note that rcutLexis assumes that values in the cut column refer to the first timescale by default, and the first of the timescales in L2 is per):
L3 <- rcutLexis(L2, cut2)
summary(L3)
##
## Transitions:
## To
## From Mic Norm Mac D(oth) D(CVD) Records: Events: Risk time: Persons:
## Mic 299 72 65 27 13 476 177 1381.57 160
## Norm 31 90 5 14 7 147 57 607.86 69
## Mac 20 3 44 14 18 99 55 427.16 64
## Sum 350 165 114 55 38 722 289 2416.59 160
boxes(L3, boxpos = TRUE, cex = 0.8)
Note in the figure the default lay-out of the 5 boxes placed on a circle, including the jumps directly between Norm and Mac
Note that there are transitions both ways between all three of Norm, Mic and Mac, which is a bit illogical since we have a natural ordering of states: Norm < Mic < Mac. Hence, transitions from Norm to Mac (and vice versa) should go through Mic.
In order to remedy this anomaly we find all transitions Norm → Mac and provide a transition Norm → Mic in between, so that each transition Norm → Mac is replaced by two: Norm → Mic and Mic → Mac. And of course similarly for transitions Mac → Norm.
The relevant “jump” transitions are easily found:
(jump <-
subset(L3, (lex.Cst == "Norm" & lex.Xst == "Mac") |
(lex.Xst == "Norm" & lex.Cst == "Mac"))[,
c("lex.id", "per", "lex.dur","lex.Cst", "lex.Xst")])
## lex.id per lex.dur lex.Cst lex.Xst
## 70 1999.49 2.67 Mac Norm
## 86 2001.76 12.82 Norm Mac
## 130 2000.91 1.88 Mac Norm
## 131 1997.76 4.24 Norm Mac
## 136 1997.21 0.47 Mac Norm
## 136 1997.69 4.24 Norm Mac
## 171 1996.39 5.34 Norm Mac
## 175 2004.58 9.88 Norm Mac
What we need to do for each of these “jumps” is to provide an extra transition to Mic at a time during the stay in either lex.Cst (either Norm or Mac), i.e. somewhere between per and per + lex.dur in these records. Quite arbitrarily we choose a random time in the middle 80% between the dates:
set.seed(1952)
xcut <- select(transform(jump,
cut = per + lex.dur * runif(per, 0.1, 0.9),
new.state = "Mic"),
c(lex.id, cut, new.state))
xcut
## lex.id cut new.state
## 70 2001.789 Mic
## 86 2012.232 Mic
## 130 2001.488 Mic
## 131 2001.032 Mic
## 136 1997.610 Mic
## 136 2000.780 Mic
## 171 1997.057 Mic
## 175 2013.472 Mic
How many extra records will be generated when cutting the follow-up?
Now make extra cuts (transitions to Mic) at these dates using rcutLexis with xcut on the L3 object, and order the levels sensibly:
L4 <- rcutLexis(L3, xcut)
L4 <- Relevel(L4, c("Norm","Mic","Mac","D(CVD)","D(oth)"))
summary(L4)
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 90 35 0 6 13 144 54 581.04 66
## Mic 72 312 65 14 30 493 181 1435.14 160
## Mac 0 22 41 18 12 93 52 400.41 60
## Sum 162 369 106 38 55 730 287 2416.59 160
We see that there are now no transitions directly between Norm and Mac in L4, so we can make a more intelligible plot of the transitions (remember to read the help page for boxes.Lexis):
clr <- c("forestgreen","orange","red","blue",gray(0.3))
boxes(L4, boxpos = list(x = c(20,20,20,80,80),
y = c(10,50,90,75,25)),
show.BE = "nz",
scale.R = 100,
digits.R = 2,
cex = 0.9,
pos.arr = 0.3,
col.bg = clr,
col.border = clr,
main = "Transitions between states in the Steno2 study",
col.txt = c("white","black")[c(1,2,1,1,1)])
Explain the arguments of boxes.
Explain the numbers in the graph.
Describe the overall effect of albuminuria on the two mortality rates.
What we now have in the Lexis object L4 is the follow-up of the 160 persons classified in the 5 states shown. So with this multistate model (well, there is no model yet) set up we can look at mortality rates and see how they depend on the current albuminuria state, or look at the transition rates between the different albuminuria states and assess how these depend on covariates.
We will model the transition rates with parametric functions, so we need to split the dataset along some time scale; we will use 1 month intervals (they should be sufficiently small to accommodate an assumption of constant transition rates in each interval):
S4 <- splitMulti(L4, tfi = seq(0, 25, 1/12))
summary(L4)
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 90 35 0 6 13 144 54 581.04 66
## Mic 72 312 65 14 30 493 181 1435.14 160
## Mac 0 22 41 18 12 93 52 400.41 60
## Sum 162 369 106 38 55 730 287 2416.59 160
summary(S4)
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 7061 35 0 6 13 7115 54 581.04 66
## Mic 72 17453 65 14 30 17634 181 1435.14 160
## Mac 0 22 4844 18 12 4896 52 400.41 60
## Sum 7133 17510 4909 38 55 29645 287 2416.59 160
We see that the number of events (transitions) and person-years are the same, in the two Lexis objects, but the number of records in S4 is substantially larger than in L4.
We can now model the overall mortality rates as functions of age and duration (time since entry) using the defaults for glm.Lexis (this function call will trigger a warning):
ma <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst)
## NOTE:
## Multiple transitions *from* state ' Mac', 'Mic', 'Norm ' - are you sure?
## The analysis requested is effectively merging outcome states.
## You may want analyses using a *stacked* dataset - see ?stack.Lexis
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Norm->D(CVD)
## Mic->D(CVD)
## Mac->D(CVD)
## Norm->D(oth)
## Mic->D(oth)
## Mac->D(oth)
The warning triggered here just tells you that you are modeling the occurrence of any type of death, which amounts to modeling of the sum of CVD and non-CVD death rates—the overall mortality.
We can illustrate the actual model underlying this by collapsing the two causes for death using Relevel:
clr <- c("forestgreen","orange","red",gray(0.3))
summary(Relevel(L4, list("Dead" = 4:5), first = FALSE))
##
## Transitions:
## To
## From Norm Mic Mac Dead Records: Events: Risk time: Persons:
## Norm 90 35 0 19 144 54 581.04 66
## Mic 72 312 65 44 493 181 1435.14 160
## Mac 0 22 41 30 93 52 400.41 60
## Sum 162 369 106 93 730 287 2416.59 160
boxes(Relevel(L4, list("Dead" = 4:5), first = FALSE),
boxpos = list(x = c(20,20,20,80),
y = c(10,50,90,50)),
show.BE = "nz",
scale.R = 100,
digits.R = 2,
cex = 0.9,
pos.arr = 0.3,
col.bg = clr,
col.border = clr,
col.txt = c("white","black")[c(1,2,1,1)])
This figure shows transitions between states in the Steno2 study, using only all-cause mortality.
The model structure with lex.Cst as an additive term is assuming that the overall mortality rates are proportional between states of albuminuria.
The default for glm.Lexis is to model all transitions to absorbing states which in this case are the two “dead” states, D(oth) and D(CVD).
# The glm.Lexis above is just a convenience wrapper for:
m1 <- glm(cbind(lex.Xst %in% c("D(oth)", "D(CVD)")
& lex.Cst != lex.Xst,
lex.dur)
~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst,
family = poisreg,
data = subset(S4, lex.Cst %in% c("Norm","Mic","Mac")))
#which will also give the same results as:
m2 <- glm((lex.Xst %in% c("D(oth)", "D(CVD)")
& lex.Cst != lex.Xst)
~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst,
offset = log(lex.dur),
family = poisson,
data = subset(S4, lex.Cst %in% c("Norm","Mic","Mac")))
Note the difference between the families poisreg and poisson: poisreg enters events and person-time more logically as part of the outcome, whereas poisson enters events as the response and person-years (lex.dur) via the offset argument.
The parameters from any of the formulations are on the log-scale so we want to see them exponentiated, so on the rate-scale:
round(ci.exp(ma), 2)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.00 0.00 0.01
## Ns(tfi, knots = seq(0, 20, 5))1 6.57 1.23 35.14
## Ns(tfi, knots = seq(0, 20, 5))2 4.29 0.97 18.89
## Ns(tfi, knots = seq(0, 20, 5))3 45.97 0.93 2265.04
## Ns(tfi, knots = seq(0, 20, 5))4 0.52 0.17 1.58
## Ns(age, knots = seq(50, 80, 10))1 3.26 1.33 8.00
## Ns(age, knots = seq(50, 80, 10))2 11.00 1.34 90.24
## Ns(age, knots = seq(50, 80, 10))3 12.41 5.56 27.71
## lex.CstMic 0.96 0.56 1.65
## lex.CstMac 1.71 0.95 3.06
What are the mortality rate-ratios (hazard ratios), what ratios do they refer to: rates of what between which groups?
We see there is a higher mortality in the Mac state but no discernible difference between the Mic and the Norm states.
It can be formally tested whether the three states carry the same mortality using a Wald test (testing whether the Norm and Mac parameters both are 0 on the log-scale):
Wald(ma, subset = "lex.Cst")
## Chisq d.f. P
## 6.15752406 2.00000000 0.04601619
What is the meaning of this test (i.e. what is the null hypothesis)?
Do you like the formal 5% significance level? What about 4.5% instead?
Now do the same analysis for the two causes of death separately, using the to argument to glm.Lexis:
# other causes of death
mo <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst,
to = "D(oth)")
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Norm->D(oth)
## Mic->D(oth)
## Mac->D(oth)
round(ci.exp(mo), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.000 0.000 7.000000e-03
## Ns(tfi, knots = seq(0, 20, 5))1 161.560 2.938 8.882984e+03
## Ns(tfi, knots = seq(0, 20, 5))2 37.189 1.461 9.467380e+02
## Ns(tfi, knots = seq(0, 20, 5))3 39009.388 4.355 3.494245e+08
## Ns(tfi, knots = seq(0, 20, 5))4 2.086 0.345 1.263200e+01
## Ns(age, knots = seq(50, 80, 10))1 2.684 0.881 8.176000e+00
## Ns(age, knots = seq(50, 80, 10))2 1.868 0.185 1.887100e+01
## Ns(age, knots = seq(50, 80, 10))3 12.728 4.572 3.543300e+01
## lex.CstMic 1.004 0.520 1.937000e+00
## lex.CstMac 1.001 0.449 2.232000e+00
#
# CVD death
mC <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst,
to = "D(CVD)")
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Norm->D(CVD)
## Mic->D(CVD)
## Mac->D(CVD)
round(ci.exp(mC), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.001 0.000 0.014
## Ns(tfi, knots = seq(0, 20, 5))1 1.171 0.164 8.344
## Ns(tfi, knots = seq(0, 20, 5))2 2.008 0.303 13.333
## Ns(tfi, knots = seq(0, 20, 5))3 1.493 0.018 126.727
## Ns(tfi, knots = seq(0, 20, 5))4 0.144 0.019 1.072
## Ns(age, knots = seq(50, 80, 10))1 6.833 1.078 43.327
## Ns(age, knots = seq(50, 80, 10))2 486.871 1.658 142982.410
## Ns(age, knots = seq(50, 80, 10))3 15.110 3.524 64.789
## lex.CstMic 0.919 0.351 2.410
## lex.CstMac 3.229 1.271 8.203
What is the conclusion w.r.t. the effect of albuminuria state on the two cause-specific mortality rates?
Now make formal tests of relevant hypotheses using Wald.
Wald(mo, subset = "Cst")
## Chisq d.f. P
## 0.0001452741 2.0000000000 0.9999273656
Wald(mC, subset = "Cst")
## Chisq d.f. P
## 13.785510275 2.000000000 0.001015113
What is the conclusion from these w.r.t. mortality dependence on albuminuria status?
We can show how fitted mortality rates look for persons currently in state Mic entering the study at a set of specific ages. The entry ages are in the vector L2$age (L2 is the initial Lexis object with one record per person):
summary(L2$age)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 37.39 48.52 56.60 55.13 61.06 67.50
Based on this we shall use ages (at entry) 45, 55 and 65, and show mortality rates for persons entering at these ages. We will show the rates as functions of their current age. We need a prediction data frame, with values for all variables in the model, (current) age and time from entry, tfi. Here expand.grid is our friend; note we are using ain for age at entry:
expand.grid(tfi = c(NA, seq(0, 20, 5)),
ain = c(45, 55, 65))
## tfi ain
## 1 NA 45
## 2 0 45
## 3 5 45
## 4 10 45
## 5 15 45
## 6 20 45
## 7 NA 55
## 8 0 55
## 9 5 55
## 10 10 55
## 11 15 55
## 12 20 55
## 13 NA 65
## 14 0 65
## 15 5 65
## 16 10 65
## 17 15 65
## 18 20 65
It will give all combinations of the values in the vectors supplied as a data.frame. The NAs are there for plotting purposes— we get a break in plotted curves if there is an NA in the data. We want the tfi points to be closer than in the illustrative example:
prf <- transform(expand.grid(tfi = c(NA, seq(0, 19, 0.5)),
ain = c(45, 55, 65))[-1,],
age = ain + tfi,
lex.Cst = "Mic")
prf[ 1: 5,]
## tfi ain age lex.Cst
## 2 0.0 45 45.0 Mic
## 3 0.5 45 45.5 Mic
## 4 1.0 45 46.0 Mic
## 5 1.5 45 46.5 Mic
## 6 2.0 45 47.0 Mic
prf[40:44,]
## tfi ain age lex.Cst
## 41 NA 55 NA Mic
## 42 0.0 55 55.0 Mic
## 43 0.5 55 55.5 Mic
## 44 1.0 55 56.0 Mic
## 45 1.5 55 56.5 Mic
matshade(prf$age, cbind(ci.pred(mo, prf),
ci.pred(mC, prf)) * 100,
lwd = 3, col = c("black","blue"),
log = "y", ylim = c(0.02,20), plot = TRUE,
xlab = "Age at follow-up (years)",
main = "CVD mortality rates (blue) and non-CVD mortality rates (black), with 95% confidence intervals as shades", cex.main = 0.78,
ylab = "Mortality rate per 100 PY")
abline(v = c(45, 55, 65), lty = 3)
The rates of death from other causes is very small at the beginning and increases steeply over the first 5 years of follow-up, while the CVD mortality rates are pretty stable with a foreseeable increase by age.Give an informal description of the curves, and a possible reason for the shape of the curves.
We can show the impact of albuminuria state on the mortality rates in a 3-panel layout:
par(mfrow=c(1,3))
for(st in c("Norm","Mic","Mac"))
{
matshade(prf$age, pmin(pmax(
cbind(ci.pred(mo, transform(prf, lex.Cst = st)),
ci.pred(mC, transform(prf, lex.Cst = st))) * 100,
0.05), 60),
lwd = 3, col = c("black","blue"),
log = "y", ylim = c(0.1,50), plot = TRUE)
text(60, 50, st, adj = 0)
}
How are the curves in the three panels related?
Describe the effect of albuminuria status on the two types of mortality.
How can you see this from the model parameters of the models mo and mC?
We would like to see how the probabilities of being in each of the states in figure 3.2 look as a function of time since entry, and we will in particular be interested in how this depends on allo, the allocation to intensified or standard treatment.
Thus we will need models for 1) the cause-specific mortality rates and 2) transition rates between albuminuria states. And of course models which all include the effect of allo (treatment allocation). We already fitted models for the mortality rates, but here we refit them in a slightly different guise, namely including the treatment allocation (allo) as covariate too.
We first model the mortality rates using a proportional hazards model, but allowing different levels of mortality between the two allocation groups (in allo), and the three albuminuria states (in lex.Cst):
mix <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst * allo,
to = "D(oth)")
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Norm->D(oth)
## Mic->D(oth)
## Mac->D(oth)
round(ci.exp(mix), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.000 0.000 5.000000e-03
## Ns(tfi, knots = seq(0, 20, 5))1 195.021 3.367 1.129679e+04
## Ns(tfi, knots = seq(0, 20, 5))2 43.667 1.644 1.159968e+03
## Ns(tfi, knots = seq(0, 20, 5))3 59106.199 5.928 5.893319e+08
## Ns(tfi, knots = seq(0, 20, 5))4 2.617 0.429 1.595000e+01
## Ns(age, knots = seq(50, 80, 10))1 2.673 0.884 8.081000e+00
## Ns(age, knots = seq(50, 80, 10))2 1.479 0.141 1.552500e+01
## Ns(age, knots = seq(50, 80, 10))3 11.747 4.215 3.274000e+01
## lex.CstMic 0.967 0.361 2.593000e+00
## lex.CstMac 1.630 0.532 5.000000e+00
## alloConv 1.797 0.591 5.463000e+00
## lex.CstMic:alloConv 1.072 0.281 4.092000e+00
## lex.CstMac:alloConv 0.362 0.072 1.821000e+00
We would however like to see the allocation effect on mortality separately for each albuminuria state; this is done by the “/” operator in the model formula (pronounced: allo effect within lex.Cst):
mox <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo,
to = "D(oth)")
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Norm->D(oth)
## Mic->D(oth)
## Mac->D(oth)
round(ci.exp(mox), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.000 0.000 5.000000e-03
## Ns(tfi, knots = seq(0, 20, 5))1 195.021 3.367 1.129679e+04
## Ns(tfi, knots = seq(0, 20, 5))2 43.667 1.644 1.159968e+03
## Ns(tfi, knots = seq(0, 20, 5))3 59106.199 5.928 5.893319e+08
## Ns(tfi, knots = seq(0, 20, 5))4 2.617 0.429 1.595000e+01
## Ns(age, knots = seq(50, 80, 10))1 2.673 0.884 8.081000e+00
## Ns(age, knots = seq(50, 80, 10))2 1.479 0.141 1.552500e+01
## Ns(age, knots = seq(50, 80, 10))3 11.747 4.215 3.274000e+01
## lex.CstMic 0.967 0.361 2.593000e+00
## lex.CstMac 1.630 0.532 5.000000e+00
## lex.CstNorm:alloConv 1.797 0.591 5.463000e+00
## lex.CstMic:alloConv 1.927 0.925 4.013000e+00
## lex.CstMac:alloConv 0.651 0.205 2.071000e+00
c(deviance(mox), deviance(mix))
## [1] 734.0855 734.0855
The use of the deviance gives a good indication that the models fitted actually are the same model, just differently parametrized.
What is the meaning of the parameters for the allo effect?
We also need a similar model for the CVD-mortality:
mCx <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo,
to = "D(CVD)")
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Norm->D(CVD)
## Mic->D(CVD)
## Mac->D(CVD)
round(ci.exp(mCx), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.000 0.000 0.013
## Ns(tfi, knots = seq(0, 20, 5))1 1.004 0.140 7.179
## Ns(tfi, knots = seq(0, 20, 5))2 2.291 0.354 14.834
## Ns(tfi, knots = seq(0, 20, 5))3 1.361 0.016 115.800
## Ns(tfi, knots = seq(0, 20, 5))4 0.125 0.016 0.988
## Ns(age, knots = seq(50, 80, 10))1 7.289 1.122 47.353
## Ns(age, knots = seq(50, 80, 10))2 641.288 1.868 220199.762
## Ns(age, knots = seq(50, 80, 10))3 20.847 4.730 91.871
## lex.CstMic 0.808 0.199 3.275
## lex.CstMac 1.250 0.245 6.373
## lex.CstNorm:alloConv 1.399 0.278 7.047
## lex.CstMic:alloConv 1.682 0.579 4.889
## lex.CstMac:alloConv 4.856 1.367 17.250
What is the conclusion for the intervention effect on CVD mortality rates?
For a complete description of transitions in the model we also need models for the transitions between albuminuria states (the vertical arrows in figure 3.2):
We will use different models for deterioration (arrows up) and improvement (arrows down) in albuminuria in figure 3.2). Again the model specification is a simplified by glm.Lexis, but now requires specification of both from and to arguments:
det <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo,
from = c("Norm","Mic"),
to = c("Mic","Mac"))
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Norm->Mic
## Mic->Mac
imp <- glm.Lexis(S4, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo,
from = c("Mac","Mic"),
to = c("Mic","Norm"))
## stats::glm Poisson analysis of Lexis object S4 with log link:
## Rates for transitions:
## Mac->Mic
## Mic->Norm
round(ci.exp(det), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.071 0.032 0.158
## Ns(tfi, knots = seq(0, 20, 5))1 0.648 0.248 1.692
## Ns(tfi, knots = seq(0, 20, 5))2 0.276 0.079 0.961
## Ns(tfi, knots = seq(0, 20, 5))3 0.281 0.043 1.823
## Ns(tfi, knots = seq(0, 20, 5))4 0.218 0.065 0.738
## Ns(age, knots = seq(50, 80, 10))1 1.977 0.839 4.656
## Ns(age, knots = seq(50, 80, 10))2 3.588 0.948 13.574
## Ns(age, knots = seq(50, 80, 10))3 2.728 0.795 9.358
## lex.CstMic 0.393 0.223 0.695
## lex.CstNorm:alloConv 0.490 0.222 1.082
## lex.CstMic:alloConv 1.965 1.178 3.278
round(ci.exp(imp), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.208 0.128 0.339
## Ns(tfi, knots = seq(0, 20, 5))1 0.239 0.075 0.759
## Ns(tfi, knots = seq(0, 20, 5))2 0.069 0.011 0.420
## Ns(tfi, knots = seq(0, 20, 5))3 0.046 0.008 0.277
## Ns(tfi, knots = seq(0, 20, 5))4 0.172 0.034 0.859
## Ns(age, knots = seq(50, 80, 10))1 0.844 0.292 2.444
## Ns(age, knots = seq(50, 80, 10))2 0.342 0.069 1.690
## Ns(age, knots = seq(50, 80, 10))3 0.580 0.073 4.624
## lex.CstMac 1.055 0.465 2.393
## lex.CstMic:alloConv 0.526 0.324 0.855
## lex.CstMac:alloConv 1.341 0.545 3.303
round(ci.exp(det, subset="al"), 1)
## exp(Est.) 2.5% 97.5%
## lex.CstNorm:alloConv 0.5 0.2 1.1
## lex.CstMic:alloConv 2.0 1.2 3.3
round(ci.exp(imp, subset="al"), 1)
## exp(Est.) 2.5% 97.5%
## lex.CstMic:alloConv 0.5 0.3 0.9
## lex.CstMac:alloConv 1.3 0.5 3.3
What was the meaning of “different models for det and imp”?
What do the parameters in the models represent?
What are the assumptions in the models?
Label the transitions in figure 3.2 with the models for each of thetransitions.
We now have statistical models for all transitions, two models for the cause specific mortality rates, and two models for transitions between albuminuria states. The state probabilities that in principle can be derived from these are not trivial to compute, essentially they can only be computed by simulation.
First we need an explicit specification of what transitions are decribed by what the model, since the simulated transitions will be using predictions from these models. This is specified in a list of lists (remember what a list is??).
There must be one element in the list for each transient state (of which we have 3):
Tr <- list(Norm = list("Mic" = det,
"D(oth)" = mox,
"D(CVD)" = mCx),
Mic = list("Mac" = det,
"Norm" = imp,
"D(oth)" = mox,
"D(CVD)" = mCx),
Mac = list("Mic" = imp,
"D(oth)" = mox,
"D(CVD)" = mCx))
lapply(Tr, names)
## $Norm
## [1] "Mic" "D(oth)" "D(CVD)"
##
## $Mic
## [1] "Mac" "Norm" "D(oth)" "D(CVD)"
##
## $Mac
## [1] "Mic" "D(oth)" "D(CVD)"
For example, the object Tr\(Norm\)Mic is the model det; the model for the transition rate Norm → Mic; we see that there are 10 entries in the specification of Tr, corresponding to each of the 10 transitions in the diagram in figure 3.2. Some of the entries in Tr point to the same model. All the models fitted were actually joint models for more than one transition, but with specific parameters representing differences between the specific transition rates modeled.
We can use the estimated rates to simulate the transition between states in a group of people with a given set of covariates—the initial cohort. The simulated data can the be used to assess the probability of being in each of the states at a given time after entry to the study, by simply counting how many of the persons from the initial cohort are simulated to be in each state. These probabilities depend on the covariates used for modeling of transition rates, that is any of the covariates in mox, mCx, det and imp. We can choose our initial cohort in (at least) two different ways:
Either way, what is needed is a data frame of persons indicating their initial status. simLexis will then simulate their individual trajectories through states (what transition takes place when) and produce a simulated cohort of persons in the form of a Lexis object. The initial (baseline) data frame should also be a Lexis object, but the values of lex.Xst and lex.dur need not be given, since these will be simulated.
First construct a cohort with the same covariate distribution as the entire study for each of the allocation groups:
ini <- L2[,c("per", "age", "tfi")]
ini <- rbind(transform(ini, lex.Cst = factor("Mic"), allo = factor("Int")),
transform(ini, lex.Cst = factor("Mic"), allo = factor("Conv")))
str(ini)
## Classes 'Lexis' and 'data.frame': 320 obs. of 5 variables:
## $ per : 'cal.yr' num 1993 1993 1993 1993 1993 ...
## $ age : 'cal.yr' num 61.1 46.6 49.9 48.5 57.3 ...
## $ tfi : num 0 0 0 0 0 0 0 0 0 0 ...
## $ lex.Cst: Factor w/ 1 level "Mic": 1 1 1 1 1 1 1 1 1 1 ...
## $ allo : Factor w/ 2 levels "Int","Conv": 1 1 1 1 1 1 1 1 1 1 ...
## - attr(*, "breaks")=List of 3
## ..$ per: NULL
## ..$ age: NULL
## ..$ tfi: NULL
## - attr(*, "time.scales")= chr [1:3] "per" "age" "tfi"
## - attr(*, "time.since")= chr [1:3] "" "" ""
This will be the initial values in the cohort we follow through states—we have the starting state in lex.Cst and the covariates (at start): timescales ( per, age, tfi) and the other covariates allo
First we simulate transitions from a large cohort that looks like the study population, say 10 copies of each person in the original data set (see ?simLexis):
?simLexis
## starting httpd help server ... done
set.seed(1952)
system.time(
Sorg <- simLexis(Tr = Tr, # models for each transition
init = ini, # cohort of straters
N = 100, # how many copies of each person in ini
t.range = 21, # how long should we simulate before censoring
n.int = 200))# how many intervals for evaluating rates
## user system elapsed
## 110.04 10.50 120.56
There is no guaranteed order of the states in the Sorg object, so we explicitly reorder the states:
Sorg <- Relevel(Sorg, c("Norm", "Mic", "Mac", "D(CVD)", "D(oth)"))
summary(Sorg, by = "allo")
## $Int
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 2293 5472 0 648 1324 9737 7444 76184.55 8601
## Mic 9737 4202 4916 1258 2590 22703 18501 151493.64 16000
## Mac 0 1231 1523 619 1543 4916 3393 33953.90 4674
## Sum 12030 10905 6439 2525 5457 37356 29338 261632.09 16000
##
## $Conv
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 1460 1574 0 597 1457 5088 3628 47906.47 4927
## Mic 5088 2237 8302 1479 3169 20275 18038 129297.70 16000
## Mac 0 2701 1439 3154 1008 8302 6863 45188.18 7404
## Sum 6548 6512 9741 5230 5634 33665 28529 222392.35 16000
subset(Sorg, lex.id %in% 28:32)
## lex.id per age tfi lex.dur lex.Cst lex.Xst allo cens
## 28 1993.33 61.05 0.00 1.16 Mic Norm Int 2014.326
## 28 1994.49 62.21 1.16 2.31 Norm Mic Int 2014.326
## 28 1996.79 64.52 3.47 3.13 Mic D(oth) Int 2014.326
## 29 1993.33 61.05 0.00 3.17 Mic Norm Int 2014.326
## 29 1996.50 64.22 3.17 14.87 Norm Mic Int 2014.326
## 29 2011.37 79.10 18.04 2.96 Mic Mic Int 2014.326
## 30 1993.33 61.05 0.00 8.20 Mic D(CVD) Int 2014.326
## 31 1993.33 61.05 0.00 9.08 Mic Norm Int 2014.326
## 31 2002.40 70.13 9.08 3.40 Norm Mic Int 2014.326
## 31 2005.80 73.53 12.48 1.90 Mic D(CVD) Int 2014.326
## 32 1993.33 61.05 0.00 3.15 Mic Mac Int 2014.326
## 32 1996.48 64.20 3.15 0.84 Mac Mic Int 2014.326
## 32 1997.32 65.05 4.00 11.54 Mic D(CVD) Int 2014.326
Describe in words how the simulated data looks, and what each record represents.
What is it really we simulated?
addmargins(table(table(Sorg$lex.id)))
##
## 1 2 3 4 5 6 7 8 9 Sum
## 8416 13680 5765 3125 708 248 44 12 2 32000
What does this table mean?
Now we can just count how many of the original 1600 persons are in each of the states at each of a set of times; this is done by the function nState:
system.time(
Nst <- nState(Sorg,
at = seq(0, 20, 0.2),
from = 0,
time.scale = "tfi"))
## user system elapsed
## 10.98 0.15 11.14
str(Nst)
## 'table' int [1:101, 1:5] 0 873 1625 2346 2985 3547 4056 4585 5055 5477 ...
## - attr(*, "dimnames")=List of 2
## ..$ when : chr [1:101] "0" "0.2" "0.4" "0.6" ...
## ..$ State: chr [1:5] "Norm" "Mic" "Mac" "D(CVD)" ...
head(Nst)
## State
## when Norm Mic Mac D(CVD) D(oth)
## 0 0 32000 0 0 0
## 0.2 873 30754 350 23 0
## 0.4 1625 29711 611 53 0
## 0.6 2346 28678 889 84 3
## 0.8 2985 27739 1160 113 3
## 1 3547 26903 1408 136 6
This is however not necessarily a relevant summary; we would be interested in seeing how things look in each of the allocation groups, Int and Conv.
Nint <- nState(subset(Sorg, allo == "Int"),
at = seq(0, 20.2, 0.1),
from = 0,
time.scale = "tfi")
Nconv<- nState(subset(Sorg, allo == "Conv"),
at = seq(0, 20.2, 0.1),
from = 0,
time.scale = "tfi")
cbind(
head(Nint), NA,
head(Nconv))
## Norm Mic Mac D(CVD) D(oth) Norm Mic Mac D(CVD) D(oth)
## 0 0 16000 0 0 0 NA 0 16000 0 0 0
## 0.1 294 15629 71 6 0 NA 157 15723 115 5 0
## 0.2 575 15287 129 9 0 NA 298 15467 221 14 0
## 0.3 820 14995 170 15 0 NA 405 15270 304 21 0
## 0.4 1091 14656 232 21 0 NA 534 15055 379 32 0
## 0.5 1327 14364 278 29 2 NA 676 14793 491 39 1
If we divide each of these by the number of persons, we get the probabilities of being in each if the states at the different times since entry.
If we want the state probabilities cumulated over states we can derive these by pState, that yields a matrix with the cumulative state probabilities.
Pint <- pState(Nint )
Pconv <- pState(Nconv)
str(Pint)
## 'pState' num [1:203, 1:5] 0 0.0184 0.0359 0.0512 0.0682 ...
## - attr(*, "dimnames")=List of 2
## ..$ when : chr [1:203] "0" "0.1" "0.2" "0.3" ...
## ..$ State: chr [1:5] "Norm" "Mic" "Mac" "D(CVD)" ...
head(Pint)
## State
## when Norm Mic Mac D(CVD) D(oth)
## 0 0.0000000 1.0000000 1.0000000 1.000000 1
## 0.1 0.0183750 0.9951875 0.9996250 1.000000 1
## 0.2 0.0359375 0.9913750 0.9994375 1.000000 1
## 0.3 0.0512500 0.9884375 0.9990625 1.000000 1
## 0.4 0.0681875 0.9841875 0.9986875 1.000000 1
## 0.5 0.0829375 0.9806875 0.9980625 0.999875 1
Describe the structure of Pint.
There is a standard plotting method for a pState object; it will plot the stacked state probabilities stacked in the order given by the perm argument (not used here because they are already in the order we want):
clr <- c("forestgreen", "orange", "red", "blue", gray(0.4))
par(mfrow = c(1,2), mar=c(3,3,2,2))
plot(Pint , col = clr, xlim = c(0, 20))
plot(Pconv, col = clr, xlim = c(20, 0))
A more sophisticated version:
clr <- c("forestgreen", "orange", "red", "blue", gray(0.4))
par(mfrow = c(1,2), mar=c(3,3,2,2))
plot(Pint, col = clr, xlim = c(0, 20))
# the survival curve
lines(as.numeric(rownames(Pint)), Pint[,"Mac"], lwd = 3, col = "black")
lines(as.numeric(rownames(Pint)), Pint[,"Mac"], lwd = 1, col = "white")
text(rownames(Pint)[150],
Pint[150,] - diff(c(0, Pint[150,]))/2,
colnames(Pint), col = "white", cex = 0.8)
plot(Pconv, col = clr, xlim = c(20, 0))
# the survival curve
lines(as.numeric(rownames(Pconv)), Pconv[,"Mac"], lwd = 3, col = "black")
lines(as.numeric(rownames(Pconv)), Pconv[,"Mac"], lwd = 1, col = "white")
text(rownames(Pconv)[150],
Pconv[150,] - diff(c(0, Pconv[150,]))/2,
colnames(Pint), col = "white", cex = 0.8)
mtext(c("Intensive care","Conventional care"),
side = 3, at = c(1,3)/4, outer = TRUE, line = -2)
The figure shows State probabilities for the two intervention groups, for populations of the same structure as the original total Steno2 population.
Redo the plot with proper labeling of axes, including units where needed.
Describe the results and conclude on the probabilities shown.
The plot 3.5 may be of limited interest; the probabilities here are really “the probability that a randomly chosen person from the Steno 2 study. . .”. So we are referring to a universe that is not generalizable, the reference is to a particular distribution of ages at entry etc. into the study. The plot is only partially relevant for showing the intervention effect, the absolute sizes of the state probabilities are strictly speaking irrelevant.
Even if we take the modeling background deeply serious and accept that occurrence rates depend only on current age (age), time since entry (tfi) and treatment allocation (allo), the assumption of age-distribution as in the Steno 2 study is quit eabsurd; who wants to refer to this? Often this is disguised in terms such as “population averaged”.
Therefore, it would be more relevant to show the results for a homogeneous population of persons at select ages at entry. This would just require a different init data frame. But note that it must be a Lexis object, easiest obtained by copying from S4:
ini <- S4[1:10,c("lex.id", "per", "age", "tfi", "lex.Cst", "allo")]
ini[,"lex.id"] <- 1:10
ini[,"per"] <- 1993 # not used but it is a time scale in S4
ini[,"age"] <-
ini[,"ain"] <- rep(seq(45,65,5), 2)
ini[,"tfi"] <- 0
ini[,"lex.Cst"] <- factor("Mic",
levels = c("Norm","Mic","Mac","D(CVD)","D(oth)"))
ini[,"allo"] <- factor(rep(c("Int","Conv"), each = 5))
ini
## lex.id per age tfi lex.Cst allo ain
## 1 1993 45 0 Mic Int 45
## 2 1993 50 0 Mic Int 50
## 3 1993 55 0 Mic Int 55
## 4 1993 60 0 Mic Int 60
## 5 1993 65 0 Mic Int 65
## 6 1993 45 0 Mic Conv 45
## 7 1993 50 0 Mic Conv 50
## 8 1993 55 0 Mic Conv 55
## 9 1993 60 0 Mic Conv 60
## 10 1993 65 0 Mic Conv 65
str(ini)
## Classes 'Lexis' and 'data.frame': 10 obs. of 7 variables:
## $ lex.id : int 1 2 3 4 5 6 7 8 9 10
## $ per : num 1993 1993 1993 1993 1993 ...
## $ age : num 45 50 55 60 65 45 50 55 60 65
## $ tfi : num 0 0 0 0 0 0 0 0 0 0
## $ lex.Cst: Factor w/ 5 levels "Norm","Mic","Mac",..: 2 2 2 2 2 2 2 2 2 2
## $ allo : Factor w/ 2 levels "Conv","Int": 2 2 2 2 2 1 1 1 1 1
## $ ain : num 45 50 55 60 65 45 50 55 60 65
## - attr(*, "time.scales")= chr [1:3] "per" "age" "tfi"
## - attr(*, "time.since")= chr [1:3] "" "" ""
## - attr(*, "breaks")=List of 3
## ..$ per: NULL
## ..$ age: NULL
## ..$ tfi: num [1:301] 0 0.0833 0.1667 0.25 0.3333 ...
Note that it is important that we enter the variable lex.Cst as a factor with the same levels as in the Lexis object S4, in the order we want the states when reporting results, because we will be using ini as basis for predictions of rates needed to do the simulations. Further, allo must also be entered as a factor, otherwise it is not possible to compute predictions from the models, because allo were included as a factor in the models.
For each of these combinations of age (at entry) and treatment allocation we will simulate 100 persons (note that we are using the same transition rates, the models in Tr):
system.time(
Sdef <- simLexis(Tr = Tr,
init = ini,
N = 1000,
t.range = 21,
n.int = 200))
## user system elapsed
## 33.86 2.22 36.08
summary(Sdef, by = "allo")
## $Conv
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 455 496 0 175 426 1552 1097 14534.05 1498
## Mic 1552 726 2591 432 1021 6322 5596 41195.79 5000
## Mac 0 826 520 941 304 2591 2071 14578.57 2316
## Sum 2007 2048 3111 1548 1751 10465 8764 70308.41 5000
##
## $Int
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 706 1648 0 216 396 2966 2260 22922.78 2652
## Mic 2966 1362 1553 381 775 7037 5675 48019.31 5000
## Mac 0 389 493 186 485 1553 1060 11131.40 1488
## Sum 3672 3399 2046 783 1656 11556 8995 82073.49 5000
subset(Sdef, lex.id < 5)
## lex.id per age tfi lex.dur lex.Cst lex.Xst allo ain cens
## 1 1993.00 45.00 0.00 2.76 Mic Norm Int 45 2014
## 1 1995.76 47.76 2.76 0.29 Norm D(oth) Int 45 2014
## 2 1993.00 45.00 0.00 4.50 Mic Mac Int 45 2014
## 2 1997.50 49.50 4.50 2.33 Mac D(oth) Int 45 2014
## 3 1993.00 45.00 0.00 2.51 Mic Norm Int 45 2014
## 3 1995.51 47.51 2.51 18.49 Norm Norm Int 45 2014
## 4 1993.00 45.00 0.00 7.19 Mic Norm Int 45 2014
## 4 2000.19 52.19 7.19 13.81 Norm Norm Int 45 2014
In real applications we would use 5000 or 10,000 replicates of each to minimize the simulation error.
Now we will repeat the graph above, but for the 10 combinations of age at enrollment (ain), and allocation; we start with the 45 year old allocated to Int:
P45i <- nState(subset(Sdef, ain == 45 & allo == "Int"),
at = seq(0, 20, 0.1),
from = 0,
time.scale = "tfi")
head(P45i)
## State
## when Norm Mic Mac D(CVD) D(oth)
## 0 0 1000 0 0 0
## 0.1 20 980 0 0 0
## 0.2 44 953 3 0 0
## 0.3 57 939 4 0 0
## 0.4 78 915 7 0 0
## 0.5 93 900 7 0 0
head(pState(P45i))
## State
## when Norm Mic Mac D(CVD) D(oth)
## 0 0.000 1.000 1 1 1
## 0.1 0.020 1.000 1 1 1
## 0.2 0.044 0.997 1 1 1
## 0.3 0.057 0.996 1 1 1
## 0.4 0.078 0.993 1 1 1
## 0.5 0.093 0.993 1 1 1
This should then be repeated for 4 other ages at enrollment and the two allocations, plus we will only store the state probabilities:
P45c <- pState(nState(subset(Sdef, ain == 45 & allo == "Conv"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P45i <- pState(nState(subset(Sdef, ain == 45 & allo == "Int"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P50c <- pState(nState(subset(Sdef, ain == 55 & allo == "Conv"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P50i <- pState(nState(subset(Sdef, ain == 55 & allo == "Int"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P55c <- pState(nState(subset(Sdef, ain == 55 & allo == "Conv"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P55i <- pState(nState(subset(Sdef, ain == 55 & allo == "Int"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P60c <- pState(nState(subset(Sdef, ain == 55 & allo == "Conv"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P60i <- pState(nState(subset(Sdef, ain == 55 & allo == "Int"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P65c <- pState(nState(subset(Sdef, ain == 65 & allo == "Conv"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
P65i <- pState(nState(subset(Sdef, ain == 65 & allo == "Int"),
at = seq(0, 21, 0.1),
from = 0,
time.scale = "tfi"))
Then we can plot these in a multiple lay-out:
par(mfrow = c(5,2), mar = c(1,1,0,0),
oma = c(3,3,1,0), mgp=c(3,1,0)/1.6)
plot(P45i, col = clr, xlim = c(0,20))
plot(P45c, col = clr, xlim = c(20,0))
plot(P50i, col = clr, xlim = c(0,20))
plot(P50c, col = clr, xlim = c(20,0))
plot(P55i, col = clr, xlim = c(0,20))
plot(P55c, col = clr, xlim = c(20,0))
plot(P60i, col = clr, xlim = c(0,20))
plot(P60c, col = clr, xlim = c(20,0))
plot(P65i, col = clr, xlim = c(0,20))
plot(P65c, col = clr, xlim = c(20,0))
mtext(c("Int","Conv"), side = 3, at = c(1,3)/4, outer = TRUE, line = 0)
mtext(paste(seq(45,65,5)), side = 2, at = (5:1*2-1)/10,
outer = TRUE, line = 0)
The figure shows Predicted probabilities of being in each of the states for persons aged 45, 50, 55, 60 and 65 at entry, separately for the two intervention groups
We see that the curves are quite ragged; this is due to the simulation errors, it would be nicer if we simulated 1000 copies of each instead of only 100.
Digression: The previous is a lot of hard-coding, we would like to be able to easily get a plot with only a subset of the ages. To this end it is more convenient to collect the state probabilities in an array:
(ain <- seq(45, 65, 5))
## [1] 45 50 55 60 65
(alo <- levels(S4$allo))
## [1] "Int" "Conv"
pdef <- NArray(c(list(ain = ain,
allo = alo),
dimnames(P45i)))
str(pdef)
## logi [1:5, 1:2, 1:211, 1:5] NA NA NA NA NA NA ...
## - attr(*, "dimnames")=List of 4
## ..$ ain : chr [1:5] "45" "50" "55" "60" ...
## ..$ allo : chr [1:2] "Int" "Conv"
## ..$ when : chr [1:211] "0" "0.1" "0.2" "0.3" ...
## ..$ State: chr [1:5] "Norm" "Mic" "Mac" "D(CVD)" ...
But when we stick the results in an array we lose the pState class of the results: so we resort to the mat2pol function that stacks probabilities and plots them, so we simply take the result from nState and divide by the number in the initial state (Mic) using sweep:
for(aa in ain)
for(gg in alo)
pdef[paste(aa), gg, ,] <-
nState(subset(Sdef, ain == aa & allo == gg),
at = as.numeric(dimnames(pdef)[["when"]]),
from = 0,
time.scale = "tfi")
pdef <- sweep(pdef, 1:2, pdef[,,1,"Mic"], "/")
str(pdef)
## num [1:5, 1:2, 1:211, 1:5] 0 0 0 0 0 0 0 0 0 0 ...
## - attr(*, "dimnames")=List of 4
## ..$ ain : chr [1:5] "45" "50" "55" "60" ...
## ..$ allo : chr [1:2] "Int" "Conv"
## ..$ when : chr [1:211] "0" "0.1" "0.2" "0.3" ...
## ..$ State: chr [1:5] "Norm" "Mic" "Mac" "D(CVD)" ...
Then we have the state probabilities in the array pdef
ain <- seq(45, 65, 10)
par(mfrow = c(length(ain),2),
mar = c(1,2,1,2)/5,
oma = c(2,4,2,3),
mgp = c(3,1,0) / 1.6)
for(aa in ain)
{
mat2pol(pdef[paste(aa),"Int" ,,], col = clr, xlim = c(0,20),
xaxs = "i", yaxs = "i", xaxt = "n", yaxt = "n")
axis(side = 2)
axis(side = 4, labels = NA)
mat2pol(pdef[paste(aa),"Conv",,], col = clr, xlim = c(20,0),
xaxs = "i", yaxs = "i", xaxt = "n", yaxt = "n")
axis(side = 2, labels = NA)
axis(side = 4)
}
mtext(c("Int","Conv"), side = 3, at = c(1,3)/4, outer = TRUE, line = 0)
mtext(ain, side = 2, at = (length(ain):1 * 2 - 1) / (length(ain) * 2),
outer = TRUE, line = 2)
Thie figure shows Predicted probabilities of being in each of the states for persons aged 45, 55 and 65 at entry, separately for the two intervention groups
The survival package allows estimation of state probabilities by the Aalen-Johansen estimator similar to what we did in competing risks. The Aalen-Johansen estimator is the multi-state counterpart of the Kaplan-Meier estimator of survival probability in a 2-state (alive/dead) model. As mentioned under competing risks, the results will refer to a population of the same structure as the study population, and so the absolute sizes of the state probabilities will not be generalizable to other populations. The results here correspond to the results we derived using the original Steno2 population cohort in section 3.3.2.1 on page 54 ff. The estimates of state probabilities in section 3.3.2.1 are based on parametric models for the transition probabilities, where some of the transition rates depend on age and duration in the same way. The estimates from the Aalen-Johansen approach is non-parametric in the sense that the transition rates can have any shape; the down side is that they cannot depend on more than one time scale (here, sensibly time since entry) and the shape and size of them are not easily retrievable.
In Epi package there is a function, AaJ.Lexis, that does the hard work of reshaping a Lexis object for use by survfit, and calls survfit to return an object of class survfitms
AaJepi <- AaJ.Lexis(L4, timeScale = "tfi")
## NOTE: Timescale is tfi
class(AaJepi)
## [1] "survfitms" "survfit"
AaJepi
## Call: survfit(formula = form, data = Lx, id = lex.id, istate = lex.Cst)
##
## n nevent rmean*
## Norm 730 72 3.718799
## Mic 730 57 9.173179
## Mac 730 65 2.574825
## D(CVD) 730 38 2.855333
## D(oth) 730 55 3.580671
## *restricted mean time in state (max time = 21.90281 )
We find the same number of transitions in the transitions slot in the result from AaJ.Lexis as from the summary.Lexis (as we should):
AaJepi$transitions
## to
## from Norm Mic Mac D(CVD) D(oth) (censored)
## Norm 72 35 0 6 13 18
## Mic 72 276 65 14 30 36
## Mac 0 22 28 18 12 13
## D(CVD) 0 0 0 0 0 0
## D(oth) 0 0 0 0 0 0
summary(L4, simp = FALSE)
##
## Transitions:
## To
## From Norm Mic Mac D(CVD) D(oth) Records: Events: Risk time: Persons:
## Norm 90 35 0 6 13 144 54 581.04 66
## Mic 72 312 65 14 30 493 181 1435.14 160
## Mac 0 22 41 18 12 93 52 400.41 60
## D(CVD) 0 0 0 0 0 0 0 NA NA
## D(oth) 0 0 0 0 0 0 0 NA NA
## Sum 162 369 106 38 55 730 287 2416.59 160
The predicted state probabilities are in the slot called pstate, and the confidence intervals in the corresponding slots lower and upper.
names(AaJepi)
## [1] "n" "time" "n.risk" "n.event" "n.censor"
## [6] "pstate" "p0" "cumhaz" "std.err" "sp0"
## [11] "logse" "transitions" "lower" "upper" "conf.type"
## [16] "conf.int" "states" "type" "call"
AaJepi$states
## [1] "Norm" "Mic" "Mac" "D(CVD)" "D(oth)"
head(AaJepi$pstate)
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.00000 0.99375 0.00625 0 0
## [2,] 0.00625 0.98750 0.00625 0 0
## [3,] 0.00625 0.98750 0.00625 0 0
## [4,] 0.01250 0.98125 0.00625 0 0
## [5,] 0.01250 0.98125 0.00625 0 0
## [6,] 0.01250 0.98125 0.00625 0 0
head(AaJepi$lower)
## [,1] [,2] [,3] [,4] [,5]
## [1,] NA 0.9816133 0.0008858142 NA NA
## [2,] 0.0008858142 0.9704340 0.0008858142 NA NA
## [3,] 0.0008858142 0.9704340 0.0008858142 NA NA
## [4,] 0.0031535032 0.9604561 0.0008858142 NA NA
## [5,] 0.0031535032 0.9604561 0.0008858142 NA NA
## [6,] 0.0031535032 0.9604561 0.0008858142 NA NA
head(AaJepi$upper)
## [,1] [,2] [,3] [,4] [,5]
## [1,] NA 1 0.04409785 NA NA
## [2,] 0.04409785 1 0.04409785 NA NA
## [3,] 0.04409785 1 0.04409785 NA NA
## [4,] 0.04954807 1 0.04409785 NA NA
## [5,] 0.04954807 1 0.04409785 NA NA
## [6,] 0.04954807 1 0.04409785 NA NA
We can now show the Aalen-Johansen estimator of the state probabilities:
par(mfrow = c(1,1))
mat2pol(AaJepi$pstate, perm = c(1:3,5,4), x = AaJepi$time, main= "Overall state probabilities from the Aalen-Johansen model", col = clr)
lines(AaJepi$time, apply(AaJepi$pstate[,1:3], 1, sum), lwd = 5)
But as above, we are interested in seeing the results from each of the allocation groups, so we do the calculation for each:
AaJallo <- AaJ.Lexis(L4, ~ allo, timeScale = "tfi")
## NOTE: Timescale is tfi
AaJallo
## Call: survfit(formula = form, data = Lx, id = lex.id, istate = lex.Cst)
##
## n nevent rmean*
## allo=Int, Norm 384 47 4.794785
## allo=Conv, Norm 346 25 2.640688
## allo=Int, Mic 384 34 9.933409
## allo=Conv, Mic 346 23 8.401154
## allo=Int, Mac 384 23 2.073443
## allo=Conv, Mac 346 42 3.077278
## allo=Int, D(CVD) 384 12 2.024196
## allo=Conv, D(CVD) 346 26 3.691880
## allo=Int, D(oth) 384 26 3.076973
## allo=Conv, D(oth) 346 29 4.091806
## *restricted mean time in state (max time = 21.90281 )
The result in the AaJallo object is in a long vector of time and pstate, the two parts corresponding to Int and Conv put after one another, with the length of each part in strata.
AaJallo$states
## [1] "Norm" "Mic" "Mac" "D(CVD)" "D(oth)"
AaJallo$strata
## allo=Int allo=Conv
## 375 337
wh <- rep(substr(names(AaJallo$strata), 6, 9), AaJallo$strata)
table(wh)
## wh
## Conv Int
## 337 375
So we just make the plots for the two subsets and place them next to each other as before:
par(mfrow = c(1,2), mar=c(3,3,2,0), oma = c(0,0,0,3), las = 1, bty = "n")
mat2pol(AaJallo$pstate[wh=="Int",],
x = AaJallo$time[wh=="Int"],
col = clr, xlim = c(0,21), xaxs = "i", yaxs = "i")
lines(AaJallo$time[wh=="Int"],
apply(AaJallo$pstate[,1:3], 1, sum)[wh=="Int"], lwd = 4)
mat2pol(AaJallo$pstate[wh=="Conv",],
x = AaJallo$time[wh=="Conv"],
col = clr, xlim = c(21,0), xaxs = "i", yaxs = "i")
lines(AaJallo$time[wh=="Conv"],
apply(AaJallo$pstate[,1:3], 1, sum)[wh=="Conv"], lwd = 4)
axis(side = 4)
mtext(c("Int","Conv"), side = 3, at = c(1,3)/4, outer = TRUE, line = -2)
The figure shows Aalen-Johansen estimator of the state probabilities for the two intervention groups, for the original Steno2 data, subdivided by intervention allocation.
The estimator of state probabilities in figure 3.9 can be considered the empirical counterpart of figure 3.5; the state probabilities for a population as the one in the study. But then, not quite so; the models underlying figure 3.5 are proportional hazards in the sense that the effects of age and time since enrollment are proportional between state by allocation (6 groups for mortality, 4 groups for albuminuria state), whereas the figures in 3.9 are based on separate models for each transition and allocation, but only with time since enrollment as timescale (no age-effect assumed).
We have confidence intervals for each of the state probabilities in the slots lower and upper, but not for the sums of these. And it is (cumulative) sums of state probabilities we have shown in the graph. Moreover we would also want confidence intervals for areas under the curves. Neither are available from the Aalen-Johansen nor from the simulation approach. The simulation approach does not even give confidence intervals for each of the state probabilities.
Besides the state probabilities at different times after entry for groups of patients, we may also want to assess the time spent in each state, during, say, the first 15 or 20 years after entry. 40. We may want to compare groups by the expected time spent in the albuminuria states during the first, say, 20 years. The expected time in a state is simply the time-integral of the probabilities, so we could compute it from pdef; each probability represents an interval of length 0.1, so we just take the midpoint of the probabilities from pstate at the ends of each interval.
This is however a bit like bringing coal to Newcastle, we have the simulated cohort Sdef, where we have simulated sojourn times in each state; so we can just sum these and use as estimates of time spent in each state.
tLive <- xtabs(lex.dur ~ ain + allo + lex.Cst, data = Sdef) /
nid(Sdef) * 10
str(mtLive <- addmargins(tLive, 3))
## 'table' num [1:5, 1:2, 1:6] 4.69 3.56 3.05 1.94 1.3 ...
## - attr(*, "dimnames")=List of 3
## ..$ ain : chr [1:5] "45" "50" "55" "60" ...
## ..$ allo : chr [1:2] "Conv" "Int"
## ..$ lex.Cst: chr [1:6] "Norm" "Mic" "Mac" "D(CVD)" ...
round(ftable(mtLive , col.vars = c(3,2)), 1)
## lex.Cst Norm Mic Mac D(CVD) D(oth) Sum
## allo Conv Int Conv Int Conv Int Conv Int Conv Int Conv Int
## ain
## 45 4.7 7.5 9.7 9.5 3.0 1.8 0.0 0.0 0.0 0.0 17.4 18.7
## 50 3.6 5.7 9.8 10.5 3.4 2.3 0.0 0.0 0.0 0.0 16.8 18.4
## 55 3.0 4.1 8.5 10.9 3.2 2.5 0.0 0.0 0.0 0.0 14.8 17.6
## 60 1.9 3.2 7.4 9.2 2.7 2.6 0.0 0.0 0.0 0.0 12.0 15.0
## 65 1.3 2.4 5.8 8.0 2.3 2.0 0.0 0.0 0.0 0.0 9.4 12.4
round(ftable(mtLive[,,-(4:5)], col.vars = c(3,2)), 1)
## lex.Cst Norm Mic Mac Sum
## allo Conv Int Conv Int Conv Int Conv Int
## ain
## 45 4.7 7.5 9.7 9.5 3.0 1.8 17.4 18.7
## 50 3.6 5.7 9.8 10.5 3.4 2.3 16.8 18.4
## 55 3.0 4.1 8.5 10.9 3.2 2.5 14.8 17.6
## 60 1.9 3.2 7.4 9.2 2.7 2.6 12.0 15.0
## 65 1.3 2.4 5.8 8.0 2.3 2.0 9.4 12.4
With the results in an array we can also easily show the difference between the intervention and the control arms of the trial:
round((mtLive[,"Int",-(4:5)] - mtLive[,"Conv",-(4:5)]), 1)
## lex.Cst
## ain Norm Mic Mac Sum
## 45 2.8 -0.2 -1.2 1.3
## 50 2.2 0.6 -1.1 1.7
## 55 1.1 2.4 -0.7 2.8
## 60 1.3 1.8 -0.1 3.0
## 65 1.1 2.2 -0.3 3.0
We might also want to know the lifetime lost (during the first 20 years) to each of the two causes of death. This timespan is not directly available in Sdef, it is the time from death till 20 years (the time-frame we have chosen). For persons who die, the time of death is tfi + lex.dur from the record with lex.Xst ∈ (D(oth), D(CVD), so quite easily evaluated with xtabs too:
tDead <- xtabs((20 - tfi - lex.dur) ~ ain + allo + lex.Xst,
data = subset(Sdef, lex.Xst %in% c("D(oth)", "D(CVD)"))) /
nid(Sdef) * 10
str(mtDead <- addmargins(tDead[,,4:5], 3))
## 'table' num [1:5, 1:2, 1:3] 0.439 1.767 3.069 4.327 5.738 ...
## - attr(*, "dimnames")=List of 3
## ..$ ain : chr [1:5] "45" "50" "55" "60" ...
## ..$ allo : chr [1:2] "Conv" "Int"
## ..$ lex.Xst: chr [1:3] "D(CVD)" "D(oth)" "Sum"
round(ftable(mtDead , col.vars = c(3,2)), 1)
## lex.Xst D(CVD) D(oth) Sum
## allo Conv Int Conv Int Conv Int
## ain
## 45 0.4 0.3 2.8 1.8 3.3 2.1
## 50 1.8 0.8 2.0 1.4 3.8 2.3
## 55 3.1 1.2 2.5 1.8 5.6 3.0
## 60 4.3 2.1 3.8 3.3 8.1 5.4
## 65 5.7 2.8 4.9 5.0 10.6 7.8
round(ftable(mtDead[,,-(4:5)], col.vars = c(3,2)), 1)
## lex.Xst D(CVD) D(oth) Sum
## allo Conv Int Conv Int Conv Int
## ain
## 45 0.4 0.3 2.8 1.8 3.3 2.1
## 50 1.8 0.8 2.0 1.4 3.8 2.3
## 55 3.1 1.2 2.5 1.8 5.6 3.0
## 60 4.3 2.1 3.8 3.3 8.1 5.4
## 65 5.7 2.8 4.9 5.0 10.6 7.8
The difference between these numbers between intervention and control groups are the years gained from the intervention, subdivided by cause of death:
round((mtDead[,"Int",] - mtDead[,"Conv",]), 1)
## lex.Xst
## ain D(CVD) D(oth) Sum
## 45 -0.2 -1.0 -1.2
## 50 -0.9 -0.6 -1.5
## 55 -1.8 -0.7 -2.5
## 60 -2.3 -0.5 -2.8
## 65 -3.0 0.1 -2.9
Draw a conclusion from these numbers.
So far we have only considered covariates that we know the value of at any time point, including future time points, that is the allocation status and timescales such as age and time since inclusion.
In the dataset st2clin are clinical measurements taken at different dates, up to six different occasions per person:
data(st2clin)
str(st2clin)
## 'data.frame': 750 obs. of 5 variables:
## $ id : num 1 2 3 4 5 6 7 8 9 10 ...
## $ doV : Date, format: "1993-05-07" "1993-05-05" ...
## $ a1c : num 87.3 66.5 73 61.2 102.7 ...
## $ chol: num 3.9 6.6 5.6 5.2 6 4.8 8.6 5.1 4.2 5.4 ...
## $ crea: num 83 83 68 97 149 55 56 78 123 79 ...
st2clin <- cal.yr(st2clin)
names(st2clin)
## [1] "id" "doV" "a1c" "chol" "crea"
names(st2clin)[1:2] <- c("lex.id","per")
summary(st2clin)
## lex.id per a1c chol
## Min. : 1.00 Min. :1993 Min. : 32.80 Min. : 2.200
## 1st Qu.: 39.00 1st Qu.:1995 1st Qu.: 54.80 1st Qu.: 4.000
## Median : 84.50 Median :1997 Median : 66.35 Median : 4.800
## Mean : 85.81 Mean :2000 Mean : 68.22 Mean : 4.941
## 3rd Qu.:131.00 3rd Qu.:2002 3rd Qu.: 79.38 3rd Qu.: 5.700
## Max. :176.00 Max. :2015 Max. :147.60 Max. :14.000
## NA's :4 NA's :3
## crea
## Min. : 28.00
## 1st Qu.: 67.00
## Median : 88.00
## Mean : 99.16
## 3rd Qu.: 115.25
## Max. :1067.00
## NA's :2
addmargins(table(table(st2clin$lex.id)))
##
## 1 2 3 4 5 6 Sum
## 2 6 23 38 31 60 160
Explain the contents of the table.
We can use addCov.Lexis to amend the follow-up data with the clinical measurements:
S5 <- addCov.Lexis(S4, st2clin, "per")
tt <- table(st2clin$lex.id)
(who <- names(tt[tt == 3])[1])
## [1] "5"
subset(st2clin, lex.id == who)
## lex.id per a1c chol crea
## 5 5 1993.151 102.7 6.0 149
## 165 5 1995.511 54.7 8.8 140
## 321 5 1997.496 41.9 5.8 141
subset(S5,
lex.id == who,
select = c(lex.id,per,tfi,tfc,exnam,a1c,chol,crea))
## lex.id per tfi tfc exnam a1c chol crea
## 5 1993.22 0.00 0.07 ex1 102.7 6.0 149
## 5 1993.31 0.08 0.15 ex1 102.7 6.0 149
## 5 1993.39 0.17 0.24 ex1 102.7 6.0 149
## 5 1993.47 0.25 0.32 ex1 102.7 6.0 149
## 5 1993.56 0.33 0.40 ex1 102.7 6.0 149
## 5 1993.64 0.42 0.49 ex1 102.7 6.0 149
## 5 1993.72 0.50 0.57 ex1 102.7 6.0 149
## 5 1993.77 0.55 0.62 ex1 102.7 6.0 149
## 5 1993.81 0.58 0.65 ex1 102.7 6.0 149
## 5 1993.89 0.67 0.74 ex1 102.7 6.0 149
## 5 1993.97 0.75 0.82 ex1 102.7 6.0 149
## 5 1994.06 0.83 0.90 ex1 102.7 6.0 149
## 5 1994.14 0.92 0.99 ex1 102.7 6.0 149
## 5 1994.22 1.00 1.07 ex1 102.7 6.0 149
## 5 1994.31 1.08 1.15 ex1 102.7 6.0 149
## 5 1994.39 1.17 1.24 ex1 102.7 6.0 149
## 5 1994.47 1.25 1.32 ex1 102.7 6.0 149
## 5 1994.56 1.33 1.40 ex1 102.7 6.0 149
## 5 1994.64 1.42 1.49 ex1 102.7 6.0 149
## 5 1994.72 1.50 1.57 ex1 102.7 6.0 149
## 5 1994.81 1.58 1.65 ex1 102.7 6.0 149
## 5 1994.89 1.67 1.74 ex1 102.7 6.0 149
## 5 1994.97 1.75 1.82 ex1 102.7 6.0 149
## 5 1995.06 1.83 1.90 ex1 102.7 6.0 149
## 5 1995.14 1.92 1.99 ex1 102.7 6.0 149
## 5 1995.22 2.00 2.07 ex1 102.7 6.0 149
## 5 1995.31 2.08 2.15 ex1 102.7 6.0 149
## 5 1995.39 2.17 2.24 ex1 102.7 6.0 149
## 5 1995.47 2.25 2.32 ex1 102.7 6.0 149
## 5 1995.51 2.29 0.00 ex2 54.7 8.8 140
## 5 1995.56 2.33 0.04 ex2 54.7 8.8 140
## 5 1995.64 2.42 0.13 ex2 54.7 8.8 140
## 5 1995.72 2.50 0.21 ex2 54.7 8.8 140
## 5 1995.81 2.58 0.29 ex2 54.7 8.8 140
## 5 1995.89 2.67 0.38 ex2 54.7 8.8 140
## 5 1995.97 2.75 0.46 ex2 54.7 8.8 140
## 5 1996.06 2.83 0.54 ex2 54.7 8.8 140
## 5 1996.14 2.92 0.63 ex2 54.7 8.8 140
## 5 1996.22 3.00 0.71 ex2 54.7 8.8 140
## 5 1996.31 3.08 0.79 ex2 54.7 8.8 140
## 5 1996.39 3.17 0.88 ex2 54.7 8.8 140
## 5 1996.47 3.25 0.96 ex2 54.7 8.8 140
## 5 1996.56 3.33 1.04 ex2 54.7 8.8 140
## 5 1996.64 3.42 1.13 ex2 54.7 8.8 140
## 5 1996.72 3.50 1.21 ex2 54.7 8.8 140
## 5 1996.81 3.58 1.29 ex2 54.7 8.8 140
## 5 1996.89 3.67 1.38 ex2 54.7 8.8 140
## 5 1996.97 3.75 1.46 ex2 54.7 8.8 140
## 5 1997.06 3.83 1.54 ex2 54.7 8.8 140
## 5 1997.07 3.85 1.56 ex2 54.7 8.8 140
## 5 1997.14 3.92 1.63 ex2 54.7 8.8 140
## 5 1997.22 4.00 1.71 ex2 54.7 8.8 140
## 5 1997.31 4.08 1.79 ex2 54.7 8.8 140
## 5 1997.39 4.17 1.88 ex2 54.7 8.8 140
## 5 1997.47 4.25 1.96 ex2 54.7 8.8 140
## 5 1997.50 4.27 0.00 ex3 41.9 5.8 141
## 5 1997.56 4.33 0.06 ex3 41.9 5.8 141
## 5 1997.64 4.42 0.14 ex3 41.9 5.8 141
## 5 1997.72 4.50 0.23 ex3 41.9 5.8 141
## 5 1997.81 4.58 0.31 ex3 41.9 5.8 141
## 5 1997.89 4.67 0.39 ex3 41.9 5.8 141
## 5 1997.97 4.75 0.48 ex3 41.9 5.8 141
timeScales(S5)
## [1] "per" "age" "tfi" "tfc"
timeSince(S5)
## per age tfi tfc
## "" "" "" "X"
We see that tfc is included as a time scale, but it is a not a proper time scale; it is reset to 0 at every clinical visit, and it also has some missing values, as do the clinical variables. The missing values are where there is follow-up before the earliest clinical measurement for a person. But tfc needs to be a time scale in the Lexis object in order to be properly handled when subsequently cutting and splitting a Lexis object.
The values of the clinical measurements in st2clin are added to the follow-up data: extra cut points at the measurement dates are added, and the values of the clinical variables are propagated as LOCF (Last Observation Carried Forward), so it is possible to model the effect of these clinical variables on transition rates—creatinine is traditionally modeled on a log-scale, here we use the base 2 logarithm.
detc <- glm.Lexis(S5, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo +
a1c + chol + log2(crea),
from = c("Norm","Mic"),
to = c("Mic","Mac"))
## stats::glm Poisson analysis of Lexis object S5 with log link:
## Rates for transitions:
## Norm->Mic
## Mic->Mac
impc <- glm.Lexis(S5, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo +
a1c + chol + log2(crea),
to = c("Norm","Mic"),
from = c("Mic","Mac"))
## stats::glm Poisson analysis of Lexis object S5 with log link:
## Rates for transitions:
## Mic->Norm
## Mac->Mic
round(ci.exp(detc), 3)
## exp(Est.) 2.5% 97.5%
## (Intercept) 0.080 0.005 1.337
## Ns(tfi, knots = seq(0, 20, 5))1 0.714 0.271 1.880
## Ns(tfi, knots = seq(0, 20, 5))2 0.300 0.084 1.067
## Ns(tfi, knots = seq(0, 20, 5))3 0.297 0.045 1.967
## Ns(tfi, knots = seq(0, 20, 5))4 0.226 0.066 0.776
## Ns(age, knots = seq(50, 80, 10))1 2.032 0.862 4.789
## Ns(age, knots = seq(50, 80, 10))2 4.453 1.131 17.535
## Ns(age, knots = seq(50, 80, 10))3 3.387 0.948 12.106
## lex.CstMic 0.390 0.220 0.692
## a1c 1.005 0.993 1.018
## chol 1.093 0.912 1.311
## log2(crea) 0.864 0.582 1.281
## lex.CstNorm:alloConv 0.433 0.193 0.975
## lex.CstMic:alloConv 1.698 0.974 2.959
We do not really need to see the first 8 parameter estimates so we omit them:
round(ci.exp(detc, subset = -(1:8), pval = T), 3)
## exp(Est.) 2.5% 97.5% P
## lex.CstMic 0.390 0.220 0.692 0.001
## a1c 1.005 0.993 1.018 0.398
## chol 1.093 0.912 1.311 0.336
## log2(crea) 0.864 0.582 1.281 0.466
## lex.CstNorm:alloConv 0.433 0.193 0.975 0.043
## lex.CstMic:alloConv 1.698 0.974 2.959 0.062
round(ci.exp(impc, subset = -(1:8), pval = T), 3)
## exp(Est.) 2.5% 97.5% P
## lex.CstMac 1.053 0.465 2.382 0.902
## a1c 0.990 0.978 1.003 0.129
## chol 0.964 0.804 1.157 0.697
## log2(crea) 0.869 0.578 1.308 0.501
## lex.CstMic:alloConv 0.598 0.359 0.996 0.048
## lex.CstMac:alloConv 1.525 0.611 3.804 0.366
We also look at the effect of the clinical variables on the mortality rates:
moc <- glm.Lexis(S5, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo +
a1c + chol + log2(crea),
to = "D(oth)")
## stats::glm Poisson analysis of Lexis object S5 with log link:
## Rates for transitions:
## Norm->D(oth)
## Mic->D(oth)
## Mac->D(oth)
mCc <- glm.Lexis(S5, ~ Ns(tfi, knots = seq( 0, 20, 5)) +
Ns(age, knots = seq(50, 80, 10)) +
lex.Cst / allo +
a1c + chol + log2(crea),
to = "D(CVD)")
## stats::glm Poisson analysis of Lexis object S5 with log link:
## Rates for transitions:
## Norm->D(CVD)
## Mic->D(CVD)
## Mac->D(CVD)
round(ci.exp(moc, subset = -(1:8), pval = T), 3)
## exp(Est.) 2.5% 97.5% P
## lex.CstMic 0.972 0.361 2.615 0.955
## lex.CstMac 1.304 0.411 4.135 0.652
## a1c 1.005 0.987 1.024 0.582
## chol 0.839 0.630 1.117 0.229
## log2(crea) 1.860 1.145 3.019 0.012
## lex.CstNorm:alloConv 1.900 0.606 5.962 0.271
## lex.CstMic:alloConv 1.944 0.878 4.306 0.101
## lex.CstMac:alloConv 0.777 0.235 2.575 0.680
round(ci.exp(mCc, subset = -(1:8), pval = T), 3)
## exp(Est.) 2.5% 97.5% P
## lex.CstMic 0.806 0.199 3.274 0.763
## lex.CstMac 1.144 0.221 5.916 0.873
## a1c 0.999 0.980 1.019 0.951
## chol 1.002 0.734 1.367 0.990
## log2(crea) 1.351 0.757 2.409 0.309
## lex.CstNorm:alloConv 1.397 0.272 7.177 0.689
## lex.CstMic:alloConv 1.680 0.552 5.113 0.361
## lex.CstMac:alloConv 5.067 1.387 18.513 0.014
Only crea has any effect; a doubling of creatinine is associated with a 1.86 times higher mortality rate from other (non-CVD) causes. Confidence interval is (1.14,3.02), so not terribly precisely determined.
There are limitations in using clinical measurements as time-dependent variables without a model for the clinical variables. In order to simulate events based on models for transition rates we must know all covariates at all (future) times, so models with non-deterministicly varying are not usable. Timescales are time-varying covariate, but they vary deterministicly, so their value for each person will be known at any time of future follow-up.
So the models with effects of clinical variables as presented here cannot be used for prediction of state probabilities—that would require a model for the change of the clinical variables over time as well.
So far, we have only jointly modeled transitions that originated in different states, for example:
Mic → Mac and Norm → Mic; Norm → D(CVD), Mic → D(CVD) and Mac → D(CVD).
As long as the different rates modeled are originating in different states, the likelihood will have at most one contribution from each record in the Lexis follow-up data set.
But if we want to create a joint model for more than one rate originating in a given state we must repeat some of risk time in different contributions to the likelihood. This means that the modeling cannot be based on (subsets of) a Lexis object, we must repeat some records. This is detailed in section on Competing Risks in the chapter on multistate likelihood in the PMM (Practical Multistate Modeling, http://bendixcarstensen.com/MSbook.pdf).
This behaviour can be achieved by the stack.Lexis function:
St4 <- stack(S4)
c(nrow(S4), nrow(St4))
## [1] 29645 106569
table(S4$lex.Cst)
##
## Norm Mic Mac D(CVD) D(oth)
## 7115 17634 4896 0 0
table(St4$lex.Tr, St4$lex.Cst)
##
## Norm Mic Mac D(CVD) D(oth)
## Mac->D(CVD) 0 0 4896 0 0
## Mac->D(oth) 0 0 4896 0 0
## Mac->Mic 0 0 4896 0 0
## Mic->D(CVD) 0 17634 0 0 0
## Mic->D(oth) 0 17634 0 0 0
## Mic->Mac 0 17634 0 0 0
## Mic->Norm 0 17634 0 0 0
## Norm->D(CVD) 7115 0 0 0 0
## Norm->D(oth) 7115 0 0 0 0
## Norm->Mic 7115 0 0 0 0
ftable(St4$lex.Tr, St4$lex.Xst, St4$lex.Fail, col.vars = 2:3)
## Norm Mic Mac D(CVD) D(oth)
## FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE
##
## Mac->D(CVD) 0 0 22 0 4844 0 0 18 12 0
## Mac->D(oth) 0 0 22 0 4844 0 18 0 0 12
## Mac->Mic 0 0 0 22 4844 0 18 0 12 0
## Mic->D(CVD) 72 0 17453 0 65 0 0 14 30 0
## Mic->D(oth) 72 0 17453 0 65 0 14 0 0 30
## Mic->Mac 72 0 17453 0 0 65 14 0 30 0
## Mic->Norm 0 72 17453 0 65 0 14 0 30 0
## Norm->D(CVD) 7061 0 35 0 0 0 0 6 13 0
## Norm->D(oth) 7061 0 35 0 0 0 6 0 0 13
## Norm->Mic 7061 0 0 35 0 0 6 0 13 0
We see that the lex.Fail is only TRUE where lex.Xst is equal to the second part if the lex.Tr.
The two ways of representing the data for person 102 are quite different:
subset(S4 , lex.id == 102)[,1:8]
## lex.id per age tfi lex.dur lex.Cst lex.Xst id
## 102 1993.54 58.27 0.00 0.08 Mic Mic 102
## 102 1993.62 58.36 0.08 0.08 Mic Mic 102
## 102 1993.71 58.44 0.17 0.08 Mic Mic 102
## 102 1993.79 58.52 0.25 0.08 Mic Mic 102
## 102 1993.87 58.61 0.33 0.08 Mic Mic 102
## 102 1993.96 58.69 0.42 0.08 Mic Mic 102
## 102 1994.04 58.77 0.50 0.08 Mic Mic 102
## 102 1994.12 58.86 0.58 0.08 Mic Mic 102
## 102 1994.21 58.94 0.67 0.08 Mic Mic 102
## 102 1994.29 59.02 0.75 0.08 Mic Mic 102
## 102 1994.37 59.11 0.83 0.08 Mic Mic 102
## 102 1994.46 59.19 0.92 0.08 Mic Mic 102
## 102 1994.54 59.27 1.00 0.08 Mic Mic 102
## 102 1994.62 59.36 1.08 0.08 Mic Mic 102
## 102 1994.71 59.44 1.17 0.08 Mic Mic 102
## 102 1994.79 59.52 1.25 0.08 Mic Mic 102
## 102 1994.87 59.61 1.33 0.08 Mic Mic 102
## 102 1994.96 59.69 1.42 0.08 Mic Mic 102
## 102 1995.04 59.77 1.50 0.08 Mic Mic 102
## 102 1995.12 59.86 1.58 0.08 Mic Mic 102
## 102 1995.21 59.94 1.67 0.03 Mic D(CVD) 102
subset(St4, lex.id == 102)[,1:9]
## lex.id per age tfi lex.dur lex.Cst lex.Xst
## 18070 102 1993.540 58.27242 0.00000000 0.08333333 Mic Mic
## 18071 102 1993.623 58.35575 0.08333333 0.08333333 Mic Mic
## 18072 102 1993.707 58.43908 0.16666667 0.08333333 Mic Mic
## 18073 102 1993.790 58.52242 0.25000000 0.08333333 Mic Mic
## 18074 102 1993.873 58.60575 0.33333333 0.08333333 Mic Mic
## 18075 102 1993.957 58.68908 0.41666667 0.08333333 Mic Mic
## 18076 102 1994.040 58.77242 0.50000000 0.08333333 Mic Mic
## 18077 102 1994.123 58.85575 0.58333333 0.08333333 Mic Mic
## 18078 102 1994.207 58.93908 0.66666667 0.08333333 Mic Mic
## 18079 102 1994.290 59.02242 0.75000000 0.08333333 Mic Mic
## 18080 102 1994.373 59.10575 0.83333333 0.08333333 Mic Mic
## 18081 102 1994.457 59.18908 0.91666667 0.08333333 Mic Mic
## 18082 102 1994.540 59.27242 1.00000000 0.08333333 Mic Mic
## 18083 102 1994.623 59.35575 1.08333333 0.08333333 Mic Mic
## 18084 102 1994.707 59.43908 1.16666667 0.08333333 Mic Mic
## 18085 102 1994.790 59.52242 1.25000000 0.08333333 Mic Mic
## 18086 102 1994.873 59.60575 1.33333333 0.08333333 Mic Mic
## 18087 102 1994.957 59.68908 1.41666667 0.08333333 Mic Mic
## 18088 102 1995.040 59.77242 1.50000000 0.08333333 Mic Mic
## 18089 102 1995.123 59.85575 1.58333333 0.08333333 Mic Mic
## 18090 102 1995.207 59.93908 1.66666667 0.03353867 Mic D(CVD)
## 180701 102 1993.540 58.27242 0.00000000 0.08333333 Mic Mic
## 180711 102 1993.623 58.35575 0.08333333 0.08333333 Mic Mic
## 180721 102 1993.707 58.43908 0.16666667 0.08333333 Mic Mic
## 180731 102 1993.790 58.52242 0.25000000 0.08333333 Mic Mic
## 180741 102 1993.873 58.60575 0.33333333 0.08333333 Mic Mic
## 180751 102 1993.957 58.68908 0.41666667 0.08333333 Mic Mic
## 180761 102 1994.040 58.77242 0.50000000 0.08333333 Mic Mic
## 180771 102 1994.123 58.85575 0.58333333 0.08333333 Mic Mic
## 180781 102 1994.207 58.93908 0.66666667 0.08333333 Mic Mic
## 180791 102 1994.290 59.02242 0.75000000 0.08333333 Mic Mic
## 180801 102 1994.373 59.10575 0.83333333 0.08333333 Mic Mic
## 180811 102 1994.457 59.18908 0.91666667 0.08333333 Mic Mic
## 180821 102 1994.540 59.27242 1.00000000 0.08333333 Mic Mic
## 180831 102 1994.623 59.35575 1.08333333 0.08333333 Mic Mic
## 180841 102 1994.707 59.43908 1.16666667 0.08333333 Mic Mic
## 180851 102 1994.790 59.52242 1.25000000 0.08333333 Mic Mic
## 180861 102 1994.873 59.60575 1.33333333 0.08333333 Mic Mic
## 180871 102 1994.957 59.68908 1.41666667 0.08333333 Mic Mic
## 180881 102 1995.040 59.77242 1.50000000 0.08333333 Mic Mic
## 180891 102 1995.123 59.85575 1.58333333 0.08333333 Mic Mic
## 180901 102 1995.207 59.93908 1.66666667 0.03353867 Mic D(CVD)
## 180702 102 1993.540 58.27242 0.00000000 0.08333333 Mic Mic
## 180712 102 1993.623 58.35575 0.08333333 0.08333333 Mic Mic
## 180722 102 1993.707 58.43908 0.16666667 0.08333333 Mic Mic
## 180732 102 1993.790 58.52242 0.25000000 0.08333333 Mic Mic
## 180742 102 1993.873 58.60575 0.33333333 0.08333333 Mic Mic
## 180752 102 1993.957 58.68908 0.41666667 0.08333333 Mic Mic
## 180762 102 1994.040 58.77242 0.50000000 0.08333333 Mic Mic
## 180772 102 1994.123 58.85575 0.58333333 0.08333333 Mic Mic
## 180782 102 1994.207 58.93908 0.66666667 0.08333333 Mic Mic
## 180792 102 1994.290 59.02242 0.75000000 0.08333333 Mic Mic
## 180802 102 1994.373 59.10575 0.83333333 0.08333333 Mic Mic
## 180812 102 1994.457 59.18908 0.91666667 0.08333333 Mic Mic
## 180822 102 1994.540 59.27242 1.00000000 0.08333333 Mic Mic
## 180832 102 1994.623 59.35575 1.08333333 0.08333333 Mic Mic
## 180842 102 1994.707 59.43908 1.16666667 0.08333333 Mic Mic
## 180852 102 1994.790 59.52242 1.25000000 0.08333333 Mic Mic
## 180862 102 1994.873 59.60575 1.33333333 0.08333333 Mic Mic
## 180872 102 1994.957 59.68908 1.41666667 0.08333333 Mic Mic
## 180882 102 1995.040 59.77242 1.50000000 0.08333333 Mic Mic
## 180892 102 1995.123 59.85575 1.58333333 0.08333333 Mic Mic
## 180902 102 1995.207 59.93908 1.66666667 0.03353867 Mic D(CVD)
## 180703 102 1993.540 58.27242 0.00000000 0.08333333 Mic Mic
## 180713 102 1993.623 58.35575 0.08333333 0.08333333 Mic Mic
## 180723 102 1993.707 58.43908 0.16666667 0.08333333 Mic Mic
## 180733 102 1993.790 58.52242 0.25000000 0.08333333 Mic Mic
## 180743 102 1993.873 58.60575 0.33333333 0.08333333 Mic Mic
## 180753 102 1993.957 58.68908 0.41666667 0.08333333 Mic Mic
## 180763 102 1994.040 58.77242 0.50000000 0.08333333 Mic Mic
## 180773 102 1994.123 58.85575 0.58333333 0.08333333 Mic Mic
## 180783 102 1994.207 58.93908 0.66666667 0.08333333 Mic Mic
## 180793 102 1994.290 59.02242 0.75000000 0.08333333 Mic Mic
## 180803 102 1994.373 59.10575 0.83333333 0.08333333 Mic Mic
## 180813 102 1994.457 59.18908 0.91666667 0.08333333 Mic Mic
## 180823 102 1994.540 59.27242 1.00000000 0.08333333 Mic Mic
## 180833 102 1994.623 59.35575 1.08333333 0.08333333 Mic Mic
## 180843 102 1994.707 59.43908 1.16666667 0.08333333 Mic Mic
## 180853 102 1994.790 59.52242 1.25000000 0.08333333 Mic Mic
## 180863 102 1994.873 59.60575 1.33333333 0.08333333 Mic Mic
## 180873 102 1994.957 59.68908 1.41666667 0.08333333 Mic Mic
## 180883 102 1995.040 59.77242 1.50000000 0.08333333 Mic Mic
## 180893 102 1995.123 59.85575 1.58333333 0.08333333 Mic Mic
## 180903 102 1995.207 59.93908 1.66666667 0.03353867 Mic D(CVD)
## lex.Tr lex.Fail
## 18070 Mic->Norm FALSE
## 18071 Mic->Norm FALSE
## 18072 Mic->Norm FALSE
## 18073 Mic->Norm FALSE
## 18074 Mic->Norm FALSE
## 18075 Mic->Norm FALSE
## 18076 Mic->Norm FALSE
## 18077 Mic->Norm FALSE
## 18078 Mic->Norm FALSE
## 18079 Mic->Norm FALSE
## 18080 Mic->Norm FALSE
## 18081 Mic->Norm FALSE
## 18082 Mic->Norm FALSE
## 18083 Mic->Norm FALSE
## 18084 Mic->Norm FALSE
## 18085 Mic->Norm FALSE
## 18086 Mic->Norm FALSE
## 18087 Mic->Norm FALSE
## 18088 Mic->Norm FALSE
## 18089 Mic->Norm FALSE
## 18090 Mic->Norm FALSE
## 180701 Mic->Mac FALSE
## 180711 Mic->Mac FALSE
## 180721 Mic->Mac FALSE
## 180731 Mic->Mac FALSE
## 180741 Mic->Mac FALSE
## 180751 Mic->Mac FALSE
## 180761 Mic->Mac FALSE
## 180771 Mic->Mac FALSE
## 180781 Mic->Mac FALSE
## 180791 Mic->Mac FALSE
## 180801 Mic->Mac FALSE
## 180811 Mic->Mac FALSE
## 180821 Mic->Mac FALSE
## 180831 Mic->Mac FALSE
## 180841 Mic->Mac FALSE
## 180851 Mic->Mac FALSE
## 180861 Mic->Mac FALSE
## 180871 Mic->Mac FALSE
## 180881 Mic->Mac FALSE
## 180891 Mic->Mac FALSE
## 180901 Mic->Mac FALSE
## 180702 Mic->D(CVD) FALSE
## 180712 Mic->D(CVD) FALSE
## 180722 Mic->D(CVD) FALSE
## 180732 Mic->D(CVD) FALSE
## 180742 Mic->D(CVD) FALSE
## 180752 Mic->D(CVD) FALSE
## 180762 Mic->D(CVD) FALSE
## 180772 Mic->D(CVD) FALSE
## 180782 Mic->D(CVD) FALSE
## 180792 Mic->D(CVD) FALSE
## 180802 Mic->D(CVD) FALSE
## 180812 Mic->D(CVD) FALSE
## 180822 Mic->D(CVD) FALSE
## 180832 Mic->D(CVD) FALSE
## 180842 Mic->D(CVD) FALSE
## 180852 Mic->D(CVD) FALSE
## 180862 Mic->D(CVD) FALSE
## 180872 Mic->D(CVD) FALSE
## 180882 Mic->D(CVD) FALSE
## 180892 Mic->D(CVD) FALSE
## 180902 Mic->D(CVD) TRUE
## 180703 Mic->D(oth) FALSE
## 180713 Mic->D(oth) FALSE
## 180723 Mic->D(oth) FALSE
## 180733 Mic->D(oth) FALSE
## 180743 Mic->D(oth) FALSE
## 180753 Mic->D(oth) FALSE
## 180763 Mic->D(oth) FALSE
## 180773 Mic->D(oth) FALSE
## 180783 Mic->D(oth) FALSE
## 180793 Mic->D(oth) FALSE
## 180803 Mic->D(oth) FALSE
## 180813 Mic->D(oth) FALSE
## 180823 Mic->D(oth) FALSE
## 180833 Mic->D(oth) FALSE
## 180843 Mic->D(oth) FALSE
## 180853 Mic->D(oth) FALSE
## 180863 Mic->D(oth) FALSE
## 180873 Mic->D(oth) FALSE
## 180883 Mic->D(oth) FALSE
## 180893 Mic->D(oth) FALSE
## 180903 Mic->D(oth) FALSE
Suppose we wanted to fit a model for the two types of mortality assuming that, say, the effect of sex was the same. Since some of the transitions we put in the same model originate from the same state we need the stacked data representation where each record corresponds to a likelihood term.
cbind(with(subset(St4, grepl("D", lex.Tr)), table(lex.Tr)))
## [,1]
## Mac->D(CVD) 4896
## Mac->D(oth) 4896
## Mac->Mic 0
## Mic->D(CVD) 17634
## Mic->D(oth) 17634
## Mic->Mac 0
## Mic->Norm 0
## Norm->D(CVD) 7115
## Norm->D(oth) 7115
## Norm->Mic 0
We can then fit a model with common effect of sex for the two types mortality:
stD <- glm(cbind(lex.Fail, lex.dur)
~ Ns(tfi, knots = seq( 0, 20, 5)) * lex.Tr +
Ns(age, knots = seq(50, 80, 10)) * lex.Tr +
lex.Tr / allo + sex,
family = poisreg,
data = subset(St4, grepl("D", lex.Tr)))
## Warning: glm.fit: fitted rates numerically 0 occurred
round(ci.exp(stD)[,1,drop=F],3)
## exp(Est.)
## (Intercept) 0.000000e+00
## Ns(tfi, knots = seq(0, 20, 5))1 2.069800e+01
## Ns(tfi, knots = seq(0, 20, 5))2 2.756800e+01
## Ns(tfi, knots = seq(0, 20, 5))3 7.291900e+01
## Ns(tfi, knots = seq(0, 20, 5))4 5.860000e-01
## lex.TrMac->D(oth) 0.000000e+00
## lex.TrMic->D(CVD) 7.322000e+00
## lex.TrMic->D(oth) 9.000000e-03
## lex.TrNorm->D(CVD) 0.000000e+00
## lex.TrNorm->D(oth) 1.350600e+01
## Ns(age, knots = seq(50, 80, 10))1 7.378000e+00
## Ns(age, knots = seq(50, 80, 10))2 2.669160e+02
## Ns(age, knots = seq(50, 80, 10))3 6.845600e+01
## sexM 1.440000e+00
## Ns(tfi, knots = seq(0, 20, 5))1:lex.TrMac->D(oth) 5.334862e+72
## Ns(tfi, knots = seq(0, 20, 5))2:lex.TrMac->D(oth) 3.913890e+51
## Ns(tfi, knots = seq(0, 20, 5))3:lex.TrMac->D(oth) 1.417514e+141
## Ns(tfi, knots = seq(0, 20, 5))4:lex.TrMac->D(oth) 2.902424e+30
## Ns(tfi, knots = seq(0, 20, 5))1:lex.TrMic->D(CVD) 5.000000e-03
## Ns(tfi, knots = seq(0, 20, 5))2:lex.TrMic->D(CVD) 7.600000e-02
## Ns(tfi, knots = seq(0, 20, 5))3:lex.TrMic->D(CVD) 3.100000e-02
## Ns(tfi, knots = seq(0, 20, 5))4:lex.TrMic->D(CVD) 1.690000e-01
## Ns(tfi, knots = seq(0, 20, 5))1:lex.TrMic->D(oth) 2.015738e+03
## Ns(tfi, knots = seq(0, 20, 5))2:lex.TrMic->D(oth) 8.679800e+01
## Ns(tfi, knots = seq(0, 20, 5))3:lex.TrMic->D(oth) 3.163383e+07
## Ns(tfi, knots = seq(0, 20, 5))4:lex.TrMic->D(oth) 3.372500e+01
## Ns(tfi, knots = seq(0, 20, 5))1:lex.TrNorm->D(CVD) 4.469074e+04
## Ns(tfi, knots = seq(0, 20, 5))2:lex.TrNorm->D(CVD) 5.744664e+04
## Ns(tfi, knots = seq(0, 20, 5))3:lex.TrNorm->D(CVD) 1.297133e+11
## Ns(tfi, knots = seq(0, 20, 5))4:lex.TrNorm->D(CVD) 1.000000e-03
## Ns(tfi, knots = seq(0, 20, 5))1:lex.TrNorm->D(oth) 2.480000e-01
## Ns(tfi, knots = seq(0, 20, 5))2:lex.TrNorm->D(oth) 1.960000e-01
## Ns(tfi, knots = seq(0, 20, 5))3:lex.TrNorm->D(oth) 1.109700e+01
## Ns(tfi, knots = seq(0, 20, 5))4:lex.TrNorm->D(oth) 2.950000e-01
## lex.TrMac->D(oth):Ns(age, knots = seq(50, 80, 10))1 2.060000e-01
## lex.TrMic->D(CVD):Ns(age, knots = seq(50, 80, 10))1 2.002000e+00
## lex.TrMic->D(oth):Ns(age, knots = seq(50, 80, 10))1 2.490000e-01
## lex.TrNorm->D(CVD):Ns(age, knots = seq(50, 80, 10))1 1.990000e+00
## lex.TrNorm->D(oth):Ns(age, knots = seq(50, 80, 10))1 1.107000e+00
## lex.TrMac->D(oth):Ns(age, knots = seq(50, 80, 10))2 1.100000e-02
## lex.TrMic->D(CVD):Ns(age, knots = seq(50, 80, 10))2 6.593000e+00
## lex.TrMic->D(oth):Ns(age, knots = seq(50, 80, 10))2 5.000000e-03
## lex.TrNorm->D(CVD):Ns(age, knots = seq(50, 80, 10))2 1.000000e-03
## lex.TrNorm->D(oth):Ns(age, knots = seq(50, 80, 10))2 1.200000e-02
## lex.TrMac->D(oth):Ns(age, knots = seq(50, 80, 10))3 1.490000e-01
## lex.TrMic->D(CVD):Ns(age, knots = seq(50, 80, 10))3 2.070000e-01
## lex.TrMic->D(oth):Ns(age, knots = seq(50, 80, 10))3 1.650000e-01
## lex.TrNorm->D(CVD):Ns(age, knots = seq(50, 80, 10))3 0.000000e+00
## lex.TrNorm->D(oth):Ns(age, knots = seq(50, 80, 10))3 2.940000e-01
## lex.TrMac->D(CVD):alloConv 8.706000e+00
## lex.TrMac->D(oth):alloConv 6.270000e-01
## lex.TrMic->D(CVD):alloConv 1.688000e+00
## lex.TrMic->D(oth):alloConv 2.096000e+00
## lex.TrNorm->D(CVD):alloConv 2.054000e+00
## lex.TrNorm->D(oth):alloConv 1.800000e+00
So under the assumption that the sex-effect is the same for all 6 mortality rates in figure 3.2 the M/W rate ratio is 1.46. But it is only rarely that we want to model different rates out of the same state, so use of stack(.Lexis) is seldom needed.