Learn R the way statisticians actually think.
R turns "loop over the data" into "describe what should happen to the data" — a small mental shift that changes how you build logic, not just what syntax you use. This page is a full working reference: why R exists, how it reshapes your coding habits, how model training actually works in R, plus deep-dive notes, a cheat sheet, practice problems, and a quiz.
> x <- c(4, 8, 15, 16, 23, 42)
> mean(x)
[1] 18
> model <- lm(mpg ~ wt + hp, data = mtcars)
> summary(model)$r.squared
[1] 0.8268
> library(ggplot2)
> ggplot(mtcars, aes(wt, mpg)) + geom_point()
▍Why you'd reach for R instead of a general-purpose language
Statistics is native, not bolted on
Distributions, hypothesis tests, and regression are base-language functions, not third-party add-ons — the vocabulary of statistics is R's own vocabulary.
Best-in-class visualization
ggplot2's layered grammar of graphics is still the reference point competing tools measure themselves against for exploratory and publication-quality plots.
A CRAN package for almost everything
20,000+ peer-checked packages cover niche statistical methods that would otherwise take weeks to implement from a paper.
Reproducible by default
R Markdown/Quarto ties code, output, and narrative into one document — the standard for reproducible research in academia and applied data science.
From "loop over it" to "describe what should happen to it"
Most people learn programming through explicit loops: walk through a list, do something to each item. R nudges you toward a different default — vectorized and functional thinking, where you describe the transformation once and let it apply across the whole structure. That habit carries over into every language you touch afterward.
| Mental habit | Typical imperative style | R's default style |
|---|---|---|
| Doubling every number | for i in list: out.append(i*2) | x * 2 |
| Filtering rows | if condition: keep.append(row) | filter(df, condition) |
| Applying a function to every item | for item in items: f(item) | sapply(items, f) |
| Grouped aggregation | manual dict of running sums | group_by(df, g) |> summarise(...) |
The practical effect: R developers tend to write shorter functions, think in terms of whole columns/vectors rather than single values, and lean on existing statistical/functional building blocks instead of re-deriving control-flow logic from scratch. That's a transferable habit — it shows up later in pandas (Python), Spark, and SQL, all of which reward the same "operate on the whole set" thinking.
The workflow you'll repeat for almost every model
- 01
Split your data
set.seed() for reproducibility, then hold out a test set (commonly 20–30%) with rsample::initial_split() or plain sample().
- 02
Preprocess
Centre/scale numeric predictors, encode categorical variables, impute or drop missing values — recipes (tidymodels) or caret::preProcess().
- 03
Choose & fit a model
Classical: lm()/glm(). Tree-based: randomForest, xgboost. Unified interface: a parsnip model spec from tidymodels.
- 04
Cross-validate & tune
k-fold CV (vfold_cv() or caret::trainControl(method='cv')) estimates performance honestly and tune_grid()/train() search hyperparameters.
- 05
Evaluate once, on the test set
RMSE/MAE for regression, accuracy/precision/recall/AUC for classification — computed only after tuning is finished.
- 06
Explain & ship
Wrap the fitted model + report in an R Markdown/Quarto document, or serve predictions through a Shiny app or plumber API.
Every topic you need to actually know R
- ▸R is a language + environment for statistical computing and graphics, born out of the S language at Bell Labs.
- ▸Distributed as 'base R', extended by 20,000+ packages hosted on CRAN (the Comprehensive R Archive Network).
- ▸RStudio / Posit is the IDE almost everyone uses — console, script editor, plots, and environment viewer in one window.
- ▸R is interpreted and interactive: you run one line, see the result immediately, then build on it. That loop is the whole point.
R cheat sheet
c(1, 2, 3)Combine values into a vectorseq(1, 10, by = 2)Sequence with a steprep(x, times = 3)Repeat a vectorlength(x)Number of elementsx[c(1,3)]Subset by positionx[x > 5]Subset by logical conditionstr(df)Structure / column typeshead(df, 5)First 5 rowsdf[df$score > 80, ]Filter rows (base R)df$new <- df$a + df$bAdd a computed columnnrow(df); ncol(df)Dimensionscolnames(df)Column namesfilter(df, x > 10)Keep matching rowsselect(df, a, b)Keep specific columnsmutate(df, z = a * b)Add/modify a columnarrange(df, desc(x))Sort rowsgroup_by(df, g) |> summarise(m = mean(x))Aggregate per groupleft_join(a, b, by = "id")Join two tablesmean(x); median(x); sd(x)Central tendency & spreadsummary(x)Quick 5-number summarycor(x, y)Correlationrnorm(n, mean, sd)Random normal drawst.test(x, y)Compare two meanslm(y ~ x, data = df)Fit a linear modelggplot(df, aes(x, y))Start a plot, map variables+ geom_point()Scatter layer+ geom_line()Line layer+ geom_boxplot()Boxplot layer+ facet_wrap(~group)Small multiples by group+ labs(title = "...")Titles & axis labelsif (x > 0) { ... } else { ... }Branchingfor (i in seq_along(x)) { ... }Loop by index (rare in idiomatic R)function(x, y = 1) { x + y }Define a function, default argsapply(x, f)Apply f() over every elementifelse(cond, a, b)Vectorized if/elseProblems to actually write R
Vector clean-up
Given `x <- c(4, NA, 8, NA, 15, 16)`, write one line that returns the mean of x, ignoring the missing values.
FizzBuzz, the R way
Print numbers 1 to 20; multiples of 3 print 'Fizz', multiples of 5 print 'Buzz', multiples of both print 'FizzBuzz'.
Group summary
Using the built-in `mtcars` dataset, find the average `mpg` for each number of cylinders (`cyl`), sorted from highest to lowest.
Write your own apply
Write a function `col_means(df)` that returns the mean of every numeric column in a data frame, without a `for` loop.
String cleanup
You have `names <- c(" Alice ", "BOB", "charlie ")`. Return a vector with whitespace trimmed and consistent Title Case.
Train/test split + logistic regression
Using a data frame `df` with a binary column `outcome`, split it 80/20, fit a logistic regression on the training set, and report accuracy on the test set.
Cross-validated RMSE
Using tidymodels (`rsample`), perform 5-fold cross-validation on a linear model predicting `mpg` from `wt` and `hp` in `mtcars`, and report the average RMSE across folds.