name: coding-in-r description: How to write R well — renv, tidyverse/data.table, project paths, style, scripts, seeds. TRIGGER when authoring or editing .R/.Rmd/.qmd files. For running R on the Yale SOM HPC cluster (Slurm, renv on /gpfs), use running-r instead. related: - programming-and-coding - running-r - accelerating-python - code-review updated: 2026-05-22
Coding in R
Rule: renv, portable paths, script as source of truth. Never rely on the global workspace.
For cluster execution, also load running R.
Defaults
renvfor package versions. Commitrenv.lockand.Rprofile.- On SOM HPC:
module load r; norigormisedefault. pakfor fast installs when available.stylerfor formatting.lintrfor linting.here::here()for paths. Nosetwd()in tracked scripts.readr::read_csv()over baseread.csv().
install.packages("renv")
renv::init()
renv::install(c("tidyverse", "data.table", "here", "fs", "styler", "lintr"))
renv::snapshot()
Restore with:
renv::restore()
On HPC, restore once before arrays. Do not let many workers install packages concurrently.
Layout
project/
├── renv.lock
├── .Rprofile
├── README.md
├── scripts/ # entry points
├── R/ # shared helpers
├── notebooks/ # .Rmd / .qmd
├── data/raw/ # immutable; usually ignored
├── data/derived/ # rebuildable
└── results/ # figures/tables/logs
Paths
Never commit setwd() or personal absolute paths.
library(here)
counts_path <- here("data", "raw", "counts.csv")
counts <- readr::read_csv(counts_path)
For machine-specific roots, read an environment variable from .Renviron or the shell.
Style
snake_casefor variables/functions.<-for assignment;=for arguments.- Native pipe
|>unless%>%materially helps. - 2-space indentation; aim for 80-char lines, max 120.
- Named arguments:
mean(x, na.rm = TRUE), notmean(x, T). library()at top.- Never
attach()or<<-. set.seed(42)before stochastic work.
Data
Default to tidyverse for clarity. Use data.table when data are large, joins/grouping dominate, or in-place mutation matters. Benchmark before rewriting clear code for speed.
CLI skeleton
#!/usr/bin/env Rscript
suppressPackageStartupMessages({
library(optparse)
})
option_list <- list(
make_option(c("-i", "--input"), type = "character"),
make_option(c("-o", "--output"), type = "character")
)
opt <- parse_args(OptionParser(option_list = option_list))
if (is.null(opt$input) || is.null(opt$output)) {
stop("--input and --output are required", call. = FALSE)
}
set.seed(42)
Checklist
-
styler::style_dir(".")run -
lintr::lint_dir(".")clean or justified - No
setwd()or personal paths -
set.seed()where needed -
renv::snapshot()if deps changed - Small smoke test run
- HPC uses
module load r+renv, notrig/mise