Skip to contents

The cTMed package provides a bootstrap approach, in addition to the delta and Monte Carlo methods, for estimating and quantifying uncertainty in total, direct, and indirect effects within continuous-time mediation models across different time intervals.

In this example, we will use the fitted model from Fit the Continuous-Time Vector Autoregressive Model Using the dynr Package. The object fit represents a fitted CT-VAR model created using the dynr package.

summary(fit)
#> Coefficients:
#>             Estimate Std. Error t value  ci.lower  ci.upper Pr(>|t|)    
#> phi_1_1    -0.293807   0.082574  -3.558 -0.455648 -0.131966   0.0002 ***
#> phi_2_1     0.824396   0.056914  14.485  0.712846  0.935946   <2e-16 ***
#> phi_3_1    -0.454316   0.057647  -7.881 -0.567303 -0.341329   <2e-16 ***
#> phi_1_2    -0.071916   0.068787  -1.045 -0.206735  0.062904   0.1479    
#> phi_2_2    -0.568999   0.048532 -11.724 -0.664119 -0.473878   <2e-16 ***
#> phi_3_2     0.707647   0.048636  14.550  0.612322  0.802972   <2e-16 ***
#> phi_1_3     0.025791   0.062589   0.412 -0.096882  0.148463   0.3402    
#> phi_2_3     0.082668   0.043529   1.899 -0.002647  0.167983   0.0288 *  
#> phi_3_3    -0.687502   0.044092 -15.593 -0.773920 -0.601084   <2e-16 ***
#> sigma_1_1   0.235806   0.022759  10.361  0.191198  0.280413   <2e-16 ***
#> sigma_2_1   0.018668   0.010045   1.858 -0.001020  0.038356   0.0316 *  
#> sigma_3_1  -0.064687   0.010768  -6.007 -0.085793 -0.043582   <2e-16 ***
#> sigma_2_2   0.077088   0.009447   8.160  0.058572  0.095603   <2e-16 ***
#> sigma_3_2   0.008278   0.006655   1.244 -0.004765  0.021321   0.1068    
#> sigma_3_3   0.080654   0.010196   7.910  0.060670  0.100639   <2e-16 ***
#> theta_1_1   0.201033   0.005107  39.363  0.191023  0.211043   <2e-16 ***
#> theta_2_2   0.191640   0.004373  43.823  0.183069  0.200211   <2e-16 ***
#> theta_3_3   0.197449   0.004521  43.677  0.188588  0.206309   <2e-16 ***
#> mu0_1_1    -0.068547   0.142210  -0.482 -0.347273  0.210180   0.3149    
#> mu0_2_1     0.084573   0.176685   0.479 -0.261722  0.430869   0.3161    
#> mu0_3_1     0.115721   0.141485   0.818 -0.161585  0.393026   0.2067    
#> sigma0_1_1  0.942679   0.205369   4.590  0.540164  1.345195   <2e-16 ***
#> sigma0_2_1  0.161788   0.179880   0.899 -0.190769  0.514346   0.1842    
#> sigma0_3_1  0.103257   0.143856   0.718 -0.178696  0.385209   0.2365    
#> sigma0_2_2  1.489721   0.311204   4.787  0.879772  2.099670   <2e-16 ***
#> sigma0_3_2 -0.100464   0.178078  -0.564 -0.449491  0.248563   0.2863    
#> sigma0_3_3  0.946144   0.204219   4.633  0.545882  1.346406   <2e-16 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> -2 log-likelihood value at convergence = 21626.57
#> AIC = 21680.57
#> BIC = 21856.53

We need to extract the estimated parameters from the fitted object, which will be used to generate bootstrap samples.

est <- coef(fit)
n
#> [1] 50
time
#> [1] 100
delta_t
#> [1] 0.1
lambda
#>      [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    1    0
#> [3,]    0    0    1
nu
#> [1] 0 0 0
mu
#> [1] 0 0 0
mu0 <- est[
  c(
    "mu0_1_1",
    "mu0_2_1",
    "mu0_3_1"
  )
]
mu0
#>     mu0_1_1     mu0_2_1     mu0_3_1 
#> -0.06854665  0.08457349  0.11572075
sigma0 <- matrix(
  data = est[
    c(
      "sigma0_1_1",
      "sigma0_2_1",
      "sigma0_3_1",
      "sigma0_2_1",
      "sigma0_2_2",
      "sigma0_3_2",
      "sigma0_3_1",
      "sigma0_3_2",
      "sigma0_3_3"
    )
  ],
  nrow = 3,
  ncol = 3
)
sigma0
#>           [,1]       [,2]       [,3]
#> [1,] 0.9426793  0.1617881  0.1032567
#> [2,] 0.1617881  1.4897213 -0.1004635
#> [3,] 0.1032567 -0.1004635  0.9461439
sigma0_l <- t(chol(sigma0))
phi <- matrix(
  data = est[
    c(
      "phi_1_1",
      "phi_2_1",
      "phi_3_1",
      "phi_1_2",
      "phi_2_2",
      "phi_3_2",
      "phi_1_3",
      "phi_2_3",
      "phi_3_3"
    )
  ],
  nrow = 3,
  ncol = 3
)
phi
#>            [,1]        [,2]        [,3]
#> [1,] -0.2938068 -0.07191554  0.02579061
#> [2,]  0.8243958 -0.56899879  0.08266777
#> [3,] -0.4543161  0.70764697 -0.68750180
sigma <- matrix(
  data = est[
    c(
      "sigma_1_1", "sigma_2_1", "sigma_3_1",
      "sigma_2_1", "sigma_2_2", "sigma_3_2",
      "sigma_3_1", "sigma_3_2", "sigma_3_3"
    )
  ],
  nrow = 3,
  ncol = 3
)
sigma
#>             [,1]        [,2]         [,3]
#> [1,]  0.23580555 0.018668186 -0.064687273
#> [2,]  0.01866819 0.077087752  0.008277734
#> [3,] -0.06468727 0.008277734  0.080654489
sigma_l <- t(chol(sigma))
theta <- diag(3)
diag(theta) <- est[
  c(
    "theta_1_1",
    "theta_2_2",
    "theta_3_3"
  )
]
theta
#>           [,1]      [,2]      [,3]
#> [1,] 0.2010327 0.0000000 0.0000000
#> [2,] 0.0000000 0.1916402 0.0000000
#> [3,] 0.0000000 0.0000000 0.1974485
theta_l <- t(chol(theta))
R <- 1000L # use at least 1000 in actual research
path <- getwd()
prefix <- "ou"

The estimated parameters are then passed as arguments to the PBSSMOUFixed function from the bootStateSpace package, which generates a parametric bootstrap sampling distribution of the parameter estimates. The argument R specifies the number of bootstrap replications. The generated data and model estimates are stored in path using the specified prefix for the file names. The ncores = parallel::detectCores() argument instructs the function to use all available CPU cores in the system.

NOTE: Fitting the CT-VAR model multiple times is computationally intensive.

library(bootStateSpace)
boot <- PBSSMOUFixed(
  R = R,
  path = path,
  prefix = prefix,
  n = n,
  time = time,
  delta_t = delta_t,
  mu0 = mu0,
  sigma0_l = sigma0_l,
  mu = mu,
  phi = phi,
  sigma_l = sigma_l,
  nu = nu,
  lambda = lambda,
  theta_l = theta_l,
  ncores = parallel::detectCores(),
  seed = 42
)

The extract function from the bootStateSpace package is used to extract the bootstrap phi matrices as well as the sigma matrices.

phi <- extract(object = boot, what = "phi")
sigma <- extract(object = boot, what = "sigma")

In this example, we aim to calculate the total, direct, and indirect effects of x on y, mediated through m, over time intervals ranging from 0 to 10.

# time intervals
delta_t <- seq(from = 0, to = 10, length.out = 1000)

We also need the estimated drift matrix from the original sample.

# estimated drift matrix
phi_hat <- matrix(
  data = est[
    c(
      "phi_1_1",
      "phi_2_1",
      "phi_3_1",
      "phi_1_2",
      "phi_2_2",
      "phi_3_2",
      "phi_1_3",
      "phi_2_3",
      "phi_3_3"
    )
  ],
  nrow = 3,
  ncol = 3
)
colnames(phi_hat) <- rownames(phi_hat) <- c("x", "m", "y")

For the standardized effects, the estimated process noise covariance matrix from the original sample is also needed.

# estimated process noise covariance matrix
sigma_hat <- matrix(
  data = est[
    c(
      "sigma_1_1", "sigma_2_1", "sigma_3_1",
      "sigma_2_1", "sigma_2_2", "sigma_3_2",
      "sigma_3_1", "sigma_3_2", "sigma_3_3"
    )
  ],
  nrow = 3,
  ncol = 3
)

Bootstrap Method

library(cTMed)
boot <- BootMed(
  phi = phi,
  phi_hat = phi_hat,
  delta_t = delta_t,
  from = "x",
  to = "y",
  med = "m",
  ncores = parallel::detectCores() # use multiple cores
)
plot(boot)

plot(boot, type = "bc")

The following generates bootstrap confidence intervals for the standardized effects.

boot <- BootMedStd(
  phi = phi,
  sigma = sigma,
  phi_hat = phi_hat,
  sigma_hat = sigma_hat,
  delta_t = delta_t,
  from = "x",
  to = "y",
  med = "m",
  ncores = parallel::detectCores() # use multiple cores
)
plot(boot)

plot(boot, type = "bc")

References

Deboeck, P. R., & Preacher, K. J. (2015). No need to be discrete: A method for continuous time mediation analysis. Structural Equation Modeling: A Multidisciplinary Journal, 23(1), 61–75. https://doi.org/10.1080/10705511.2014.973960
R Core Team. (2024). R: A language and environment for statistical computing. R Foundation for Statistical Computing. https://www.R-project.org/
Ryan, O., & Hamaker, E. L. (2021). Time to intervene: A continuous-time approach to network analysis and centrality. Psychometrika, 87(1), 214–252. https://doi.org/10.1007/s11336-021-09767-0