Last modified: 2026-10-11 02:07:28 (PDT)
Each study has at least one code repository that typically holds R code, shell scripts with Unix code, and research outputs (results .RDS files, tables, figures). Repositories may also include datasets. This chapter outlines how to organize these files. Adhering to a standard format makes it easier to collaborate across projects.
We recommend R package structure for most R-based projects. This provides benefits for reproducibility, collaboration, and code quality even for analysis-only projects.
When starting a new package, begin from the lab’s template repository (Morrison-Lab/rpt) rather than an empty repository. On GitHub, click Use this template to create a new repository that already includes our standard structure, CI workflows, and documentation scaffolding. Then replace the placeholder package name, Title, and Description in DESCRIPTION with your project’s details.
GitHub-style template repositories do not have a direct equivalent in GitLab Community Edition (CE):
In short: if the lab ever mirrors this workflow onto a self-managed GitLab CE instance, plan to either fork a seed repository or budget for a paid tier.
All R projects in our lab should be structured as R packages, even if they are primarily analysis projects and not intended for distribution on CRAN or Bioconductor. This standardized structure provides numerous benefits:
R/), documentation (man/), tests (tests/), data (data/), and vignettes/analysesDESCRIPTION file explicitly declares all package dependencies and version restrictions, which simplifies installing those dependencies.roxygen2 generates help files from inline commentstestthat framework integrates seamlessly with package structuredevtools::check() and lintr enforce best practicesmyproject/
├── DESCRIPTION # Package metadata and dependencies
├── NAMESPACE # Auto-generated, don't edit manually
├── R/ # All R functions (reusable code)
│ ├── analysis_functions.R
│ ├── data_prep.R
│ └── plotting.R
├── man/ # Auto-generated documentation
├── tests/
│ └── testthat/ # Unit tests
├── data/ # Processed data objects (.rda files)
├── data-raw/ # Raw data and data processing scripts
│ ├── 0-prep-data.sh # Shell scripts for data preparation
│ ├── process_survey_data.R
│ └── clean_lab_results.R
├── vignettes/ # Long-form documentation
│ ├── intro.qmd # Main vignettes (shipped with package)
│ ├── tutorial.qmd
│ └── articles/ # Website-only articles (not shipped)
│ ├── advanced-topics.qmd
│ └── case-studies.qmd
├── inst/ # Additional files to include in package
│ ├── extdata/ # External data files and .RDS results
│ │ ├── analysis_results.rds
│ │ └── processed_data.rds
│ ├── output/ # Figure and table outputs
│ │ ├── figures/
│ │ │ ├── fig1.pdf
│ │ │ └── fig2.png
│ │ └── tables/
│ │ ├── table1.csv
│ │ └── table2.xlsx
│ └── analyses/ # Analyses using restricted data (see below)
└── .Rproj # RStudio project file
Vignettes (vignettes/*.qmd): - Shipped with the package when installed - Accessible via vignette() and browseVignettes() in R - Displayed on CRAN - Built at package build time - Use for core package documentation and tutorials - Created with usethis::use_vignette("name")
Articles (vignettes/articles/*.qmd): - Website-only (not shipped with the package) - Only appear on the pkgdown website - Not accessible via vignette() in R - Not displayed on CRAN - Use for supplementary content, blog posts, extended tutorials, or frequently updated material - Created with usethis::use_article("name") - Automatically added to .Rbuildignore
When to use each: - Vignette: Essential tutorials users need offline, core package workflows - Article: Supplementary material, case studies, advanced topics, blog-style content
Additional considerations for choosing articles over vignettes:
Articles are particularly useful when (Wickham and Bryan 2023, sec. 17.4.1):
Use vignettes/ for analysis workbooks that:
Use vignettes/articles/ for:
All vignettes and articles will be rendered by pkgdown::build_site() on your package website.
For analyses that rely on private, sensitive, or restricted data, place .qmd or .qmd files in inst/analyses/:
myproject/
├── inst/
│ ├── analyses/
│ │ ├── 01-confidential-data-analysis.qmd
│ │ ├── 02-unpublished-results.qmd
│ │ └── README.md # Document data access requirements
│ └── extdata/
└── vignettes/
├── 01-public-analysis.qmd
└── 02-demo-with-simulated-data.qmd
Benefits of this approach:
inst/analyses/ is excluded from pkgdown builds and package documentationNote on privacy: Files in inst/analyses/ are not inherently private—they will be visible if your repository is public. Use this folder for analyses that rely on restricted data that is stored separately, not for storing the restricted data itself. If you need to keep the analysis code private, use a private repository.
Best practices for analyses with restricted data:
inst/analyses/ explaining:
vignettes/ using:
inst/analyses/ doesn’t cause package checks to fail:Decompose reusable functions from your analysis notebooks into the R/ directory. Your vignettes should:
Example of what NOT to do (all code in vignette):
Example of what TO do (functions in R/, simple calls in vignette):
Shell scripts are useful for automating workflows and ensuring reproducibility. Place shell scripts in data-raw/ alongside the R scripts they coordinate:
data-raw/
├── 0-prep-data.sh # Shell script to run all data prep
├── 01-load-survey.R
├── 02-clean-survey.R
├── 03-merge-datasets.R
└── 04-create-analysis-data.R
Using shell scripts:
This is especially useful when data upstream changes — you can simply run the shell script to reproduce everything. After running shell scripts, .Rout log files will be generated for each script. It is important to check these files to ensure everything has run correctly.
Results files (.RDS): Save analysis results in inst/extdata/:
Figures and tables: Save publication outputs in inst/output/:
Organization:
inst/
├── extdata/
│ ├── analysis_results.rds
│ ├── model_fits.rds
│ └── processed_data.rds
└── output/
├── figures/
│ ├── fig1_incidence_trends.pdf
│ ├── fig2_risk_factors.png
│ └── figS1_sensitivity.pdf
└── tables/
├── table1_demographics.csv
├── table2_main_results.xlsx
└── tableS1_detailed_results.csv
.Rproj filesAn “R Project” can be created within RStudio by going to File >> New Project. Depending on where you are with your research, choose the most appropriate option. This will save preferences, working directories, and even the results of running code/data (though I’d recommend starting from scratch each time you open your project, in general). Then, ensure that whenever you are working on that specific research project, you open your created project to enable the full utility of .Rproj files. This also automatically sets the directory to the top level of the project.
data-raw folderThe data-raw folder serves as a catch-all for scripts that do not (yet) fit into the package structure described above. The data-raw folder should still be organized. We recommend the following subdirectory structure for data-raw:
0-run-project.sh
0-config.R
1 - Data-Management/
0-prep-data.sh
1-prep-cdph-fluseas.R
2a-prep-absentee.R
2b-prep-absentee-weighted.R
3a-prep-absentee-adj.R
3b-prep-absentee-adj-weighted.R
2 - Analysis/
0-run-analysis.sh
1 - Absentee-Mean/
1-absentee-mean-primary.R
2-absentee-mean-negative-control.R
3-absentee-mean-CDC.R
4-absentee-mean-peakwk.R
5-absentee-mean-cdph2.R
6-absentee-mean-cdph3.R
2 - Absentee-Positivity-Check/
3 - Absentee-P1/
4 - Absentee-P2/
3 - Figures/
0-run-figures.sh
...
4 - Tables/
0-run-tables.sh
...
5 - Results/
1 - Absentee-Mean/
1-absentee-mean-primary.RDS
2-absentee-mean-negative-control.RDS
3-absentee-mean-CDC.RDS
4-absentee-mean-peakwk.RDS
5-absentee-mean-cdph2.RDS
6-absentee-mean-cdph3.RDS
...
.gitignore
For brevity, not every directory is “expanded”, but we can glean some important takeaways from what we do see.
This is the single most important file for your project. It will be responsible for a variety of common tasks, declare global variables, load functions, declare paths, and more. Every other file in the project will begin with source("0-config"), and its role is to reduce redundancy and create an abstraction layer that allows you to make changes in one place (0-config.R) rather than 5 different files. To this end, paths which will be reference in multiple scripts (i.e. a merged_data_path) can be declared in 0-config.R and simply referred to by its variable name in scripts. If you ever want to change things, rename them, or even switch from a downsample to the full data, all you would then to need to do is modify the path in one place and the change will automatically update throughout your project. See the example config file for more details. The paths defined in the 0-config.R file assume that users have opened the .Rproj file, which sets the directory to the top level of the project.
This makes the jumble of alphabetized filenames much more coherent and places similar code and files next to one another. This also helps us understand how data flows from start to finish and allows us to easily map a script to its output (i.e. 2 - Analysis/1 - Absentee-Mean/1-absentee-mean-primary.R => 5 - Results/1 - Absentee-Mean/1-absentee-mean-primary.RDS). If you take nothing else away from this guide, this is the single most helpful suggestion to make your workflow more coherent. Often the particular order of files will be in flux until an analysis is close to completion. At that time it is important to review file order and naming and reproduce everything prior to drafting a manuscript.
Bash scripts are useful components of a reproducible workflow. At many of the directory levels (i.e. in 3 - Analysis), there is a bash script that runs each of the analysis scripts. This is exceptionally useful when data “upstream” changes – you simply run the bash script. See Unix for further details.
After running bash scripts, .Rout log files will be generated for each script that has been executed. It is important to check these files. Scripts may appear to have run correctly in the terminal, but checking the log files is the only way to ensure that everything has run completely.
Communicating the development and support status of a repository helps both human collaborators and automated tools understand whether a project is actively maintained, a work in progress, or an archived analysis. The repostatus.org project provides a standardized, machine-readable and human-readable convention for declaring project status through badges in the repository README.md.
repostatus.org defines eight standardized statuses spanning both implementation progress and ongoing maintenance commitment:
Place the status badge near the top of your repository README.md, immediately beneath the main title. The badge links to the corresponding status definition on repostatus.org.
For an active project:
For a project in initial development:
For a completed analysis repository that is stable but no longer actively adding features:
(Note: An ASCII hyphen - is used in the badge alt text above as an ASCII-safe approximation of repostatus.org’s Unicode en-dash to ensure compatibility across all plain-text and build tools.)
Update the status badge as your project evolves through its lifecycle:
Concept (for early prototypes) or WIP (when building an analysis pipeline or package).Active once the codebase reaches a functional, reproducible state with working scripts (and, where applicable, tests).Inactive or Unsupported so readers know whether future bug fixes or dependency updates are planned.