Introduction to TTM

Overview

The TTM package fits a retrospective-time longitudinal model for outcomes measured before death using weighted generalized estimating equations (WGEE). The main idea is to model the longitudinal outcome on a retrospective time scale,

\[Y = \beta_\mu(t^*) + A \times \beta_A(t^*) + \beta_X^T X + \epsilon,\]

where \(t^*\) is the retrospective time, \(A\) is a treatment indicator, \(X\) are baseline covariates, and \(\epsilon\) is a random error term. The retrospective time is defined as

\[ t^* = OS\_time - t_{pros}, \]

where \(OS\_time\) is the observed survival time and \(t_{pros}\) is the prospective measurement time. This makes it natural to study how a longitudinal outcome changes as a subject approaches death.

The package supports two model interfaces:

The target trajectories are:

The weighted GEE step uses inverse-probability-type weights built from a Cox dropout model and censoring information, and uncertainty is estimated using delete-group jackknife replicates.

Data structure

The input data should be in long format, with one row per subject-visit. Each row should include:

The event variable is assumed to be coded as:

Example dataset

The package includes a long-format example dataset:

library(TTM)

data("TTM_data", package = "TTM")

A typical analysis uses:

Main fitting function: TTM()

The default entry point is TTM(). It fits a retrospective-time model using a natural-spline basis for time and selects the number of internal knots from n_inner_knot_list using the supplied model-selection criterion.

The original TTM() implementation is the package’s primary model-fitting function. It begins by cleaning the long-format data and constructing retrospective time as \(t^* = OS\_time - t_{pros}\). It then fits a Cox model for dropout, computes the corresponding subject-level weights, and uses a weighted GEE among observations from subjects with observed death events to estimate the baseline trajectory \(\beta_\mu(t^*)\) and the treatment effect trajectory \(\beta_A(t^*)\). Uncertainty is obtained with delete-group jackknife replicates.

The most important arguments are:

fit <- TTM(data_long = TTM_data,
                id = "id",
                treatment = "A",
                outcome = "Y",
                covariates = c("X1", "X2"),
                os_time = "OS_time",
                event = "event",
                t_pros = "t_pros",
                jk_block_size = NULL,
                n_inner_knot_list = range(10),
                corstr = "ar1",
                criteria = "RJC")

Difference between TTM() and TTM_linear()

The two functions share the same overall objective and weighting framework, but they differ in the spline specification:

  • TTM() uses the original natural-spline formulation with internal knots chosen from n_inner_knot_list and therefore returns the selected knot count in fit$n_inner_knots.
  • TTM_linear() uses a simpler linear spline specification and allows direct control through knots and spline_type.
  • TTM() is the default package workflow for flexible retrospective trajectories.
  • TTM_linear() is a simpler alternative intended for a lower-dimensional or more directly specified basis.

In other words, TTM() is the more general original implementation, while TTM_linear() is a streamlined version built on the same retrospective-time model structure.

The function returns a list with components such as:

  • fit$dropout_formula
  • fit$dropout_model
  • fit$longitudinal_formula
  • fit$weighted_gee
  • fit$spline_boundary_knots
  • fit$spline_inner_knots
  • fit$n_inner_knots
  • fit$variable_name_map
  • fit$weights
  • fit$data
  • fit$gee_metrics

The weighted GEE results are stored in fit$weighted_gee, including:

  • fixed_effects
  • fixed_effects_vcov
  • fixed_effects_sd

Linear-spline variant: TTM_linear()

TTM_linear() is a lower-dimensional alternative that constructs a spline basis using a linear spline representation. This can be useful when a simpler basis is desired or when you want to specify knots directly rather than using the automatic knot-selection procedure in TTM().

fit_linear <- TTM_linear(
  data_long = TTM_data,
  id = "id",
  treatment = "A",
  outcome = "Y",
  covariates = c("X1", "X2"),
  os_time = "OS_time",
  event = "event",
  t_pros = "t_pros",
  knots = 6,
  jk_block_size = NULL,
  corstr = "ar1",
  spline_type = "bs"
)

TTM_linear() has the same overall structure as TTM() but stores additional information such as:

This is a convenient option when the main interest is a simpler retrospective-time specification while still using the same weighting and jackknife framework.

Extracting trajectory estimates

Once a model has been fit, the estimated retrospective trajectories can be evaluated at specific time points with get_beta_mu() and get_beta_A().

To evaluate the baseline trajectory \(\beta_\mu(t^*)\) at selected retrospective times:

get_beta_mu(new_times = 1:10, results = fit, conf.level = 0.95)
#> $time_grid
#>  [1]  1  2  3  4  5  6  7  8  9 10
#> 
#> $estimate
#>  [1]  0.7740267  6.9714909  6.0756224  7.3843167  1.6809690 -0.4555065
#>  [7]  4.6371415  5.7410942  3.1163691  1.6053977
#> 
#> $standard_error
#>  [1] 1.588910 2.480748 1.987006 2.345090 1.983520 1.890608 1.558362 1.393686
#>  [9] 1.429043 1.351779
#> 
#> $CI.lower
#>  [1] -2.340180  2.109315  2.181163  2.788025 -2.206659 -4.161030  1.582808
#>  [8]  3.009520  0.315496 -1.044041
#> 
#> $CI.upper
#>  [1]  3.888233 11.833667  9.970082 11.980608  5.568597  3.250017  7.691475
#>  [8]  8.472668  5.917242  4.254837

To evaluate the treatment-effect trajectory \(\beta_A(t^*)\) at selected retrospective times:

get_beta_A(new_times = 1:10, results = fit, conf.level = 0.95)
#> $time_grid
#>  [1]  1  2  3  4  5  6  7  8  9 10
#> 
#> $estimate
#>  [1] 17.037264  5.173413 12.170176  9.908289 13.684041 16.790033 13.137171
#>  [8] 11.754000 13.345193 15.072384
#> 
#> $standard_error
#>  [1] 2.629696 4.925482 3.148467 3.384966 2.507748 2.478915 2.166689 2.201105
#>  [9] 2.155414 1.979189
#> 
#> $CI.lower
#>  [1] 11.883154 -4.480355  5.999294  3.273877  8.768946 11.931449  8.890539
#>  [8]  7.439915  9.120660 11.193245
#> 
#> $CI.upper
#>  [1] 22.19137 14.82718 18.34106 16.54270 18.59914 21.64862 17.38380 16.06809
#>  [9] 17.56973 18.95152

The same functions also work for TTM_linear() results, because the helper functions reconstruct the appropriate spline basis from the fitted object.

These functions return a list containing the evaluated time grid, estimated trajectory, pointwise standard error, and lower/upper confidence limits.

Visualization

The package includes plotting functions for the fitted retrospective trajectories.

To plot the estimated baseline trajectory \(\beta_\mu(t^*)\) with pointwise confidence intervals:

plot_beta_mu(results = fit, conf.level = 0.95)

To plot the estimated treatment-effect trajectory \(\beta_A(t^*)\) with pointwise confidence intervals:

plot_beta_A(results = fit, conf.level = 0.95)

The same plotting functions can be used with fit_linear as well:

plot_beta_mu(results = fit_linear, conf.level = 0.95)

plot_beta_A(results = fit_linear, conf.level = 0.95)

## Test slope difference for TTM_linear() object

Test the difference in the slope of \(\beta_\mu(t*)\) for between and after the knots specified. new_times should be a vector of three retrospective times, where the first one is before the knot and the last is after the knot. The middle one is the knot in the TTM_linear() function.

test_beta_slope(new_times = c(5, 6, 7), fit = fit_linear, var_name = "X0")
#>   slope1  slope2   diff se_diff p_value
#> 1 -0.176 -10.753 -10.58    5.09    0.04

Test the difference in the slope of \(\beta_A(t*)\) for between and after the knots specified.

test_beta_slope(new_times = c(5, 6, 7), fit = fit_linear, var_name = "A")
#>   slope1 slope2 diff se_diff p_value
#> 1  1.748  7.179 5.43     6.7    0.42

Summary

The package provides two complementary retrospective-time model fits:

Both approaches estimate treatment and baseline retrospective trajectories using weighted GEE and return trajectory summaries and plots through the helper functions in the package.