Chapter 11 dplyr and the tidy data concept
11.1 Introduction
Data wrangling refers to the process of manipulating raw data into the format that we want it in, for example for data visualisation or statistical analyses. There are a wide range of ways we may want to manipulate our data, for example by creating new variables, subsetting the data, or calculating summaries. Data wrangling is often a time consuming process. It is also not the most interesting part of any analysis - we are interested in answering biological questions, not in formatting data. However, it is a necessary step to go through to be able to conduct the analyses that we’re really interested in. Learning how to manipulate data efficiently can save us a lot of time and trouble and is therefore a really important skill to master.
11.2 The value of dplyr
The dplyr package has been carefully designed to make life easier to manipulate data frames and other kinds of similar objects. A key reason for its ease-of-use is that dplyr is very consistent in the way its functions work. For example, the first argument of the main dplyr functions is always an object containing our data. This consistency makes it very easy to get to grips with each of the main dplyr functions—it’s often possible to understand how one works by seeing one or two examples of its use.
A second reason for favouring dplyr is that it is orientated around a few core functions, each of which is designed to do one thing well. The key dplyr functions are often referred to as “verbs”, reflecting the fact that they “do something” to data. For example: (1) select
is used to obtain a subset of variables; (2) mutate
is used to construct new variables; (3) filter
is used to obtain a subset of rows; (4) arrange
is used to reorder rows; and (5) summarise
is used to calculate information about groups. We’ll cover each of these verbs in detail in later chapters, as well as a few additional functions such as rename
and group_by
.
Apart from being easy to use, dplyr is also fast compared to base R functions. This won’t matter much for the small data sets we use in this book, but dplyr is a good option for large data sets. The dplyr package also allows us to work with data stored in different ways, for example, by interacting directly with a number of database systems. We won’t work with anything other than data frames (and the closely-related “tibble”) but it is worth knowing about this facility. Learning to use dplyr with data frames makes it easy to work with these other kinds of data objects.
A dplyr cheat sheet
The developers of RStudio have produced a very usable cheat sheat that summarises the main data wrangling tools provided by dplyr. Our advice is to download this, print out a copy and refer to this often as you start working with dplyr.
11.3 Tidy data
dplyr will work with any data frame, but it’s at its most powerful when our data are organised as tidy data. The word “tidy” has a very specific meaning in this context. Tidy data has a specific structure that makes it easy to manipulate, model and visualise. A tidy data set is one where each variable is in only one column and each row contains only one observation. This might seem like the “obvious” way to organise data, but many people fail to adopt this convention.
We aren’t going to explore the tidy data concept in great detail, but the basic principles are not difficult to understand. We’ll use an example to illustrate what the “one variable = one column” and “one observation = one row” idea means. Let’s return to the made-up experiment investigating the response of communities to fertilizer addition. This time, imagine we had only measured biomass, but that we had measured it twice over the course of the experiment.
We’ll examine some artificial data for the experiment and look at two ways to organise it to help us understand the tidy data idea. The first way uses a separate column for each biomass measurement:
## Treatment BiomassT1 BiomassT2
## 1 Control 284 324
## 2 Control 328 400
## 3 Control 291 355
## 4 Fertilser 956 1197
## 5 Fertilser 954 1012
## 6 Fertilser 685 859
This often seems like the natural way to store such data, especially for experienced Excel users. However, this format is not tidy. Why? The biomass variable has been split across two columns (“BiomassT1” and “BiomassT2”), which means each row corresponds to two observations.
We won’t go into the “whys” here, but take our word for it: adopting this format makes it difficult to efficiently work with data. This is not really an R-specific problem. This non-tidy format is sub-optimal in many different data analysis environments.
A tidy version of the example data set would still have three columns, but now these would be: “Treatment”, denoting the experimental treatment applied; “Time”, denoting the sampling occasion; and “Biomass”, denoting the biomass measured:
## Treatment Time Biomass
## 1 Control T1 284
## 2 Control T1 328
## 3 Control T1 291
## 4 Fertilser T1 956
## 5 Fertilser T1 954
## 6 Fertilser T1 685
## 7 Control T2 324
## 8 Control T2 400
## 9 Control T2 355
## 10 Fertilser T2 1197
## 11 Fertilser T2 1012
## 12 Fertilser T2 859
These data are tidy: each variable is in only one column, and each observation has its own unique row. These data are well-suited to use with dplyr.
Always try to start with tidy data
The best way to make sure your data set is tidy is to store in that format when it’s first collected and recorded. There are packages that can help convert non tidy data into the tidy data format (e.g. the tidyr package), but life is much simpler if we just make sure our data are tidy from the very beginning.
11.4 A quick look at dplyr
We’ll finish up this chapter by taking a quick look at a few features of the dplyr package, before really drilling down into how it works. The package is not part of the base R installation, so we have to install it first via install.packages("dplyr")
. Remember, we only have to install dplyr once, so there’s no need to leave the install.packages
line in script that uses the package. We do have to add library
to the top of any scripts using the package to load and attach it:
library("dplyr")
We need some data to work with. We’ll use two data sets to illustrate the key ideas in the next few chapters: the iris
data set in the datasets package and the storms
data set in the nasaweather package.
The datasets package ships with R and is loaded and attached at start up, so there’s no need to do anything to make iris
available. The nasaweather package doesn’t ship with R so it needs to be installed via install.packages("nasaweather")
. Finally, we have to add library
to the top of our script to load and attach the package:
library("nasaweather")
The nasaweather package is a bare bones data package. It doesn’t contain any new R functions, just data. We’ll be using the storms
data set from nasaweather: this contains information about tropical storms in North America (from 1995-2000). We’re just using it as a convenient example to illustrate the workings of the dplyr, and later, the ggplot2 packages.
11.4.1 Tibble (tbl
) objects
The primary purpose of the dplyr package is to make it easier to manipulate data interactively. In order to facilitate this kind of work dplyr implements a special kind of data object known as a tbl
(pronounced “tibble”). We can think of a tibble as a special data frame with a few extra whistles and bells.
We can convert an ordinary data frame to a a tibble using the tbl_df
function. It’s a good idea (though not necessary) to convert ordinary data frames to tibbles. Why? When a data frame is printed to the Console R will try to print every column and row until it reaches a (very large) maximum permitted amount of output. The result is a mess of text that’s virtually impossible to make sense of. In contrast, when a tibble is printed to the Console, it does so in a compact way. To see this, we can convert the iris
data set to a tibble using tbl_df
and then print the resulting object to the Console:
# make a "tibble" copy of iris
iris_tbl <- tbl_df(iris)
# print it
iris_tbl
## # A tibble: 150 x 5
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## <dbl> <dbl> <dbl> <dbl> <fct>
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 10 4.9 3.1 1.5 0.1 setosa
## # … with 140 more rows
Notice that only the first 10 rows are printed. This is much nicer than trying to wade through every row of a data frame.
11.4.2 The glimpse
function
Sometimes we just need a quick, compact summary of a data frame or tibble. This is the job of the glimpse
function from dplyr. The glimpse function is very similar to str
:
glimpse(iris_tbl)
## Observations: 150
## Variables: 5
## $ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5…
## $ Sepal.Width <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3…
## $ Petal.Length <dbl> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1…
## $ Petal.Width <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0…
## $ Species <fct> setosa, setosa, setosa, setosa, setosa, setosa, set…
The function takes one argument: the name of a data frame or tibble. It then tells us how many rows it has, how many variables there are, what these variables are called, and what kind of data are associated with each variable. This function is useful when we’re working with a data set containing many variables.