Code
mmrm() registered as emmeans extension
Forest Plot for Mixed-Effect Model Repeated Measures
For the following part, an MMRM model is fitted for the dataset and from this result forest plots are constructed for each set of subgroups, treatment arm, and visit that is of interest.
First an MMRM model is fitted for the whole dataset.
mmrm() registered as emmeans extension
Applying the tern.mmrm::extract_mmrm_subgroups function prepares the obtained mmrm_results for a specific visit - in this case we use the SCREENING visit - and treatment arm relative to the reference arm. From these results a table is obtained using the tern.mmrm::tabulate_mmrm_subgroups function from which the graphic can be directly obtained with tern::g_forest.
Here we compare ARM A with the reference arm, ARM B.
df_a <- extract_mmrm_subgroups(
fit = mmrm_results,
visit = "SCREENING",
subgroups = c("group", "SEX"),
treatment_arm = "ARM A"
)
tab_a <- basic_table() %>%
tabulate_mmrm_subgroups(
df = df_a,
vars = c("n_tot", "diff", "ci", "pval")
)
plot <- g_forest(
tab_a,
logx = FALSE,
xlim = c(-5, 2.5),
x_at = c(-5, -2.5, 0, 2.5),
vline = 0
)
plotThen we compare ARM C with ARM B.
df_c <- extract_mmrm_subgroups(
fit = mmrm_results,
visit = "SCREENING",
subgroups = c("group", "SEX"),
treatment_arm = "ARM C"
)
tab_c <- basic_table() %>%
tabulate_mmrm_subgroups(
df = df_c,
vars = c("n_tot", "diff", "ci", "pval")
)
plot <- g_forest(
tab_c,
logx = FALSE,
xlim = c(-5, 2.5),
x_at = c(-5, -2.5, 0, 2.5),
vline = 0
)
plotlibrary(dplyr)
library(tern.mmrm)
library(nestcolor)
adsl <- random.cdisc.data::cadsl
adqs <- random.cdisc.data::cadqs
adqs_f <- adqs %>%
dplyr::filter(PARAMCD == "FKSI-FWB" & !AVISIT %in% c("BASELINE")) %>%
droplevels() %>%
dplyr::mutate(ARMCD = factor(ARMCD, levels = c("ARM B", "ARM A", "ARM C"))) %>%
dplyr::mutate(
AVISITN = rank(AVISITN) %>%
as.factor() %>%
as.numeric() %>%
as.factor()
)
# Simulation of groups.
set.seed(2)
adqs_f_with_groups <- rbind(
within(
adqs_f[sample(seq_len(nrow(adqs_f)), size = 1 / 2 * nrow(adqs_f)), ],
group <- "subpopulation 1"
),
within(
adqs_f,
{
group <- "subpopulation 2"
AVAL <- AVAL + rnorm(length(AVAL), mean = 10, sd = 2)
USUBJID <- paste0(USUBJID, "-S2")
}
)
)
adqs_f_with_groups$group <- factor(adqs_f_with_groups$group)[1] "2026-06-13 18:33:56 UTC"
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.5.2 (2025-10-31)
os Ubuntu 24.04.4 LTS
system x86_64, linux-gnu
ui X11
language (EN)
collate en_US.UTF-8
ctype en_US.UTF-8
tz Etc/UTC
date 2026-06-13
pandoc 3.9 @ /usr/bin/ (via rmarkdown)
quarto 1.9.38 @ /usr/local/bin/quarto
─ Packages ───────────────────────────────────────────────────────────────────
package * version date (UTC) lib source
backports 1.5.1 2026-04-03 [1] CRAN (R 4.5.2)
brio 1.1.5 2024-04-24 [1] CRAN (R 4.5.2)
broom 1.0.13 2026-05-14 [1] CRAN (R 4.5.2)
checkmate 2.3.4 2026-02-03 [1] CRAN (R 4.5.2)
cli 3.6.6 2026-04-09 [1] CRAN (R 4.5.2)
coda 0.19-4.1 2024-01-31 [1] CRAN (R 4.5.2)
codetools 0.2-20 2024-03-31 [2] CRAN (R 4.5.2)
cowplot 1.2.0 2025-07-07 [1] CRAN (R 4.5.2)
curl 7.1.0 2026-04-22 [1] CRAN (R 4.5.2)
dichromat 2.0-0.1 2022-05-02 [1] CRAN (R 4.5.2)
digest 0.6.39 2025-11-19 [1] CRAN (R 4.5.2)
dplyr * 1.2.1 2026-04-03 [1] CRAN (R 4.5.2)
emmeans 2.0.3 2026-04-09 [1] CRAN (R 4.5.2)
estimability 1.5.1 2024-05-12 [1] CRAN (R 4.5.2)
evaluate 1.0.5 2025-08-27 [1] CRAN (R 4.5.2)
farver 2.1.2 2024-05-13 [1] CRAN (R 4.5.2)
fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.5.2)
formatters * 0.5.12.9003 2026-05-21 [1] https://p~
generics 0.1.4 2025-05-09 [1] CRAN (R 4.5.2)
ggplot2 4.0.3 2026-04-22 [1] CRAN (R 4.5.2)
glue 1.8.1 2026-04-17 [1] CRAN (R 4.5.2)
gtable 0.3.6 2024-10-25 [1] CRAN (R 4.5.2)
htmltools 0.5.9 2025-12-04 [1] CRAN (R 4.5.2)
htmlwidgets 1.6.4 2023-12-06 [1] CRAN (R 4.5.2)
jsonlite 2.0.0 2025-03-27 [1] CRAN (R 4.5.2)
knitr 1.51 2025-12-20 [1] CRAN (R 4.5.2)
labeling 0.4.3 2023-08-29 [1] CRAN (R 4.5.2)
lattice 0.22-9 2026-02-09 [2] CRAN (R 4.5.2)
lifecycle 1.0.5 2026-01-08 [1] CRAN (R 4.5.2)
magrittr * 2.0.5 2026-04-04 [1] CRAN (R 4.5.2)
Matrix 1.7-5 2026-03-21 [1] CRAN (R 4.5.2)
mmrm 0.3.17.9038 2026-06-12 [1] https://p~
mvtnorm 1.4-1 2026-06-06 [1] RSPM
nestcolor * 0.1.3.9000 2025-01-21 [1] https://p~
nlme 3.1-169 2026-03-27 [2] CRAN (R 4.5.2)
otel 0.2.0 2025-08-29 [1] CRAN (R 4.5.2)
parallelly 1.47.0 2026-04-17 [1] CRAN (R 4.5.2)
pillar 1.11.1 2025-09-17 [1] CRAN (R 4.5.2)
pkgcache 2.2.5 2026-04-09 [1] CRAN (R 4.5.2)
pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.5.2)
processx 3.9.0 2026-04-22 [1] CRAN (R 4.5.2)
ps 1.9.3 2026-04-20 [1] CRAN (R 4.5.2)
purrr 1.2.2 2026-04-10 [1] CRAN (R 4.5.2)
R6 2.6.1 2025-02-15 [1] CRAN (R 4.5.2)
random.cdisc.data 0.3.16.9007 2025-11-13 [1] https://p~
rbibutils 2.4.1 2026-01-21 [1] CRAN (R 4.5.2)
RColorBrewer 1.1-3 2022-04-03 [1] CRAN (R 4.5.2)
Rcpp 1.1.1-1.1 2026-04-24 [1] CRAN (R 4.5.2)
Rdpack 2.6.6 2026-02-08 [1] CRAN (R 4.5.2)
rlang 1.2.0 2026-04-06 [1] CRAN (R 4.5.2)
rmarkdown 2.31 2026-03-26 [1] CRAN (R 4.5.2)
rtables * 0.6.16.9001 2026-05-21 [1] https://p~
S7 0.2.2 2026-04-22 [1] CRAN (R 4.5.2)
scales 1.4.0 2025-04-24 [1] CRAN (R 4.5.2)
sessioninfo 1.2.4 2026-06-04 [1] CRAN (R 4.5.2)
stringi 1.8.7 2025-03-27 [1] CRAN (R 4.5.2)
stringr 1.6.0 2025-11-04 [1] CRAN (R 4.5.2)
survival 3.8-6 2026-01-16 [2] CRAN (R 4.5.2)
tern * 0.9.10.9012 2026-06-11 [1] https://p~
tern.mmrm * 0.3.3.9000 2025-07-07 [1] https://p~
testthat 3.3.2 2026-01-11 [1] CRAN (R 4.5.2)
tibble 3.3.1 2026-01-11 [1] CRAN (R 4.5.2)
tidyr 1.3.2 2025-12-19 [1] CRAN (R 4.5.2)
tidyselect 1.2.1 2024-03-11 [1] CRAN (R 4.5.2)
TMB 1.9.21 2026-03-23 [1] CRAN (R 4.5.2)
vctrs 0.7.3 2026-04-11 [1] CRAN (R 4.5.2)
withr 3.0.2 2024-10-28 [1] CRAN (R 4.5.2)
xfun 0.58 2026-06-01 [1] CRAN (R 4.5.2)
xtable 1.8-8 2026-02-22 [1] CRAN (R 4.5.2)
yaml 2.3.12 2025-12-10 [1] CRAN (R 4.5.2)
[1] /usr/local/lib/R/site-library
[2] /usr/local/lib/R/library
[3] /github/home/R/x86_64-pc-linux-gnu-library/4.5
* ── Packages attached to the search path.
──────────────────────────────────────────────────────────────────────────────
.lock fileDownload the .lock file and use renv::restore() on it to recreate environment used to generate this website.
---
title: MMRMG02
subtitle: Forest Plot for Mixed-Effect Model Repeated Measures
---
------------------------------------------------------------------------
{{< include ../../_utils/envir_hook.qmd >}}
```{r setup, echo = FALSE, warning = FALSE, message = FALSE}
library(dplyr)
library(tern.mmrm)
library(nestcolor)
adsl <- random.cdisc.data::cadsl
adqs <- random.cdisc.data::cadqs
adqs_f <- adqs %>%
dplyr::filter(PARAMCD == "FKSI-FWB" & !AVISIT %in% c("BASELINE")) %>%
droplevels() %>%
dplyr::mutate(ARMCD = factor(ARMCD, levels = c("ARM B", "ARM A", "ARM C"))) %>%
dplyr::mutate(
AVISITN = rank(AVISITN) %>%
as.factor() %>%
as.numeric() %>%
as.factor()
)
# Simulation of groups.
set.seed(2)
adqs_f_with_groups <- rbind(
within(
adqs_f[sample(seq_len(nrow(adqs_f)), size = 1 / 2 * nrow(adqs_f)), ],
group <- "subpopulation 1"
),
within(
adqs_f,
{
group <- "subpopulation 2"
AVAL <- AVAL + rnorm(length(AVAL), mean = 10, sd = 2)
USUBJID <- paste0(USUBJID, "-S2")
}
)
)
adqs_f_with_groups$group <- factor(adqs_f_with_groups$group)
```
::: panel-tabset
## Comparing Multiple Populations
For the following part, an MMRM model is fitted for the dataset and from this result forest plots are constructed for each set of subgroups, treatment arm, and visit that is of interest.
First an MMRM model is fitted for the whole dataset.
```{r mmrm_results}
#| code-fold: show
mmrm_results <- fit_mmrm(
data = adqs_f_with_groups,
vars = list(
response = "AVAL",
covariates = c(),
id = "USUBJID",
arm = "ARMCD",
visit = "AVISIT"
),
cor_struct = "unstructured",
weights_emmeans = "equal",
parallel = TRUE
)
```
Applying the `tern.mmrm::extract_mmrm_subgroups` function prepares the obtained `mmrm_results` for a specific visit - in this case we use the `SCREENING` visit - and treatment arm relative to the reference arm. From these results a table is obtained using the `tern.mmrm::tabulate_mmrm_subgroups` function from which the graphic can be directly obtained with `tern::g_forest`.
Here we compare `ARM A` with the reference arm, `ARM B`.
```{r plot1, test = list(plot_v1 = "plot"), fig.width = 15, fig.height = 4}
df_a <- extract_mmrm_subgroups(
fit = mmrm_results,
visit = "SCREENING",
subgroups = c("group", "SEX"),
treatment_arm = "ARM A"
)
tab_a <- basic_table() %>%
tabulate_mmrm_subgroups(
df = df_a,
vars = c("n_tot", "diff", "ci", "pval")
)
plot <- g_forest(
tab_a,
logx = FALSE,
xlim = c(-5, 2.5),
x_at = c(-5, -2.5, 0, 2.5),
vline = 0
)
plot
```
Then we compare `ARM C` with `ARM B`.
```{r plot2, test = list(plot_v2 = "plot"), fig.width = 15, fig.height = 4}
df_c <- extract_mmrm_subgroups(
fit = mmrm_results,
visit = "SCREENING",
subgroups = c("group", "SEX"),
treatment_arm = "ARM C"
)
tab_c <- basic_table() %>%
tabulate_mmrm_subgroups(
df = df_c,
vars = c("n_tot", "diff", "ci", "pval")
)
plot <- g_forest(
tab_c,
logx = FALSE,
xlim = c(-5, 2.5),
x_at = c(-5, -2.5, 0, 2.5),
vline = 0
)
plot
```
```{r test parameters, test = list(width = "width", width = "width"), echo = FALSE}
width <- 15
height <- 4
```
## Data Setup
```{r setup}
#| code-fold: show
```
:::
{{< include ../../_utils/save_results.qmd >}}
{{< include ../../repro.qmd >}}