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 your BB852 project, then create a new script (File > New File > R Script). Save it as practice.R in your scripts folder.

To run code from a script, put the cursor on a line (or select several lines) and press Cmd + Enter (Mac) or Ctrl + Enter (Windows), or click the Run button at the top of the script. The code is sent to the Console, and the result appears there.

Anything after a # on a line is a comment: R ignores it. Use comments to explain what your code does and why, so that you (and anyone marking your work) can follow it later:

# Import the carnivore data
carni <- read.csv("CourseData/carnivora.csv")
mean(carni$FW) # mean female body mass (kg)

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, run lines with Cmd/Ctrl + Enter, and comment your code with #.


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

Note that log() is the natural logarithm (base e). Use log10() for base-10 logarithms, or log(x, base = 2) for any other base.

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)
odds <- seq(from = 1, to = 12, by = 2)
D <- seq(from = 1, to = 5, by = 0.1)
sex1 <- rep(c("Male", "Female"), each = 3)
sex2 <- rep(c("Male", "Female"), times = c(2, 4))

A
## [1] 1 2 3 4 5
odds
## [1]  1  3  5  7  9 11
sex1
## [1] "Male"   "Male"   "Male"   "Female" "Female" "Female"
sex2
## [1] "Male"   "Male"   "Female" "Female" "Female" "Female"

Choosing names: Object names can contain letters, numbers, . and _, but must not start with a number, and they are case sensitive (A and a are different objects). Avoid names that R already uses, such as T, F (short for TRUE and FALSE), c or mean. Short, descriptive names such as body_mass are easiest to read.

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, which it does by drawing a box plot for each category:

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, follow the instructions in Getting set up.

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

Look closely: str(carni) shows that AM (age at sexual maturity) and IB (inter-birth interval) were imported as factors, not numbers. Why? If even one value in a column is not a number, R treats the whole column as text. Run levels(carni$AM) and levels(carni$IB) and look for the odd ones out ("t 132" and " S"). These are typos in the original file. Problems like this are common in real data, which is why you should always check str() after importing.

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 data are life history traits of species in the mammal order Carnivora, from Gittleman (1986).2 Besides the taxonomy columns (Order, SuperFamily, Family, Genus, Species), the variables are:

Column Variable Units
FW Female body mass kg
SW Average body mass of adult males and females kg
FB Female brain mass g
SB Average brain mass of adult males and females g
LS Litter size number of young
GL Gestation length days
BW Birth mass g
WA Weaning age days
AI Age of independence days
LY Longevity months
AM Age at sexual maturity days
IB Inter-birth interval months
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 in suburbs in west-central California.3 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: Age should run from 1 to 29 years.

  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. Gittleman, J. L. (1986). Carnivore life history patterns: allometric, phylogenetic and ecological associations. American Naturalist, 127, 744–771. The same data set is included in the R package ape as carnivora.↩︎

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