Skip to main content

Section 2.3 5NG#1: Scatterplots

The simplest of the 5NG are scatterplots, also called bivariate plots. They allow you to visualize the relationship between two numerical variables. While you may already be familiar with scatterplots, let’s view them through the lens of the grammar of graphics we presented in Section 2.1. Specifically, we will visualize the relationship between the following two numerical variables in the envoy_flights data frame included in the moderndive package:
  1. dep_delay: departure delay on the horizontal “x” axis and
  2. arr_delay: arrival delay on the vertical “y” axis
for Envoy Airlines flights leaving NYC in 2023. In other words, envoy_flights does not consist of all flights that left NYC in 2023, but rather only those flights where carrier is MQ (which is Envoy Airlines’ carrier code).

Checkpoint 2.3.1. Learning Check 2.1.

Take a look at both the flights data frame from the nycflights23 package and the envoy_flights data frame from the moderndive package by running View(flights) and View(envoy_flights). In what respect do these data frames differ? For example, think about the number of rows in each dataset.

Subsection 2.3.1 Scatterplots via geom_point

Let’s now go over the code that will create the desired scatterplot, while keeping in mind the grammar of graphics framework we introduced in Section 2.1. Let’s take a look at the code and break it down piece-by-piece.
ggplot(data = envoy_flights, mapping = aes(x = dep_delay, y = arr_delay)) + 
  geom_point()
Warning: Removed 3 rows containing missing values or values outside the scale range 
(`geom_point()`).
Within the ggplot() function, we specify two of the components of the grammar of graphics as arguments (i.e., inputs):
  1. The data as the envoy_flights data frame via data = envoy_flights.
  2. The aesthetic mapping by setting mapping = aes(x = dep_delay, y = arr_delay). Specifically, the variable dep_delay maps to the x position aesthetic, while the variable arr_delay maps to the y position.
We then add a layer to the ggplot() function call using the + sign. The added layer in question specifies the third component of the grammar: the geometric object. In this case, the geometric object is set to be points by specifying geom_point(). After running these two lines of code in your console, you’ll notice two outputs: a warning message and the graphic shown in Figure 2.3.2.
Scatterplot showing arrival delay (y-axis) versus departure delay (x-axis) for Envoy Air flights from NYC in 2023. A dense cluster of points is visible near (0, 0) with a positive linear trend.
Figure 2.3.2. Arrival delays versus departure delays for Envoy Air flights from NYC in 2023.
Let’s first unpack the graphic in Figure 2.3.2. Observe that a positive relationship exists between dep_delay and arr_delay: as departure delays increase, arrival delays tend to also increase. Observe also the large mass of points clustered near (0, 0), the point indicating flights that neither departed nor arrived late.
Let’s turn our attention to the warning message. R is alerting us to the fact that three rows were ignored due to them being missing. For these three rows, either the value for dep_delay or arr_delay or both were missing (recorded in R as NA), and thus these rows were ignored in our plot.
Before we continue, let’s make a few more observations about this code that created the scatterplot. Note that the + sign comes at the end of lines, and not at the beginning. You’ll get an error in R if you put it at the beginning of a line. When adding layers to a plot, you are encouraged to start a new line after the + (by pressing the Return/Enter button on your keyboard) so that the code for each layer is on a new line. As we add more and more layers to plots, you’ll see this will greatly improve the legibility of your code.
To stress the importance of adding the layer specifying the geometric object, consider Figure 2.3.3 where no layers are added. Because the geometric object was not specified, we have a blank plot that is not very useful!
ggplot(data = envoy_flights, mapping = aes(x = dep_delay, y = arr_delay))
An empty ggplot canvas with x-axis labeled dep_delay and y-axis labeled arr_delay but no data points or geometric objects shown.
Figure 2.3.3. A plot with no layers.

Checkpoint 2.3.4. Learning Check 2.2.

What are practical reasons why dep_delay and arr_delay have a positive relationship?

Checkpoint 2.3.5. Learning Check 2.3.

What variables in the weather data frame would you expect to have a negative correlation (i.e., a negative relationship) with dep_delay? Why? Remember that we are focusing on numerical variables here. Hint: Explore the weather dataset by using the View() function.

Checkpoint 2.3.6. Learning Check 2.4.

Why do you believe there is a cluster of points near (0, 0)? What does (0, 0) correspond to in terms of the Envoy Air flights?

Checkpoint 2.3.7. Learning Check 2.5.

Checkpoint 2.3.8. Learning Check 2.6.

Create a new scatterplot using different variables in the envoy_flights data frame by modifying the example given.

Subsection 2.3.2 Overplotting

The large mass of points near (0, 0) in Figure 2.3.2 can cause some confusion since it is hard to tell the true number of points that are plotted. This is the result of a phenomenon called overplotting. As one may guess, this corresponds to points being plotted on top of each other over and over again. When overplotting occurs, it is difficult to know the number of points being plotted. There are two methods to address the issue of overplotting. Either by
  1. Adjusting the transparency of the points or
  2. Adding a little random “jitter” (or random “nudges”) to each of the points.
Method 1: Changing the transparency
The first way of addressing overplotting is to change the transparency/opacity of the points by setting the alpha argument in geom_point(). We can change the alpha argument to be any value between 0 and 1, where 0 sets the points to be 100% transparent and 1 sets the points to be 100% opaque. By default, alpha is set to 1. In other words, if we don’t explicitly set an alpha value, R will use alpha = 1.
Note how the following code is identical to the code in Section 2.3 that created the scatterplot with overplotting, but with alpha = 0.2 added to the geom_point() function:
ggplot(data = envoy_flights, mapping = aes(x = dep_delay, y = arr_delay)) + 
  geom_point(alpha = 0.2)
Scatterplot of arrival delay versus departure delay with alpha transparency set to 0.2. Areas with more overplotting appear darker, revealing the density of points clustered near (0, 0).
Figure 2.3.9. Arrival vs. departure delays scatterplot with alpha = 0.2.
The key feature to note in Figure 2.3.9 is that the transparency of the points is cumulative: areas with a high-degree of overplotting are darker, whereas areas with a lower degree are less dark. Note, furthermore, that there is no aes() surrounding alpha = 0.2. This is because we are not mapping a variable to an aesthetic attribute, but rather merely changing the default setting of alpha. In fact, you’ll receive an error if you try to change the second line to read geom_point(aes(alpha = 0.2)).
Method 2: Jittering the points
The second way of addressing overplotting is by jittering all the points. This means giving each point a small “nudge” in a random direction. You can think of “jittering” as shaking the points around a bit on the plot. Let’s illustrate using a simple example first. Say we have a data frame with 4 identical rows of x and y values: (0,0), (0,0), (0,0), and (0,0). In Figure 2.3.10, we present both the regular scatterplot of these 4 points (on the left) and its jittered counterpart (on the right).
Two side-by-side plots. The left shows a regular scatterplot where 4 identical points are stacked on top of each other at (0,0). The right shows a jittered scatterplot where the same 4 points are slightly offset in random directions.
Figure 2.3.10. Regular and jittered scatterplot.
In the left-hand regular scatterplot, observe that the 4 points are superimposed on top of each other. While we know there are 4 values being plotted, this fact might not be apparent to others. In the right-hand jittered scatterplot, it is now plainly evident that this plot involves four points since each point is given a random “nudge.”
Keep in mind, however, that jittering is strictly a visualization tool; even after creating a jittered scatterplot, the original values saved in the data frame remain unchanged.
To create a jittered scatterplot, instead of using geom_point(), we use geom_jitter(). Observe how the following code is very similar to the code that created the scatterplot with overplotting in Subsection 2.3.1, but with geom_point() replaced with geom_jitter().
ggplot(data = envoy_flights, mapping = aes(x = dep_delay, y = arr_delay)) + 
  geom_jitter(width = 30, height = 30)
Jittered scatterplot of arrival delay versus departure delay for Envoy Air flights, with width and height jitter of 30 minutes applied to reveal the distribution of overlapping points.
Figure 2.3.11. Arrival versus departure delays jittered scatterplot.
In order to specify how much jitter to add, we adjusted the width and height arguments to geom_jitter(). This corresponds to how hard you’d like to shake the plot in horizontal x-axis units and vertical y-axis units, respectively. In this case, both axes are in minutes. How much jitter should we add using the width and height arguments? On the one hand, it is important to add just enough jitter to break any overlap in points, but on the other hand, not so much that we completely alter the original pattern in points.
As can be seen in the resulting Figure 2.3.11, in this case jittering doesn’t really provide much new insight. In this particular case, it can be argued that changing the transparency of the points by setting alpha proved more effective. When would it be better to use a jittered scatterplot? When would it be better to alter the points’ transparency? There is no single right answer that applies to all situations. You need to make a subjective choice and own that choice. At the very least when confronted with overplotting, however, we suggest you make both types of plots and see which one better emphasizes the point you are trying to make.

Checkpoint 2.3.12. Learning Check 2.7.

Why is setting the alpha argument value useful with scatterplots? What further information does it give you that a regular scatterplot cannot?

Checkpoint 2.3.13. Learning Check 2.8.

After viewing Figure 2.3.9, give an approximate range of arrival delays and departure delays that occur most frequently. How has that region changed compared to when you observed the same plot without alpha = 0.2 set in Figure 2.3.2?

Subsection 2.3.3 Summary

Scatterplots display the relationship between two numerical variables. They are among the most commonly used plots because they can provide an immediate way to see the trend in one numerical variable versus another. However, if you try to create a scatterplot where either one of the two variables is not numerical, you might get strange results. Be careful!
With medium to large datasets, you may need to play around with the different modifications to scatterplots we saw such as changing the transparency/opacity of the points or by jittering the points. This tweaking is often a fun part of data visualization, since you’ll have the chance to see different relationships emerge as you tinker with your plots.