Coding · Programming languages
R programming tutorial for beginners
Short answer
R is a free language and environment for statistics, data analysis and graphics. Its core idea is vectorization: operations apply to whole vectors and data frame columns at once, so analysis code stays short. This page covers installing R, running scripts, vectors and types, control flow, functions, lists and data frames, statistical models, plotting and error handling.
What R is and what it is used for
R is a free, open-source language and environment for statistical computing and graphics. It descends from the S language developed at Bell Labs and is maintained by the R Core Team. R is vectorized: most operations work on whole vectors of data at once, which makes data analysis code short.
R is used by statisticians, data scientists, bioinformaticians, economists and researchers for data cleaning, statistical modeling, visualization and reproducible reports. Thousands of add-on packages are available from CRAN (the Comprehensive R Archive Network) and Bioconductor.
Install R and run Hello, world
Download R from CRAN for Windows, macOS or Linux. Many people also install RStudio or Positron, which are IDEs with an editor, console, plot viewer and help pane. You can type code directly in the R console or save it in a .R file.
# hello.R
print("Hello, world!")
cat("Hello, world!\n")Rscript hello.R
# [1] "Hello, world!"
# Hello, world!print shows a value the way the console does, with the [1] index prefix. cat writes plain text, so you add the newline yourself.
Variables, basic types and vectors
Assign with <- (the conventional style) or =. There are no scalars in R: a single number is a vector of length 1. c() combines values into a vector, and arithmetic applies element by element.
x <- 42 # numeric (double)
n <- 7L # integer (L suffix)
name <- "Ada" # character
ok <- TRUE # logical
class(x); class(n); class(name) # "numeric" "integer" "character"
v <- c(2, 4, 6, 8)
v * 10 # [1] 20 40 60 80
v[2] # [1] 4 (indexing starts at 1)
v[v > 4] # [1] 6 8
length(v); sum(v); mean(v) # 4 20 5
1:5 # [1] 1 2 3 4 5
seq(0, 1, by = 0.25) # [1] 0.00 0.25 0.50 0.75 1.00| Type | Example | Notes |
|---|---|---|
| numeric (double) | 3.14, 42 | default for numbers |
| integer | 42L | whole numbers with the L suffix |
| character | "text" | strings |
| logical | TRUE, FALSE | NA means a missing value |
| factor | factor(c("lo", "hi")) | categorical data with fixed levels |
Control flow
R has if/else, for, while and repeat. For vectors, the vectorized ifelse() is often better than a loop.
temp <- 25
if (temp > 30) {
print("hot")
} else if (temp > 15) {
print("mild") # [1] "mild"
} else {
print("cold")
}
for (i in 1:3) {
cat("step", i, "\n") # step 1 / step 2 / step 3
}
scores <- c(45, 80, 62)
ifelse(scores >= 50, "pass", "fail")
# [1] "fail" "pass" "pass"if needs a single TRUE or FALSE. Passing a longer vector to if is an error in R 4.2 and later; use any(), all() or ifelse() instead.
Functions
Functions are created with function and assigned to a name. The last evaluated expression is returned automatically; return() is only needed for an early exit. Arguments can have defaults and can be passed by name.
bmi <- function(weight_kg, height_m = 1.75) {
weight_kg / height_m^2
}
bmi(70) # [1] 22.85714
bmi(height_m = 1.6, weight_kg = 60) # [1] 23.4375
square <- function(x) x^2
square(1:4) # [1] 1 4 9 16 (vectorized)
sapply(c(1, 4, 9), sqrt) # [1] 1 2 3
(\(x) x + 1)(10) # [1] 11 (short lambda syntax)The short lambda syntax \(x) is available since R 4.1, as is the native pipe |>, which passes the left side as the first argument: c(1, 4, 9) |> sqrt() |> sum() returns 6.
Core data structures: vectors, lists and data frames
Beyond atomic vectors, R has lists (which can hold values of different types), matrices, and data frames. A data frame is a table where each column is a vector of the same length; it is the central structure for data analysis.
person <- list(name = "Ann", age = 31, langs = c("R", "SQL"))
person$name # [1] "Ann"
person[["langs"]][2] # [1] "SQL"
df <- data.frame(
name = c("Ann", "Bo", "Cy"),
age = c(31, 25, 40),
city = c("Oslo", "Rome", "Oslo")
)
nrow(df) # [1] 3
df$age # [1] 31 25 40
df[df$age > 30, "name"] # [1] "Ann" "Cy"
df$senior <- df$age >= 40 # add a column
aggregate(age ~ city, data = df, FUN = mean)
# city age
# 1 Oslo 35.5
# 2 Rome 25.0
str(df) # compact structure summarydf[rows, columns] selects by position, name or logical condition. Leaving one side empty, as in df[1, ], means all rows or all columns.
Statistics and plotting
Statistical functions are built in: summary, sd, quantile, cor, t.test and lm (linear models), among many others. Base graphics draw a plot with one call.
set.seed(1)
x <- 1:20
y <- 3 * x + rnorm(20, sd = 2)
model <- lm(y ~ x)
round(coef(model), 2) # intercept near 0, slope near 3
summary(model)$r.squared # close to 1
plot(x, y, main = "y vs x")
abline(model, col = "red")The formula syntax y ~ x means "model y as a function of x" and is used across modeling functions. For advanced charts and data wrangling, most users install the tidyverse packages, such as ggplot2 and dplyr, with install.packages("tidyverse").
Error handling
Raise an error with stop() and a warning with warning(). Catch them with tryCatch(), which takes handler functions for error, warning and an optional finally block.
safe_log <- function(x) {
if (!is.numeric(x)) stop("x must be numeric")
if (any(x <= 0)) warning("non-positive values give NaN or -Inf")
log(x)
}
result <- tryCatch(
safe_log("ten"),
error = function(e) {
message("Caught: ", conditionMessage(e))
NA
},
finally = message("done")
)
# Caught: x must be numeric
# done
result # [1] NAA small complete program: summarize sales
This script builds a small sales table, computes revenue per region with base R and prints the best region.
sales <- data.frame(
region = c("North", "South", "North", "East", "South"),
units = c(10, 4, 6, 8, 12),
price = c(2.5, 3.0, 2.5, 4.0, 3.0)
)
sales$revenue <- sales$units * sales$price
by_region <- aggregate(revenue ~ region, data = sales, FUN = sum)
by_region <- by_region[order(-by_region$revenue), ]
print(by_region, row.names = FALSE)
# region revenue
# South 48
# North 40
# East 32
best <- by_region$region[1]
cat(sprintf("Best region: %s (%.2f)\n", best, max(by_region$revenue)))
# Best region: South (48.00)Where to go next
- An Introduction to R, the official manual on CRAN.
- The built-in help: type
?meanorhelp("lm")in the console. - R for Data Science by Hadley Wickham and colleagues, free online, for the tidyverse approach.
- CRAN Task Views, which list packages by field such as econometrics or machine learning.
FAQ
Is R or Python better for data analysis?
Both are strong. R has a deep library of statistical methods and excellent plotting, and is common in academia and biostatistics; Python is more general-purpose and dominant in machine learning engineering. Many analysts learn both.
What is the difference between <- and = in R?
Both assign at the top level, and <- is the conventional style. Inside a function call, = names an argument, so f(x = 1) passes an argument instead of creating a variable.
Why does R start counting at 1?
R follows mathematical and statistical convention, as its predecessor S did, so the first element of a vector is v[1]. Index 0 returns an empty vector rather than an error.
What is the tidyverse?
The tidyverse is a collection of R packages, including dplyr, ggplot2 and tidyr, that share a consistent design for importing, cleaning, transforming and plotting data.
How do I install an R package?
Run install.packages("name") once to download it from CRAN, then library(name) in each session to load it.