---
: ".docx"
title
:
format:
docx: true
toc
:
execute: false
echo---
Fit and display models in a for-loop
There are situations where fitting a series of models in a for-loop in R is beneficial, especially when making minor modifications such as replacing one predictor variable with another. Instead of manually fitting each model separately, a for-loop can be structured to dynamically iterate over a vector of predictors. While implementing this in R is straightforward, a greater challenge arises when rendering the model outputs into a Quarto-generated Word (.docx) document.
I frequently use Quarto and the gtsummary package to generate Word output to share results with my team. This includes tables, listings, and figures that allow team members to review the findings and provide feedback.
There are several ways to incorporate iterative model results into a Quarto-rendered Word document. One approach is to store model outputs in a list and manually create subheadings in the Quarto document to display each result. Another option is to structure the for-loop to generate separate Word files, each corresponding to a model with its respective modification. The method I have settled on involves printing a Quarto heading and output directly within the for-loop. This results in a well orgarnized document with headings, a table of contents, and a table of each model’s ouptu.
However, this requires additional modifications to how model output is saved and rendered in Quarto. The key components of this method include setting an R chunk to render with “asis” formatting, saving each model output as a Word object, and using the cat() function to generate a heading and display the Word output within the loop.
This approach ensures that results are structured dynamically while keeping the document organized and readable.
Specify the Quarto yaml header options
Load packages
library(tidyverse)
library(gtsummary)
Create a vector of all variables iterate through
<- c("moderator1", "moderator2", "moderator3") moderators
Create a new R chunk with options specifif to the for-loop
In a new R chunk in a Quarto document, set results: to ‘asis’ . “#| results: ‘asis’” since this approach will rely on printing a gtsummary table to openxml so that it will show up as a neatly formatted table in Word.
# results: 'asis'
# echo: false
Set up a for loop for each new predictor to be added
I will usually start a for-loop by specifying the values to iteratre through and setting up the beginning and closing curly braces.
for (i in 1:length(moderators)) {
}
Insert a heading
After setting the for loop, I add a cat() command. The purpose of this line is to create a subheading “##” followed by the moderator, and then a new line “”. This separates each table’s output to make navigating the results easier. If the table of contents in the Quarto yaml header is set to true, then these should also represent sections that one can use to navigate in the output Word document.
for (i in 1:length(moderators)) {
cat("## ", moderators[i], "\n") # Display a subheading in the rendered document
}
Set up a formula
The next part of the loop is to specify formulat. One way to do this is to use the str_c() function to concatenate different elements of the model formula. In this example, I fit a linear mixed effect model, where each moderator is dynamically included as a predictor and combined as an interaction with predictors x3 and x4, and includes a random effect for the subject.
for (i in 1:length(moderators)) {
cat("## ", moderators[i], "\n")
<- as.formula(
formula str_c(
"y ~ x1 + x2 + ", # dependent variable and covariates
# moderator to iterate over
moderators[i], " + x3 + x4 + ", # predictors to be combined with moderators
"x3:", moderators[i], # interaction between predictor 3 and moderator
" + x4:", moderators[i], # interaction between predictor 4 and moderator
" + ",
"(1 | subject_id)" # clustering term/random effect
)
)
}
Fit the model
Once the formula is specified, the model can be fit. In this case, I am using the lmerTest package because we are interesting in retrieving a p value from the results section. The lme4 package, for reasons beyond the scope of this write-up, does not provide p-values.
for (i in 1:length(moderators)) {
cat("## ", moderators[i], "\n")
<- as.formula(
formula str_c(
"y ~ x1 + x2 + ",
moderators[i], " + x3 + x4 + ",
"x3:", moderators[i],
" + x4:", moderators[i],
" + ",
"(1 | subject_id)"
)
)
# Fit the model
<- lmerTest::lmer(formula, data = modeling_data_set)
model
}
Generate a table to display
After the model is fit, I like to use the tbl_regression() function from the gtsummary package to display model output. In order for the tables to display properly, they need to go through a series of transformations. Initially, the tbl_regression() function creates a gtsummary object, that is then converted to a gt object. Finally the gt object is then converted to a Word object. If we were to print the gt::as_word() object at this point in the R console, it would be in openxml format and would be very difficult to read.
for (i in 1:length(moderators)) {
cat("## ", moderators[i], "\n")
<- as.formula(
formula str_c(
"y ~ x1 + x2 + ",
moderators[i], " + x3 + x4 + ",
"x3:", moderators[i],
" + x4:", moderators[i],
" + ",
"(1 | subject_id)"
)
)
# Fit the model
<- lmerTest::lmer(formula, data = modeling_data_set)
model
# Generate a table to display
<- model %>%
tab tbl_regression(., estimate_fun = ~style_sigfig(., digits = 4)) %>%
as_gt() %>%
::as_word()
gt }
Print the table in the Word document
However, the gt::as_word() object can be printed in a rendered document “asis” which will be displayed as a table in Word document. To accomplish this, we will print a backtick fence with the option as openxml, the gt::as_word() object, the closing backtick fence using a new line (“”) as a separator. This completes printing the table to Word. The last line of code in the for-loop prints two new lines to separate each table in the for-loop.
for (i in 1:length(moderators)) {
cat("## ", moderators[i], "\n")
<- as.formula(
formula str_c(
"y ~ x1 + x2 + ",
moderators[i], " + x3 + x4 + ",
"x3:", moderators[i],
" + x4:", moderators[i],
" + ",
"(1 | subject_id)"
)
)
# Fit the model
<- lmerTest::lmer(formula, data = modeling_data_set)
model
# Generate a table to display
<- model %>%
tab tbl_regression(., estimate_fun = ~style_sigfig(., digits = 4)) %>%
as_gt() %>%
::as_word()
gt
# Print a word gt table object in a for-loop
cat("```{=openxml}", tab, "```", sep = "\n")
cat("\n\n")
}