| name | r-ops |
| description | Modern R for data analysis and statistics — tidyverse-first (dplyr, tidyr, ggplot2, native |> pipe), with base R and data.table as alternatives. Triggers on: R, Rstats, tidyverse, dplyr, tidyr, ggplot2, pivot/join/group, data.table, purrr map, broom, renv, Quarto. |
| when_to_use | Use for any R question — wrangling, visualization, modeling, time series — or when reviewing/modernizing R code to current (2024+) idioms. |
| license | MIT |
| compatibility | R >= 4.1 (native |> pipe); tidyverse 2.0; Quarto |
| allowed-tools | Read Write Bash |
| metadata | {"author":"claude-mods","related-skills":"sql-ops, postgres-ops, python-database-ops"} |
Modern R Operations
A tidyverse-first, current-best-practice reference for working in R (2024+): data analysis, statistics, visualization, and reproducible workflow. Opinionated where the community has converged, with base R and data.table flagged as the right tool when they are.
The modern R stack at a glance
| Job | Reach for | Not (anymore) |
|---|
| Pipe | native |> (R 4.1+) | %>% only when you need its placeholder/. features |
| Data frame | tibble | data.frame defaults (but it's fine) |
| Wrangle | dplyr + tidyr | hand-rolled [, subset, aggregate |
| Read CSV | readr::read_csv (prod), data.table::fread (speed) | read.csv |
| Excel / Parquet / DB | readxl / arrow / DBI+dbplyr | — |
| Strings / dates / factors | stringr / lubridate / forcats | base grepl/POSIXlt/factor juggling |
| Plot | ggplot2 | base graphics (fine for throwaway plots) |
| Iterate | purrr::map_* + across() | sapply (type-unstable); lapply ok in package code |
| Big / fast | data.table (or dtplyr, arrow+duckdb) | — |
| Model | base lm/glm + broom; tidymodels for CV/tuning | caret |
| Time series | tsibble + fable | forecast::auto.arima (maintenance-only) |
| Reports | Quarto (.qmd) | R Markdown (still works) |
| Reproducibility | renv + Projects + here() | setwd(), saving .RData |
The analysis workflow (and where each reference lives)
import → tidy → transform → visualize → model → communicate
- Import — get data in: import-io.md
- Tidy & transform — the dplyr/tidyr core: tidyverse-core.md
- Clean types — strings, dates, factors: strings-dates-factors.md
- Iterate — map over many things, list-columns: iteration-functional.md
- Visualize — ggplot2 + EDA: visualization.md
- Model — tests, lm/glm, broom, tidymodels: modeling-stats.md
- Scale up — when dplyr is too slow: data-table.md
- Time series — tsibble/fable, xts: time-series.md
- Ship it — projects, renv, Quarto, testing: workflow-tooling.md
Open the reference for the task at hand — they load on demand. For broad orientation, this file is enough.
Core idioms (internalize these)
library(tidyverse)
diamonds |>
filter(carat > 0.5) |>
mutate(price_per_carat = price / carat) |>
summarise(
mean_ppc = mean(price_per_carat),
n = n(),
.by = cut
) |>
arrange(desc(mean_ppc))
df |> summarise(across(where(is.numeric), \(x) mean(x, na.rm = TRUE)))
files |> map(read_csv) |> list_rbind(names_to = "source")
ggplot(df, aes(x = displ, y = hwy, colour = class)) +
geom_point() +
geom_smooth(method = "lm")
Decision shortcuts
Grouping: prefer per-operation .by = over group_by() |> ... |> ungroup() — it avoids sticky-group bugs.
Joins: always write join_by(...) explicitly. Natural joins on shared names are almost always wrong on real data.
Which CSV reader? read_csv (readable, good defaults, production) · fread (fastest, big files) · vroom (many files, column subset).
dplyr or data.table? dplyr for readability and teams; data.table (or dtplyr) when profiling says dplyr is the bottleneck or data is large. arrow+duckdb for larger-than-memory.
lm or tidymodels? Base lm/glm is the right default — reach for tidymodels only when you need cross-validation, tuning, or uniform multi-model comparison.
base R or tidyverse? Tidyverse for analysis, readability, teams. Base R (or data.table) for package development, minimal-dependency scripts, and performance-critical inner loops. The |> pipe is base and dependency-free — use it everywhere.
High-value gotchas
These bite people repeatedly — full detail in the referenced files:
stringsAsFactors is FALSE since R 4.0 (2020). Old advice warning about automatic factor conversion on import is stale and sometimes backwards. (import-io)
predict(glm_model, type = "response") for probabilities — the default returns link-scale (log-odds). (modeling-stats)
cor.test(), not cor() when you care whether a correlation is real. (modeling-stats)
sapply is type-unstable — never in function bodies; use a typed map_*. (iteration-functional)
map_dfr/map_dfc are superseded → map() |> list_rbind() / list_cbind(). (iteration-functional)
- ggplot mapping vs setting:
aes(colour = class) maps a variable; colour = "blue" sets a constant. Putting a constant inside aes() is the #1 ggplot mistake. (visualization)
coord_cartesian(ylim=) zooms; scale_y_continuous(limits=) drops data — the latter silently corrupts smooths/boxplots. (visualization)
- Factor order is not cosmetic — it sets ggplot axis/legend order and regression reference levels.
fct_reorder for plots, fct_relevel for models. (strings-dates-factors)
- lubridate periods vs durations:
months(1) (calendar) vs dmonths(1) (fixed seconds); use %m+% for safe month-end arithmetic. (strings-dates-factors)
data.table := mutates in place — DT2 <- DT is not a copy; use copy(DT). (data-table)
- xts
lag(k = +1) leads (future data); use k = -1. rollapply defaults to center alignment — set align = "right" to avoid look-ahead bias. (time-series)
- Never
setwd() with an absolute path — use an RStudio Project + here::here(). Don't save/restore .RData. (workflow-tooling)
Currency note
Reflects the R ecosystem as of 2024–2026: R ≥ 4.3, tidyverse 2.0, native |>, dplyr .by=, the \(x) lambda, list_rbind/list_cbind, the tidyverts (tsibble/fable) time-series stack, and Quarto. Where a once-standard approach has been superseded (base apply → purrr, forecast → fable, R Markdown → Quarto, map_dfr → list_rbind), the modern form leads and the older one is noted for when you encounter it in the wild.
This currency is verified, not asserted — scripts/check-r-facts.py guards it against silent drift:
python scripts/check-r-facts.py --offline
python scripts/check-r-facts.py --live
The canonical package list lives in assets/r-packages.json; when you add or drop a recommendation, update it to match or --offline fails CI.