R
First appeared 1993 (1.0 in 2000) · Ross Ihaka
Built by statisticians for statistics — and still the place new methods appear first.
Overview
R is a language and environment built specifically for statistical computing and graphics, and it remains the lingua franca of academic statistics, biostatistics and much of quantitative research. Its data structures — vectors, data frames and factors — are designed around the shape of statistical data rather than adapted from general-purpose programming, and vectorised operations mean most analysis is written without explicit loops. CRAN, its package archive, holds more than twenty thousand peer-reviewed packages covering essentially every published statistical method, which is why R is often the only place a particular technique is implemented at all. The tidyverse — dplyr, ggplot2, tidyr and their companions — reshaped how R is written, providing a consistent grammar for data manipulation and visualisation. R is not a general-purpose language and does not pretend to be; it is a specialised tool that is unusually good within its domain.
Key facts
The reference details, without the paragraph.
- First appeared
- 1993 (1.0 in 2000)
- Designed by
- Ross Ihaka and Robert Gentleman, University of Auckland
- Typing
- Dynamic and weak, with implicit coercion between vector types
- Execution
- Interpreted, with vectorised operations implemented in C and Fortran
- Memory model
- Automatic — copy-on-modify semantics with garbage collection
- Package manager
- install.packages from CRAN; renv for project isolation
- File extensions
- .R, .Rmd, .qmd, .RData
- Package archive
- CRAN, with over 20,000 peer-reviewed packages
- Indexing
- One-based, unlike almost every other language
- Licence
- GPL-2 / GPL-3
History
How the language got here — the decisions that still shape how you write it.
R was created by Ross Ihaka and Robert Gentleman at the University of Auckland in the early 1990s, as a free implementation of the S language developed at Bell Labs by John Chambers. The name is a play on both authors' first initials and on S itself. It was released as free software under the GPL in 1995, and the Comprehensive R Archive Network followed in 1997 — a decision that shaped everything after it, because CRAN's review and testing requirements gave academic researchers confidence that a package implementing a published method actually worked. That trust is why statisticians publishing a new technique typically ship an R package alongside the paper. R's second transformation came from Hadley Wickham, whose ggplot2 in 2007 introduced a grammar of graphics, and whose later dplyr and the broader tidyverse gave the language a coherent, teachable style markedly friendlier than base R. Python has since taken much of the applied machine learning and production data work, but R has held its ground in statistics, epidemiology, clinical research and anywhere a specific statistical method needs to be exactly right.
- 1976
S at Bell Labs
John Chambers creates S as a language for statistical analysis, aiming to let researchers move from ideas to results without writing Fortran.
- 1993
R begins in Auckland
Ross Ihaka and Robert Gentleman start a free implementation of S. The name plays on both their first initials and on S itself.
- 1997
CRAN launches
The Comprehensive R Archive Network establishes review and automated testing requirements. That quality bar is why researchers trust a CRAN package implementing a published method.
- 2007
ggplot2 and the grammar of graphics
Hadley Wickham implements Leland Wilkinson's grammar of graphics, changing how statistical visualisation is written and becoming R's most recognisable output.
- 2011
RStudio
A purpose-built IDE arrives, and R becomes considerably more approachable for people who are analysts first and programmers second.
- 2014–2017
The tidyverse
dplyr, tidyr, purrr and friends consolidate into a coherent, teachable style with consistent conventions — effectively a second dialect of R, and now the one most people learn.
- 2020–2025
Quarto and the native pipe
R 4.1 adds a built-in pipe operator, and Quarto generalises R Markdown into a multi-language publishing system. R holds its position in statistics while Python takes applied machine learning.
What it is good at
The reasons teams pick it, stated concretely.
Every published statistical method, implemented
CRAN's twenty thousand packages cover mixed models, survival analysis, Bayesian inference, spatial statistics and much more. For a specific technique from a paper, R is frequently the only implementation that exists.
ggplot2 is genuinely exceptional
The grammar of graphics separates data, aesthetic mapping and geometry, so complex layered plots compose from small consistent pieces. It remains the benchmark other plotting libraries are measured against.
Vectorisation as the default
Operations apply to whole vectors and data frames at once, in compiled C underneath. Idiomatic R rarely contains an explicit loop, and analysis code reads close to the statistical notation it implements.
The tidyverse is a coherent teaching language
dplyr's verbs — filter, select, mutate, group_by, summarise — compose through the pipe into readable pipelines. Non-programmers reach genuine productivity remarkably quickly.
Reproducible reporting is built in
R Markdown and Quarto weave code, output and prose into one document, so an analysis regenerates its own tables and figures. In research and regulated industries this is a decisive advantage.
Trade-offs
Every language costs you something. Knowing what, before you commit, is the whole point.
Not a general-purpose language
R is excellent at statistics and awkward at almost everything else. Web services, application code and systems work all belong elsewhere — this is a specialised tool and it does not pretend otherwise.
Single-threaded and memory-hungry
Copy-on-modify semantics mean data can be duplicated silently, and the whole dataset must fit in RAM. Large data requires data.table, arrow, duckdb or moving the work to a database.
Two dialects to read
Base R and tidyverse code look strikingly different. Most material teaches one or the other, and reading real projects means understanding both.
Sharp inconsistencies in the base language
One-based indexing, three object systems (S3, S4 and R5), silent type coercion, and `stringsAsFactors` historically defaulting to TRUE. Much of this is legacy that cannot be changed without breaking decades of code.
Weak production deployment story
Getting R into a reliable production pipeline requires real effort — dependency pinning with renv, containerisation, and plumber or Shiny Server for services. Python's path here is far better trodden.
Code examples
Not syntax tours — the idioms that make code read like the language rather than a translation of another one.
# Operations apply to whole vectors — no loop needed.
prices <- c(19.99, 45.00, 8.50, 120.00)
with_tax <- prices * 1.20
discounted <- ifelse(prices > 50, prices * 0.9, prices)
# Recycling: the shorter vector repeats. Powerful, and a common
# source of silent bugs when lengths do not divide evenly.
c(1, 2, 3, 4) + c(10, 20) # 11 22 13 24
# Data frames are the core structure.
books <- data.frame(
title = c("Dune", "Neuromancer", "Snow Crash"),
year = c(1965L, 1984L, 1992L),
rating = c(4.5, 4.2, 4.0)
)
books[books$year > 1980, c("title", "rating")]
summary(books$rating)library(dplyr)
library(ggplot2)
summary_by_decade <- books |>
filter(!is.na(rating)) |>
mutate(decade = (year %/% 10) * 10) |>
group_by(decade) |>
summarise(
n = n(),
mean_rating = mean(rating),
.groups = "drop" # avoid a silently grouped result
) |>
arrange(desc(mean_rating))
ggplot(summary_by_decade, aes(x = decade, y = mean_rating, size = n)) +
geom_point(colour = "steelblue") +
geom_smooth(method = "lm", se = TRUE) +
labs(title = "Mean rating by decade", x = NULL, y = "Rating") +
theme_minimal()# Formula notation: outcome ~ predictors.
model <- lm(rating ~ year + pages + factor(genre), data = books)
summary(model) # coefficients, standard errors, p-values, R-squared
confint(model) # confidence intervals
par(mfrow = c(2, 2)); plot(model) # diagnostic plots
# Mixed-effects model with a random intercept per publisher.
library(lme4)
mixed <- lmer(rating ~ year + (1 | publisher), data = books)
# A t-test, with everything you need reported.
t.test(rating ~ in_print, data = books)
# Tidy the output into a data frame for further work.
library(broom)
tidy(model)
glance(model)library(purrr)
# map_dbl guarantees a numeric vector — map() would return a list.
mean_ratings <- map_dbl(split(books, books$genre), ~ mean(.x$rating))
# Read and combine many files.
all_data <- list.files("data/", pattern = "\\.csv$", full.names = TRUE) |>
map(readr::read_csv) |>
list_rbind()
# safely() captures errors rather than stopping the whole run.
safe_fit <- safely(\(df) lm(rating ~ year, data = df))
results <- map(datasets, safe_fit)
successes <- keep(results, \(r) is.null(r$error)) |> map("result")Common pitfalls
The mistakes that cost everyone an afternoon at least once.
Growing objects in a loop
`result <- c(result, value)` reallocates and copies the whole vector every iteration, making the loop quadratic. Preallocate with `vector("numeric", n)`, or use vectorised operations.
Silent vector recycling
Adding vectors of mismatched length recycles the shorter one. If the lengths divide evenly you get wrong answers with no warning at all.
Comparing floating point with ==
`0.1 + 0.2 == 0.3` is FALSE, as in every IEEE-754 language. Use `all.equal()` or `dplyr::near()`.
Confusing NA, NULL, NaN and NA_character_
They are four different things and behave differently in comparisons and aggregations. Most summary functions return NA unless you pass `na.rm = TRUE`.
Forgetting drop = FALSE
Subsetting a data frame to one column silently returns a vector rather than a data frame, breaking downstream code. Use `df[, "col", drop = FALSE]` or the tidyverse, which does not do this.
Leaving results grouped after summarise
dplyr keeps one level of grouping by default, so subsequent operations apply per group unexpectedly. Pass `.groups = "drop"` or call `ungroup()`.
In production
Where it is running at scale, and what it is doing there.
The New York Times
Data journalism and the graphics behind its election and COVID-19 coverage.
Pfizer
Clinical trial analysis and regulatory submissions to the FDA.
Airbnb
Statistical analysis and internal data science tooling.
Bank of England
Economic modelling and published statistical research.
Learning path
A realistic order to learn things in, with something to build at each step.
- 1
Week 1
Vectors and data frames
Atomic vectors, one-based indexing, recycling, factors, and data frames. Understand that almost everything is a vector — it explains most of R's behaviour.
Build this: Load a CSV, filter and summarise it with base R, and plot a histogram.
- 2
Weeks 2–3
The tidyverse
dplyr's verbs, the pipe, tidyr for reshaping between long and wide, readr for import, and ggplot2's layered grammar. This is how most modern R is written.
Build this: Reproduce a published chart from a public dataset using ggplot2.
- 3
Week 4
Statistics, properly
Formula notation, lm and glm, reading model summaries, diagnostic plots, and the difference between statistical and practical significance. R makes it easy to fit a model you do not understand — do not.
Build this: Fit a regression, check its assumptions with diagnostic plots, and write up what it does and does not support.
- 4
Months 2–3
Reproducibility and scale
R Markdown or Quarto, renv for dependency pinning, writing functions rather than scripts, and data.table or arrow when data outgrows memory.
Build this: Turn an ad hoc analysis into a Quarto document that regenerates every figure from raw data.
- 5
Ongoing
Packages and production
Write your own package with usethis, testthat and roxygen2. Learn Shiny for interactive applications and plumber for HTTP APIs, and Rcpp when a hot loop genuinely needs C++.
Build this: Package your reusable analysis functions with tests and documentation.
Ecosystem and tooling
The tools you will end up installing whichever project you join.
| Tool | Category | What it does |
|---|---|---|
| tidyverse | Core | dplyr, ggplot2, tidyr, purrr and readr — the dominant modern style |
| RStudio / Posit | Tooling | The IDE most R is written in, with an integrated console, plots and environment |
| data.table | Data | Very fast, memory-efficient data manipulation with a terse syntax; the base for large in-memory data |
| Quarto / R Markdown | Reporting | Reproducible documents weaving code, output and prose |
| Shiny | Web | Interactive web applications written entirely in R |
| renv | Packaging | Project-local package libraries with a lockfile — essential for reproducibility |
| testthat + usethis | Testing | Testing and package scaffolding for writing your own R packages |
| Rcpp / arrow / duckdb | Performance | Escape hatches for speed and for data larger than memory |
R libraries
Library coverage for R is on the way.
The guide above is complete. In the meantime, the catalogues for Python, Java, JavaScript, C and C++ are fully written.
Browse all librariesFrequently asked
R or Python for data work?
Python for machine learning, production pipelines and anything that must integrate with software engineering. R for statistics, exploratory analysis, publication-quality graphics and any specific method from the literature. Many teams use both — R to explore and Python to deploy — and the choice is much less tribal than it was a decade ago.
Should I learn base R or the tidyverse?
Learn tidyverse first — it is more consistent and you will be productive faster. Then learn enough base R to read older code and to understand what tidyverse functions do underneath, because you will hit situations where the tidy abstraction does not fit.
Is R too slow to be useful?
The interpreter is slow; the vectorised operations underneath are compiled C and Fortran and are fast. Idiomatic R that pushes work into vectorised calls performs perfectly well. Explicit loops over millions of rows do not — that is when you reach for data.table, arrow or Rcpp.
Can R handle data larger than memory?
Not natively. The options are data.table for efficient in-memory work, arrow for larger-than-memory columnar data, duckdb for SQL over files, or pushing the aggregation into a database and pulling back only the summary. Loading a 50 GB CSV into a data frame will not work.
Is R used outside academia?
Yes, particularly in pharmaceutical and clinical research, where the FDA accepts R-based submissions, and in finance, insurance, epidemiology, government statistics and data journalism. It is less common in general software companies, where Python dominates.
What is Shiny actually good for?
Internal dashboards and interactive analytical tools built by analysts rather than web developers — that is where it is genuinely excellent. It is not a good choice for a public, high-traffic application, since each session holds a server-side R process.

