mmrm: Mixed Models for Repeated Measures in R

repository·main·Indexed 41 days ago

https://github.com/openpharma/mmrm

An R package for analyzing longitudinal continuous outcomes in randomized clinical trials using Mixed Models for Repeated Measures (MMRM). It implements marginal linear models via a fast C++ backend and Template Model Builder (TMB), supporting flexible covariance structures (unstructured, Toeplitz, AR1, compound symmetry, ante-dependence, and spatial exponential) and REML/ML inference. Includes integration with tidymodels, emmeans, and tern.

Tokens
2.7K
Snippets
7
Records
12
Agent score
50%

What's inside mmrm

  1. Overview of mmrm features and scope

    main

    Scope

    • Continuous responses: Assumes normal (but potentially heteroscedastic) residuals.
    • Marginal linear models: Implements models without individual-level random effects.

    Key Features

    • Flexible Covariance Structures: Supports unstructured, Toeplitz, AR1, compound symmetry, ante-dependence, and spatial exponential. You can specify shared covariance for all subjects or group-specific estimates, and homogeneous or heterogeneous variances across time points.
    • Inference: Supports both REML and ML, and allows for weighted observations.
    • Hypothesis Testing:
      • Supports Satterthwaite and Kenward-Roger adjusted degrees of freedom.
      • Supports one- and multi-dimensional linear contrasts.
      • Integration with emmeans for obtaining least square means.
    • Performance: Uses a C++ backend with automatic differentiation for fast and robust model fitting.
    • Ecosystem Integration:
      • tidymodels: Includes a dedicated parsnip engine for linear regression and integration with recipes.
      • tern: Integration via tern.mmrm for automated tabulation, plots of least square means, and model diagnostics.
  2. Run R simulations using Snakemake

    main

    R simulations can be executed on any platform with R and Snakemake installed. The provided Snakefile is compatible with High-Performance Computing (HPC) environments using batch systems like LSF.

    To run simulations on an LSF-based system, ensure the snakemake-executor-plugin-lsf is installed, then use the --executor lsf flag to submit jobs.

    # Install the LSF plugin
    pip install snakemake-executor-plugin-lsf
    
    # Submit jobs using the LSF executor
    snakefile --executor lsf all_r_analysis
  3. Install the mmrm package

    main

    You can install mmrm via CRAN for the stable release, or via R-Universe for the development version (which provides pre-compiled binaries for Windows and MacOS to avoid long compilation times). You can also install directly from GitHub using remotes.

    # Install stable release from CRAN
    install.packages("mmrm")
    
    # Install development version from R-Universe (recommended for Win/macOS)
    install.packages(
      "mmrm",
      repos = c("https://openpharma.r-universe.dev", "https://cloud.r-project.org")
    )
    
    # Install development version from GitHub (requires compilation)
    if (!require("remotes")) {
      install.packages("remotes")
    }
    remotes::install_github("openpharma/mmrm")
  4. Run SAS simulations

    main

    SAS simulations require both SAS and R to be available on the system. If you are working on separate machines (one with R and one with SAS), you must perform the simulation in two distinct steps:

    1. Data Generation: Run on a machine with R using snakemake all_simulated_data.
    2. Estimation: Run on a machine with SAS using snakemake all_sas_analysis.
    # Step 1: Generate data (Run on R machine)
    snakemake all_simulated_data
    
    # Step 2: Generate estimates (Run on SAS machine)
    snakemake all_sas_analysis
  5. Overview of mmrm

    main

    The mmrm package implements Mixed Models for Repeated Measures (MMRM) based on the marginal linear model without random effects. It uses the Template Model Builder (TMB) backend for fast and robust model fitting via C++ and automatic differentiation.

    Core Scope:

    • Continuous responses with normal (potentially heteroscedastic) residuals.
    • Marginal linear models (no individual-level random effects).

    Key Capabilities:

    • Flexible Covariance Structures: Supports unstructured, Toeplitz, AR1, compound symmetry, ante-dependence, and spatial exponential structures. It allows for shared or group-specific covariance and homogeneous or heterogeneous variances across time points.
    • Inference & Testing: Supports Restricted Maximum Likelihood (REML) and Maximum Likelihood (ML). Provides Satterthwaite or Kenward-Roger adjustments for degrees of freedom and coefficient covariance matrices.
    • Integration: Works with emmeans for least square means, tidymodels (via a parsnip engine), and tern (via tern.mmrm for automated tabulation and plotting).
  6. Benchmark mmrm() against SAS PROC MIXED

    main

    To assess the correctness of mmrm(), you can compare its results against established software like SAS's PROC MIXED. When using the sasr R package to facilitate this comparison, you must be careful with the repeated measures variable (e.g., time).

    If the repeated measures variable is a factor, its alphanumeric ordering must match the expected temporal ordering. Because sasr converts R data.frame objects into pandas DataFrames (via SASPy) before transforming them into SAS datasets, factor levels like c("1", "2", ..., "10") may be reordered as "1", "10", "2", ..., "9" in SAS due to string sorting.

    To avoid incorrect parameter estimation (such as swapped variances), you should:

    1. Use explicit factor levels for the time variable.
    2. Pad zeroes in the time variable (e.g., "01", "02"...).
    3. Or, pass the time variable as a numeric type to avoid string-based reordering entirely.
  7. Use the simChef framework for simulation studies

    main
    The mmrm package relies on the simChef R package for conducting numerical experiments and simulation studies. simChef provides a framework for constructing efficient, high-quality, and reproducible simulations. For an mmrm-specific example, refer to the simulations/csh-benchmarks/ directory in this repository.
  8. Install the Cholesky caching branch for simulation

    main

    To test the Cholesky caching implementation, you can install the specific development branch 232_cholesky from GitHub. This implementation caches the Cholesky decomposition for each visit structure to save computational time when visit structures are reused.

    Use the following R code to install two versions into separate library paths for comparison: one with caching enabled and one without.

    # Install version with caching
    withr::with_libpaths(
      "simulations/chols/cached_chols",
      remotes::install_github("openpharma/mmrm", ref = "232_cholesky")
    )
    
    # Install version without caching
    withr::with_libpaths(
      "simulations/chols/no_cached_chols",
      remotes::install_github("openpharma/mmrm", ref = "232_cholesky")
    )
  9. Inspect mmrm model results

    main

    After fitting a model with mmrm(), you can use standard R methods to inspect the results:

    • print(fit): Displays the function call, data summary, covariance structure, likelihood method, deviance, estimated coefficients, and convergence information.
    • summary(fit): Provides a detailed coefficients table including Satterthwaite degrees of freedom, standard errors, t-values, and p-values, as well as the covariance matrix estimate.
    # Print basic model information and coefficients
    fit
    
    # Print detailed coefficients with Satterthwaite df and covariance matrix
    summary(fit)
  10. Fit a Mixed Model for Repeated Measures (MMRM) with mmrm()

    main

    The mmrm() function implements marginal linear models using Template Model Builder (TMB). It is designed for continuous responses with normal (potentially heteroscedastic) residuals.

    To fit a model, provide a formula, data, and a covariance structure specification. By default, the function uses Restricted Maximum Likelihood (REML) inference, but Maximum Likelihood (ML) can also be specified.

    library(mmrm)
    fit <- mmrm(
      formula = FEV1 ~ RACE + SEX + ARMCD * AVISIT + us(AVISIT | USUBJID),
      data = fev_data
    )
  11. Example: Comparing mmrm with PROC MIXED using sasr

    main

    This example demonstrates how to generate data and use a wrapper function to run SAS PROC MIXED via sasr. It highlights the importance of how the time variable is formatted to prevent alphanumeric reordering in SAS.

    #' PROC MIXED Wrapper Function
    #'
    #' @description This function takes as input the ouput of a data-generating
    #'   process function, and fits PROC MIXED repeated measures model on this data.
    #'
    #' @param participant A factor vector of participant IDs.
    #' @param time A factor vector of time points.
    #' @param y A numeric vector of outcomes.
    #' @param trt A binary vector of treatment group indicators.
    #'
    #' @return Estimated parameters of the repeated measures' covariance matrix.
    #'   These parameters are stored in a data.frame object.
    proc_mixed_fun <- function(
        participant,
        time,
        y,
        trt) {
      ## assemble the vectors into a data.frame
      df <- data.frame(
        "participant" = participant,
        "time" = time,
        "y" = y,
        "trt" = trt
      )
      rownames(df) <- NULL
    
    ## create SAS dataset
      sasr::df2sd(df, "sas_df")
    
    ## specify the SAS code: only return covariance matrix estimates for
      ## repeated measures
      sas_code <- "ods output CovParms = cov_parms;
        PROC MIXED DATA = sas_df;
          CLASS trt time participant;
          MODEL y = trt;
          REPEATED time / subject=participant type=CSH;
        RUN;"
    
    ## run the SAS code, and capture the output
      sas_result <- sasr::run_sas(sas_code)
      cov_mat_df <- sasr::sd2df("cov_parms")
    
    ## prepare the fit object
      return(list(fit = cov_mat_df))
    }
  12. Benchmark mmrm performance with Cholesky caching

    main

    You can evaluate the performance impact of the Cholesky cache using microbenchmark. The caching mechanism is most effective when multiple subjects share the same visit structures, as the Cholesky decomposition for a given structure is stored and reused.

    Example benchmark using a standard formula and fev_data:

    formula <- FEV1 ~ RACE + SEX + ARMCD * AVISIT + ad(AVISIT | USUBJID)
    microbenchmark::microbenchmark(
      mmrm(formula, fev_data, reml = TRUE),
      times = 100L
    )