Section 1.8 Plotting data with ggplot2
Now that we have this
sf object, how can we map it? We mentioned before about the graphical package ggplot2. We can use this, and its syntax, in order to map spatial data using the geom_sf() geometry.
First, a quick refresher on
ggplot2 and the grammar of graphics. The grammar of graphics upon which this package is based defines various components of a graphic. Some of the most important are:
-
The data: For using
ggplot2the data has to be stored as a data-frame or tibble (sfobjects are of class tibble). -
The geoms: They describe the objects that represent the data (e.g., points, lines, polygons, etc.). This is what gets drawn. And you can have various different types layered over each other in the same visualisation.
-
The aesthetics: They describe the visual characteristics that represent data (e.g., position, size, colour, shape, transparency).
-
Facets: They describe how data is split into subsets and displayed as multiple small graphs.
-
Stats: They describe statistical transformations that typically summarise data.
Let’s take it one step at time. Essentially, the philosophy behind this is that all graphics are made up of layers. You can build every graph from the same few components: a dataset, a set of geoms visual marks that represent data points, and a coordinate system. Take this example from our crimes dataframe. You have a table such as:
library(dplyr)
df <- crimes %>%
filter(crime_type %in% c("Robbery", "Shoplifting", "Theft from the person")) %>%
group_by(crime_type) %>%
count()
knitr::kable(df, caption = 'Example data table')
| crime_type | n |
|---|---|
| Robbery | 463 |
| Shoplifting | 1479 |
| Theft from the person | 718 |
You then want to plot this. To do so, you want to create a plot that combines the following layers:
p1 <- ggplot(df, aes(x = crime_type, y = n)) +
geom_bar(stat = "identity") +
theme_void()
p2 <- ggplot(df, aes(x = gsub(" from the person", "",crime_type), y = n)) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 15, hjust = 1)) +
xlab("Crime type") +
ylab("Number of crimes")
p3 <- ggplot(df, aes(x = crime_type, y = n)) +
theme_void() +
labs(title = "Freq. of crime types") +
theme(panel.border = element_rect(colour = "black", fill=NA, size=1))
gridExtra::grid.arrange(p1, p2, p3, nrow = 1)

This will result in a final plot:
ggplot(df, aes(x = gsub(" from the person", "",crime_type), y = n)) +
geom_bar(stat = "identity") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 15, hjust = 1),
panel.border = element_rect(colour = "black", fill=NA, size=1)) +
labs(title = "Frequency of crime types") +
xlab("Crime type") +
ylab("Number of crimes")

Taking our crime data as an example, we would build up our plot as follows. First let’s create a small illustrative dataset of only the crimes of "Robbery", "Shoplifting", and "Theft from the person".
library(dplyr)
df <- crimes %>%
filter(crime_type %in% c("Robbery","Shoplifting", "Theft from the person")) %>%
group_by(crime_type) %>%
count()
df
## # A tibble: 3 × 2 ## # Groups: crime_type [3] ## crime_type n ## <chr> <int> ## 1 Robbery 463 ## 2 Shoplifting 1479 ## 3 Theft from the person 718
Now let us add the layers to our ggplot object. First, the data layer:
library(ggplot2)
ggplot(df, aes(x = crime_type, y = n))

Then add the geometry (in this case,
geom_col()):
ggplot(df, aes(x = crime_type, y = n)) +
geom_col()

Then our annotations:
ggplot(df, aes(x = crime_type, y = n)) +
geom_col() +
labs(title = "Frequency of crime types") +
xlab("Crime type") +
ylab("Number of crimes")

We can also specify our themes, by using a custom theme such as
theme_minimal(), and by adding our own specifications within an additional theme() function:
ggplot(df, aes(x = crime_type, y = n)) +
geom_col() +
labs(title = "Frequency of crime types") +
xlab("Crime type") +
ylab("Number of crimes") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 15, hjust = 1),
panel.border = element_rect(colour = "black", fill=NA, size=1))

We can add further specifications within the
aes() (aesthetics) function, where we add additional layers from our data, to represent even more information in our charts, for example the outcomes for each crime.
# create new dataframe (df) including last_outcome_category variable
df <- crimes %>%
filter(crime_type %in% c("Robbery", "Shoplifting", "Theft from the person")
) %>%
group_by(crime_type, last_outcome_category) %>%
count()
# plot including new variable with fill= parameter in aes() function
ggplot(df, aes(x = crime_type, y = n, fill = last_outcome_category)) +
geom_col() +
labs(title = "Frequency of crime types") +
xlab("Crime type") +
ylab("Number of crimes") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 15, hjust = 1),
panel.border = element_rect(colour = "black", fill=NA, size=1))

We can be explicit about the colours we want to use with the function
scale_fill_brewer(), which we can also use to rename our legend. Don’t worry too much at this point about where the palette comes from; in the chapter 5 on cartography we will discuss colour in more detail.
ggplot(df, aes(x = crime_type, y = n, fill = last_outcome_category)) +
geom_col() +
labs(title = "Frequency of crime types") +
xlab("Crime type") +
ylab("Number of crimes") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 15, hjust = 1),
panel.border = element_rect(colour = "black", fill=NA, size=1)) +
scale_fill_brewer(type = "qual", palette = 3, name = "Outcome")

There are many more options. While this here is not the greatest graph you’ll ever see, it illustrates the process of building up your graphics the ggplot way. Do read up on
ggplot2 for example in [216]. In later chapters, we will talk more about visualisation, colour choice, and more!
