Introduction to metaLong

Overview

metaLong provides a coherent workflow for synthesising evidence from studies that report outcomes at multiple follow-up time points. The package covers:

1. Simulating data

library(metaLong)

dat <- sim_longitudinal_meta(
  k    = 10,
  times = c(0, 6, 12, 24),
  mu   = c("0" = 0.30, "6" = 0.50, "12" = 0.42, "24" = 0.20),
  tau  = 0.20,
  seed = 42
)

head(dat, 6)
#>   study time         vi        yi pub_year quality   n
#> 1   s01    0 0.11040314 0.7255240     2003    0.58 372
#> 2   s01    6 0.09375956 0.8372688     2003    0.58 372
#> 3   s01   12 0.05795592 0.7716921     2003    0.58 372
#> 4   s01   24 0.05334272 0.3893750     2003    0.58 372
#> 5   s02    0 0.03387102 0.3167792     2001    0.77 223
#> 6   s02    6 0.10110551 0.2722546     2001    0.77 223

The data are in long format: one row per study x time combination.

2. Longitudinal pooling: ml_meta()

ml_meta() fits an intercept-only random-effects model at each time point with CR2 sandwich variance and Satterthwaite degrees of freedom.

meta <- ml_meta(dat, yi = "yi", vi = "vi", study = "study", time = "time")
#> Registered S3 method overwritten by 'clubSandwich':
#>   method    from    
#>   bread.mlm sandwich
print(meta)
#> 
#> === metaLong: Longitudinal Pooled Effects ===
#> Engine: rma.uni 
#> Time points: 4 
#> Small-sample correction: TRUE 
#> 
#>  time  k theta    se    df p_val ci_lb ci_ub  tau2
#>     0 10 0.315 0.077 6.565 0.005 0.131 0.498 0.000
#>     6 10 0.602 0.073 5.638 0.000 0.419 0.784 0.001
#>    12 10 0.534 0.097 7.705 0.001 0.308 0.761 0.023
#>    24 10 0.417 0.081 7.769 0.001 0.229 0.606 0.010
plot(meta, main = "Pooled Effects Across Follow-Up")

3. Sensitivity analysis: ml_sens()

ml_sens() computes ITCV_alpha(t) — the minimum partial correlation an omitted confounder must have with both treatment and outcome to render the result non-significant.

sens <- ml_sens(dat, meta, yi = "yi", vi = "vi",
                study = "study", time = "time")
print(sens)
#> 
#> === metaLong: Longitudinal Sensitivity (ITCV) ===
#> delta (fragility benchmark): 0.15 
#> ITCV_alpha range: [ 0.691 , 0.961 ]
#> Fragile proportion: 0 
#> 
#>  time theta    sy  itcv itcv_alpha fragile
#>     0 0.315 0.242 0.890      0.691   FALSE
#>     6 0.602 0.174 0.980      0.961   FALSE
#>    12 0.534 0.298 0.935      0.848   FALSE
#>    24 0.417 0.220 0.941      0.849   FALSE
plot(sens)

Key trajectory summaries:

cat("Minimum ITCV_alpha:", round(attr(sens, "itcv_min"),  3), "\n")
#> Minimum ITCV_alpha: 0.691
cat("Mean ITCV_alpha:   ", round(attr(sens, "itcv_mean"), 3), "\n")
#> Mean ITCV_alpha:    0.837
cat("Fragile proportion:", round(attr(sens, "fragile_prop"), 3), "\n")
#> Fragile proportion: 0

4. Nonlinear time trend: ml_spline()

spl <- ml_spline(meta, df = 2)
print(spl)
#> 
#> === metaLong: Spline Time Trend ===
#> Spline df: 2 
#> Weighted R-squared: 0.807 
#> Nonlinearity test p-value: 0.291 
#> 
#> Prediction range: time in [ 0 , 24 ]
plot(spl, main = "Spline Fit: Nonlinear Trajectory")

5. Combined figure: ml_plot()

ml_plot(meta, sens_obj = sens, spline_obj = spl,
        main = "Longitudinal Meta-Analysis Profile")

6. Benchmark calibration: ml_benchmark()

ml_benchmark() regresses each observed study-level covariate and flags those whose partial correlation exceeds the ITCV_alpha threshold.

bench <- ml_benchmark(
  dat, meta, sens,
  yi         = "yi", vi = "vi", study = "study", time = "time",
  covariates = c("pub_year", "quality")
)
print(bench)
#> 
#> === metaLong: Benchmark Calibration ===
#> 
#> Fragility summary by time:
#>  time itcv_alpha n_covariates n_beats prop_beats any_beats
#>     0      0.691            2       0          0     FALSE
#>     6      0.961            2       0          0     FALSE
#>    12      0.848            2       0          0     FALSE
#>    24      0.849            2       0          0     FALSE
#> 
#>  time covariate  k r_partial itcv_alpha beats_threshold p_val
#>     0  pub_year 10    -0.331      0.691           FALSE 0.477
#>     0   quality 10    -0.495      0.691           FALSE 0.380
#>     6  pub_year 10    -0.279      0.961           FALSE 0.577
#>     6   quality 10     0.598      0.961           FALSE 0.242
#>    12  pub_year 10     0.167      0.848           FALSE 0.702
#>    12   quality 10     0.068      0.848           FALSE 0.900
#>    24  pub_year 10    -0.119      0.849           FALSE 0.800
#>    24   quality 10     0.236      0.849           FALSE 0.664

7. Fragility analysis: ml_fragility()

The fragility index is the minimum number of study removals that flip significance at a given time point.

frag <- ml_fragility(dat, meta,
                     yi = "yi", vi = "vi", study = "study", time = "time",
                     max_k = 1L, seed = 1)
print(frag)
#> 
#> === metaLong: Fragility Analysis ===
#> 
#>  time k_studies p_original sig_original fragility_index fragility_quotient
#>     0        10      0.005         TRUE              NA                 NA
#>     6        10      0.000         TRUE              NA                 NA
#>    12        10      0.001         TRUE              NA                 NA
#>    24        10      0.001         TRUE              NA                 NA
#>  study_removed
#>           <NA>
#>           <NA>
#>           <NA>
#>           <NA>

8. Accessing stored fits

f <- fits(meta)
cat("Stored model objects:", sum(!sapply(f, is.null)), "/", length(f), "\n")
#> Stored model objects: 4 / 4

References

Frank, K. A. (2000). Impact of a confounding variable on a regression coefficient. Sociological Methods & Research, 29(2), 147–194. doi:10.1177/0049124100029002003

Hedges, L. V., Tipton, E., & Johnson, M. C. (2010). Robust variance estimation in meta-regression with dependent effect size estimates. Research Synthesis Methods, 1(1), 39–65. doi:10.1002/jrsm.5

Tipton, E. (2015). Small sample adjustments for robust variance estimation with meta-regression. Psychological Methods, 20(3), 375–393. doi:10.1037/met0000011

mirror server hosted at Truenetwork, Russian Federation.