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.

Nonlinear terms in longitudinal formulas

Overview

longitudinalSub() fits each biomarker’s fixed effects (long_sub_fixed) and random effects (long_sub_random) as ordinary nlme::lme() formulas, so any term lme() accepts – polynomials, splines, factors, interactions, log/sqrt transforms – can appear on the right-hand side. The one thing that matters for correctness, not just syntax, is whether a term’s basis depends on the data it is computed from:

continuous long_sub_fixed ordinal long_sub_fixed long_sub_random
poly(x, df, raw = TRUE), I(x^2), log(x), sqrt(x), interactions of these safe safe safe
poly(x, df) (orthogonal, the default), splines::ns()/splines::bs(), factor() safe (basis cached and reused) warns warns

This vignette walks through both groups on pbc3, shows what the warning looks like when a data-dependent term ends up somewhere unsafe, and finishes with a spline term carried all the way through to predictRisk().

library(BJM)
data(pbc3)
data_fit <- pbc3[pbc3$status3 == 1, ]

Terms that are always safe

Explicit polynomials and transforms

poly(..., raw = TRUE) returns the raw powers x, x^2, ..., x^df instead of an orthogonalized basis, so (unlike the default poly()) it computes the same columns regardless of what other data happen to be present:

fit_poly_raw <- longitudinalSub(
  data_fit, serBilir ~ poly(year, 2, raw = TRUE) + age + sex, ~ year | id
)

I(x^2), log(x), and sqrt(x) are even simpler – each is just an ordinary arithmetic transform of one column, with nothing to disagree about:

fit_explicit <- longitudinalSub(
  data_fit, serBilir ~ log(year + 1) + sqrt(year) + age + sex, ~ year | id
)

Interactions

Interactions of safe terms are themselves safe – e.g. letting the quadratic time trend differ by age:

fit_interaction <- longitudinalSub(
  data_fit, serBilir ~ year + I(year^2) * age + sex, ~ year | id
)

Terms that are safe only in a continuous biomarker’s long_sub_fixed

Orthogonal poly(), splines, and factor()

Used in a continuous biomarker’s long_sub_fixed, these are fine: the basis lme() was fit with, and the factor levels observed in the full training data, are cached on the returned longitudinalSub.BJM object and reused both when estimating Sigma_fit and at prediction time – never recomputed from a smaller slice of data.

fit_orthogonal_poly <- longitudinalSub(
  data_fit, serBilir ~ year + poly(age, 2) + factor(sex), ~ year | id
)

fit_ns <- longitudinalSub(
  data_fit, serBilir ~ splines::ns(year, df = 3) + age + sex, ~ year | id
)

What goes wrong elsewhere

The same splines::ns() term in long_sub_random – instead of long_sub_fixed – is not cached, because long_sub_random’s design matrix is always rebuilt from whatever rows are at hand. longitudinalSub() warns rather than silently risking a basis mismatch:

fit_ns_random <- longitudinalSub(
  data_fit, serBilir ~ year + age + sex, ~ splines::ns(year, df = 2) | id
)
#> Warning: `long_sub_random[[1]]` uses splines::ns(year, df = 2). Its
#> basis/contrasts depend on the data it is computed from, and for this formula
#> BJM rebuilds the design matrix from the data at hand (a single patient's rows
#> at prediction time) instead of reusing the training-time basis -- so this can
#> silently produce incorrect predictions, or fail outright when there are too few
#> distinct values. Prefer poly(..., raw = TRUE), I(x^2), log(), sqrt(), or other
#> terms that do not depend on the surrounding data.

The same warning fires for an ordinal biomarker’s long_sub_fixed, since ordinal fits (via ordinal::clmm()) rebuild their design matrix from the patient-specific data at prediction time the same way long_sub_random does. In both cases, switch to poly(..., raw = TRUE), I(x^2), log(), or sqrt().

Precomputed time transforms with survivalTrans()

A transformed-time column built once, ahead of fitting, is just an ordinary numeric column by the time long_sub_fixed sees it – always safe, including in long_sub_random:

trans <- survivalTrans(c(1, 3, 5, 7))
data_fit_trans <- data_fit
data_fit_trans$Tyears1 <- trans$survival_trans_function[[1]](data_fit_trans$year)

fit_trans <- longitudinalSub(
  data_fit_trans, serBilir ~ year + Tyears1 + age + sex, ~ year | id
)

Putting it together: a spline term through to prediction

The cached-basis guarantee above means a spline fixed effect is not just safe to fit – predictions built on it are exactly as valid as with a linear term. years has to appear in long_sub_fixed as well (the convention every BJM formula follows, so the model can be evaluated at the grid of remaining-survival-time values predictRisk() integrates over):

fit_spline <- longitudinalSub(
  data_fit, serBilir ~ splines::ns(year, df = 3) + age + sex + years, ~ year | id
)

data_survival_fitting <- pbc3[!duplicated(pbc3$id), ]
survival_fit <- survivalSub(
  data_survival_fitting,
  form_marginal_surv = Surv(years, status3) ~ age + sex,
  form_conditional_cr = NULL
)

data_predict <- pbc3[pbc3$id == 2 & pbc3$year <= 5, ]
risk <- predictRisk(
  data_predict, fit_spline, survival_fit,
  prediction_time = 5, horizon = 1, time_variable = "year",
  survival_variable_all = list(), survival_trans_function = list(),
  bandcount1 = 10, bandcount2 = 20
)
risk
#> 
#> =================================================================
#>  Dynamic Prediction - Event Risk
#> -----------------------------------------------------------------
#>   Competing risks   : No
#>   Subjects          : 1
#> -----------------------------------------------------------------
#> 
#>  Subject Risk Prob
#>        2     0.091
#> 
#> =================================================================

See vignette("BJM-intro", package = "BJM") for the full four-step pipeline, and ?longitudinalSub for the complete list of nonlinear-term examples shown there.

These 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.