Skip to content
R logo

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 2011

    RStudio

    A purpose-built IDE arrives, and R becomes considerably more approachable for people who are analysts first and programmers second.

  6. 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.

  7. 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.

Vectorisation instead of loops
# 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)
Vector recycling is genuinely useful and genuinely dangerous: adding vectors of length 4 and 2 works silently, while lengths 4 and 3 produce a warning and wrong results. Check your lengths.
A tidyverse pipeline
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()
`|>` is R's native pipe, available since 4.1 — older code uses magrittr's `%>%`. Setting `.groups = "drop"` matters: without it the result stays grouped and later operations behave unexpectedly.
Statistical modelling, R's home ground
# 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)
Formula notation is why statisticians stay with R. `rating ~ year + factor(genre)` is close to how the model is written in a paper, and the same syntax carries across linear models, mixed models, survival analysis and machine learning packages.
Functional iteration with purrr
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")
The typed variants — `map_dbl`, `map_chr`, `map_int` — fail loudly if a result is the wrong type, which catches problems that the untyped `map` would silently pass along as a malformed list.

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. 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. 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. 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. 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. 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.

ToolWhat it does
tidyversedplyr, ggplot2, tidyr, purrr and readr — the dominant modern style
RStudio / PositThe IDE most R is written in, with an integrated console, plots and environment
data.tableVery fast, memory-efficient data manipulation with a terse syntax; the base for large in-memory data
Quarto / R MarkdownReproducible documents weaving code, output and prose
ShinyInteractive web applications written entirely in R
renvProject-local package libraries with a lockfile — essential for reproducibility
testthat + usethisTesting and package scaffolding for writing your own R packages
Rcpp / arrow / duckdbEscape 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 libraries

Frequently 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.