Chapter 5 An R refresher

Learning goals

By the end of this chapter you should be able to:

  • Use RStudio to run code from scripts.
  • Create and inspect basic objects in R.
  • Work with vectors and data frames.
  • Import a CSV file using a relative path.
  • Make simple plots and summaries.

Prerequisites

  • You have R and RStudio installed and have created the course project described in the previous chapter.

A tiny motivating example

Let’s make a tiny plot so we know everything is working.

body_length <- c(4.2, 5.1, 5.6, 6.8, 7.4)
body_mass <- c(9.5, 12.1, 14.0, 18.2, 21.5)

plot(body_length, body_mass)

Try this: Change one value and re-run. What changes on the plot?


5.1 Getting started with RStudio

What we’re about to do: We will set up a simple workflow so your work is reproducible.

RStudio is not R itself. It is the interface where you write scripts, run code, and view results.

Do this: Open RStudio and create a new script (File > New File > R Script). Save it as practice.R.

Why this matters: Scripts let you save, revisit, and debug your work. Copy-pasting into the console will not scale.

Common mistake: Running everything in the console and losing your work. If this happens, start a script and re-run your commands there.

Takeaway: Work in scripts, not just the console.


5.2 Getting help

What we’re about to do: Learn how to ask R for help.

?rep
help.search("bar plot")
??"bar plot"

What just happened: ? opens a help page when you know a function name. help.search() and its shortcut, ??, search across help pages when you do not.

Try this: Look up mean or plot and read the arguments section.

Takeaway: R has built-in help for almost everything.


5.3 R as a calculator

What we’re about to do: Use R for basic arithmetic and logic.

4 + 3
## [1] 7
9 - 12
## [1] -3
6 / 3
## [1] 2
7 * 3
## [1] 21
(2 * 7) + 2 - 0.4
## [1] 15.6
sqrt(945)
## [1] 30.74085
3^5
## [1] 243
abs(-23.4)
## [1] 23.4
round(2.35425, digits = 2)
## [1] 2.35
log(1.2)
## [1] 0.1823216
exp(1)
## [1] 2.718282
log10(6)
## [1] 0.7781513

Logical comparisons:

3 < 10
## [1] TRUE
5 > 7
## [1] FALSE
5 == 5
## [1] TRUE
6 != 5
## [1] TRUE
3 %in% c(1, 2, 3, 4, 5)
## [1] TRUE
6 %in% c(1, 2, 3, 4, 5)
## [1] FALSE

Takeaway: R handles arithmetic and logical tests directly.


5.4 Objects and vectors

What we’re about to do: Create objects and simple vectors.

x <- 4
x
## [1] 4

The assignment operator <- stores the value on its right under the name on its left. You can read this as “x gets 4”. Typing the object’s name prints its value.

Vectors are sequences of values of the same basic type, such as numbers or text.

A <- 1:5
B <- c(1, 3, 6, 1, 7, 9)
C <- seq(from = 1, to = 12, by = 2)
D <- seq(from = 1, to = 5, by = 0.1)
E <- rep(c("Male", "Female"), each = 3)
F <- rep(c("Male", "Female"), times = c(2, 4))

A
## [1] 1 2 3 4 5
C
## [1]  1  3  5  7  9 11
E
## [1] "Male"   "Male"   "Male"   "Female" "Female" "Female"
F
## [1] "Male"   "Male"   "Female" "Female" "Female" "Female"

Try changing seq() or rep() arguments. What changes?

Common mistake: Forgetting c() when creating a vector of values.

Takeaway: Vectors are the building blocks of most data in R.


5.5 Manipulating vectors

What we’re about to do: Do arithmetic on vectors.

B
## [1] 1 3 6 1 7 9
B * 3
## [1]  3  9 18  3 21 27
B - 2
## [1] -1  1  4 -1  5  7
c(A, B)
##  [1] 1 2 3 4 5 1 3 6 1 7 9
B * c(1, 2, 3, 4, 5, 6)
## [1]  1  6 18  4 35 54
A / B
## [1] 1.0000000 0.6666667 0.5000000 4.0000000 0.7142857 0.1111111

What just happened: R works element-by-element when vector lengths match. In the final calculation, A has five values but B has six. R recycles the shorter vector and produces a warning. Do not ignore this warning: unequal vector lengths usually indicate a mistake.

Takeaway: Vectorised operations are fast and convenient.


5.6 Missing values, infinity, and NaN

What we’re about to do: See how R handles missing values and edge cases.

mean(c(1, 3, 6, 1, 7, 9, NA))
## [1] NA
mean(c(1, 3, 6, 1, 7, 9, NA), na.rm = TRUE)
## [1] 4.5

The first result is NA because one value is missing. The argument na.rm = TRUE tells mean() to remove missing values before doing the calculation.

5 / 0
## [1] Inf
-4 / 0
## [1] -Inf
0 / 0
## [1] NaN
Inf - Inf
## [1] NaN

Inf and -Inf represent positive and negative infinity. NaN means “not a number” and appears when a calculation has no defined numerical result. NA instead represents a missing value.

Takeaway: Use na.rm = TRUE when you deliberately want a function to ignore missing values, and pay attention to non-finite results such as Inf and NaN.


5.7 Data frames

What we’re about to do: Create and inspect a data frame.

height <- c(173, 145, 187, 155, 179, 133)
sex <- c("Male", "Female", "Male", "Female", "Male", "Female")
age <- c(17, 22, 32, 20, 27, 30)

mydata <- data.frame(height = height, age = age, sex = sex)
mydata
##   height age    sex
## 1    173  17   Male
## 2    145  22 Female
## 3    187  32   Male
## 4    155  20 Female
## 5    179  27   Male
## 6    133  30 Female

Inspect a data frame:

summary(mydata)
##      height           age               sex   
##  Min.   :133.0   Min.   :17.00   Length   :6  
##  1st Qu.:147.5   1st Qu.:20.50   N.unique :2  
##  Median :164.0   Median :24.50   N.blank  :0  
##  Mean   :162.0   Mean   :24.67   Min.nchar:4  
##  3rd Qu.:177.5   3rd Qu.:29.25   Max.nchar:6  
##  Max.   :187.0   Max.   :32.00
str(mydata)
## 'data.frame':    6 obs. of  3 variables:
##  $ height: num  173 145 187 155 179 133
##  $ age   : num  17 22 32 20 27 30
##  $ sex   : chr  "Male" "Female" "Male" "Female" ...

Subset rows/columns:

mydata[1, ]
##   height age  sex
## 1    173  17 Male
mydata[, 2]
## [1] 17 22 32 20 27 30
mydata[1, 2]
## [1] 17
subset(mydata, sex == "Female")
##   height age    sex
## 2    145  22 Female
## 4    155  20 Female
## 6    133  30 Female

Takeaway: Data frames are tables: rows are observations, columns are variables.


5.8 Classes and factors

What we’re about to do: Check classes and use factors for categories.

class(height)
## [1] "numeric"
class(sex)
## [1] "character"
class(mydata)
## [1] "data.frame"

Factors tell R that a variable represents categories and record the possible category levels. Some plotting and modelling functions use this information:

plot(mydata$sex, mydata$age)

The command above fails because mydata$sex contains character data. Converting it to a factor tells plot() to compare the numerical values across categories:

plot(as.factor(mydata$sex), mydata$age)

mydata$sex <- as.factor(mydata$sex)
str(mydata)
## 'data.frame':    6 obs. of  3 variables:
##  $ height: num  173 145 187 155 179 133
##  $ age   : num  17 22 32 20 27 30
##  $ sex   : Factor w/ 2 levels "Female","Male": 2 1 2 1 2 1

Common mistake: Leaving a categorical variable as character data when a function expects a factor.

Takeaway: Use factors for categorical variables when needed.


5.9 Organising your work and importing data

What we’re about to do: Import a CSV file, and meet the two functions you will see for doing this.

Keep your data in a CourseData folder inside your project. We will use relative paths throughout this course. If you do not have CourseData, download it from the course site.

There are two common ways to read a CSV file into R, and you will come across both:

  • read.csv() comes with base R. It returns a data.frame and, with stringsAsFactors = TRUE, turns text columns into factors — which is convenient for the statistics later in the course.
  • read_csv() comes from the readr package (part of the tidyverse, which we cover later). It is a little faster, prints a short summary of the column types it guessed, and returns a tibble (a modern data frame). It leaves text columns as character.

Both work well and you will see both in other people’s code. To keep things consistent, this book uses read.csv() from here on.

carni <- read.csv("CourseData/carnivora.csv", stringsAsFactors = TRUE)

What just happened: read.csv() imported the data as a data.frame and, thanks to stringsAsFactors = TRUE, stored the text columns as factors.

Try this: Run str(carni) and compare it to summary(carni).

Common mistake: File not found. Check spelling and folder names.

Takeaway: This book uses read.csv(); read_csv() is the tidyverse alternative you will also encounter.


5.10 Inspecting the data

summary(carni)
##        Order         SuperFamily         Family        Genus   
##  Carnivora:112   Caniformia:57   Viverridae :32   Mustela : 9  
##                  Feliformia:55   Mustelidae :30   Herpetes: 8  
##                                  Felidae    :19   Panthera: 5  
##                                  Canidae    :18   Canis   : 4  
##                                  Hyaenidae  : 4   Martes  : 4  
##                                  Procyonidae: 4   Felis   : 3  
##                                  (Other)    : 5   (Other) :79  
##                     Species          FW                SW         
##  Acinonyx jubatus       :  1   Min.   :  0.050   Min.   :  0.050  
##  Ailuropoda melanoleuca :  1   1st Qu.:  1.245   1st Qu.:  1.400  
##  Alopex lagopus         :  1   Median :  3.400   Median :  3.895  
##  Aonyx capensis         :  1   Mean   : 18.099   Mean   : 20.084  
##  Arctictis binturong    :  1   3rd Qu.: 10.363   3rd Qu.: 11.592  
##  Arctogalidia trivirgata:  1   Max.   :320.000   Max.   :365.000  
##  (Other)                :106                                      
##        FB               SB               LS              GL        
##  Min.   :  1.00   Min.   :  1.00   Min.   :1.000   Min.   : 23.50  
##  1st Qu.: 15.25   1st Qu.: 15.68   1st Qu.:2.500   1st Qu.: 53.80  
##  Median : 33.00   Median : 33.75   Median :3.000   Median : 63.00  
##  Mean   : 53.40   Mean   : 56.43   Mean   :3.232   Mean   : 65.79  
##  3rd Qu.: 57.38   3rd Qu.: 57.17   3rd Qu.:3.800   3rd Qu.: 73.50  
##  Max.   :365.00   Max.   :459.50   Max.   :8.800   Max.   :168.00  
##                                    NAs    :2       NAs    :21      
##        BW                WA              AI               LY            AM    
##  Min.   :   0.01   Min.   : 21.0   Min.   :  56.0   Min.   : 96          :57  
##  1st Qu.:  41.88   1st Qu.: 54.5   1st Qu.: 183.8   1st Qu.:141   365    : 8  
##  Median : 116.25   Median : 70.0   Median : 365.0   Median :162   730    : 5  
##  Mean   : 249.31   Mean   :104.0   Mean   : 407.8   Mean   :182   913    : 4  
##  3rd Qu.: 286.88   3rd Qu.:117.0   3rd Qu.: 592.5   3rd Qu.:207   450    : 2  
##  Max.   :1650.00   Max.   :730.0   Max.   :1080.0   Max.   :408   1095   : 1  
##  NAs    :50        NAs    :49      NAs    :82       NAs    :63    (Other):35  
##        IB    
##         :55  
##  12     :27  
##  6      :13  
##  24     : 2  
##  27     : 2  
##  4      : 2  
##  (Other):11
dim(carni)
## [1] 112  17
names(carni)
##  [1] "Order"       "SuperFamily" "Family"      "Genus"       "Species"    
##  [6] "FW"          "SW"          "FB"          "SB"          "LS"         
## [11] "GL"          "BW"          "WA"          "AI"          "LY"         
## [16] "AM"          "IB"

The dataset includes life history variables. For example:

  • FW = Female body mass (kg)
  • GL = Gestation length (days)
  • BW = Birth mass (g)
summary(carni$FW)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.050   1.245   3.400  18.099  10.363 320.000

5.11 Tables and summary statistics

class(carni$Family)
## [1] "factor"
levels(carni$Family)
## [1] "Ailuridae"   "Canidae"     "Felidae"     "Hyaenidae"   "Mustelidae" 
## [6] "Procyonidae" "Ursidae"     "Viverridae"
table(carni$Family)
## 
##   Ailuridae     Canidae     Felidae   Hyaenidae  Mustelidae Procyonidae 
##           1          18          19           4          30           4 
##     Ursidae  Viverridae 
##           4          32
tapply(carni$FW, carni$Family, mean, na.rm = TRUE)
##   Ailuridae     Canidae     Felidae   Hyaenidae  Mustelidae Procyonidae 
##  120.000000    9.050000   31.432105   33.540000    3.989000    3.642500 
##     Ursidae  Viverridae 
##  198.250000    2.672813

Takeaway: Tables and summaries give you a quick sense of patterns before you plot.


5.12 Basic plotting

plot(
  log(carni$FW), log(carni$GL),
  xlab = "Log female body mass (kg)",
  ylab = "Log gestation length (days)"
)
A simple scatter plot

Figure 5.1: A simple scatter plot

Interpretation: Species with larger female body mass tend to have longer gestation lengths.

  • What to look for:
    • Is the relationship roughly linear?
    • Are there any clear outliers?

Try this: Plot carni$FW against carni$GL without the log transformations and compare the shape.


5.13 Exercise: Californian bird diversity

Surveys of bird abundance were carried out near Oakland, California.2 The locations were developed in different years, which lets us ask how species richness varies with the age of a suburb.

5.13.1 The data

The file is suburbanBirds.csv. It contains the columns Name, Year, HabitatIndex, nIndividuals, and nSpecies. The surveys were conducted in 1975, so subtracting Year from 1975 gives the approximate age of each suburb at the time of the survey.

5.13.2 Your tasks

Level 1 (warm-up)

  1. Import the data as an object called birds, then check the columns with str(). Checkpoint: You should see the five columns listed above.

  2. Calculate the mean, minimum, and maximum of nSpecies. Checkpoint: The values should be approximately 9.65, 3, and 15, respectively.

Level 2 (practice)

  1. Create a new column with birds$Age <- 1975 - birds$Year and check that it looks sensible. Checkpoint: Younger suburbs have smaller Age values.

  2. Plot Age against nSpecies with plot(birds$Age, birds$nSpecies). Checkpoint: The x-axis is age and the y-axis is species richness.

Level 3 (extension)

  1. Immediately after creating the scatter plot, add a smoother with lines(lowess(birds$Age, birds$nSpecies)). Does it change your interpretation?

Hint: If the plotting code fails, check the column names with names(birds).


5.14 Key takeaways

  • Use scripts for reproducible work.
  • Vectors and data frames are the core data types.
  • read.csv() imports CSV files; read_csv() is the tidyverse alternative.
  • Check classes with str() and convert categorical variables to factors when needed.
  • Always inspect your data before analysing it.

5.15 Common pitfalls recap

  • Running code only in the console.
  • File paths that point to the wrong folder.
  • Treating categories as numbers or characters when they should be factors.

  1. Vale, T. R., & Vale, G. R. (1976). Suburban bird populations in west-central California. Journal of Biogeography, 3(2), 157–165.↩︎