| Title: | Generate Model Diagrams for Linear Mixed Effect Models |
|---|---|
| Description: | Generates 'DiagrammeR' model diagrams for hierarchical linear mixed effects models. Details can be found in Linse (2026) <doi:10.6339/26-JDS1222>. |
| Authors: | Greta Linse [aut, cre], Mark Greenwood [aut] |
| Maintainer: | Greta Linse <[email protected]> |
| License: | GPL-3 |
| Version: | 0.2.1 |
| Built: | 2026-05-15 09:14:25 UTC |
| Source: | https://github.com/glinse-stat/modeldiagramr |
Results of a study comparing three staining methods to determine age of dolphins from six species of cetaceans from two different locations (Spain and Scotland). It is unclear how the numeric values of sex are coded so they are left as integers. This dataset has been cleaned to remove the dolphin with undetermined sex (DolphinID = 10).
cetaceanscetaceans
a data.frame with 177 observations on 11 variables from 59 dolphins:
Dolphin ID from 1 to 61 (excluding 10 and 16)
Species of cetacean (6 species)
Age determined by staining method from a tooth, numeric
Sex of dolphin, integer
Treatment, staining method (Mayer, Elrich, Toluidine), categorical
Location of stranding (Scotland, Spain), categorical
Factor version of Species variable
Factor version of DolphinID
Factor version of Stain
Factor version of Location
Factor version of Sex
Additional variables include sex of the animal, location of the stranding, and stain (Mayer Haematoxylin, Ehlrich Haematoxylin, and Toluidine Blue).
https://www.highstat.com/Books/Book2/ZuurDataMixedModelling.zip
Zuur AF, Ieno EN, Walker N, Saveliev AA, and Smith GM. 2009. Mixed effects models and extensions in ecology with R. Statistics for Biology and Health. Springer New York, p. 460. DOI: 10.1007/978-0-387-87458-6
library(dplyr) data("cetaceans") # Exploratory Data Plots library(ggplot2) library(viridis) p1 <- cetaceans %>% ggplot(aes(x = DolphinID, y = Age, group=fDolphinID)) + geom_boxplot(aes(color=fSpecies)) + scale_color_viridis_d(end = 0.8) + theme_bw() + theme(legend.position="none") p2 <- cetaceans %>% ggplot(aes(x = fSpecies, y = Age, group=fSpecies)) + geom_boxplot(aes(color = fSpecies)) + scale_color_viridis_d(end = 0.8) + theme_bw() + theme(axis.text.x = element_text(angle=90), legend.position="none") p3 <- cetaceans %>% ggplot(aes(x = fSpecies, y = Age)) + geom_boxplot(aes(color = fSpecies)) + scale_color_viridis_d(end = 0.8) + facet_wrap(~fLocation)+ theme_bw() + theme(axis.text.x = element_text(angle=90)) library(patchwork) (p1) / (p2) / (p3) # Simple model for testing library(nlme) lme_simple <- lme(Age ~ fSex*fStain*fLocation, random = ~ 1|fSpecies/fDolphinID, data = cetaceans) model_diagram(lme_simple) # Intended full model library(lme4) library(lmerTest) lmer1 <- lmer(Age ~ fSex*fStain*fLocation + (1|fSpecies/fDolphinID), data = cetaceans) summary(lmer1) model_diagram(lmer1)library(dplyr) data("cetaceans") # Exploratory Data Plots library(ggplot2) library(viridis) p1 <- cetaceans %>% ggplot(aes(x = DolphinID, y = Age, group=fDolphinID)) + geom_boxplot(aes(color=fSpecies)) + scale_color_viridis_d(end = 0.8) + theme_bw() + theme(legend.position="none") p2 <- cetaceans %>% ggplot(aes(x = fSpecies, y = Age, group=fSpecies)) + geom_boxplot(aes(color = fSpecies)) + scale_color_viridis_d(end = 0.8) + theme_bw() + theme(axis.text.x = element_text(angle=90), legend.position="none") p3 <- cetaceans %>% ggplot(aes(x = fSpecies, y = Age)) + geom_boxplot(aes(color = fSpecies)) + scale_color_viridis_d(end = 0.8) + facet_wrap(~fLocation)+ theme_bw() + theme(axis.text.x = element_text(angle=90)) library(patchwork) (p1) / (p2) / (p3) # Simple model for testing library(nlme) lme_simple <- lme(Age ~ fSex*fStain*fLocation, random = ~ 1|fSpecies/fDolphinID, data = cetaceans) model_diagram(lme_simple) # Intended full model library(lme4) library(lmerTest) lmer1 <- lmer(Age ~ fSex*fStain*fLocation + (1|fSpecies/fDolphinID), data = cetaceans) summary(lmer1) model_diagram(lmer1)
Results of a study of 10 subjects on sweat rates and sodium concentrations, paired between two locations on each subject I digitized the plotted data to create the provided data set, results do not exactly match the original results.
combinedtattoocombinedtattoo
a data.frame with 20 observations on 10 variables from 10 subjects:
Subject ID from 1 to 10 0
Rate of sweat from a location, mg per cm-squared per min
Tattoo location or Not
Na concentration, mMol/L
Height of subject in cm
Weight of subject in kg
Age of subject in years
Side of subject, left or right
Location of tattoo, categorical
Age of tattoo (replicated both subject observations), years
Subject demographics are assumed to be correctly matched but were merged based on the information in a plot and in a separate table
Digitized version of published Figures 1 and 2 combined with Table 1 (does not exactly match published numerical summaries!)
Luetkemeier, M., Hanisko, J., and K. Aho (2017) Skin Tattoos Alter Sweat Rate and Na+ Concentration. Medicine & Science in Sports & Exercise 49(7):p 1432-1436. DOI: 10.1249/MSS.0000000000001244
library(dplyr) data("combinedtattoo") combinedtattoo <- combinedtattoo %>% mutate(SubjectF = factor(Subject)) # Exploratory Data Plots library(ggthemes) library(ggplot2) library(viridis) p1 <- combinedtattoo %>% ggplot(aes(x = Tat_not, y = SweatRate, group = SubjectF)) + geom_line(aes(color = SubjectF)) + scale_color_viridis_d(end = 0.8) p2 <- combinedtattoo %>% ggplot(aes(x = Tat_not, y = Na_conc, group = SubjectF)) + geom_line(aes(color = SubjectF)) + scale_color_viridis_d(end = 0.8) p3 <- combinedtattoo %>% ggplot(aes(x = Weight_kg, y = SweatRate)) + geom_point(aes(color = Subject)) + scale_color_viridis_c(end = 0.8) + geom_smooth(method = "lm") + facet_wrap(~Tat_not) library(patchwork) (p1 + p2) / (p3) library(lmerTest) lmer1 <- lmer(SweatRate ~ Tat_not + (1|Subject), data = combinedtattoo) summary(lmer1) model_diagram(lmer1) lmerFL <- lmer(SweatRate ~ Tat_not*Weight_kg + (1|Subject), data = combinedtattoo) summary(lmerFL) model_diagram(lmerFL)library(dplyr) data("combinedtattoo") combinedtattoo <- combinedtattoo %>% mutate(SubjectF = factor(Subject)) # Exploratory Data Plots library(ggthemes) library(ggplot2) library(viridis) p1 <- combinedtattoo %>% ggplot(aes(x = Tat_not, y = SweatRate, group = SubjectF)) + geom_line(aes(color = SubjectF)) + scale_color_viridis_d(end = 0.8) p2 <- combinedtattoo %>% ggplot(aes(x = Tat_not, y = Na_conc, group = SubjectF)) + geom_line(aes(color = SubjectF)) + scale_color_viridis_d(end = 0.8) p3 <- combinedtattoo %>% ggplot(aes(x = Weight_kg, y = SweatRate)) + geom_point(aes(color = Subject)) + scale_color_viridis_c(end = 0.8) + geom_smooth(method = "lm") + facet_wrap(~Tat_not) library(patchwork) (p1 + p2) / (p3) library(lmerTest) lmer1 <- lmer(SweatRate ~ Tat_not + (1|Subject), data = combinedtattoo) summary(lmer1) model_diagram(lmer1) lmerFL <- lmer(SweatRate ~ Tat_not*Weight_kg + (1|Subject), data = combinedtattoo) summary(lmerFL) model_diagram(lmerFL)
Similar to ggplot2::ggplot()'s ggplot2::theme() system, the md_ functions
specify the display of how the node components of the diagram are drawn.
md_color(): border of the node.
md_fill(): fill color of the node.
md_fontColor(): font color of the text in the nodes.
md_color(diagram = "gray25", random = "gray25", fixed = "gray25") md_fill(diagram = "aliceblue", random = "aliceblue", fixed = "darkseagreen1") md_fontColor(diagram = "black", random = "black", fixed = "black")md_color(diagram = "gray25", random = "gray25", fixed = "gray25") md_fill(diagram = "aliceblue", random = "aliceblue", fixed = "darkseagreen1") md_fontColor(diagram = "black", random = "black", fixed = "black")
diagram |
Specifies the outline color, fill color, or font color
for the elements in the measurement diagram circles (hierarchical diagram).
Default is |
random |
Specifies the outline color, fill color, or font color
for the elements in the random effect variable boxes.
Default is |
fixed |
Specifies the outline color, fill color, or font color
for the elements in the fixed effect variable boxes.
Default is |
A list object containing color specifications for the border of the nodes.
A list object containing color specifications for the fill of the nodes.
A list object containing color specifications for the font color of the nodes.
# merMod object example library(lme4) sleepstudy_lmer <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) summary(sleepstudy_lmer) model_diagram(sleepstudy_lmer, nodeColors = md_color(diagram="steelblue", random="steelblue", fixed="darkolivegreen1"), nodeFillColors = md_fill(diagram="aliceblue", random="aliceblue", fixed="darkseagreen1"), nodeFontColors = md_fontColor(diagram="black", random="blue", fixed="black"))# merMod object example library(lme4) sleepstudy_lmer <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) summary(sleepstudy_lmer) model_diagram(sleepstudy_lmer, nodeColors = md_color(diagram="steelblue", random="steelblue", fixed="darkolivegreen1"), nodeFillColors = md_fill(diagram="aliceblue", random="aliceblue", fixed="darkseagreen1"), nodeFontColors = md_fontColor(diagram="black", random="blue", fixed="black"))
model_diagram() takes a hierarchical nested model and returns a DiagrammeR
object visualizing the fixed and random effects structure.
model_diagram( modelObject, filePath = NULL, fileType = "PNG", width = 800, height = 1600, includeSizes = TRUE, includeLabels = TRUE, orientation = "vertical", scaleFontSize = 1, shiftFixed = 0, shiftRandom = 0, nodeColors = md_color(diagram = "gray25", random = "gray25", fixed = "gray25"), nodeFillColors = md_fill(diagram = "aliceblue", random = "aliceblue", fixed = "darkseagreen1"), nodeFontColors = md_fontColor(diagram = "black", random = "black", fixed = "black"), exportObject = 1 )model_diagram( modelObject, filePath = NULL, fileType = "PNG", width = 800, height = 1600, includeSizes = TRUE, includeLabels = TRUE, orientation = "vertical", scaleFontSize = 1, shiftFixed = 0, shiftRandom = 0, nodeColors = md_color(diagram = "gray25", random = "gray25", fixed = "gray25"), nodeFillColors = md_fill(diagram = "aliceblue", random = "aliceblue", fixed = "darkseagreen1"), nodeFontColors = md_fontColor(diagram = "black", random = "black", fixed = "black"), exportObject = 1 )
modelObject |
Input model. Either a lme or merMod (including glmerMod) object with a nested random effects structure. |
filePath |
Optional. Path to a location to export the diagram.
Default is |
fileType |
Optional. File type to export the diagram.
Default is |
width |
Optional. Width of diagram in pixels. Default is |
height |
Optional. Height of diagram in pixels. Default is |
includeSizes |
Optional. Include group sizes in random effect labels.
Default is |
includeLabels |
Optional. Include labels for the model diagram components.
Default is |
orientation |
Optional. Orientation of the diagram, either vertically or
horizontally. Options are |
scaleFontSize |
Optional. Proportional font size adjustment for model
diagram component, fixed effect, and random effect labels.
Multiplies these label font sizes by the specified amount. Default is |
shiftFixed |
Optional. Additive x-axis adjustment for fixed effect labels,
only used when |
shiftRandom |
Optional. Additive x-axis adjustment for random effect labels,
only used when |
nodeColors |
Optional. Function specifying the colors ( |
nodeFillColors |
Optional. Function specifying the colors ( |
nodeFontColors |
Optional. Function specifying the colors ( |
exportObject |
Optional. Object(s) to return, |
NOTE: When including this function in an RMD document and knitting to PDF, the
graphic size variables width and height do not currently work. It is
recommended that instead the image is exported to a file such as a PDF and then
reimported to the document. See the examples below.
A rendered DiagrammeR graph as an SVG document (default), or a dgr_graph
object, or a list containing both (dgr_graph_obj, rendered_graph_obj).
Greta M. Linse, Mark C. Greenwood, Ronald K. June, Data-Driven Model Structure Diagrams for Hierarchical Linear Mixed Models, J. data sci.(2026), 1-21, DOI 10.6339/26-JDS1222
# merMod object example library(lme4) sleepstudy_lmer <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) summary(sleepstudy_lmer) model_diagram(sleepstudy_lmer) # lme object example library(nlme) sleepstudy_lme <- lme(Reaction ~ Days, random=~Days|Subject, data=sleepstudy) summary(sleepstudy_lme) model_diagram(sleepstudy_lme) library(rsvg) # required to produce a PDF file temp_path <- tempfile("sleepstudy_lmer_modeldiagram", fileext=".pdf") # Knitting to PDF example model_diagram(sleepstudy_lmer, filePath= temp_path, fileType="PDF")# merMod object example library(lme4) sleepstudy_lmer <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy) summary(sleepstudy_lmer) model_diagram(sleepstudy_lmer) # lme object example library(nlme) sleepstudy_lme <- lme(Reaction ~ Days, random=~Days|Subject, data=sleepstudy) summary(sleepstudy_lme) model_diagram(sleepstudy_lme) library(rsvg) # required to produce a PDF file temp_path <- tempfile("sleepstudy_lmer_modeldiagram", fileext=".pdf") # Knitting to PDF example model_diagram(sleepstudy_lmer, filePath= temp_path, fileType="PDF")
The data is simulated using simulate_SSPD3_data()
SSPD3SSPD3
a data.frame with 120 observations on 13 variables:
Observation/split-split-plot identification number, numeric
One of three locations "A", "B", and "C", a blocking variable, categorical
Plot identification variable, one for each location and whole plot combination, numeric
Replication number, all 1 as there was no replication, integer
Treatment, whether or not the plot was irrigated "IRR_NO" for not irrigated and "IRR_YES" for irrigated, categorical
Treatment, fungicide applied to split-plot, 4 fungicides and one control, "Fung1", ..., "Fung4", and "NFung", categorical
Treatment, variety of bean seeded in split-split-plot, 4 varieties, "Beans1", ..., "Beans4", categorical
Treatments combined over WHOLE_PLOT, SPLIT_PLOT, and SPLIT_SPLIT_PLOT
Simulated response, not based on a meaningful input values, numeric
Factor version of LOCATION
Factor version of WHOLE_PLOT treatment
Factor version of SPLIT_PLOT treatment
Factor version of SPLIT_SPLIT_PLOT treatment
Murillo D, Gezan S (2024). FielDHub: A Shiny App for Design of Experiments in Life Sciences. R package version 1.4.2, https://CRAN.R-project.org/package=FielDHub.
data("SSPD3") # Simple model for testing library(nlme) lme_simple <- lme(RESP ~ Fungicide + Variety, random=~1|LOCATION/WHOLE_PLOT, data=SSPD3) model_diagram(lme_simple) # Intended full model aov_results <- aov(RESP ~ Irrigation*Fungicide*Variety + Error(LOCATION/WHOLE_PLOT/SPLIT_PLOT), data=SSPD3) summary(aov_results) library(lme4) library(modeldiagramR) lmer_results <- lmer(RESP ~ Irrigation*Fungicide*Variety + (1|LOCATION/WHOLE_PLOT/SPLIT_PLOT), data=SSPD3) summary(lmer_results) anova(lmer_results) model_diagram(lmer_results) model_diagram(lmer_results, width = 800, height=400, orientation="horizontal", shiftFixed = 2, shiftRandom = 5, scaleFontSize = 2)data("SSPD3") # Simple model for testing library(nlme) lme_simple <- lme(RESP ~ Fungicide + Variety, random=~1|LOCATION/WHOLE_PLOT, data=SSPD3) model_diagram(lme_simple) # Intended full model aov_results <- aov(RESP ~ Irrigation*Fungicide*Variety + Error(LOCATION/WHOLE_PLOT/SPLIT_PLOT), data=SSPD3) summary(aov_results) library(lme4) library(modeldiagramR) lmer_results <- lmer(RESP ~ Irrigation*Fungicide*Variety + (1|LOCATION/WHOLE_PLOT/SPLIT_PLOT), data=SSPD3) summary(lmer_results) anova(lmer_results) model_diagram(lmer_results) model_diagram(lmer_results, width = 800, height=400, orientation="horizontal", shiftFixed = 2, shiftRandom = 5, scaleFontSize = 2)