
Left Truncation with survivalMPL
left-truncation.RmdOverview
A survival time is left truncated when any event occurring before some time could never have been observed at all. The truncation time is also called a delayed entry time, because it usually arises from a condition on entering the study: a subject who fails before meeting that condition never appears in the data.
Truncation is not censoring, and the difference is fundamental. Censoring means a survival time is only partially observed: the subject is in the data, and contributes the information that the event had not yet happened. Truncation means an event time that could have been observed becomes entirely unobservable when it falls before : the subject is absent from the data altogether. Treating a truncation time as time zero for follow-up, and so ignoring the truncation, leads to biased parameter estimates (Bhaskaran et al., n.d.).
Each subject therefore contributes : the truncation time, the observed event or censoring time, the event indicator, and the covariates, with strictly less than .
Encoding left-truncated observations
Truncation times are not part of the Surv() response.
Pass them through the entry argument instead:
Three things to know:
-
entryrequiresbasis = "uniform", which is the default, so nothing else has to be set. Left truncation is implemented by differencing the cumulative basis, , which the package supports for the piecewise-constant basis. This is not a limitation of the method so much as its natural setting: with indicator basis functions over bins , each has a closed form given , which is what makes the profile likelihood of Bhaskaran et al. (n.d.) possible. Asking for any other basis together withentrygets you a uniform fit and a warning saying so, rather than a silently wrong fit or a refusal. - Entry times must strictly precede the event or censoring time. A violation is an error, not a warning.
- Truncated and untruncated subjects may be mixed in one call. Use an entry time of zero, or any value below the first event time, for subjects observed from the origin.
Example: atomic bomb survivors
The bundled hiroshima data are left truncated by
construction. Follow-up in the Life Span Study begins on 1 October 1950,
several years after the 1945 exposure, so a subject enters the risk set
at the age they had reached by that date, and anyone who died in between
is absent from the cohort. entry is that attained age, and
time is attained age at death or at end of follow-up.
library(survivalMPL)
#> Loading required package: survival
#> Loading required package: MASS
library(survival)
data(hiroshima)
c(subjects = nrow(hiroshima), deaths = sum(hiroshima$status))
#> subjects deaths
#> 86611 50620
round(range(hiroshima$entry), 1)
#> [1] 5.2 93.8
round(range(hiroshima$time), 1)
#> [1] 7.0 114.9Entry ages run from 5 to 94 years, so the truncation is substantial rather than incidental: at any attained age, the subjects at risk are only those who had already reached that age by late 1950.
fit_entry <- coxph_mpl(
Surv(time, status) ~ dose + sex + city,
data = hiroshima,
entry = entry
)
summary(fit_entry)
#>
#> coxph_mpl(formula = Surv(time, status) ~ dose + sex + city, data = hiroshima,
#> entry = entry)
#>
#> -----
#>
#> Cox Proportional Hazards Model Fit Using MPL
#>
#>
#> Penalized log-likelihood : -218085.3
#> Estimated smoothing value : 486.6378
#> Convergence : Yes (12 + 292 iter.)
#>
#> Data : hiroshima
#> Number of obs. : 86611
#> Number of events : 50620 (58.44523%)
#> Number of cens. : 35991 (41.55477%)
#>
#> Regression parameters : Surv(time, status) ~ dose + sex + city
#> Estimate Std. Error z-value Pr(>|z|)
#> dose 0.1635084 0.0151308 10.8063 < 2.2e-16 ***
#> sexFemale -0.5518858 0.0092909 -59.4005 < 2.2e-16 ***
#> cityNagasaki 0.0515481 0.0098435 5.2368 1.634e-07 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Baseline hasard parameters approximated using Uniform :
#> (11 equal events bins)
#> 1 2 3 4 5 6
#> 0.001058039 0.002478473 0.004673498 0.009799451 0.019100936 0.033959813
#> 7 8 9 10 11
#> 0.062309691 0.111161804 0.214221247 0.340579097 0.348117394
#>
#> -----The estimated baseline hazard is a hazard by attained age, and because the risk set at each age contains only subjects who had already reached that age by late 1950, it is the truncation-corrected one:
plot(fit_entry, which = 2, ask = FALSE, cex.main = 0.9)
Requesting a different basis alongside entry does not
fail; the fit falls back to "uniform" and says so:
Limitations
predict.coxph_mpl() and
residuals.coxph_mpl() do not yet account for
entry: they compute cumulative hazard and survival from
time zero rather than from each subject’s entry time. Coefficients,
baseline hazard estimates and their standard errors are unaffected. See
?coxph_mpl for the current status.