Techniques of Event History Modeling, in R
- 2. Event History Data Structures
- 3. Descriptive Methods
- 4. Exponential Transition Rate Models
- 5. Piecewise Constant Exponential Models
- 6. Exponential Models with Time-dependent Covariates
- 7. Parametric Models of Time-dependence
- 8. Methods to Check Parametric Assumptions
- 9. Semi-parametric Transition Rate Models
- 10. Models with a Mixture Distribution
The command files distributed with Blossfeld and Rohwer,
Techniques of Event History Modeling, are the second body of
TDA examples, and the one most people met the program through. They live
in examples/ehhnew, about ninety of them, in eight
families:
| prefix | book chapter | what it covers |
|---|---|---|
ehb |
2 | episode data |
ehc |
3 | life tables and product-limit estimation |
ehd |
4 | exponential models |
ehe |
5 | piecewise constant models |
ehf |
6 | episode splitting and time-varying covariates |
ehg |
7 | parametric models |
ehh |
8 | graphical tests of distributional assumptions |
ehi |
9 | Cox models |
The suffixes are worth knowing, since they are not what they look
like: a file ending in a plots the fitted rate, while
s and p are the second and third steps of the
pseudoresidual check of the book’s section 8.2 – s
estimates a product-limit survivor function for a model’s residuals,
p plots log S against them. They are separate
command files in TDA only because each step writes its result to disk
for the next to read; here they follow the fit directly.
This vignette walks the families in order, naming the command file
each box corresponds to and, in brackets, the page and box number in the
book where the same output is printed, so the two can be read side by
side. tests/vignette-qa/vignette-qa-book.csv in the sources
is the other direction: one row per box and figure in the book, with the
vignette box that reproduces it. The data are the ones of the manual’s
section 3.3.3, and the covariate set is the book’s own.
Box 1The data, and the book’s covariates
d <- tda_rrdat() # rrdat.1, with the derived variables
d4 <- tda_rrdat(states = 4) # job changes split by prestige
XVARS <- c("EDU", "COHO2", "COHO3", "LFX", "PNOJ", "PRES")
with_x <- function(lhs) stats::reformulate(XVARS, response = lhs)
Most plots below share one coordinate system, taken from the command
files’ own psetup rather than from the range of the data,
so that a picture can be laid beside the book.
2. Event History Data Structures
Book chapter 2, pp. 41-55. Command files ehb1 to
ehb3.
ehb1 is the data before any episode definition, and ehb2 the episode data itself: one transition, from 0 to 1.
Box 2ehb1, ehb2: the data and the episodes [book pp. 43-52, Boxes 2.2.1-2.2.5]
# the twelve variables of rrdat.1; DES and TFP are derived by ehb1.cf
str(d[, 1:12])
'data.frame': 600 obs. of 12 variables:
$ ID : int 1 2 2 2 3 3 3 3 3 4 ...
$ NOJ : int 1 1 2 3 1 2 3 4 5 1 ...
$ TStart: int 555 593 639 673 688 700 730 742 817 872 ...
$ TFin : int 982 638 672 892 699 729 741 816 828 926 ...
$ SEX : int 1 2 2 2 2 2 2 2 2 2 ...
$ TI : int 982 982 982 982 982 982 982 982 982 982 ...
$ TB : int 351 357 357 357 473 473 473 473 473 604 ...
$ TE : int 555 593 593 593 688 688 688 688 688 872 ...
$ TMAR : int 679 762 762 762 870 870 870 870 870 872 ...
$ PRES : int 34 22 46 46 41 41 44 44 44 55 ...
$ PRESN : int -1 46 46 -1 41 44 44 44 -1 -1 ...
$ EDU : int 17 10 10 10 11 11 11 11 11 13 ...
head(d[, 1:12], 9)
ID NOJ TStart TFin SEX TI TB TE TMAR PRES PRESN EDU
1 1 1 555 982 1 982 351 555 679 34 -1 17
2 2 1 593 638 2 982 357 593 762 22 46 10
3 2 2 639 672 2 982 357 593 762 46 46 10
4 2 3 673 892 2 982 357 593 762 46 -1 10
5 3 1 688 699 2 982 473 688 870 41 41 11
6 3 2 700 729 2 982 473 688 870 41 44 11
7 3 3 730 741 2 982 473 688 870 44 44 11
8 3 4 742 816 2 982 473 688 870 44 44 11
9 3 5 817 828 2 982 473 688 870 44 -1 11
head(d[, c("ID", "NOJ", "TStart", "TFin", "SEX", "TFP", "DES")], 4)
ID NOJ TStart TFin SEX TFP DES
1 1 1 555 982 1 428 0
2 2 1 593 638 2 46 1
3 2 2 639 672 2 34 1
4 2 3 673 892 2 220 1
tda_rate(Surv(TFP, DES) ~ 1, d)$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehb3 is the multi-episode form of the same data (Box
2.2.6): the spells of a person are chained, the origin state of spell k
is 1 + 2(k - 1), the destination the next number up if the
job ended, and time runs from the person’s first job. id=
and spell= are edef(id=, sn=); the episode
table is the book’s Box 2.2.8.
Box 3ehb3: multi-episode data [book pp. 53-55, Boxes 2.2.6-2.2.8]
dm <- d
dm$ORG <- 1 + 2 * (dm$NOJ - 1)
dm$DESM <- ifelse(dm$TFin < dm$TI, dm$ORG + 1, dm$ORG)
dm$TFC <- ave(dm$TStart, dm$ID, FUN = function(x) x[1])
dm$TSP <- dm$TStart - dm$TFC
dm$TFPM <- dm$TFin - dm$TFC + 1
# the episodes of the first three individuals, as edef() will see them
head(dm[dm$ID <= 3, c("ID", "NOJ", "TStart", "TFin", "SEX", "ORG", "DESM",
"TSP", "TFPM")], 9)
ID NOJ TStart TFin SEX ORG DESM TSP TFPM
1 1 1 555 982 1 1 1 0 428
2 2 1 593 638 2 1 2 0 46
3 2 2 639 672 2 3 4 46 80
4 2 3 673 892 2 5 6 80 300
5 3 1 688 699 2 1 2 0 12
6 3 2 700 729 2 3 4 12 42
7 3 3 730 741 2 5 6 42 54
8 3 4 742 816 2 7 8 54 129
9 3 5 817 828 2 9 10 129 141
eb3 <- suppressWarnings(
tda_rate(Surv(TSP, TFPM, ORG, DESM) ~ 1, dm, id = "ID", spell = "NOJ"))
eb3$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 1 1 16 16 233.43750 0 428 FALSE
2 1 1 2 185 185 52.72973 0 326 FALSE
3 2 3 3 36 36 150.16667 13 450 FALSE
4 2 3 4 126 126 52.30159 3 406 FALSE
5 3 5 5 38 38 121.73684 9 463 FALSE
6 3 5 6 69 69 50.36232 8 403 FALSE
7 4 7 7 24 24 85.33334 52 465 FALSE
8 4 7 8 38 38 37.71053 9 329 FALSE
9 5 9 9 9 9 110.22222 66 441 FALSE
10 5 9 10 23 23 22.60870 39 348 FALSE
11 6 11 11 8 8 45.75000 151 379 FALSE
12 6 11 12 12 12 43.75000 46 324 FALSE
13 7 13 13 7 7 114.14286 132 427 FALSE
14 7 13 14 4 4 52.75000 112 368 FALSE
15 8 15 15 3 3 65.00000 129 440 FALSE
16 8 15 16 1 1 72.00000 213 285 FALSE
17 9 17 17 1 1 34.00000 285 319 TRUE
attr(eb3$episodes, "sums") # the totals under each spell; sn = NA is all spells
sn episodes weighted
1 1 201 201
2 2 162 162
3 3 107 107
4 4 62 62
5 5 32 32
6 6 20 20
7 7 11 11
8 8 4 4
9 9 1 1
10 NA 600 600
The ninth spell holds a single, censored episode – no transition to
estimate – and TDA marks it with * in the Excl
column, which is excluded = TRUE here; the warning that
says so is the one suppressed above.
3. Descriptive Methods
Book chapter 3, pp. 57-85. Life tables and the product-limit
estimator; command files ehc1 to
ehc10.
ehc1 is the life table over 30-month intervals, ehc3 the same by sex.
Box 4ehc1, ehc3: life tables [book pp. 60-66, Boxes 3.1.1-3.1.4]
lt <- tda_ltb(Surv(TFP, DES) ~ 1, d, tp = seq(0, 500, by = 30))
summary(lt, n = nrow(lt$tables[[1]]))
Call: tda_ltb(formula = Surv(TFP, DES) ~ 1, data = d, tp = seq(0, 500,
by = 30))
Life table. SN 1. Origin state 0.
start midpoint entering censored exposed events prob
0 15 600 28 586.0 223 0.38054608
30 45 349 23 337.5 113 0.33481481
60 75 213 15 205.5 51 0.24817518
90 105 147 16 139.0 25 0.17985612
120 135 106 15 98.5 24 0.24365482
150 165 67 5 64.5 9 0.13953488
180 195 53 9 48.5 4 0.08247423
210 225 40 5 37.5 3 0.08000000
240 255 32 5 29.5 0 0.00000000
270 285 27 7 23.5 2 0.08510638
300 315 18 5 15.5 2 0.12903226
330 345 11 1 10.5 2 0.19047619
360 375 8 3 6.5 0 0.00000000
390 405 5 4 3.0 0 0.00000000
420 435 1 1 0.5 0 0.00000000
Median duration: 47.27858
Cases: 600 weighted: 600
lt2 <- tda_ltb(Surv(TFP, DES) ~ as.factor(SEX), d, tp = seq(0, 500, by = 30))
lt2$episodes # the episodes behind both groups
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
lt2$summary
sn origin cases weighted median group
1 1 0 348 348 56.84110 1
2 1 0 252 252 39.63376 2
ehc2 plots the survivor function the table implies, and ehc2a and ehc2b the density and the rate, each on its own scale.
Box 5ehc2: the life table survivor function [book p. 65, Fig. 3.1.1]
sv <- tda_survivor(lt)
p <- tda_ps(sv, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl_lines(tda_pl(p, "plframe"), "time", "survivor")
plot(tda_pl_text(p, "Life Table Survivor Function", at = c(80, 0.8)))
Box 6ehc2a, ehc2b: the density and the rate [book p. 65, Fig. 3.1.1]
p <- tda_ps(sv, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 0.02))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 10)
p <- tda_pl_lines(tda_pl(p, "plframe"), "time", "density")
plot(tda_pl_text(p, "Life Table Density Function", at = c(80, 0.017)))
p <- tda_ps(sv, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 0.02))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 10)
p <- tda_pl_lines(tda_pl(p, "plframe"), "time", "rate")
plot(tda_pl_text(p, "Life Table Transition Rate", at = c(80, 0.017)))
ehc4 draws one curve per sex.
Box 7ehc4: survivor functions by sex [book p. 67, Fig. 3.1.2]
sv2 <- tda_survivor(lt2)
p <- tda_ps(sv2, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl_lines(p, "time", "survivor", by = "group")
p <- tda_pl_text(p, "Men", at = c(240, 0.25))
p <- tda_pl_text(p, "Women", at = c(240, 0.09))
plot(tda_pl_text(p, "Life Table Survivor Function", at = c(80, 0.8)))
ehc5 is the product-limit estimate, ehc7 the comparison of the two curves, and ehc9 the four destination states.
Box 8ehc5, ehc7, ehc9: product-limit estimation [book pp. 74-83, Boxes 3.2.1-3.2.2, 3.3.1-3.3.4a, 3.3.4b]
pl <- tda_ple(Surv(TFP, DES) ~ 1, d)
pl$episodes # the episode table TDA prints above the estimate
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
print(pl, n = 10)
Call: tda_ple(formula = Surv(TFP, DES) ~ 1, data = d)
SN 1. Transition: 0,1 - Product-Limit Estimation
id index time events censored n.risk survivor std.err cum.rate
0 0 0 0 0 600 1.0000000 0.000000000 0.000000000
0 1 2 2 0 600 0.9966667 0.002353091 0.003338901
0 2 3 5 1 597 0.9883194 0.004389012 0.011749380
0 3 4 9 2 590 0.9732433 0.006599178 0.027121160
0 4 5 3 0 581 0.9682180 0.007174619 0.032298048
0 5 6 10 1 577 0.9514378 0.008796359 0.049781011
0 6 7 9 0 567 0.9363356 0.009993927 0.065781352
0 7 8 6 1 557 0.9262494 0.010700980 0.076611783
0 8 9 7 3 548 0.9144177 0.011460673 0.089467791
0 9 10 8 1 540 0.9008708 0.012250884 0.104393441
... 122 more rows
Median duration: 43.03101
Duration times limited to: 350
Cases: 600 weighted: 600
pl2 <- tda_ple(Surv(TFP, DES) ~ as.factor(SEX), d, compare = TRUE)
pl2$comparison
sn org des test statistic df signif
1 1 0 1 Log-Rank (Savage) 20.603209 1 0.9999943
2 1 0 1 Wilcoxon (Breslow) 9.364723 1 0.9977880
3 1 0 1 Wilcoxon (Tarone-Ware) 14.326703 1 0.9998463
4 1 0 1 Wilcoxon (Prentice) 10.695087 1 0.9989258
# one product-limit table per destination: upward, then lateral moves
pl4 <- tda_ple(Surv(TFP, DES) ~ 1, d4)
pl4$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 84 84 45.04762 0 326 FALSE
3 1 0 2 219 219 45.91324 0 350 FALSE
4 1 0 3 155 155 56.40000 0 332 FALSE
pl4$summary
transition median limit cases weighted
1 0,1 NA NA 600 600
2 0,2 122.2409 350 600 600
3 0,3 215.9553 332 600 600
for (b in pl4$blocks[1:2]) print(utils::head(b, 10), row.names = FALSE)
id index time events censored n.risk survivor std.err cum.rate
0 0 0 0 0 600 1.0000000 0.000000000 0.000000000
0 1 2 1 0 600 0.9983333 0.001665277 0.001668057
0 2 4 2 10 590 0.9949492 0.002908833 0.005063646
0 3 6 3 10 577 0.9897761 0.004152988 0.010276516
0 4 7 1 9 567 0.9880305 0.004497599 0.012041742
0 5 8 1 11 557 0.9862566 0.004826665 0.013838687
0 6 9 1 7 548 0.9844569 0.005142460 0.015665172
0 7 10 3 6 540 0.9789877 0.006005599 0.021236217
0 8 12 5 15 524 0.9696462 0.007257296 0.030824018
0 9 13 1 18 499 0.9677030 0.007498389 0.032830037
id index time events censored n.risk survivor std.err cum.rate
1 0 0 0 0 600 1.0000000 0.000000000 0.000000000
1 1 3 2 3 597 0.9966499 0.002364896 0.003355708
1 2 4 4 5 590 0.9898930 0.004105345 0.010158455
1 3 5 2 5 581 0.9864854 0.004745918 0.013606734
1 4 6 4 5 577 0.9796467 0.005815798 0.020563284
1 5 7 3 3 567 0.9744634 0.006509588 0.025868336
1 6 8 4 7 557 0.9674654 0.007343240 0.033075575
1 7 9 5 5 548 0.9586382 0.008269553 0.042241542
1 8 10 2 4 540 0.9550877 0.008611598 0.045952121
1 9 11 3 9 528 0.9496611 0.009114803 0.051650142
ehc6 plots the estimate, ehc10 one curve per destination state, and ehc8 adds the confidence bands, cut at 290 months where they widen past usefulness.
Box 9ehc6, ehc10: the estimated survivor functions [book pp. 76-85, Box 3.2.3; Fig. 3.2.1, Fig. 3.2.3]
p <- tda_ps(tda_survivor(pl), width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl_lines(p, "time", "survivor")
plot(tda_pl_text(p, "Product-Limit Survivor Function", at = c(60, 0.8)))
s4 <- tda_survivor(pl4)
p <- tda_ps(s4, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
for (i in seq_along(g4 <- c("0,1", "0,2", "0,3")))
p <- tda_pl_lines(p, "time", "survivor", by = "group",
select = g4[i], lty = c(1, 5, 8)[i])
p <- tda_pl_text(p, "upward moves", at = c(200, 0.75))
p <- tda_pl_text(p, "lateral moves", at = c(200, 0.3))
plot(tda_pl_text(p, "downward moves", at = c(200, 0.55)))
A band is two fills, the upper curve to the axis in grey and the
lower curve to the axis in white on top of it; ehc8.cf
draws the axes first, so the white fill lies over half the width of the
x axis line and the axis looks thinner under the bands than beyond them.
The axes go last here.
Box 10ehc8: with confidence bands [book p. 78, Fig. 3.3.1]
sb <- tda_survivor(pl2)
sb <- sb[sb$time < 290, ]
p <- tda_ps(sb, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl_lines(p, "time", "survivor", by = "group", select = "1",
band = c("upper", "lower"), lty = 1)
p <- tda_pl_lines(p, "time", "survivor", by = "group", select = "2",
band = c("upper", "lower"), lty = 5)
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl_text(p, "Men", at = c(240, 0.30))
p <- tda_pl_text(p, "Women", at = c(50, 0.10))
plot(tda_pl_text(p, "Survivor Functions for Men and Women", at = c(60, 0.8)))
4. Exponential Transition Rate Models
Book chapter 4, pp. 87-118. Command files ehd1 to
ehd7.
ehd1 is the null model and ehd2 the same with the book’s covariates.
Box 11ehd1, ehd2: exponential models [book pp. 91-98, Boxes 4.1.1-4.1.5]
ehd1 <- tda_rate(Surv(TFP, DES) ~ 1, d, prate = "0(10)300")
ehd1
Call: tda_rate(formula = Surv(TFP, DES) ~ 1, data = d, prate = "0(10)300")
Episodes: 600
Model: exponential (TDA 2)
logLik (starting values): -2514.02
logLik: -2514.02
Converged in 1 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.4891 0.0467 -96.0715 1.0000
ehd1$episodes # the episodes TDA built, before the estimates
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
tda_rates(ehd1)
group time rate survivor density
1 0 0 0.01123044 1.00000000 0.0112304448
2 0 10 0.01123044 0.89377211 0.0100374583
3 0 20 0.01123044 0.79882858 0.0089712003
4 0 30 0.01123044 0.71397071 0.0080182086
5 0 40 0.01123044 0.63812710 0.0071664512
6 0 50 0.01123044 0.57034021 0.0064051742
7 0 60 0.01123044 0.50975417 0.0057247661
8 0 70 0.01123044 0.45560406 0.0051166362
9 0 80 0.01123044 0.40720620 0.0045731068
10 0 90 0.01123044 0.36394955 0.0040873153
11 0 100 0.01123044 0.32528795 0.0036531284
12 0 110 0.01123044 0.29073330 0.0032650643
13 0 120 0.01123044 0.25984931 0.0029182234
14 0 130 0.01123044 0.23224607 0.0026082267
15 0 140 0.01123044 0.20757506 0.0023311602
16 0 150 0.01123044 0.18552480 0.0020835260
17 0 160 0.01123044 0.16581689 0.0018621974
18 0 170 0.01123044 0.14820251 0.0016643801
19 0 180 0.01123044 0.13245927 0.0014875765
20 0 190 0.01123044 0.11838840 0.0013295544
21 0 200 0.01123044 0.10581225 0.0011883187
22 0 210 0.01123044 0.09457204 0.0010620861
23 0 220 0.01123044 0.08452585 0.0009492629
24 0 230 0.01123044 0.07554685 0.0008484247
25 0 240 0.01123044 0.06752167 0.0007582983
26 0 250 0.01123044 0.06034898 0.0006777459
27 0 260 0.01123044 0.05393824 0.0006057504
28 0 270 0.01123044 0.04820849 0.0005414028
29 0 280 0.01123044 0.04308741 0.0004838907
30 0 290 0.01123044 0.03851032 0.0004324880
31 0 300 0.01123044 0.03441945 0.0003865457
summary(tda_rate(with_x("Surv(TFP, DES)"), d))
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d)
Episodes: 600
Model: exponential
Converged in 5 iterations
Estimate Std. Error C/Error Pr(>|t|)
Constant -4.48944489 0.27950029 -16.0624 < 2.2e-16 ***
EDU 0.07730132 0.02470334 3.1292 0.0017529 **
COHO2 0.60803578 0.11355087 5.3547 8.568e-08 ***
COHO3 0.61079970 0.11854198 5.1526 2.569e-07 ***
LFX -0.00317926 0.00093783 -3.3900 0.0006989 ***
PNOJ 0.05963838 0.04415253 1.3507 0.1767802
PRES -0.02800646 0.00553034 -5.0641 4.102e-07 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
logLik (starting values): -2514.02
logLik: -2465.987 df: 7 AIC: 4945.975
Null model (exponential, constant rate) logLik: -2514.02
LR test against it: chi2 = 96.0657 on 6 df, p = < 2.2e-16
The rate this model implies is a constant, exp(-4.4891)
= 0.0112, and the book prints it as the table ehd1.prs
above rather than drawing it. Everything that follows is an attempt to
let that constant vary.
ehd3 estimates the same specification over three destination states.
Box 12ehd3: three destinations [book pp. 102-104, Boxes 4.2.1-4.2.2]
ehd3 <- tda_rate(with_x("Surv(TFP, DES)"), d4)
ehd3$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 84 84 45.04762 0 326 FALSE
3 1 0 2 219 219 45.91324 0 350 FALSE
4 1 0 3 155 155 56.40000 0 332 FALSE
ehd3
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d4)
Episodes: 600
Model: exponential (TDA 2)
logLik (starting values): -2986
logLik: -2884.788
Converged in 7 iterations
Transitions: 0->1, 0->2, 0->3
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -5.1036 0.6175 -8.2648 1.0000
2 1 0 1 A EDU 0.3018 0.0430 7.0215 1.0000
3 1 0 1 A COHO2 0.6379 0.2713 2.3510 0.9813
4 1 0 1 A COHO3 0.7354 0.2765 2.6595 0.9922
5 1 0 1 A LFX -0.0023 0.0021 -1.0898 0.7242
6 1 0 1 A PNOJ 0.1731 0.1004 1.7239 0.9153
7 1 0 1 A PRES -0.1441 0.0141 -10.1970 1.0000
8 1 0 2 A Constant -5.8043 0.4054 -14.3164 1.0000
9 1 0 2 A EDU 0.0033 0.0380 0.0881 0.0702
10 1 0 2 A COHO2 0.6722 0.1643 4.0926 1.0000
11 1 0 2 A COHO3 0.6843 0.1732 3.9501 0.9999
12 1 0 2 A LFX -0.0031 0.0014 -2.2579 0.9760
13 1 0 2 A PNOJ 0.0323 0.0645 0.5010 0.3836
14 1 0 2 A PRES 0.0081 0.0081 1.0094 0.6872
15 1 0 3 A Constant -4.9051 0.5251 -9.3405 1.0000
16 1 0 3 A EDU -0.0167 0.0490 -0.3404 0.2664
17 1 0 3 A COHO2 0.5300 0.1935 2.7387 0.9938
18 1 0 3 A COHO3 0.5705 0.2030 2.8101 0.9950
19 1 0 3 A LFX -0.0037 0.0017 -2.2649 0.9765
20 1 0 3 A PNOJ 0.0407 0.0775 0.5252 0.4005
21 1 0 3 A PRES -0.0157 0.0097 -1.6208 0.8949
ehd4 and ehd5 take random
subsamples so that the destinations are comparably sized.
tda_runif() is TDA’s generator, a multiplicative
congruential one with a fixed seed, so the episodes drawn here are the
episodes TDA drew; runif() would give a different subsample
and so different estimates.
Box 13ehd4, ehd5: subsamples, one transition at a time [book pp. 106-110, Boxes 4.2.3-4.2.6]
sub <- d4[tda_runif(nrow(d4)) <= 84 / 219, ]
sub$DES <- ifelse(sub$DES == 2, 2L, 0L)
cat(sprintf("%d of %d episodes selected\n", nrow(sub), nrow(d4)))
235 of 600 episodes selected
ehd4 <- tda_rate(with_x("Surv(TFP, DES)"), sub)
ehd4$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 145 145 82.71034 0 428 FALSE
2 1 0 2 90 90 49.33333 0 209 FALSE
ehd4
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = sub)
Episodes: 235
Model: exponential (TDA 2)
logLik (starting values): -558.6513
logLik: -552.8855
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 2 A Constant -5.3301 0.6960 -7.6583 1.0000
2 1 0 2 A EDU -0.0097 0.0594 -0.1627 0.1293
3 1 0 2 A COHO2 0.6587 0.2516 2.6181 0.9912
4 1 0 2 A COHO3 0.4869 0.2732 1.7824 0.9253
5 1 0 2 A LFX -0.0025 0.0023 -1.1042 0.7305
6 1 0 2 A PNOJ -0.0109 0.1138 -0.0961 0.0765
7 1 0 2 A PRES 0.0014 0.0127 0.1073 0.0854
d5 <- d4[tda_runif(nrow(d4)) <= 84 / 155, ]
d5$DES <- ifelse(d5$DES == 3, 3L, 0L)
cat(sprintf("%d of %d episodes selected\n", nrow(d5), nrow(d4)))
314 of 600 episodes selected
ehd5 <- tda_rate(with_x("Surv(TFP, DES)"), d5)
ehd5$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 235 235 76.25106 0 428 FALSE
2 1 0 3 79 79 50.83544 0 312 FALSE
ehd5
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d5)
Episodes: 314
Model: exponential (TDA 2)
logLik (starting values): -523.4849
logLik: -516.5031
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 3 A Constant -5.6720 0.7092 -7.9981 1.0000
2 1 0 3 A EDU 0.0169 0.0601 0.2806 0.2210
3 1 0 3 A COHO2 0.5486 0.2714 2.0218 0.9568
4 1 0 3 A COHO3 0.5347 0.2890 1.8498 0.9357
5 1 0 3 A LFX -0.0056 0.0027 -2.0972 0.9640
6 1 0 3 A PNOJ 0.1471 0.1122 1.3114 0.8103
7 1 0 3 A PRES -0.0085 0.0131 -0.6488 0.4836
Box 4.2.7 of the book lays the two sets of estimates side by side,
which is the point of the exercise: with the events standardized,
COHO3 and LFX lose their significance for
lateral moves and COHO3 for downward ones, so the same data
can support different substantive conclusions depending on how many
events each transition happens to have. Upward moves are the baseline
and appear in both columns unchanged.
Box 14Box 4.2.7: standardized against unstandardized estimates [book p. 111, Box 4.2.7]
unstd <- function(des) {
e <- ehd3$estimates
e[e$Des == des, c("Variable", "Coeff", "C/Error")]
}
std <- list(`1` = unstd(1),
`2` = ehd4$estimates[, c("Variable", "Coeff", "C/Error")],
`3` = ehd5$estimates[, c("Variable", "Coeff", "C/Error")])
for (des in 1:3) {
a <- unstd(des)
b <- std[[as.character(des)]]
cmp <- data.frame(Variable = a$Variable,
Coeff = a$Coeff, `C/Error` = a$`C/Error`,
Coeff.s = b$Coeff, `C/Error.s` = b$`C/Error`,
check.names = FALSE)
cat(sprintf("\n%s moves. Events: %d (unstandardized) %d (standardized)\n",
c("Upward", "Lateral", "Downward")[des],
ehd3$episodes$episodes[ehd3$episodes$des == des],
if (des == 1) ehd3$episodes$episodes[ehd3$episodes$des == 1]
else list(NULL, ehd4, ehd5)[[des]]$episodes$episodes[2]))
print(format(cmp, digits = 4), row.names = FALSE)
}
Upward moves. Events: 84 (unstandardized) 84 (standardized)
Variable Coeff C/Error Coeff.s C/Error.s
Constant -5.103617 -8.265 -5.103617 -8.265
EDU 0.301790 7.022 0.301790 7.022
COHO2 0.637895 2.351 0.637895 2.351
COHO3 0.735415 2.660 0.735415 2.660
LFX -0.002265 -1.090 -0.002265 -1.090
PNOJ 0.173106 1.724 0.173106 1.724
PRES -0.144116 -10.197 -0.144116 -10.197
Lateral moves. Events: 219 (unstandardized) 90 (standardized)
Variable Coeff C/Error Coeff.s C/Error.s
Constant -5.804317 -14.31637 -5.330081 -7.65834
EDU 0.003345 0.08806 -0.009672 -0.16271
COHO2 0.672225 4.09264 0.658747 2.61809
COHO3 0.684335 3.95009 0.486906 1.78241
LFX -0.003085 -2.25785 -0.002496 -1.10420
PNOJ 0.032293 0.50100 -0.010935 -0.09608
PRES 0.008136 1.00939 0.001362 0.10729
Downward moves. Events: 155 (unstandardized) 79 (standardized)
Variable Coeff C/Error Coeff.s C/Error.s
Constant -4.905088 -9.3405 -5.671986 -7.9981
EDU -0.016686 -0.3404 0.016866 0.2806
COHO2 0.529999 2.7387 0.548629 2.0218
COHO3 0.570488 2.8101 0.534684 1.8498
LFX -0.003745 -2.2649 -0.005638 -2.0972
PNOJ 0.040694 0.5252 0.147083 1.3114
PRES -0.015691 -1.6208 -0.008525 -0.6488
ehd6 holds the cohort effects equal across the three transitions, which is what a constraint is for.
Box 15ehd6: cohort effects constrained equal [book pp. 112-113, Boxes 4.2.8-4.2.9]
ehd6 <- tda_rate(with_x("Surv(TFP, DES)"), d4,
constraints = c("b3 - b10 = 0", "b3 - b17 = 0",
"b4 - b11 = 0", "b4 - b18 = 0"))
ehd6
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d4, constraints = c("b3 - b10 = 0",
"b3 - b17 = 0", "b4 - b11 = 0", "b4 - b18 = 0"))
Episodes: 600
Model: exponential (TDA 2)
logLik (starting values): -2986
logLik: -2885.017
Converged in 7 iterations
Transitions: 0->1, 0->2, 0->3
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -5.0782 0.5981 -8.4902 1.0000
2 1 0 1 A EDU 0.3006 0.0423 7.1066 1.0000
3 1 0 1 A COHO2 0.6182 0.1136 5.4404 1.0000
4 1 0 1 A COHO3 0.6543 0.1190 5.4985 1.0000
5 1 0 1 A LFX -0.0024 0.0020 -1.1832 0.7633
6 1 0 1 A PNOJ 0.1780 0.0991 1.7962 0.9275
7 1 0 1 A PRES -0.1435 0.0140 -10.2516 1.0000
8 1 0 2 A Constant -5.7656 0.3950 -14.5984 1.0000
9 1 0 2 A EDU 0.0017 0.0376 0.0445 0.0355
10 1 0 2 A COHO2 0.6182 0.1136 5.4404 1.0000
11 1 0 2 A COHO3 0.6543 0.1190 5.4985 1.0000
12 1 0 2 A LFX -0.0031 0.0013 -2.3396 0.9807
13 1 0 2 A PNOJ 0.0341 0.0641 0.5328 0.4058
14 1 0 2 A PRES 0.0083 0.0080 1.0365 0.7000
15 1 0 3 A Constant -4.9621 0.5197 -9.5479 1.0000
16 1 0 3 A EDU -0.0152 0.0491 -0.3092 0.2428
17 1 0 3 A COHO2 0.6182 0.1136 5.4404 1.0000
18 1 0 3 A COHO3 0.6543 0.1190 5.4985 1.0000
19 1 0 3 A LFX -0.0036 0.0016 -2.2089 0.9728
20 1 0 3 A PNOJ 0.0362 0.0774 0.4683 0.3604
21 1 0 3 A PRES -0.0161 0.0097 -1.6623 0.9035
ehd6$episodes # the episodes TDA built, as it reports them
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 84 84 45.04762 0 326 FALSE
3 1 0 2 219 219 45.91324 0 350 FALSE
4 1 0 3 155 155 56.40000 0 332 FALSE
ehd7 keeps the first four spells and gives each its
coefficients: the effect of education on leaving a first job need not be
its effect on leaving a fourth. id= and spell=
are TDA’s edef(id=, sn=), and spell_terms=
gives one spell a covariate set of its own – the first job has no labour
force experience to speak of, so LFX is left out of it, as
ehd7.cf does.
Box 16ehd7: a multi-episode model [book pp. 115-118, Boxes 4.3.1-4.3.2a, 4.3.2b]
d7 <- d[d$NOJ <= 4, ]
d7 <- d7[order(d7$ID, d7$NOJ), ]
# ehd7.cf measures time from the start of the FIRST job, not from the
# start of each spell: TFC is the first spell's starting time
# carried forward, and the episode runs from TStart - TFC to
# TFin - TFC + 1. Using each spell's duration instead would start
# every spell at 0 and give a different episode structure.
d7$TFC <- ave(ifelse(d7$NOJ == 1, d7$TStart, NA), d7$ID,
FUN = function(v) v[!is.na(v)][1])
d7$TSP <- d7$TStart - d7$TFC
d7$TFPC <- d7$TFin - d7$TFC + 1
ehd7 <- tda_rate(Surv(TSP, TFPC, DES) ~ EDU + COHO2 + COHO3 + LFX + PRES, d7,
id = "ID", spell = "NOJ",
spell_terms = list("1" = c("EDU", "COHO2", "COHO3", "PRES")))
ehd7$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 16 16 233.43750 0 428 FALSE
2 1 0 1 185 185 52.72973 0 326 FALSE
3 2 0 0 36 36 150.16667 13 450 FALSE
4 2 0 1 126 126 52.30159 3 406 FALSE
5 3 0 0 38 38 121.73684 9 463 FALSE
6 3 0 1 69 69 50.36232 8 403 FALSE
7 4 0 0 24 24 85.33334 52 465 FALSE
8 4 0 1 38 38 37.71053 9 329 FALSE
ehd7
Call: tda_rate(formula = Surv(TSP, TFPC, DES) ~ EDU + COHO2 + COHO3 +
LFX + PRES, data = d7, id = "ID", spell = "NOJ", spell_terms = list(`1` = c("EDU",
"COHO2", "COHO3", "PRES")))
Episodes: 532
Model: exponential (TDA 2)
logLik (starting values): -2286.085
logLik: -2233.703
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -5.0179 0.4174 -12.0225 1.0000
2 1 0 1 A EDU 0.0622 0.0327 1.9052 0.9432
3 1 0 1 A COHO2 0.7221 0.1888 3.8245 0.9999
4 1 0 1 A COHO3 0.6638 0.1792 3.7050 0.9998
5 1 0 1 A PRES -0.0095 0.0091 -1.0452 0.7041
6 2 0 1 A Constant -3.7352 0.5778 -6.4644 1.0000
7 2 0 1 A EDU 0.1261 0.0516 2.4442 0.9855
8 2 0 1 A COHO2 0.3423 0.2157 1.5867 0.8874
9 2 0 1 A COHO3 0.2704 0.2334 1.1582 0.7532
10 2 0 1 A LFX -0.0062 0.0018 -3.3525 0.9992
11 2 0 1 A PRES -0.0510 0.0110 -4.6237 1.0000
12 3 0 1 A Constant -4.2628 0.7397 -5.7631 1.0000
13 3 0 1 A EDU 0.0522 0.0749 0.6976 0.5146
14 3 0 1 A COHO2 0.1975 0.2972 0.6645 0.4936
15 3 0 1 A COHO3 0.7143 0.3289 2.1719 0.9701
16 3 0 1 A LFX -0.0039 0.0020 -1.9843 0.9528
17 3 0 1 A PRES -0.0223 0.0139 -1.6008 0.8906
18 4 0 1 A Constant -2.2232 1.3489 -1.6481 0.9007
19 4 0 1 A EDU 0.0438 0.1329 0.3295 0.2583
20 4 0 1 A COHO2 1.1354 0.3830 2.9643 0.9970
21 4 0 1 A COHO3 0.3163 0.4492 0.7041 0.5186
22 4 0 1 A LFX -0.0052 0.0027 -1.9131 0.9443
23 4 0 1 A PRES -0.0654 0.0205 -3.1986 0.9986
5. Piecewise Constant Exponential Models
Book chapter 5, pp. 119-130. Command files ehe1 to
ehe3.
The rate is constant within periods and free between them, which buys duration dependence without committing to a shape.
Box 17The piecewise constant helper
pc <- function(f, data, model = "exponential_periods", tp = "0 (12) 96", ...)
tda_rate(f, data, model = model, options = list(tp = tp), ...)
ehe1 is the null model, ehe2 the same with covariates.
Box 18ehe1, ehe2: piecewise constant exponential [book pp. 122-127, Boxes 5.2.1-5.2.2, 5.3.1-5.3.2]
ehe1 <- pc(Surv(TFP, DES) ~ 1, d, prate = "0(1)300")
ehe1
Call: tda_rate(formula = f, data = data, model = model, prate = "0(1)300",
options = list(tp = tp))
Episodes: 600
Model: exponential_periods (TDA 3)
logLik (starting values): -2514.02
logLik: -2456.609
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Period-1 -4.6829 0.1260 -37.1691 1.0000
2 1 0 1 A Period-2 -4.0420 0.1021 -39.6029 1.0000
3 1 0 1 A Period-3 -3.7854 0.1026 -36.8954 1.0000
4 1 0 1 A Period-4 -4.3178 0.1508 -28.6410 1.0000
5 1 0 1 A Period-5 -4.2955 0.1622 -26.4792 1.0000
6 1 0 1 A Period-6 -4.3690 0.1857 -23.5278 1.0000
7 1 0 1 A Period-7 -4.7274 0.2425 -19.4915 1.0000
8 1 0 1 A Period-8 -5.6864 0.4082 -13.9289 1.0000
9 1 0 1 A Period-9 -5.1650 0.1195 -43.2137 1.0000
ehe1$episodes # the episodes TDA built
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehe1$periods # the period boundaries and the events in each
start end starting ending events
1 0 12 600 76 63
2 12 24 0 104 96
3 24 36 0 108 95
4 36 48 0 55 44
5 48 60 0 44 38
6 60 72 0 38 29
7 72 84 0 22 17
8 84 96 0 9 6
9 96 Inf 0 144 70
ehe2 <- pc(with_x("Surv(TFP, DES)"), d)
ehe2
Call: tda_rate(formula = f, data = data, model = model, options = list(tp = tp))
Episodes: 600
Model: exponential_periods (TDA 3)
logLik (starting values): -2514.02
logLik: -2417.175
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Period-1 -4.6053 0.3019 -15.2522 1.0000
2 1 0 1 A Period-2 -3.9236 0.2922 -13.4281 1.0000
3 1 0 1 A Period-3 -3.6302 0.2904 -12.5000 1.0000
4 1 0 1 A Period-4 -4.1514 0.3101 -13.3852 1.0000
5 1 0 1 A Period-5 -4.1106 0.3157 -13.0201 1.0000
6 1 0 1 A Period-6 -4.1584 0.3292 -12.6327 1.0000
7 1 0 1 A Period-7 -4.5272 0.3609 -12.5459 1.0000
8 1 0 1 A Period-8 -5.4741 0.4876 -11.2257 1.0000
9 1 0 1 A Period-9 -4.9094 0.2887 -17.0067 1.0000
10 1 0 1 A EDU 0.0683 0.0249 2.7409 0.9939
11 1 0 1 A COHO2 0.4841 0.1155 4.1931 1.0000
12 1 0 1 A COHO3 0.3823 0.1228 3.1146 0.9982
13 1 0 1 A LFX -0.0037 0.0009 -3.9943 0.9999
14 1 0 1 A PNOJ 0.0618 0.0442 1.3987 0.8381
15 1 0 1 A PRES -0.0266 0.0055 -4.8228 1.0000
ehe2$periods
start end starting ending events
1 0 12 600 76 63
2 12 24 0 104 96
3 24 36 0 108 95
4 36 48 0 55 44
5 48 60 0 44 38
6 60 72 0 38 29
7 72 84 0 22 17
8 84 96 0 9 6
9 96 Inf 0 144 70
ehe1a plots the rate. The steps are the periods.
(ehe2p is not a second rate plot but the pseudoresidual
check of ehe2, which comes below with the rest of section
8.2.)
Box 19ehe1a: the estimated rate [book pp. 122-124, Boxes 5.2.1-5.2.2; Fig. 5.2.1]
er <- tda_rates(ehe1)
p <- tda_ps(er, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 0.03))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 2)
p <- tda_pl_lines(p, "time", "rate")
plot(tda_pl_text(p, "Piecewise Constant Exponential Rate", at = c(60, 0.025)))
ehe3 uses three periods chosen by hand rather than a regular grid, which is model 16 rather than model 3.
Box 20ehe3: three periods [book pp. 129-130, Boxes 5.4.1-5.4.2]
ehe3 <- pc(with_x("Surv(TFP, DES)"), d,
model = "exponential_periods2", tp = "0,24,60")
ehe3
Call: tda_rate(formula = f, data = data, model = model, options = list(tp = tp))
Episodes: 600
Model: exponential_periods2 (TDA 16)
logLik (starting values): -2514.02
logLik: -2434.896
Converged in 6 iterations
Idx SN Org Des MT P Variable Coeff Error C/Error Signif
1 1 0 1 A 1.0000 Period-1 -4.4627 0.4522 -9.8688 1.0000
2 1 0 1 A 2.0000 Period-2 -3.7235 0.4451 -8.3647 1.0000
3 1 0 1 A 3.0000 Period-3 -5.0902 0.5780 -8.8058 1.0000
4 1 0 1 A 1.0000 EDU 0.1016 0.0384 2.6455 0.9918
5 1 0 1 A 2.0000 EDU 0.0548 0.0435 1.2590 0.7920
6 1 0 1 A 3.0000 EDU 0.0470 0.0494 0.9508 0.6583
7 1 0 1 A 1.0000 COHO2 0.5849 0.2038 2.8692 0.9959
8 1 0 1 A 2.0000 COHO2 0.5112 0.1864 2.7426 0.9939
9 1 0 1 A 3.0000 COHO2 0.4116 0.2192 1.8776 0.9396
10 1 0 1 A 1.0000 COHO3 0.4666 0.2061 2.2638 0.9764
11 1 0 1 A 2.0000 COHO3 0.2824 0.1939 1.4562 0.8547
12 1 0 1 A 3.0000 COHO3 0.6422 0.2518 2.5505 0.9892
13 1 0 1 A 1.0000 LFX -0.0059 0.0017 -3.4044 0.9993
14 1 0 1 A 2.0000 LFX -0.0031 0.0014 -2.1951 0.9718
15 1 0 1 A 3.0000 LFX -0.0017 0.0018 -0.9437 0.6547
16 1 0 1 A 1.0000 PNOJ 0.1668 0.0742 2.2486 0.9755
17 1 0 1 A 2.0000 PNOJ 0.0326 0.0697 0.4683 0.3604
18 1 0 1 A 3.0000 PNOJ -0.0445 0.0917 -0.4856 0.3727
19 1 0 1 A 1.0000 PRES -0.0336 0.0089 -3.7938 0.9999
20 1 0 1 A 2.0000 PRES -0.0265 0.0091 -2.9246 0.9966
21 1 0 1 A 3.0000 PRES -0.0121 0.0113 -1.0723 0.7164
# the two tables TDA prints before the estimates: the episodes it built,
# and how they fall across the periods
ehe3$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehe3$periods
start end starting ending events
1 0 24 600 180 159
2 24 60 0 207 177
3 60 Inf 0 213 122
6. Exponential Models with Time-dependent Covariates
Book chapter 6, pp. 131-157. Episode splitting; command files
ehf1 to ehf6.
ehf1 splits every episode at the date of marriage
and writes the pieces out (rrdat.s1, Box 6.4.2);
ehf5 splits on a fixed grid of 60 months
(rrdat.d60, Box 6.5.2). tda_split() is both,
at= for a per-episode split time and grid= for
the fixed one.
Box 21ehf1, ehf5: the split episode data [book pp. 143-154, Boxes 6.4.1-6.4.2, 6.5.1-6.5.2]
ds <- d
ds$MarrDate <- ifelse(ds$TMAR <= 0, 10000, ds$TMAR - ds$TStart)
sp <- tda_split(stats::reformulate(c(XVARS, "SEX"),
response = "Surv(TFP, DES)"),
ds, at = "MarrDate")
cat(sprintf("%d episodes split into %d\n", nrow(ds), nrow(sp)))
600 episodes split into 761
attr(sp, "episodes") # the episodes epdat reports before writing them
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
head(sp, 6)
episode case subsample transition org des ts tf EDU COHO2 COHO3 LFX PNOJ PRES SEX MarrDate
1 1 1 2 1 0 0 0 124 17 0 0 0 0 34 1 124
2 1 1 2 2 0 0 124 428 17 0 0 0 0 34 1 124
3 2 1 1 1 0 1 0 46 10 0 0 0 0 22 2 169
4 3 1 1 1 0 1 0 34 10 0 0 46 1 46 2 123
5 4 1 2 1 0 0 0 89 10 0 0 80 2 46 2 89
6 4 1 2 2 0 1 89 220 10 0 0 80 2 46 2 89
sp6 <- tda_split(with_x("Surv(TFP, DES)"), d, grid = seq(60, 420, by = 60))
cat(sprintf("%d episodes split into %d on the 60-month grid\n",
nrow(d), nrow(sp6)))
600 episodes split into 1021 on the 60-month grid
attr(sp6, "episodes")
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
head(sp6, 6)
episode case subsample transition org des ts tf EDU COHO2 COHO3 LFX PNOJ PRES
1 1 1 8 1 0 0 0 60 17 0 0 0 0 34
2 1 1 8 2 0 0 60 120 17 0 0 0 0 34
3 1 1 8 3 0 0 120 180 17 0 0 0 0 34
4 1 1 8 4 0 0 180 240 17 0 0 0 0 34
5 1 1 8 5 0 0 240 300 17 0 0 0 0 34
6 1 1 8 6 0 0 300 360 17 0 0 0 0 34
A covariate that changes during an episode is handled by cutting the
episode where it changes. Marriage is the example: MarrDate
is the month it happens, or past the end of the episode if it never
does. ehf2, ehf3,
ehf4 and ehf6 are the models on the
pieces.
Box 22ehf2, ehf3: episode splitting at marriage [book pp. 145-149, Boxes 6.4.3-6.4.6]
sp$MARR <- as.integer(sp$MarrDate <= sp$ts)
cat(sprintf("%d episodes split into %d pieces\n", nrow(ds), nrow(sp)))
600 episodes split into 761 pieces
# Surv(ts, tf, des), not Surv(tf - ts, des): a piece that begins at ts is
# left truncated, and a model that is told otherwise puts it into risk
# sets it was never in. An exponential rate only ever sees the difference
# and so is unharmed, but the Cox models of ehi3 to ehi5 below are not.
ehf2 <- tda_rate(Surv(ts, tf, des) ~ MARR, sp)
ehf2$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 303 303 76.35313 0 428 FALSE
2 1 0 1 458 458 38.53057 0 350 FALSE
ehf2
Call: tda_rate(formula = Surv(ts, tf, des) ~ MARR, data = sp)
Episodes: 761
Model: exponential (TDA 2)
logLik (starting values): -2514.02
logLik: -2498.534
Converged in 4 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.2129 0.0639 -65.9430 1.0000
2 1 0 1 A MARR -0.5212 0.0937 -5.5629 1.0000
ehf3 <- tda_rate(stats::reformulate(c("MARR", XVARS),
response = "Surv(ts, tf, des)"), sp)
ehf3$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 303 303 76.35313 0 428 FALSE
2 1 0 1 458 458 38.53057 0 350 FALSE
ehf3
Call: tda_rate(formula = stats::reformulate(c("MARR", XVARS), response = "Surv(ts, tf, des)"),
data = sp)
Episodes: 761
Model: exponential (TDA 2)
logLik (starting values): -2514.02
logLik: -2460.248
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.3878 0.2756 -15.9222 1.0000
2 1 0 1 A MARR -0.3447 0.1022 -3.3716 0.9993
3 1 0 1 A EDU 0.0770 0.0246 3.1358 0.9983
4 1 0 1 A COHO2 0.5971 0.1137 5.2534 1.0000
5 1 0 1 A COHO3 0.6138 0.1188 5.1651 1.0000
6 1 0 1 A LFX -0.0023 0.0010 -2.3731 0.9824
7 1 0 1 A PNOJ 0.0645 0.0440 1.4674 0.8577
8 1 0 1 A PRES -0.0274 0.0055 -4.9533 1.0000
ehf4 distinguishes married men from married women.
Box 23ehf4: marriage, by sex [book pp. 150-151, Boxes 6.4.7-6.4.8]
sp$MarrMen <- as.integer(sp$SEX == 1 & sp$MarrDate <= sp$ts)
ehf4 <- tda_rate(stats::reformulate(c(XVARS, "MARR", "MarrMen"),
response = "Surv(ts, tf, des)"), sp)
ehf4
Call: tda_rate(formula = stats::reformulate(c(XVARS, "MARR", "MarrMen"),
response = "Surv(ts, tf, des)"), data = sp)
Episodes: 761
Model: exponential (TDA 2)
logLik (starting values): -2514.02
logLik: -2434.175
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.5842 0.2694 -17.0163 1.0000
2 1 0 1 A EDU 0.0943 0.0234 4.0227 0.9999
3 1 0 1 A COHO2 0.5423 0.1132 4.7916 1.0000
4 1 0 1 A COHO3 0.5304 0.1192 4.4494 1.0000
5 1 0 1 A LFX -0.0031 0.0010 -3.1550 0.9984
6 1 0 1 A PNOJ 0.1029 0.0445 2.3126 0.9793
7 1 0 1 A PRES -0.0264 0.0054 -4.9363 1.0000
8 1 0 1 A MARR 0.2735 0.1231 2.2209 0.9736
9 1 0 1 A MarrMen -1.0231 0.1401 -7.3019 1.0000
ehf4$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 303 303 76.35313 0 428 FALSE
2 1 0 1 458 458 38.53057 0 350 FALSE
ehf6 is the model on the episodes
ehf5 split on a fixed grid – at 60, 120, … 420 months –
so that a covariate measured in calendar time can change within a job.
grid= takes those times; at= is for a split
time that differs per episode, as marriage does. LFX60 is
the sub-episode’s starting time, which is labour force experience
accumulated within the job.
Box 24ehf6: splitting on a fixed grid [book pp. 155-156, Boxes 6.5.3-6.5.4]
sp6$LFX60 <- sp6$ts
cat(sprintf("%d episodes split into %d pieces\n", nrow(d), nrow(sp6)))
600 episodes split into 1021 pieces
ehf6 <- tda_rate(stats::reformulate(c(XVARS, "LFX60"),
response = "Surv(ts, tf, des)"), sp6)
ehf6
Call: tda_rate(formula = stats::reformulate(c(XVARS, "LFX60"), response = "Surv(ts, tf, des)"),
data = sp6)
Episodes: 1021
Model: exponential (TDA 2)
logLik (starting values): -2514.02
logLik: -2432.308
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.0138 0.2764 -14.5194 1.0000
2 1 0 1 A EDU 0.0646 0.0248 2.6051 0.9908
3 1 0 1 A COHO2 0.4073 0.1149 3.5462 0.9996
4 1 0 1 A COHO3 0.2946 0.1216 2.4224 0.9846
5 1 0 1 A LFX -0.0039 0.0009 -4.2429 1.0000
6 1 0 1 A PNOJ 0.0647 0.0440 1.4705 0.8586
7 1 0 1 A PRES -0.0253 0.0054 -4.6474 1.0000
8 1 0 1 A LFX60 -0.0071 0.0010 -7.1098 1.0000
ehf6$episodes # the split episodes, as TDA reports them
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 563 563 51.61812 0 428 FALSE
2 1 0 1 458 458 25.59170 0 350 FALSE
Box 6.5.5 of the book does the same with finer grids – 24 and 12 months – and shows how little the estimates move with the grid.
Box 25Box 6.5.5: the grid at 60, 24 and 12 months [book p. 157, Box 6.5.5]
grid_fit <- function(step) {
spg <- tda_split(with_x("Surv(TFP, DES)"), d, grid = seq(step, 420, by = step))
spg$LFXG <- spg$ts
fit <- tda_rate(stats::reformulate(c(XVARS, "LFXG"),
response = "Surv(ts, tf, des)"), spg)
e <- fit$estimates
stats::setNames(data.frame(e$Coeff, e$`C/Error`), c("Coeff", "T-Stat"))
}
# the three grids side by side, coefficient and t statistic for each
g <- lapply(c(60, 24, 12), grid_fit)
out <- do.call(cbind, g)
names(out) <- paste(rep(c("60", "24", "12"), each = 2), names(out))
rownames(out) <- c("Const", XVARS, "LFXG")
print(format(out, digits = 5))
60 Coeff 60 T-Stat 24 Coeff 24 T-Stat 12 Coeff 12 T-Stat
Const -4.0137800 -14.5194 -3.9498113 -14.1626 -3.9187212 -14.0056
EDU 0.0646116 2.6051 0.0628974 2.5367 0.0626471 2.5264
COHO2 0.4073219 3.5462 0.4059659 3.5276 0.4049094 3.5177
COHO3 0.2946223 2.4224 0.3006367 2.4641 0.2996529 2.4555
LFX -0.0039428 -4.2429 -0.0038975 -4.1976 -0.0038894 -4.1903
PNOJ 0.0647431 1.4705 0.0632658 1.4359 0.0628425 1.4263
PRES -0.0253234 -4.6474 -0.0249290 -4.5755 -0.0248351 -4.5595
LFXG -0.0070767 -7.1098 -0.0063135 -6.9933 -0.0062674 -7.0140
7. Parametric Models of Time-dependence
Book chapter 7, pp. 159-212. Gompertz, Weibull, log-logistic,
log-normal and sickle; command files ehg1 to
ehg13.
Five families, each fitted without covariates and then with them.
on= selects which parameter of the distribution the
covariates attach to — xa, xb or
xc — which is the difference between the
ehg2/ehg3/ ehg4 group of
files.
Box 26The two fits each family gets
fit1 <- function(model, ...)
tda_rate(Surv(TFP, DES) ~ 1, d, model = model, prate = "0(1)300", ...)
fitx <- function(model, on = "xa", ...)
tda_rate(with_x("Surv(TFP, DES)"), d, model = model, on = on, ...)
ehg1, ehg5, ehg8, ehg10 and ehg12 are the null models: Gompertz, Weibull, log-logistic, log-normal and sickle.
Box 27ehg1, ehg5, ehg8, ehg10, ehg12: five families [book pp. 183-210, Boxes 7.2.1-7.2.2, 7.3.1-7.3.2, 7.4.1-7.4.2, 7.5.1-7.5.2, 7.6.1-7.6.2]
models <- c("gompertz", "weibull", "loglogistic", "lognormal", "sickle")
fits <- lapply(models, fit1)
names(fits) <- models
for (m in models) print(fits[[m]])
Call: tda_rate(formula = Surv(TFP, DES) ~ 1, data = d, model = model,
prate = "0(1)300")
Episodes: 600
Model: gompertz (TDA 6)
logLik (starting values): -2557.365
logLik: -2474.506
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 B Constant -4.0729 0.0633 -64.3172 1.0000
2 1 0 1 C Constant -0.0067 0.0009 -7.7201 1.0000
Call: tda_rate(formula = Surv(TFP, DES) ~ 1, data = d, model = model,
prate = "0(1)300")
Episodes: 600
Model: weibull (TDA 7)
logLik (starting values): -2514.02
logLik: -2504.899
Converged in 4 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.4616 0.0544 -82.0244 1.0000
2 1 0 1 B Constant -0.1477 0.0360 -4.1025 1.0000
Call: tda_rate(formula = Surv(TFP, DES) ~ 1, data = d, model = model,
prate = "0(1)300")
Episodes: 600
Model: loglogistic (TDA 9)
logLik (starting values): -2521.785
logLik: -2460.487
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -3.8434 0.0549 -70.0613 1.0000
2 1 0 1 B Constant 0.2918 0.0385 7.5742 1.0000
Call: tda_rate(formula = Surv(TFP, DES) ~ 1, data = d, model = model,
prate = "0(1)300")
Episodes: 600
Model: lognormal (TDA 12)
logLik (starting values): -2603.168
logLik: -2456.138
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant 3.8852 0.0548 70.8561 1.0000
2 1 0 1 B Constant 0.2436 0.0342 7.1252 1.0000
Call: tda_rate(formula = Surv(TFP, DES) ~ 1, data = d, model = model,
prate = "0(1)300")
Episodes: 600
Model: sickle (TDA 8)
logLik (starting values): -2897.762
logLik: -2486.366
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -6.5843 0.0775 -84.9960 1.0000
2 1 0 1 B Constant 3.6467 0.0481 75.8802 1.0000
# all five are fitted to the same episodes, which TDA reports before
# each set of estimates
fits$gompertz$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehg1a, ehg5a and the rest plot each fitted rate, which is where the families differ from each other and from the exponential. All five command files set the same axes – 0 to 300 months against a rate of 0 to 0.02 – so the shapes can be compared directly. The book prints them one to a section, beside the model they belong to: Figures 7.2.2, 7.3.2, 7.4.2, 7.5.2 and 7.6.2, in that order. The exponential’s constant 0.0112 would be a horizontal line across the middle of each.
Box 28ehg1a, ehg5a, ehg8a, ehg10a, ehg12a: the five fitted rates [book pp. 185-211, Fig. 7.2.2, Fig. 7.3.2, Fig. 7.4.2, Fig. 7.5.2, Fig. 7.6.2]
p <- tda_ps(tda_rates(fits$gompertz), file = "ehg1a.ps", width = 80, height = 40,
xlim = c(0, 300), ylim = c(0, 0.02))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 5)
plot(tda_pl_lines(p, "time", "rate"))
p <- tda_ps(tda_rates(fits$weibull), file = "ehg5a.ps", width = 80, height = 40,
xlim = c(0, 300), ylim = c(0, 0.02))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 5)
plot(tda_pl_lines(p, "time", "rate"))
p <- tda_ps(tda_rates(fits$loglogistic), file = "ehg8a.ps", width = 80, height = 40,
xlim = c(0, 300), ylim = c(0, 0.02))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 5)
plot(tda_pl_lines(p, "time", "rate"))
p <- tda_ps(tda_rates(fits$lognormal), file = "ehg10a.ps", width = 80, height = 40,
xlim = c(0, 300), ylim = c(0, 0.02))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 5)
plot(tda_pl_lines(p, "time", "rate"))
p <- tda_ps(tda_rates(fits$sickle), file = "ehg12a.ps", width = 80, height = 40,
xlim = c(0, 300), ylim = c(0, 0.02))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 0.01, ic = 5)
plot(tda_pl_lines(p, "time", "rate"))
ehg2, ehg6, ehg9,
ehg11 and ehg13 add the covariates. In
the Gompertz-Makeham model of ehg2 they go on
xb, the term the rate’s growth depends on, not on the
Makeham constant xa; the other four families put them on
xa.
Box 29ehg2, ehg6, ehg9, ehg11, ehg13: with covariates [book pp. 186-212, Boxes 7.2.3-7.2.4, 7.3.3-7.3.4, 7.4.3-7.4.4, 7.5.3-7.5.4, 7.6.3-7.6.4]
sickle13 <- fitx("sickle")
sickle13$episodes # the same episodes for all five, as TDA reports
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
fitx("gompertz", on = "xb", prate = "0(10)300")
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on, prate = "0(10)300")
Episodes: 600
Model: gompertz (TDA 6)
logLik (starting values): -2557.365
logLik: -2437.975
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 B Constant -3.9105 0.2814 -13.8985 1.0000
2 1 0 1 B EDU 0.0634 0.0248 2.5565 0.9894
3 1 0 1 B COHO2 0.4130 0.1152 3.5849 0.9997
4 1 0 1 B COHO3 0.3119 0.1222 2.5512 0.9893
5 1 0 1 B LFX -0.0039 0.0009 -4.1613 1.0000
6 1 0 1 B PNOJ 0.0627 0.0441 1.4241 0.8456
7 1 0 1 B PRES -0.0250 0.0055 -4.5893 1.0000
8 1 0 1 C Constant -0.0059 0.0009 -6.7272 1.0000
fitx("weibull", prate = "0(10)300")
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on, prate = "0(10)300")
Episodes: 600
Model: weibull (TDA 7)
logLik (starting values): -2514.02
logLik: -2462.759
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.4063 0.3060 -14.3996 1.0000
2 1 0 1 A EDU 0.0779 0.0270 2.8848 0.9961
3 1 0 1 A COHO2 0.6062 0.1242 4.8821 1.0000
4 1 0 1 A COHO3 0.5777 0.1303 4.4318 1.0000
5 1 0 1 A LFX -0.0036 0.0010 -3.5040 0.9995
6 1 0 1 A PNOJ 0.0637 0.0484 1.3184 0.8126
7 1 0 1 A PRES -0.0292 0.0060 -4.8396 1.0000
8 1 0 1 B Constant -0.0903 0.0364 -2.4830 0.9870
fitx("loglogistic")
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on)
Episodes: 600
Model: loglogistic (TDA 9)
logLik (starting values): -2521.785
logLik: -2418.904
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -3.7785 0.2926 -12.9144 1.0000
2 1 0 1 A EDU 0.0819 0.0269 3.0485 0.9977
3 1 0 1 A COHO2 0.5338 0.1249 4.2726 1.0000
4 1 0 1 A COHO3 0.4005 0.1318 3.0382 0.9976
5 1 0 1 A LFX -0.0043 0.0009 -4.6566 1.0000
6 1 0 1 A PNOJ 0.0970 0.0476 2.0368 0.9583
7 1 0 1 A PRES -0.0297 0.0056 -5.2900 1.0000
8 1 0 1 B Constant 0.3615 0.0386 9.3736 1.0000
fitx("lognormal")
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on)
Episodes: 600
Model: lognormal (TDA 12)
logLik (starting values): -2603.168
logLik: -2415.882
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant 3.8567 0.2916 13.2280 1.0000
2 1 0 1 A EDU -0.0813 0.0270 -3.0127 0.9974
3 1 0 1 A COHO2 -0.5164 0.1247 -4.1422 1.0000
4 1 0 1 A COHO3 -0.4514 0.1295 -3.4868 0.9995
5 1 0 1 A LFX 0.0039 0.0009 4.4413 1.0000
6 1 0 1 A PNOJ -0.0849 0.0464 -1.8296 0.9327
7 1 0 1 A PRES 0.0287 0.0056 5.1330 1.0000
8 1 0 1 B Constant 0.1794 0.0342 5.2437 1.0000
fitx("sickle")
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on)
Episodes: 600
Model: sickle (TDA 8)
logLik (starting values): -2897.762
logLik: -2443.935
Converged in 8 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -6.4245 0.2831 -22.6909 1.0000
2 1 0 1 A EDU 0.0767 0.0250 3.0711 0.9979
3 1 0 1 A COHO2 0.4259 0.1149 3.7073 0.9998
4 1 0 1 A COHO3 0.2785 0.1206 2.3095 0.9791
5 1 0 1 A LFX -0.0043 0.0009 -4.5725 1.0000
6 1 0 1 A PNOJ 0.0782 0.0438 1.7867 0.9260
7 1 0 1 A PRES -0.0288 0.0055 -5.1914 1.0000
8 1 0 1 B Constant 3.6897 0.0502 73.4793 1.0000
ehg3 and ehg4 put the same
covariates on the other parameters as well: xb and
xc, then all three. ehg7 does the same for
the Weibull, on xa and xb.
Box 30ehg3, ehg4, ehg7: covariates on more than one term [book pp. 188-197, Boxes 7.2.5-7.2.8, 7.3.5-7.3.6]
ehg4 <- fitx("gompertz", on = c("xa", "xb", "xc"))
ehg4$episodes # the same episodes for every model in this group
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
fitx("gompertz", on = c("xb", "xc"))
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on)
Episodes: 600
Model: gompertz (TDA 6)
logLik (starting values): -2557.365
logLik: -2433.387
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 B Constant -4.0167 0.3663 -10.9652 1.0000
2 1 0 1 B EDU 0.0918 0.0318 2.8843 0.9961
3 1 0 1 B COHO2 0.5074 0.1547 3.2789 0.9990
4 1 0 1 B COHO3 0.2069 0.1687 1.2269 0.7801
5 1 0 1 B LFX -0.0051 0.0013 -3.9165 0.9999
6 1 0 1 B PNOJ 0.1513 0.0604 2.5060 0.9878
7 1 0 1 B PRES -0.0323 0.0071 -4.5315 1.0000
8 1 0 1 C Constant -0.0054 0.0057 -0.9497 0.6577
9 1 0 1 C EDU -0.0005 0.0005 -1.1416 0.7464
10 1 0 1 C COHO2 -0.0019 0.0022 -0.8685 0.6149
11 1 0 1 C COHO3 0.0030 0.0028 1.0626 0.7120
12 1 0 1 C LFX 0.0000 0.0000 1.3730 0.8303
13 1 0 1 C PNOJ -0.0019 0.0010 -2.0254 0.9572
14 1 0 1 C PRES 0.0002 0.0001 1.6056 0.8916
fitx("gompertz", on = c("xa", "xb", "xc"))
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on)
Episodes: 600
Model: gompertz (TDA 6)
logLik (starting values): -2538.685
logLik: -2423.848
Converged in 18 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -14.9029 3.6611 -4.0706 1.0000
2 1 0 1 A EDU -0.1348 0.2272 -0.5932 0.4470
3 1 0 1 A COHO2 2.2350 1.7415 1.2834 0.8006
4 1 0 1 A COHO3 -3.4706 14.2396 -0.2437 0.1926
5 1 0 1 A LFX 0.0162 0.0082 1.9770 0.9520
6 1 0 1 A PNOJ -2.9917 0.8958 -3.3397 0.9992
7 1 0 1 A PRES 0.2087 0.0601 3.4714 0.9995
8 1 0 1 B Constant -3.8740 0.3855 -10.0491 1.0000
9 1 0 1 B EDU 0.0988 0.0334 2.9581 0.9969
10 1 0 1 B COHO2 0.4783 0.1673 2.8581 0.9957
11 1 0 1 B COHO3 0.2640 0.1741 1.5164 0.8706
12 1 0 1 B LFX -0.0057 0.0014 -3.9031 0.9999
13 1 0 1 B PNOJ 0.1898 0.0624 3.0432 0.9977
14 1 0 1 B PRES -0.0407 0.0080 -5.1086 1.0000
15 1 0 1 C Constant -0.0045 0.0064 -0.7146 0.5251
16 1 0 1 C EDU -0.0005 0.0005 -1.0523 0.7073
17 1 0 1 C COHO2 -0.0024 0.0024 -0.9801 0.6729
18 1 0 1 C COHO3 0.0031 0.0029 1.0850 0.7221
19 1 0 1 C LFX 0.0000 0.0000 1.4054 0.8401
20 1 0 1 C PNOJ -0.0018 0.0010 -1.8275 0.9324
21 1 0 1 C PRES 0.0002 0.0001 1.2268 0.7801
fitx("weibull", on = c("xa", "xb"))
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = model,
on = on)
Episodes: 600
Model: weibull (TDA 7)
logLik (starting values): -2514.02
logLik: -2459.595
Converged in 7 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -4.4951 0.3073 -14.6293 1.0000
2 1 0 1 A EDU 0.0858 0.0274 3.1351 0.9983
3 1 0 1 A COHO2 0.5985 0.1285 4.6586 1.0000
4 1 0 1 A COHO3 0.5582 0.1297 4.3040 1.0000
5 1 0 1 A LFX -0.0032 0.0011 -2.9481 0.9968
6 1 0 1 A PNOJ 0.0571 0.0496 1.1512 0.7503
7 1 0 1 A PRES -0.0290 0.0061 -4.7176 1.0000
8 1 0 1 B Constant -0.1613 0.2224 -0.7252 0.5317
9 1 0 1 B EDU -0.0027 0.0188 -0.1441 0.1146
10 1 0 1 B COHO2 0.0835 0.0856 0.9751 0.6705
11 1 0 1 B COHO3 0.2017 0.0914 2.2051 0.9726
12 1 0 1 B LFX 0.0010 0.0007 1.4443 0.8513
13 1 0 1 B PNOJ -0.0385 0.0363 -1.0614 0.7115
14 1 0 1 B PRES 0.0005 0.0041 0.1249 0.0994
8. Methods to Check Parametric Assumptions
Book chapter 8, pp. 213-228. The transformed survivor function
and the pseudoresiduals; command files ehh1 to
ehh4 and the *s/*p
pairs.
Each of these transforms the survivor function so that one distribution appears as a straight line, and regresses it on time. The closer to a line, the better that distribution fits.
The regression runs on the survivor function at five decimals, the
precision ehc5.ple carries: the command files read that
file rather than the estimate in memory, and the transformations are
sensitive enough for it to show in the t statistics.
The axes are each command file’s own.
Box 31ehh1 to ehh4: the four transformations [book pp. 215-218, Boxes 8.1.1-8.1.3]
s <- tda_survivor(pl, conf.int = NULL)
s <- s[s$survivor > 0 & s$survivor < 1 & s$time > 0, ]
s$survivor <- round(s$survivor, 5) # the precision ehc5.ple carries
checks <- list(
Exponential = list(y = log(s$survivor), x = s$time,
xlim = c(0, 300), ylim = c(-3, 0), xsc = 60, ysc = 1),
Weibull = list(y = log(-log(s$survivor)), x = log(s$time),
xlim = c(0, 6), ylim = c(-6, 2), xsc = 1, ysc = 1),
`Log-Logistic` = list(y = log(1 / s$survivor - 1), x = log(s$time),
xlim = c(0, 6), ylim = c(-6, 2), xsc = 1, ysc = 1),
`Log-Normal` = list(y = stats::qnorm(1 - s$survivor), x = log(s$time),
xlim = c(0, 6), ylim = c(-3, 2), xsc = 1, ysc = 1))
ehh <- lapply(checks, function(z) {
keep <- is.finite(z$y) & is.finite(z$x)
tda_lsreg(y ~ x, data.frame(x = z$x[keep], y = z$y[keep]))
})
# each regression in full: standard errors and t statistics with the
# two coefficients
for (nm in names(ehh)) {
cat("\n", nm, "\n", sep = "")
print(ehh[[nm]])
}
Exponential
Call: tda_lsreg(formula = y ~ x, data = data.frame(x = z$x[keep], y = z$y[keep]))
Cases: 130
Idx Wave Variable Coeff Error C/Error Signif
1 NA Intercept -0.3527 0.0280 -12.6000 1.0000
2 1.0000 x -0.0075 0.0002 -30.7721 1.0000
Weibull
Call: tda_lsreg(formula = y ~ x, data = data.frame(x = z$x[keep], y = z$y[keep]))
Cases: 130
Idx Wave Variable Coeff Error C/Error Signif
1 NA Intercept -4.4238 0.1135 -38.9803 1.0000
2 1.0000 x 1.0080 0.0270 37.3474 1.0000
Log-Logistic
Call: tda_lsreg(formula = y ~ x, data = data.frame(x = z$x[keep], y = z$y[keep]))
Cases: 130
Idx Wave Variable Coeff Error C/Error Signif
1 NA Intercept -5.1604 0.0771 -66.9338 1.0000
2 1.0000 x 1.3268 0.0183 72.3629 1.0000
Log-Normal
Call: tda_lsreg(formula = y ~ x, data = data.frame(x = z$x[keep], y = z$y[keep]))
Cases: 130
Idx Wave Variable Coeff Error C/Error Signif
1 NA Intercept -2.9220 0.0301 -97.2089 1.0000
2 1.0000 x 0.7578 0.0071 105.9948 1.0000
The slope of the exponential line is the rate the transformation
implies, 0.0075, against the maximum likelihood estimate of
ehd1. Each command file ends by drawing its own fitted line
over the curve, the endpoints written into plotp by hand
(plotp = 0, -0.3527, 300, -2.6027 in ehh1.cf);
here the line comes from the fit above, and the four panels are laid out
as the book’s Figure 8.1.1.
Box 32ehh1, ehh2, ehh3, ehh4: the transformed survivor functions [book pp. 215-217, Boxes 8.1.1-8.1.2; Fig. 8.1.1]
z <- checks$`Exponential`
b <- coef(ehh$`Exponential`)
p <- tda_ps(data.frame(x = z$x, y = z$y), file = "ehh1.ps",
width = 80, height = 40, xlim = c(0, 300), ylim = c(-3, 0))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
p <- tda_pl(p, "plframe")
p <- tda_pl_lines(p, "x", "y")
p <- tda_pl_polyline(p, c(0, 300), b[[1L]] + b[[2L]] * c(0, 300))
ehh1 <- tda_pl_text(p, "Graphical Test (Exponential)", at = c(150.0, -0.30000000000000004))
z <- checks$`Weibull`
b <- coef(ehh$`Weibull`)
p <- tda_ps(data.frame(x = z$x, y = z$y), file = "ehh2.ps",
width = 80, height = 40, xlim = c(0, 6), ylim = c(-6, 2))
p <- tda_pl(p, "plxa", sc = 1, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 2)
p <- tda_pl(p, "plframe")
p <- tda_pl_lines(p, "x", "y")
p <- tda_pl_polyline(p, c(0, 6), b[[1L]] + b[[2L]] * c(0, 6))
ehh2 <- tda_pl_text(p, "Graphical Test (Weibull)", at = c(3.0, 1.2))
z <- checks$`Log-Logistic`
b <- coef(ehh$`Log-Logistic`)
p <- tda_ps(data.frame(x = z$x, y = z$y), file = "ehh3.ps",
width = 80, height = 40, xlim = c(0, 6), ylim = c(-6, 2))
p <- tda_pl(p, "plxa", sc = 1, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 2)
p <- tda_pl(p, "plframe")
p <- tda_pl_lines(p, "x", "y")
p <- tda_pl_polyline(p, c(0, 6), b[[1L]] + b[[2L]] * c(0, 6))
ehh3 <- tda_pl_text(p, "Graphical Test (Log-Logistic)", at = c(3.0, 1.2))
z <- checks$`Log-Normal`
b <- coef(ehh$`Log-Normal`)
p <- tda_ps(data.frame(x = z$x, y = z$y), file = "ehh4.ps",
width = 80, height = 40, xlim = c(0, 6), ylim = c(-3, 2))
p <- tda_pl(p, "plxa", sc = 1, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 2)
p <- tda_pl(p, "plframe")
p <- tda_pl_lines(p, "x", "y")
p <- tda_pl_polyline(p, c(0, 6), b[[1L]] + b[[2L]] * c(0, 6))
ehh4 <- tda_pl_text(p, "Graphical Test (Log-Normal)", at = c(3.0, 1.5))
r <- tda_combine_ps(list(list(ehh1, ehh2), list(ehh3, ehh4)), width = 170, height = 90)
tda_plot_ps(tda_read_ps(file.path(r$dir, "combined.ps")))
The book’s section 8.2 checks a model through its pseudoresiduals, in
three steps: residuals = TRUE writes them
(ehd2.res, Box 8.2.3); a product-limit estimate treats them
as durations (ehd2s.cf, Box 8.2.5); and
ehd2p.cf plots log S against the residual. If
the model is right the residuals are standard exponential, so that curve
should lie on a straight line of slope -1 through the origin.
ehd2p.cf draws that line beside it,
plotp = 0,0,3,-3; the plot is read as the distance between
the two.
Box 33ehd2.res, ehd2s: the pseudoresiduals and their survivor function [book pp. 97-225, Boxes 4.1.4-4.1.5, 8.2.1, 8.2.3-8.2.5]
ehd2r <- tda_rate(with_x("Surv(TFP, DES)"), d, residuals = TRUE)
head(ehd2r$residuals, 10)
Case Org Des TS TF Rate Function Residual Weight
1 1 0 0 0 428 0.016122457 0.001007371 6.9004117 1
2 2 0 1 0 46 0.013133700 0.546538678 0.6041502 1
3 3 0 1 0 34 0.006149795 0.811319744 0.2090930 1
4 4 0 1 0 220 0.005858903 0.275557591 1.2889586 1
5 5 0 1 0 12 0.015308310 0.832185660 0.1836997 1
6 6 0 1 0 30 0.015640803 0.625487409 0.4692241 1
7 7 0 1 0 12 0.013875494 0.846617799 0.1665059 1
8 8 0 1 0 75 0.014176867 0.345326480 1.0632650 1
9 9 0 1 0 12 0.011855660 0.867388832 0.1422679 1
10 10 0 1 0 55 0.012105645 0.513856882 0.6658105 1
tail(ehd2r$residuals, 6)
Case Org Des TS TF Rate Function Residual Weight
595 595 0 1 0 119 0.008084062 0.3821266 0.9620033 1
596 596 0 1 0 66 0.005404227 0.6999972 0.3566790 1
597 597 0 0 0 112 0.005058187 0.5674986 0.5665169 1
598 598 0 0 0 103 0.017055411 0.1726123 1.7567073 1
599 599 0 1 0 22 0.019696756 0.6483474 0.4333286 1
600 600 0 1 0 13 0.019126466 0.7798575 0.2486441 1
rp <- tda_ple(Surv(Residual, Des) ~ 1, ehd2r$residuals)
rp$episodes # the residuals treated as durations
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 1.2893610 0 6.900412 FALSE
2 1 0 1 458 458 0.6002418 0 3.595082 FALSE
print(rp, n = 10)
Call: tda_ple(formula = Surv(Residual, Des) ~ 1, data = ehd2r$residuals)
SN 1. Transition: 0,1 - Product-Limit Estimation
id index time events censored n.risk survivor std.err cum.rate
0 0 0.00000000 0 0 600 1.0000000 0.000000000 0.000000000
0 1 0.01860143 1 1 599 0.9983306 0.001668055 0.001670844
0 2 0.01961462 1 0 598 0.9966611 0.002357013 0.003344485
0 3 0.03639071 1 2 595 0.9949860 0.002887553 0.005026571
0 4 0.03860349 1 0 594 0.9933110 0.003333319 0.006711491
0 5 0.04352060 1 1 592 0.9916331 0.003726134 0.008402109
0 6 0.04435162 1 0 591 0.9899552 0.004080157 0.010095589
0 7 0.04449827 1 0 590 0.9882773 0.004404752 0.011791943
0 8 0.04466022 1 0 589 0.9865994 0.004706012 0.013491178
0 9 0.04862678 1 0 588 0.9849215 0.004988167 0.015193306
... 447 more rows
Median duration: 0.5141081
Duration times limited to: 3.595082
Cases: 600 weighted: 600
Box 34ehd2p: log S against the residual, with its reference line [book p. 226, Box 8.2.6]
resid_panel <- function(fit, label, file) {
rp <- tda_ple(Surv(Residual, Des) ~ 1, fit$residuals)
rs <- tda_survivor(rp)
rs <- rs[rs$time > 0 & rs$survivor > 0 & rs$survivor < 1, ]
rs$LogSurv <- log(rs$survivor)
p <- tda_ps(rs, file = file, width = 80, height = 40,
xlim = c(0, 4), ylim = c(-3, 0))
p <- tda_pl(p, "plxa", sc = 1, ic = 0)
p <- tda_pl(p, "plya", sc = 1, ic = 0)
p <- tda_pl_lines(tda_pl(p, "plframe"), "time", "LogSurv")
p <- tda_pl_polyline(p, c(0, 3), c(0, -3)) # the standard exponential
tda_pl_text(p, label, at = c(1.5, -0.7))
}
plot(resid_panel(ehd2r, "Exponential Model", "ehd2p.ps"))
The exponential curve sits above the reference at the start, crosses
it around 1.7 and falls away below it after that – the same departure
the transformed survivor function showed in ehh1.
Box 8.2.6’s footnote lists the command files for the other models, and the book’s Figure 8.2.1 puts eight of them side by side. Each is the same three steps on a different fit.
Box 35ehd2p to ehg13p: the pseudoresidual check [book pp. 226-227, Box 8.2.6; Fig. 8.2.1]
resid_fits <- list(
"Exponential Model" = function()
tda_rate(with_x("Surv(TFP, DES)"), d, residuals = TRUE),
"Piecewise Constant Exponential (I)" = function()
pc(with_x("Surv(TFP, DES)"), d, residuals = TRUE),
"Piecewise Constant Exponential (II)" = function()
pc(with_x("Surv(TFP, DES)"), d, model = "exponential_periods2",
tp = "0,24,60", residuals = TRUE),
"Gompertz Model" = function()
fitx("gompertz", on = "xb", residuals = TRUE),
"Weibull Model" = function() fitx("weibull", residuals = TRUE),
"Log-logistic Model" = function() fitx("loglogistic", residuals = TRUE),
"Log-normal Model" = function() fitx("lognormal", residuals = TRUE),
"Sickle Model" = function() fitx("sickle", residuals = TRUE))
# the arrangement of Figure 8.2.1: exponential, Gompertz, Weibull and
# sickle down the left column, the two piecewise constant models,
# log-logistic and log-normal down the right
i <- 0L
panels <- lapply(names(resid_fits), function(nm) {
i <<- i + 1L
resid_panel(resid_fits[[nm]](), nm, sprintf("res%02d.ps", i))
})
r <- tda_combine_ps(split(panels[c(1, 2, 4, 3, 5, 6, 8, 7)], rep(1:4, each = 2)), width = 170, height = 180)
tda_plot_ps(tda_read_ps(file.path(r$dir, "combined.ps")))
The book reads these as the exponential and the Weibull departing most from the reference, the Gompertz, log-logistic and log-normal tracking it closely, and the two piecewise constant models closest of all. They are hints for model selection rather than a goodness-of-fit test: the log-logistic, log-normal and piecewise constant models resemble each other so closely here that the choice between them has to rest on something other than these plots.
9. Semi-parametric Transition Rate Models
Book chapter 9, pp. 229-251. Cox models; command files
ehi1 to ehi13.
ehi1 is the Cox model with the book’s covariates.
tda_coxph() is tda_rate(model = 1) under a
name that says what it is.
Box 36ehi1: a Cox model [book pp. 232-243, Boxes 9.1.2, 9.3.3]
ehi1 <- tda_coxph(with_x("Surv(TFP, DES)"), d)
summary(ehi1)
Call: tda_coxph(formula = with_x("Surv(TFP, DES)"), data = d)
Episodes: 600
Model: cox
Converged in 5 iterations
Estimate Std. Error C/Error Pr(>|t|)
EDU 0.06685932 0.02491489 2.6835 0.0072854 **
COHO2 0.41130737 0.11534702 3.5658 0.0003627 ***
COHO3 0.30525142 0.12198338 2.5024 0.0123354 *
LFX -0.00398905 0.00093243 -4.2781 1.885e-05 ***
PNOJ 0.06862675 0.04419255 1.5529 0.1204463
PRES -0.02616777 0.00550043 -4.7574 1.961e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
logLik (starting values): -2584.57
logLik: -2546.776 df: 6 AIC: 5105.551
Null model (exponential, constant rate) logLik: -2514.02
ehi1$episodes # the episode table TDA reports first
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehi2 adds marriage as a time-varying covariate.
time is TDA’s position within the episode, so the
definition has to be evaluated inside TDA rather than computed here;
helpers= writes MDate for it to refer to,
without MDate entering the model.
Box 37ehi2: marriage as a time-varying covariate [book pp. 233-235, Boxes 9.1.3, 9.2.1-9.2.2]
dm <- d
dm$MDate <- ifelse(dm$TMAR <= 0, 10000, dm$TMAR - dm$TStart)
ehi2 <- tda_coxph(stats::reformulate(c(XVARS, "MARR"),
response = "Surv(TFP, DES)"), dm,
helpers = "MDate",
define = list(MARR = "gt(time,MDate)"))
ehi2
Call: tda_coxph(formula = stats::reformulate(c(XVARS, "MARR"), response = "Surv(TFP, DES)"),
data = dm, helpers = "MDate", define = list(MARR = "gt(time,MDate)"))
Episodes: 600
Model: cox (TDA 1)
logLik (starting values): -2584.57
logLik: -2546.622
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A EDU 0.0665 0.0249 2.6647 0.9923
2 1 0 1 A COHO2 0.4056 0.1158 3.5038 0.9995
3 1 0 1 A COHO3 0.2901 0.1249 2.3225 0.9798
4 1 0 1 A LFX -0.0042 0.0010 -4.1789 1.0000
5 1 0 1 A PNOJ 0.0674 0.0443 1.5220 0.8720
6 1 0 1 A PRES -0.0263 0.0055 -4.7804 1.0000
7 1 0 1 A MARR 0.0660 0.1192 0.5541 0.4205
ehi2$episodes # the episode table TDA reports first
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehi3, ehi4 and
ehi5 are Cox models on the split episodes of
ehf – covariates alone, then with marriage, then
distinguishing married men.
Box 38ehi3, ehi4, ehi5: Cox models on the split episodes [book pp. 237-239, Boxes 9.2.3-9.2.6]
ehi3 <- tda_coxph(stats::reformulate(XVARS,
response = "Surv(ts, tf, des)"), sp)
ehi3
Call: tda_coxph(formula = stats::reformulate(XVARS, response = "Surv(ts, tf, des)"),
data = sp)
Episodes: 761
Model: cox (TDA 1)
logLik (starting values): -2584.57
logLik: -2546.776
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A EDU 0.0669 0.0249 2.6835 0.9927
2 1 0 1 A COHO2 0.4113 0.1153 3.5658 0.9996
3 1 0 1 A COHO3 0.3053 0.1220 2.5024 0.9877
4 1 0 1 A LFX -0.0040 0.0009 -4.2781 1.0000
5 1 0 1 A PNOJ 0.0686 0.0442 1.5529 0.8796
6 1 0 1 A PRES -0.0262 0.0055 -4.7574 1.0000
ehi3$episodes # all three are fitted to these same split episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 303 303 76.35313 0 428 FALSE
2 1 0 1 458 458 38.53057 0 350 FALSE
tda_coxph(stats::reformulate(c(XVARS, "MARR"),
response = "Surv(ts, tf, des)"), sp)
Call: tda_coxph(formula = stats::reformulate(c(XVARS, "MARR"), response = "Surv(ts, tf, des)"),
data = sp)
Episodes: 761
Model: cox (TDA 1)
logLik (starting values): -2584.57
logLik: -2546.622
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A EDU 0.0665 0.0249 2.6647 0.9923
2 1 0 1 A COHO2 0.4056 0.1158 3.5038 0.9995
3 1 0 1 A COHO3 0.2901 0.1249 2.3225 0.9798
4 1 0 1 A LFX -0.0042 0.0010 -4.1789 1.0000
5 1 0 1 A PNOJ 0.0674 0.0443 1.5220 0.8720
6 1 0 1 A PRES -0.0263 0.0055 -4.7804 1.0000
7 1 0 1 A MARR 0.0660 0.1192 0.5541 0.4205
tda_coxph(stats::reformulate(c(XVARS, "MARR", "MarrMen"),
response = "Surv(ts, tf, des)"), sp)
Call: tda_coxph(formula = stats::reformulate(c(XVARS, "MARR", "MarrMen"),
response = "Surv(ts, tf, des)"), data = sp)
Episodes: 761
Model: cox (TDA 1)
logLik (starting values): -2584.57
logLik: -2528.446
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A EDU 0.0792 0.0241 3.2867 0.9990
2 1 0 1 A COHO2 0.3767 0.1158 3.2537 0.9989
3 1 0 1 A COHO3 0.2928 0.1243 2.3556 0.9815
4 1 0 1 A LFX -0.0045 0.0010 -4.4408 1.0000
5 1 0 1 A PNOJ 0.1012 0.0449 2.2524 0.9757
6 1 0 1 A PRES -0.0252 0.0054 -4.6858 1.0000
7 1 0 1 A MARR 0.5046 0.1303 3.8733 0.9999
8 1 0 1 A MarrMen -0.8608 0.1420 -6.0624 1.0000
ehi7 and ehi8 ask the proportionality question twice: once as an interaction with log time, once by letting the effect vary between periods. Either way, a proportional effect is one that does not move.
Box 39ehi7, ehi8: proportional hazards [book pp. 243-245, Boxes 9.3.2, 9.3.4-9.3.5]
dw <- d
dw$Women <- as.integer(dw$SEX == 2)
ehi7 <- tda_coxph(stats::reformulate(c(XVARS, "Women", "WTest"),
response = "Surv(TFP, DES)"), dw,
define = list(WTest = "Women * (log(time) - 4.73)"))
ehi7
Call: tda_coxph(formula = stats::reformulate(c(XVARS, "Women", "WTest"),
response = "Surv(TFP, DES)"), data = dw, define = list(WTest = "Women * (log(time) - 4.73)"))
Episodes: 600
Model: cox (TDA 1)
logLik (starting values): -2584.57
logLik: -2536.272
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A EDU 0.0803 0.0247 3.2566 0.9989
2 1 0 1 A COHO2 0.3937 0.1155 3.4097 0.9993
3 1 0 1 A COHO3 0.2716 0.1222 2.2231 0.9738
4 1 0 1 A LFX -0.0041 0.0009 -4.3657 1.0000
5 1 0 1 A PNOJ 0.0888 0.0447 1.9872 0.9531
6 1 0 1 A PRES -0.0259 0.0054 -4.7654 1.0000
7 1 0 1 A Women 0.7111 0.1607 4.4244 1.0000
8 1 0 1 A WTest 0.2587 0.0987 2.6211 0.9912
ehi7$episodes
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehi8 <- tda_coxph(stats::reformulate(c(XVARS, "Women"),
response = "Surv(TFP, DES)"), dw,
options = list(tp = "0 (12) 96"))
summary(ehi8) # the global goodness-of-fit test comes with the summary
Call: tda_coxph(formula = stats::reformulate(c(XVARS, "Women"), response = "Surv(TFP, DES)"),
data = dw, options = list(tp = "0 (12) 96"))
Episodes: 600
Model: cox
Converged in 5 iterations
Estimate Std. Error C/Error Pr(>|t|)
EDU 0.07632115 0.02466457 3.0944 0.0019724 **
COHO2 0.38596002 0.11536101 3.3457 0.0008208 ***
COHO3 0.29586568 0.12199590 2.4252 0.0152995 *
LFX -0.00408664 0.00093302 -4.3800 1.187e-05 ***
PNOJ 0.09030560 0.04482867 2.0145 0.0439612 *
PRES -0.02492391 0.00542188 -4.5969 4.288e-06 ***
Women 0.36988945 0.09763713 3.7884 0.0001516 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
logLik (starting values): -2584.57
logLik: -2539.679 df: 7 AIC: 5093.358
Null model (exponential, constant rate) logLik: -2514.02
Global goodness-of-fit:
sn org des statistic df significance
1 0 1 62.92011 56 0.7552702
ehi6 asks it graphically instead:
log(-log(S)) per sex, parallel if the hazards are
proportional. The axes, tick spacing and the two labels are
ehi6.cf’s own.
Box 40ehi6: the graphical check [book pp. 241-242, Box 9.3.1; Fig. 9.3.1]
lls <- tda_survivor(pl2, conf.int = NULL)
lls <- lls[lls$survivor > 0 & lls$survivor < 1, ]
lls$LLS <- log(-log(lls$survivor))
p <- tda_ps(lls, width = 80, height = 40, xlim = c(0, 240), ylim = c(-5, 1))
p <- tda_pl(p, "plxa", sc = 24, ic = 0)
p <- tda_pl(p, "plya", sc = 1, ic = 0)
p <- tda_pl_lines(p, "time", "LLS", by = "group", lty = 1) # ehi6.cf sets no lt=
p <- tda_pl_text(p, "Women", at = c(192, 0.6))
plot(tda_pl_text(p, "Men", at = c(216, 0.1)))
ehi9 gives each sex its baseline rate instead of assuming one.
Box 41ehi9: a stratified Cox model [book pp. 246-247, Boxes 9.3.6-9.3.7]
ehi9 <- tda_coxph(stats::reformulate(c(XVARS, "strata(SEX)"),
response = "Surv(TFP, DES)"), d)
ehi9
Call: tda_coxph(formula = stats::reformulate(c(XVARS, "strata(SEX)"),
response = "Surv(TFP, DES)"), data = d)
Episodes: 600
Model: cox (TDA 1)
logLik (starting values): -2259.567
logLik: -2224.632
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A EDU 0.0793 0.0248 3.2013 0.9986
2 1 0 1 A COHO2 0.3975 0.1156 3.4391 0.9994
3 1 0 1 A COHO3 0.2801 0.1226 2.2848 0.9777
4 1 0 1 A LFX -0.0040 0.0009 -4.3158 1.0000
5 1 0 1 A PNOJ 0.0889 0.0448 1.9856 0.9529
6 1 0 1 A PRES -0.0258 0.0054 -4.7261 1.0000
ehi9$episodes # one block per stratum, as TDA reports them
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
ehi10 evaluates the baseline rate and its cumulation, ehi11 draws it, ehi12 smooths it with a spline and ehi13 draws the smoothed rate.
Box 42ehi10: the baseline rate, two cohorts [book pp. 249-250, Boxes 9.4.1-9.4.2]
# ehi10.cf asks for two constellations, cohort 1 and cohort 3, both
# written to ehi10.bl; they come back as sub-tables of $rates, told
# apart by ID
ehi10 <- tda_coxph(with_x("Surv(TFP, DES)"), d,
prate = list(
list(EDU = 13, COHO2 = 0, COHO3 = 0,
LFX = 5, PNOJ = 1, PRES = 30),
list(EDU = 13, COHO2 = 0, COHO3 = 1,
LFX = 5, PNOJ = 1, PRES = 30)))
ehi10
Call: tda_coxph(formula = with_x("Surv(TFP, DES)"), data = d, prate = list(list(EDU = 13,
COHO2 = 0, COHO3 = 0, LFX = 5, PNOJ = 1, PRES = 30), list(EDU = 13,
COHO2 = 0, COHO3 = 1, LFX = 5, PNOJ = 1, PRES = 30)))
Episodes: 600
Model: cox (TDA 1)
logLik (starting values): -2584.57
logLik: -2546.776
Converged in 5 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A EDU 0.0669 0.0249 2.6835 0.9927
2 1 0 1 A COHO2 0.4113 0.1153 3.5658 0.9996
3 1 0 1 A COHO3 0.3053 0.1220 2.5024 0.9877
4 1 0 1 A LFX -0.0040 0.0009 -4.2781 1.0000
5 1 0 1 A PNOJ 0.0686 0.0442 1.5529 0.8796
6 1 0 1 A PRES -0.0262 0.0055 -4.7574 1.0000
ehi10$episodes # the episodes behind the baseline
sn org des episodes weighted mean.duration ts.min tf.max excluded
1 1 0 0 142 142 128.17606 0 428 FALSE
2 1 0 1 458 458 49.30349 0 350 FALSE
bl <- ehi10$rates
cols <- c("ID", "Time", "RiskSet", "CumRate", "BaselineRate")
head(bl[, cols], 10)
ID Time RiskSet CumRate BaselineRate
1 0 0 600 0.000000000 0.000000000
2 0 2 600 0.004324803 0.001893449
3 0 3 597 0.015201628 0.009524000
4 0 4 590 0.034991229 0.017328233
5 0 5 581 0.041721469 0.005893155
6 0 6 577 0.064307187 0.019776577
7 0 7 567 0.085179004 0.018275846
8 0 8 557 0.099372708 0.012428336
9 0 9 548 0.116206159 0.014739759
10 0 10 540 0.135804437 0.017160707
tail(bl[bl$ID == 0, cols], 8)
ID Time RiskSet CumRate BaselineRate
124 0 215 39 2.811890 0.0068633098
125 0 220 38 2.851719 0.0069749534
126 0 275 26 2.909198 0.0009150944
127 0 293 20 2.986824 0.0037761719
128 0 312 16 3.076748 0.0041441830
129 0 326 14 3.176270 0.0062245309
130 0 332 11 3.298348 0.0178158501
131 0 350 9 3.443908 0.0070808389
ehi11.cf draws the two cumulated rates against time (the
book’s Figure 9.4.1).
Box 43ehi11: the two cohorts’ cumulated rates [book p. 250, Fig. 9.4.1]
p <- tda_ps(bl, width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 5))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 0)
p <- tda_pl_lines(p, "Time", "CumRate", by = "ID", select = "0", lty = 1)
p <- tda_pl_lines(p, "Time", "CumRate", by = "ID", select = "1", lty = 5)
p <- tda_pl_text(p, "COHO1", at = c(240, 2.3))
plot(tda_pl_text(p, "COHO3", at = c(240, 4.2)))
The rate itself jumps at every event time, so the book does not
smooth it directly. ehi12 smooths the cumulative
rate – column 7 of ehi10.bl, the one for cohort 1 – with a
cubic spline over a half-month grid, and ehi13 plots that
spline’s first derivative, which is the rate.
Box 44ehi12: the cumulated rate smoothed [book p. 251]
# ehi12.cf selects the first sub-table, cohort 1
b1 <- bl[bl$ID == 0, ]
sm <- tda_spl(b1$Time, b1$CumRate, sig = 0.2, deg = 3,
rx = seq(0, 300, by = 0.5))$table
head(sm[, c("x", "fitted", "d1")], 4)
x fitted d1
1 0.0 -0.026485261 0.01191835
2 0.5 -0.020365553 0.01255804
3 1.0 -0.013929678 0.01318301
4 1.5 -0.007184992 0.01379328
ehi13.cf draws the smoother’s first derivative, which is
the rate itself (the book’s Figure 9.4.2).
Box 45ehi13: the smoothed baseline rate [book p. 251, Fig. 9.4.2]
10. Models with a Mixture Distribution
Book chapter 10, pp. 261-271. A gamma-distributed error term on the exponential and the Weibull; no command file ships for either, so these two fits are checked against the book’s boxes rather than against TDA’s reference output.
The book’s chapter 10 adds unobserved heterogeneity:
mix = 1 is a gamma mixture on the exponential model (Box
10.2.1) and on the Weibull (Box 10.2.2); D Constant is the
log of the mixture variance.
Box 46Boxes 10.2.1, 10.2.2: gamma mixtures [book pp. 268-271, Boxes 10.2.1-10.2.2]
tda_rate(with_x("Surv(TFP, DES)"), d, options = list(mix = 1))
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, options = list(mix = 1))
Episodes: 600
Model: exponential (TDA 2)
logLik (starting values): -2521.785
logLik: -2442.128
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -3.9869 0.3430 -11.6219 1.0000
2 1 0 1 A EDU 0.0873 0.0310 2.8108 0.9951
3 1 0 1 A COHO2 0.5707 0.1445 3.9503 0.9999
4 1 0 1 A COHO3 0.4163 0.1512 2.7529 0.9941
5 1 0 1 A LFX -0.0046 0.0011 -4.2132 1.0000
6 1 0 1 A PNOJ 0.0850 0.0545 1.5594 0.8811
7 1 0 1 A PRES -0.0321 0.0066 -4.8423 1.0000
8 1 0 1 D Constant -0.8019 0.1837 -4.3659 1.0000
tda_rate(with_x("Surv(TFP, DES)"), d, model = "weibull",
options = list(mix = 1))
Call: tda_rate(formula = with_x("Surv(TFP, DES)"), data = d, model = "weibull",
options = list(mix = 1))
Episodes: 600
Model: weibull (TDA 7)
logLik (starting values): -2521.785
logLik: -2410.713
Converged in 6 iterations
Idx SN Org Des MT Variable Coeff Error C/Error Signif
1 1 0 1 A Constant -3.4926 0.2953 -11.8272 1.0000
2 1 0 1 A EDU 0.0720 0.0266 2.7106 0.9933
3 1 0 1 A COHO2 0.4641 0.1205 3.8505 0.9999
4 1 0 1 A COHO3 0.4014 0.1284 3.1274 0.9982
5 1 0 1 A LFX -0.0041 0.0008 -4.8731 1.0000
6 1 0 1 A PNOJ 0.1144 0.0470 2.4345 0.9851
7 1 0 1 A PRES -0.0260 0.0053 -4.8769 1.0000
8 1 0 1 B Constant 0.6693 0.0880 7.6057 1.0000
9 1 0 1 D Constant 0.7833 0.1716 4.5648 1.0000
Finally, the product-limit survivor function on its own, the picture
the whole exercise starts from. This one answers to no command file: the
book has no such figure, and ple5p.cf – which the name
would suggest – draws something else, the two groups with their
confidence bands, as ehc8 already does above.
Box 47The product-limit survivor function (no book box)
p <- tda_ps(tda_survivor(pl), width = 80, height = 40, xlim = c(0, 300), ylim = c(0, 1))
p <- tda_pl(p, "plxa", sc = 60, ic = 5)
p <- tda_pl(p, "plya", sc = 1, ic = 10)
plot(tda_pl_lines(p, "time", "survivor"))