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.4698862  0.3734948  -1.258 -1.2019225  0.2621501   0.1045    
#> phi_2_1     0.4809285  0.1781928   2.699  0.1316771  0.8301800   0.0036 ** 
#> phi_3_1    -0.2711150  0.2083709  -1.301 -0.6795145  0.1372845   0.0969 .  
#> phi_1_2    -0.1084163  0.2321649  -0.467 -0.5634512  0.3466185   0.3204    
#> phi_2_2    -0.2331007  0.1105811  -2.108 -0.4498357 -0.0163658   0.0178 *  
#> phi_3_2     0.5796100  0.1363571   4.251  0.3123549  0.8468651   <2e-16 ***
#> phi_1_3     0.1213907  0.1565825   0.775 -0.1855054  0.4282868   0.2193    
#> phi_2_3    -0.0744270  0.0735117  -1.012 -0.2185074  0.0696533   0.1559    
#> phi_3_3    -0.5738682  0.0916326  -6.263 -0.7534648 -0.3942715   <2e-16 ***
#> sigma_1_1   0.3007396  0.1122916   2.678  0.0806521  0.5208271   0.0038 ** 
#> sigma_2_1   0.0498648  0.0401853   1.241 -0.0288969  0.1286265   0.1076    
#> sigma_3_1  -0.0741897  0.0428654  -1.731 -0.1582043  0.0098249   0.0421 *  
#> sigma_2_2   0.0457407  0.0234338   1.952 -0.0001887  0.0916700   0.0258 *  
#> sigma_3_2   0.0122009  0.0206733   0.590 -0.0283181  0.0527198   0.2777    
#> sigma_3_3   0.0881988  0.0337203   2.616  0.0221082  0.1542894   0.0046 ** 
#> theta_1_1   0.1991190  0.0178903  11.130  0.1640546  0.2341833   <2e-16 ***
#> theta_2_2   0.2112520  0.0148880  14.189  0.1820720  0.2404319   <2e-16 ***
#> theta_3_3   0.1913294  0.0140468  13.621  0.1637981  0.2188606   <2e-16 ***
#> mu0_1_1    -0.2189028  0.1678919  -1.304 -0.5479650  0.1101593   0.0965 .  
#> mu0_2_1     0.5735116  0.2813058   2.039  0.0221625  1.1248608   0.0210 *  
#> mu0_3_1     0.6406938  0.7675896   0.835 -0.8637542  2.1451418   0.2022    
#> sigma0_1_1  0.0663477  0.0789889   0.840 -0.0884677  0.2211631   0.2007    
#> sigma0_2_1  0.0480017  0.1080453   0.444 -0.1637632  0.2597665   0.3285    
#> sigma0_3_1 -0.3808122  0.3502062  -1.087 -1.0672038  0.3055793   0.1387    
#> sigma0_2_2  0.3573753  0.2484766   1.438 -0.1296299  0.8443804   0.0755 .  
#> sigma0_3_2  0.1946208  0.5038433   0.386 -0.7928940  1.1821355   0.3497    
#> sigma0_3_3  2.8986422  1.8778449   1.544 -0.7818662  6.5791505   0.0617 .  
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> -2 log-likelihood value at convergence = 2176.87
#> AIC = 2230.87
#> BIC = 2344.67

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

est <- coef(fit)
n
#> [1] 5
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.2189028  0.5735116  0.6406938
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.06634768 0.04800168 -0.3808122
#> [2,]  0.04800168 0.35737526  0.1946208
#> [3,] -0.38081224 0.19462078  2.8986422
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.4698862 -0.1084163  0.12139067
#> [2,]  0.4809285 -0.2331007 -0.07442704
#> [3,] -0.2711150  0.5796100 -0.57386817
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.30073959 0.04986477 -0.07418968
#> [2,]  0.04986477 0.04574067  0.01220087
#> [3,] -0.07418968 0.01220087  0.08819880
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.199119 0.000000 0.0000000
#> [2,] 0.000000 0.211252 0.0000000
#> [3,] 0.000000 0.000000 0.1913294
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)
start <- Sys.time()
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
)
end <- Sys.time()
elapsed <- end - start
elapsed
#> Time difference of 55.5705 mins

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)
start <- Sys.time()
boot <- BootMed(
  phi = phi,
  phi_hat = phi_hat,
  delta_t = delta_t,
  from = "x",
  to = "y",
  med = "m",
  ncores = parallel::detectCores() # use multiple cores
)
end <- Sys.time()
elapsed <- end - start
elapsed
#> Time difference of 9.527816 mins
plot(boot)

plot(boot, type = "bc")

The following generates bootstrap confidence intervals for the standardized effects.

start <- Sys.time()
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
)
end <- Sys.time()
elapsed <- end - start
elapsed
#> Time difference of 10.40956 mins
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