This book is Work in Progress. I appreciate your feedback to make the book better.

Intro to R

R is a language you speak with a computer, and the conversation has two forms. In the console you say one thing and get one answer, like sending a message. In a script you write the whole conversation down, so it can be repeated, corrected and sent to someone else.

An .R script is a plain text document. You can open it in a text editor, in a browser, or in a proper development environment such as RStudio.1 Nothing about it is secret or binary — this link opens one straight in your browser.

Everything in this section runs in the page. You do not need to install anything yet.

R is a calculator

The box below is not a picture of R — it is R, running inside this page. Edit the code, press Run, and the answer appears underneath, exactly as it would in a console. Test it.

The first Run takes a moment, because R itself has to be downloaded first. Everything after that is instant, and nothing you type ever leaves your computer.

R is not loaded yet

Try deleting everything and typing 1:20 instead.

Definition

Basic arithmetic operators are:

  • + Addition
  • - Subtraction
  • * Multiplication
  • / Division
  • ^ Exponent

R is more than a calculator

A calculator returns a number. R returns objects — and it draws.

Your Turn: Adjust the code

If you have never seen R before, change the main title to something that suits you, or change col from "lightblue" to "aliceblue".

If you have some experience, order the bars using sort().

R is not loaded yet

Wrap counts in sort() to order the bars.

Notice what happened in that example. The five numbers were given a name, counts, and then handed to a function, barplot(), together with a few instructions. Naming things and passing them to functions is essentially all of R.

Objects: giving things a name

The standard assignment operator is <-. It stores a value under a name so you can use it later.

a <- 2
b <- 3
a + b
#> [1] 5

Names are case-sensitive: Age and age are two different objects. They may be almost anything, but the useful test is whether the name will still make sense to you in three months — and to whoever reads your code after that.

Assignment is silent by design: a <- 2 stores, it does not print. Wrap the line in parentheses if you want both at once.

(mean_age <- 23.5)
#> [1] 23.5

Truly Dedicated

Why the arrow? R inherited <- from its ancestor S, which ran on terminals whose keyboards had a dedicated ← key. The key is gone; the arrow stayed.

= also assigns, and inside a function call it does something different — it names an argument. Compare:

x = 5              # assignment
mean(x = 5)        # naming the argument "x" of mean()
mean(x <- 5)       # assignment inside a call: creates x, then computes

The two meanings never collide as long as you use <- for assignment and = for arguments. In RStudio, Alt + - types the arrow for you.

Vectors

A single value is rarely interesting. R's natural unit is the vector: several values of the same type, in order. Think of one column of a spreadsheet.

c() combines values into a vector; the colon : builds a sequence.

temps <- c(19.1, 21.4, 18.7, 23.0, 22.2)
days  <- 1:5

length(temps)
#> [1] 5
mean(temps)
#> [1] 20.88

Arithmetic works on the whole vector at once. There is no loop to write.

temps - 273.15   # every element is shifted
#> [1] -254.05 -251.75 -254.45 -250.15 -250.95
temps * 2        # every element is doubled
#> [1] 38.2 42.8 37.4 46.0 44.4
temps > 21       # every element is compared
#> [1] FALSE  TRUE FALSE  TRUE  TRUE

Square brackets pull elements out, by position or by condition.

temps[1]         # the first element (R counts from 1, not 0)
#> [1] 19.1
temps[c(1, 5)]   # the first and the last
#> [1] 19.1 22.2
temps[temps > 21]  # every element above 21
#> [1] 21.4 23.0 22.2

That last line is worth a second look: temps > 21 produces TRUE/FALSE values, and the brackets keep the TRUE ones. Filtering data — the thing we spend half of this book doing — is that idea, one size larger.

Your Turn: Vectors

R is not loaded yet

sum() on TRUE/FALSE values counts the TRUEs, because TRUE counts as 1.

Types of values

Every vector has a type, and R will tell you with class().

Numbers come as double (with decimals) or integer (whole).

Text is character, always in quotes. Single and double quotes work alike; " is the convention.

emily <- "She is a friend."
libby <- 'She is a colleague.'
class(emily)
#> [1] "character"

Logical values are TRUE and FALSE — the answers to questions. They quietly count as 1 and 0, which is why sum() counts and mean() gives a proportion.

passed <- c(TRUE, TRUE, FALSE, TRUE)
sum(passed)    # how many
#> [1] 3
mean(passed)   # what share
#> [1] 0.75

The abbreviations T and F also work, but avoid them: unlike TRUE, they are ordinary names and can be overwritten.

Factors are categorical values with a fixed set of levels. R sorts levels alphabetically unless you say otherwise — which is fine for fruit and wrong for almost everything else.

dose <- factor(c("low", "medium", "high"))
dose   # alphabetical: high, low, medium
#> [1] low    medium high  
#> Levels: high low medium

dose <- factor(c("low", "medium", "high"),
               levels = c("low", "medium", "high"))
dose   # the order we mean
#> [1] low    medium high  
#> Levels: low medium high

Software cannot know whether an ordering makes sense. That is the analyst's job — and the order will resurface later in every table, axis and legend you produce.

Missing values are NA. Not zero, not empty text: unknown. Missingness is contagious, which is a feature rather than a bug.

weights <- c(3200, 4100, NA, 3750)
mean(weights)                # NA: one value is unknown, so the mean is too
#> [1] NA
mean(weights, na.rm = TRUE)  # ... unless we say to drop it
#> [1] 3683.333
is.na(weights)
#> [1] FALSE FALSE  TRUE FALSE

na.rm = TRUE is a decision, not a formality. Section 4.1 returns to what dropping those cases quietly assumes.

Data frames

Put several vectors of the same length side by side and you have a data frame: the rectangle that most of data analysis lives in. Rows are observations, columns are variables.

students <- data.frame(
  name      = c("Ana", "Ben", "Chi"),
  age       = c(23, 21, 25),
  enrolled  = c(TRUE, TRUE, FALSE)
)

students
#>   name age enrolled
#> 1  Ana  23     TRUE
#> 2  Ben  21     TRUE
#> 3  Chi  25    FALSE

Three functions tell you almost everything about a new data set.

dim(students)   # rows, columns
#> [1] 3 3
str(students)   # structure: types and first values
#> 'data.frame':    3 obs. of  3 variables:
#>  $ name    : chr  "Ana" "Ben" "Chi"
#>  $ age     : num  23 21 25
#>  $ enrolled: logi  TRUE TRUE FALSE
summary(students)
#>         name        age      enrolled      
#>  Length   :3   Min.   :21   Mode :logical  
#>  N.unique :3   1st Qu.:22   FALSE:1        
#>  N.blank  :0   Median :23   TRUE :2        
#>  Min.nchar:3   Mean   :23                  
#>  Max.nchar:3   3rd Qu.:24                  
#>                Max.   :25

Getting things out follows one rule: [rows, columns].

students$age        # one column, by name
#> [1] 23 21 25
students[2, ]       # one row
#>   name age enrolled
#> 2  Ben  21     TRUE
students[1, 2]      # one cell: row 1, column 2
#> [1] 23
students[students$age > 22, ]   # every row that meets a condition
#>   name age enrolled
#> 1  Ana  23     TRUE
#> 3  Chi  25    FALSE

The $ and the brackets are base R. The tidyverse will soon offer a more readable way to say the same thing — but this is what runs underneath, and it is worth recognising when you meet it in someone else's code.

Functions and their arguments

mean(), barplot() and factor() are functions: a name, a pair of parentheses, and arguments inside.

Arguments can be given by position or by name. Named arguments are slower to type and much easier to read.

round(3.14159, 2)              # by position
#> [1] 3.14
round(x = 3.14159, digits = 2) # by name — unmistakable
#> [1] 3.14

Most arguments have defaults, which is why round(3.14159) works at all. To find out what a function expects, ask it:

?round        # the help page
args(round)   # just the arguments

Reading a help page is a skill in itself, and the fastest way to acquire it is to jump straight to the Examples at the bottom, run them, and work upwards from there.

Packages

Base R is the language. Packages are everything else people have built with it, and there are thousands of them on CRAN, the official archive.

Two commands, and beginners mix them up constantly:

install.packages("palmerpenguins")  # once per computer — downloads it
library(palmerpenguins)             # once per session — switches it on

Installing is like buying a book for your shelf. library() is taking it off the shelf and opening it. A new R session starts with the shelf full and the desk empty, which is why every script should load the packages it needs at the top.

Reading

Posit publishes one-page cheat sheets for dplyr, ggplot2, RStudio and many others. Printed and kept next to the keyboard, they replace a surprising amount of searching.

Plots in base R

R could draw before ggplot2 existed, and base graphics remain excellent for a quick look. One function opens a plot, further functions add to it.

x  <- 1:10
y1 <- x * x
y2 <- 2 * y1

plot(x, y1, type = "b", frame = FALSE, pch = 19,
     col = "red", xlab = "x", ylab = "y")
lines(x, y2, pch = 18, col = "blue", type = "b", lty = 2)
legend("topleft", legend = c("Line 1", "Line 2"),
       col = c("red", "blue"), lty = 1:2, cex = 0.8)
Base graphics built in layers: plot() opens the frame with x squared in red, lines() adds twice that in blue.

Figure 0.1: Base graphics built in layers: plot() opens the frame with x squared in red, lines() adds twice that in blue.

Every argument here is an aesthetic decision written down: colour, symbol, line type, where the legend goes. Later, ggplot2 will make those decisions systematic rather than individual.

Your Turn: A plot in three lines

iris is a data set built into R: measurements of 150 flowers from three species. Run the code, then colour the points by something else, or change pch (the plotting symbol) to a number between 0 and 25.

R is not loaded yet

Three clouds, one per species. Which two species overlap?

When something goes wrong

It will. Errors are not a verdict on your abilities; they are R's way of saying which line it could not carry out.

Message Usually means
object 'x' not found The name does not exist yet — a typo, or a line you have not run.
could not find function "filter" The package is installed but not loaded. library(...) first.
unexpected symbol / unexpected ')' A bracket, quote or comma is missing.
A lonely + in the console R is still waiting for you to close something. Press Esc.
argument "x" is missing A required argument was not supplied.

A warning is different from an error. An error stops the code; a warning means it ran but R would like a word with you. Both are worth reading rather than scrolling past.

The most effective debugging habit is also the least glamorous: run your code one line at a time and look at the object after each step. Half of all bugs are simply an object that is not what you assumed it was.


  1. Download RStudio: https://posit.co/downloads/. We set it up properly later in this chapter.↩︎