Tutorials › Generalized Linear Mixed Models (GLMM) › Organizing a Reproducible GLMM Project

Software and Case Studies · Tutorial 653 of 698

Organizing a Reproducible GLMM Project

Build a project whose data, scripts, fitted objects, and reports are organized and can be regenerated from a clean R session.

Intermediate 12 min read

What You'll Learn

  • Organize a GLMM project into raw-data, analysis-data, scripts, fitted-object, and report folders.
  • Use project-relative paths instead of machine-specific file locations.
  • Separate scripts by workflow stage and run them in a declared order.
  • Record package versions and other information needed to understand the computational environment.
  • Check whether a clean-session run regenerates the project outputs without hidden interactive state.
  • Recognize what project organization can and cannot guarantee about reproducibility.

Why Project Organization Matters

A GLMM analysis often involves more than one model-fitting command. It may begin with source files, proceed through a set of transformations, produce one or more fitted objects, and end with diagnostic evidence and a report. If these steps depend on files scattered across a computer or objects left in an interactive R session, another analyst may be unable to reproduce the results—even if the model itself was specified correctly.

A reproducible project makes the path from inputs to outputs explicit. Its organization should let a reader identify which data were supplied, which scripts were run, where intermediate and fitted objects were written, and how the report was generated. This is computational organization, not a substitute for sound statistical decisions: a well-organized project cannot repair an unsuitable model or inadequate data.

Definition: A reproducible GLMM project is an organized set of files and documented computational steps from which the reported analysis can be rerun from declared inputs and a sufficiently specified software environment. Reproduction means that the analysis can be repeated and its results checked; it does not guarantee identical numerical output on every computer or prove that the scientific conclusions are correct.

A Practical Project Layout

A useful starting point is one project directory, opened as an RStudio Project or otherwise treated as the project root. Keep distinct roles distinct. The names below are conventions rather than requirements; consistency and clear documentation matter more than a particular spelling.

Folder or fileRoleTypical contents
data-raw/Inputs as receivedOriginal files, with a note describing their source and date
data/Analysis-ready dataFiles produced by documented preparation steps
R/Analysis scriptsSetup, preparation, model-fitting, checking, and reporting scripts
fits/Fitted objectsSaved model objects and other computational results needed downstream
reports/Reports and figuresRendered reports and generated figures
run_all.RWorkflow entry pointCommands that run the analysis scripts in a declared order
renv.lockPackage-version recordRecorded R package versions for the project environment

Treat files in data-raw/ as source inputs: do not silently overwrite them during analysis. Keep generated files in their designated folders so it is clear which files are inputs and which are outputs. If raw data cannot be stored in a version-control repository because of confidentiality, document how an authorized analyst obtains the exact input files, and keep them in an approved secure location. Never include sensitive records merely to make a repository appear complete.

A project-relative path names a file from the project root rather than from a particular computer's home directory. For example, the path to an analysis data file can be constructed as project root + “data” + “analysis.rds”. Moving the entire project changes the root but not the relative components. This is why a path such as /Users/ana/Desktop/study/data/analysis.rds is fragile: it encodes one person's directory structure, not the project's structure.

Key principle: Every input and output should have a declared location relative to the project root. In R, a project-aware path helper such as here::here() can construct these paths; avoid relying on an undocumented working directory or changing it with setwd().

Worked Example: Assigning Files to Project Folders

Suppose a hypothetical team analyzes follow-up outcomes from 12 clinics. The team receives a CSV file from its data manager and wants to keep the source, prepared data, fitted model, and report clearly separated. Its project root is named clinic-glmm.

The received file is stored as clinic-glmm/data-raw/followup.csv. A preparation script in R/ writes the analysis-ready file to clinic-glmm/data/analysis.rds. A model-fitting script writes the fitted object to clinic-glmm/fits/primary_glmm.rds, and a report is rendered to clinic-glmm/reports/primary-results.html.

The relative path to the fitted object is fits/primary_glmm.rds; the relative path to the prepared data is data/analysis.rds. If the project directory moves from a desktop to an approved shared drive, those relative paths remain the same. The team still needs to preserve the source file and the scripts that produce each output, but it does not need to edit a path for each computer.

This layout also makes a missing step easier to detect. If the report exists but the fitted object and fitting script do not, the reported result cannot be readily traced to a rerunnable analysis. The directory structure is not proof of reproducibility, but it provides a map for checking it.

Scripts, Dependencies, and a Declared Run Order

Divide work into scripts that have recognizable inputs and outputs. For example, R/01_prepare_data.R can create the analysis-ready data, R/02_fit_model.R can fit and save the specified model, and R/03_checks.R and R/04_report.R can create the planned checks and report. The model specification and diagnostic methods must be chosen on statistical grounds; this tutorial concerns how their implementation is organized, not how to choose or fit a particular GLMM.

A single entry-point script should make the order explicit. A simple run_all.R can source the project scripts in sequence, using project-relative paths. For example, it can contain these commands: source(here::here("R", "01_prepare_data.R")); source(here::here("R", "02_fit_model.R")); source(here::here("R", "03_checks.R")); and source(here::here("R", "04_report.R")). Each script should read its inputs from the declared folders and write its outputs there, rather than expecting an analyst to select files interactively.

Reproducibility also requires attention to software dependencies. R packages change, and changes can affect functions, defaults, or numerical calculations. The renv package can record project package versions in a lockfile. A typical project initializes an environment with renv::init(), records the packages used with renv::snapshot(), and restores the recorded package library on another setup with renv::restore(). The lockfile should be kept with the project. It records package versions, not every aspect of the operating system, R installation, or external system libraries, so it improves reproducibility without making all environments identical.

Worked Example: Running the Workflow From a Clean Session

Suppose the hypothetical clinic project contains 48 eligible records in its analysis-ready dataset. Its preparation script creates data/analysis.rds; its fitting script reads that file and saves fits/primary_glmm.rds. The project scripts have already been written to implement the team's chosen analysis.

First, close the existing R session or start a fresh one, open the project, and restore the recorded package environment if needed. Then run the entry point from the project root. In a terminal opened at that root, the command is Rscript --vanilla run_all.R. The --vanilla option starts R without loading personal startup files or restoring a saved workspace, reducing the chance that unrecorded objects or settings affect the run.

The workflow can include explicit checks such as dat <- readRDS(here::here("data", "analysis.rds")) followed by stopifnot(nrow(dat) == 48L). The check compares the observed number of rows with the documented expectation: if 48 rows are present, the condition is true and the workflow continues; if there are 47 or 49, it stops instead of silently producing an output from an unexpected file. This check is appropriate only if 48 is in fact the correct expected count for this project's specified input and processing.

After the run, check that fits/primary_glmm.rds and the intended report have been created or updated. A fresh session is important: success in an already-used session does not show that the scripts created all required objects themselves. The run also tests the declared order: a fitting script that requires an object produced only by manually running an unrelated command will fail when the workflow is run from the beginning.

Why a Clean-Session Run Is a Real Test

An interactive session can contain objects, options, package attachments, or a working directory that were established informally. A script may therefore appear to work even though it omits a necessary step. A clean-session run removes much of that hidden state and asks a practical question: can the project recreate its outputs from its declared inputs and instructions?

The underlying path logic is simple. Let \(R\) denote the project root and \(r\) a relative path within the project. The file being accessed is the composition of the root and the relative path, \(R \oplus r\). If the project is moved, the root changes from \(R_1\) to \(R_2\), but the internal path \(r\) remains fixed. A machine-specific absolute path instead embeds \(R_1\) directly in each script, so moving the project requires edits and can leave inconsistent references behind. Project-aware path construction implements the first approach.

A clean run is not a guarantee of exact numerical identity. Optimization algorithms may behave slightly differently across software versions or platforms, and simulation-based procedures require deliberate random-number handling. When a script uses random simulation, record and set the seed, for example with set.seed(20261024), immediately before the relevant stochastic computation. Record the R version and session details, such as by saving the output of sessionInfo(). A seed controls R's random-number stream under a given computational setup; it does not ensure identical results across all software versions or guarantee that a stochastic method is scientifically appropriate.

1
Declare the inputs.
Identify the source data and required software. Keep source files unchanged and record how unavailable or restricted inputs can be obtained.
2
Run scripts in order.
Use one entry point and project-relative paths so each stage receives the files created by the preceding stage.
3
Check the outputs.
Confirm that expected analysis files, fitted objects, and reports exist, and use suitable assertions to catch unexpected inputs.
4
Record the environment.
Keep the package lockfile and session information, and record seeds when random computation is part of the workflow.

Diagnostics, Alternatives, and Limitations

A project organization is working poorly when a clean run fails, output files appear in unexpected locations, scripts request manual file selection, or results change because of an unrecorded package update. Other warning signs include a report that reads from a personal desktop path, a fitted object that has no script showing how it was produced, and a script that succeeds only after objects have been created manually in the console. These are workflow failures, not evidence about whether the GLMM fits the data.

There are reasonable alternatives to a simple folder-and-script workflow. An interactive notebook can combine narrative, code, and output in one document, which is useful for exploration and teaching; however, it still needs declared inputs and a clean re-execution check. A more formal pipeline tool can track dependencies and rerun only stages affected by changed inputs, which becomes valuable for large or computationally expensive projects. A plain sequence of scripts is often easier to inspect for a small analysis. In each case, the essential standard is the same: the inputs, execution order, software dependencies, and generated outputs are explicit.

Version control can preserve changes to scripts and documentation, but it should not be confused with data backup or automatic provenance. Large files, restricted records, and generated outputs may need to be excluded from version control while remaining securely preserved elsewhere. Likewise, saving a fitted object is useful for downstream reporting, but it does not replace the script and inputs that produced the object. A lockfile cannot capture every system-level dependency, and a successful rerun cannot establish that the analysis was free of data errors or statistical misinterpretation.

Common pitfall: “The script runs on my computer, so the analysis is reproducible.” This conclusion is too strong if the run depends on a saved workspace, a manually selected file, an absolute path, or an unrecorded package version. The correction is to start from a clean session, run the declared entry point, and verify that the expected outputs are rebuilt from the documented inputs.

Worked Example: Checking Whether a Result Is Traceable

Suppose a hypothetical report gives a GLMM estimate, while the project contains the report and a fitted object but no model-fitting script. The result is not fully traceable: a reader can inspect the saved object, but cannot establish from the project how it was fitted or regenerate it from the source data.

The repair is to include a fitting script that reads the declared analysis-ready file, fits the already specified model, and saves the fitted object to the stated folder. The report should then obtain the object through that declared path rather than from a manually loaded workspace. In a clean session, run the entry point and check that both the object and report are produced. If the source input is not distributable, document its location and access procedure, and distinguish that limitation from the parts of the workflow that can be rerun.

This check does not ask whether the model is substantively correct; it checks whether the computational chain connecting inputs, model object, and reported result is inspectable. Statistical validity and computational reproducibility are related but separate requirements.

Check Your Understanding

Answer each question, then compare with the solutions below.

  1. Why is a project-relative path usually preferable to a path beginning with a user's home directory?
  2. A fitting script works only after an analyst has manually loaded an object in the console. What does this indicate, and what should be tested?
  3. What does an R package lockfile help preserve, and what does it not guarantee?
  4. A report and fitted object are saved, but the script that generated the object is missing. What part of reproducibility is incomplete?

Solutions. (1) A relative path names a location within the project, so the project can move without rewriting the internal path; a home-directory path encodes a machine-specific location. (2) The script depends on hidden session state. Run the full entry point from a fresh session and ensure the script creates or loads each required object through declared steps. (3) A lockfile records package versions and supports restoring the project library; it does not guarantee identical operating systems, R installations, external libraries, or numerical results in every environment. (4) The computational provenance is incomplete: the saved object may be inspectable, but the project does not show how to regenerate it from the declared inputs.

Clinical Biostats