The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
Unlike stepwise Cox survfit curves, spsurv
evaluates survival on a dense time grid using the
fitted Bernstein baseline. Kaplan–Meier stays a step
function; Bernstein-polynomial curves are always smooth
lines (geom_line, or
plot(survfit(fit))). This vignette connects observed KM to
model-based predictions.
library(spsurv)
library(KMsurv)
library(survival)
library(ggplot2)
data(larynx)
larynx$stage <- factor(larynx$stage)km_stage <- survfit(Surv(time, delta) ~ stage, data = larynx)
km_long <- data.frame(
time = km_stage$time,
surv = km_stage$surv,
stage = rep(levels(larynx$stage), km_stage$strata)
)
ggplot(km_long, aes(x = time, y = surv, color = stage)) +
geom_step(linewidth = 0.6) +
geom_point(
data = subset(larynx, delta == 0),
aes(x = time, y = 0, color = stage),
shape = 3,
size = 1.5,
alpha = 0.5,
inherit.aes = FALSE
) +
labs(x = "Time (years)", y = "Survival probability", color = "Stage") +
theme_bw() +
theme(legend.position = "bottom")We compare predictions at age = 70 across all stages — a clinically interpretable profile that holds age fixed while varying disease stage.
KM curves show unadjusted stage separation.
Censoring marks (+) remind you that late follow-up
estimates rest on fewer subjects.
stage in the
model is justified.fit <- bpph(
Surv(time, delta) ~ age + stage,
degree = 5,
data = larynx,
approach = "mle",
init = 0
)
summary(fit)
#> Call:
#> bpph(formula = Surv(time, delta) ~ age + stage, degree = 5, data = larynx,
#> approach = "mle", init = 0, model = "ph")
#>
#> Bernstein PH model:
#> Regression coefficients:
#> Estimate 2.5% 97.5% Std. Error z value Pr(>|z|)
#> age 0.0197 -0.0085 0.0478 0.0143 1.4 0.17
#> stage2 0.1730 -0.7324 1.0783 0.4619 0.4 0.71
#> stage3 0.6521 -0.0450 1.3492 0.3557 1.8 0.07 .
#> stage4 1.7778 0.9471 2.6086 0.4239 4.2 3e-05 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Exponentiated coefficients:
#> Estimate 2.5% 97.5%
#> age 1.02 0.99 1.0
#> stage2 1.19 0.48 2.9
#> stage3 1.92 0.96 3.9
#> stage4 5.92 2.58 13.6
#>
#> ---
#> loglik = -141 AIC = 299predict() — tidy data framepr <- predict(fit, newdata = newdata, times = plot_times)
head(pr)
#> age stage id time surv lower upper cumhaz std.err
#> 1 70 1 1 0.00000000 1.0000000 1.0000000 1 0.000000000 0.00000000
#> 1.1 70 1 1 0.08916667 0.9922984 0.9666007 1 0.007731445 0.01338715
#> 1.2 70 1 1 0.17833333 0.9847620 0.9390056 1 0.015355286 0.02427520
#> 1.3 70 1 1 0.26750000 0.9773821 0.9160002 1 0.022877617 0.03309298
#> 1.4 70 1 1 0.35666667 0.9701500 0.8964400 1 0.030304583 0.04031670
#> 1.5 70 1 1 0.44583333 0.9630573 0.8792141 1 0.037642381 0.04647250Panel A: observed KM (steps) vs model prediction (smooth dashed) per stage.
pr$stage <- newdata$stage[match(as.character(pr$id), as.character(seq_len(nrow(newdata))))]
ggplot() +
geom_step(
data = km_long,
aes(x = time, y = surv, color = stage),
linewidth = 0.5,
alpha = 0.8
) +
geom_line(
data = pr,
aes(x = time, y = surv, color = stage),
linetype = "dashed",
linewidth = 0.6
) +
labs(x = "Time (years)", y = "Survival probability", color = "Stage") +
theme_bw() +
theme(legend.position = "bottom")Panel B: model-based ribbons (adjusted prediction
with uncertainty), still drawn with geom_line rather than
geom_step.
ggplot(pr, aes(x = time, y = surv, color = stage, fill = stage)) +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = 0.2, colour = NA) +
geom_line(linewidth = 0.5) +
labs(x = "Time (years)", y = "Survival probability", color = NULL, fill = NULL) +
theme_bw() +
theme(legend.position = "bottom")Panel A connects the smooth BP fit to observed risk sets. Panel B shows covariate-adjusted uncertainty — ribbons often widen late in follow-up when data are sparse.
predict() output directly to
your ggplot pipeline.survfit() with tidy = TRUEsf <- survfit(fit, newdata = newdata, times = plot_times, tidy = TRUE)
head(sf)
#> age stage id time surv lower upper cumhaz std.err
#> 1 70 1 1 0.00000000 1.0000000 1.0000000 1 0.000000000 0.00000000
#> 1.1 70 1 1 0.08916667 0.9922984 0.9666007 1 0.007731445 0.01338715
#> 1.2 70 1 1 0.17833333 0.9847620 0.9390056 1 0.015355286 0.02427520
#> 1.3 70 1 1 0.26750000 0.9773821 0.9160002 1 0.022877617 0.03309298
#> 1.4 70 1 1 0.35666667 0.9701500 0.8964400 1 0.030304583 0.04031670
#> 1.5 70 1 1 0.44583333 0.9630573 0.8792141 1 0.037642381 0.04647250For approach = "bayes", credible bands use posterior
draws with interval.type = "hpd" and
monotone = TRUE (default for Bayes).
Log–log intervals are undefined at S = 1
(time = 0), so we evaluate at time > 0
only:
loglog_times <- plot_times[plot_times > 0]
head(predict(fit, newdata = newdata, times = loglog_times, type = "log-log"))
#> age stage id time surv lower upper cumhaz
#> 1 70 1 1 0.08916667 0.9922984 0.7943630 0.9997404 0.007731445
#> 1.1 70 1 1 0.17833333 0.9847620 0.7115221 0.9993075 0.015355286
#> 1.2 70 1 1 0.26750000 0.9773821 0.6772829 0.9986577 0.022877617
#> 1.3 70 1 1 0.35666667 0.9701500 0.6629301 0.9977685 0.030304583
#> 1.4 70 1 1 0.44583333 0.9630573 0.6549430 0.9966575 0.037642381
#> 1.5 70 1 1 0.53500000 0.9560957 0.6459815 0.9953977 0.044897252
#> std.err
#> 1 0.01338715
#> 1.1 0.02427520
#> 1.2 0.03309298
#> 1.3 0.04031670
#> 1.4 0.04647250
#> 1.5 0.05212577These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.