5  Line Graphs

5.1 Introduction

In this chapter, we will build line graphs. To be more specific we will learn to

  • create line plots
  • add color to lines
  • modify line type/style
  • modify line width
  • add points to the lines
  • modify axis range
  • add additional lines to the plot
  • add error bars with segments()
  • annotate the plot with arrows()
  • shade a confidence band with polygon()

5.2 Basic Plot

To build a line graph, we will learn a new argument in the plot() function called type. It allows us to specify the symbol that must be used to represent the data. Let us begin by building a simple line graph. We will use the AirPassengers data set in this chapter. Before we begin to build the plot, let us take a quick look at the data in order to understand what we are plotting.

head(AirPassengers)
     Jan Feb Mar Apr May Jun
1949 112 118 132 129 121 135

In order to build a line plot, we will set the type argument in the plot() function to l (line). There are other values which type takes but we will explore them later.

data <- head(AirPassengers)
plot(data, type = 'l')

If you do not like plain lines, you can represent the data using lines interspersed with points by setting the type argument to b (both lines and points).

plot(data, type = 'b')

Another option is to have the points and lines overplotted. It can be achieved by setting the type argument to o (overplotted).

plot(data, type = 'o')

You can also create lines without points but with breaks instead by setting the type argument to c.

plot(data, type = 'c')

5.3 Color

So now we know how to build a simple line graph. Let us now make this plot more elegant by modifying its appearance. Let us begin by adding some color to the line using the col argument in the plot() function.

plot(data, type = 'l', col = 'blue')

If you have points along with the line, they will have the same color as well.

plot(data, type = 'b', col = 'blue')

5.4 Line Type

The line type can be modified using the lty argument. It takes values from 1 to 6 and the default value is 1. Below is an example:

  • 1:solid
  • 2:dashed
  • 3:dotted
  • 4:dotdash
  • 5:longdash
  • 6:twodash
plot(data, type = 'l', lty = 3)

Let us look at all the line types in the below example:

Instead of specifying the numbers 1:6, you can use their description as well.

5.5 Line Width

The width of the lines can be modified using the lwd argument in the plot() function. The default value for width is 1.

plot(data, type = 'l', lwd = 2.5)

In the below example, we look at the width of the lines relative to the default value.

5.6 Enhance Points

We can enhance the points in the line plot in the same way as we enhanced the points in the scatter plot in this previous chapter. Let us look at an example:

plot(data, type = 'b', pch = 23, col = 'red', cex = 1.5)

We have used the pch, col and cex arguments to modify the shape, color and size of the points. One drawback of the above method is that the color of the line and the points will be the same. What if we want them to have different colors? The solution is as follows:

  • build the line graph using the plot() function
  • add the points to the above plot using the points() function

In the next example, let us first build the line plot, add points using the points() function and then specify separate colors to the line and the points.

plot(data, type = 'l', col = 'red')
points(data, pch = 23, col = 'blue', bg = 'green', cex = 1.5)

5.7 Additional Lines

If you want to compare variables, you would want to add additional lines to the line graph. In R, this can be achieved using the lines() function. First we create the line plot using the base variable and then we can add as many lines as we want using the lines() function.

Before you add additional lines, it is important to ensure that the range of both the axis are modified to accommodate the data of the additional lines. If we do not modify the axis range, some of the lines will be outside the plot.

Let us now create a line plot and add an additional line using the lines() function.We will use some dummy data for this example:

data1 <- c(7.2, 7.6, 6.8, 6.5, 7)
data2 <- c(6.8, 7.2, 7.8, 7, 6.2)
plot(data1, type = "b", col = "blue")
lines(data2, type = "b", col = "red")

As you can see the second line is outside the plot. Let us recreate the plot but this time we will modify the range of the axis to accommodate the second line (data2).

plot(data1, type = "b", col = "blue", ylim = c(5, 9))
lines(data2, type = "b", col = "red")
Two line series. The blue series rises to a peak of 7.6 at the second point. The red series peaks at 7.8 at the third point. Because the axis has been widened to cover both, every point of both series is visible inside the frame.
Figure 5.1: Widening the y axis so a second series fits inside the plot region

5.8 Uncertainty and Annotation

A line through data points claims more precision than the data usually supports. Three functions close that gap, and they are the ones you will reach for the moment a plot leaves the screen and someone asks what it means.

5.8.1 segments()

segments() draws straight line segments between coordinate pairs. Its commonest use on a line graph is an error bar: a vertical segment centred on each point.

data1 <- c(7.2, 7.6, 6.8, 6.5, 7)
sd1 <- c(0.4, 0.6, 0.5, 0.7, 0.3)
plot(data1, type = 'b', col = 'blue', ylim = c(5, 9),
     xlab = 'Index', ylab = 'Data')
segments(1:5, data1 - sd1, 1:5, data1 + sd1, col = 'red', lwd = 2)
A line chart of five points. Each point has a red vertical bar through it, extending one standard deviation above and below. The widest bar is on the second point, which also sits highest.
Figure 5.2: Vertical error bars drawn with segments()

segments() takes four vectors: the x of each segment’s start, the y of the start, the x of the end, and the y of the end. Because all four are vectorised, one call covers every point. It also draws caps if you want them, via the horizontal segments underneath:

plot(data1, type = 'b', col = 'blue', ylim = c(5, 9),
     xlab = 'Index', ylab = 'Data')
segments(1:5, data1 - sd1, 1:5, data1 + sd1, col = 'red', lwd = 2)
# caps: two short horizontal segments per error bar
segments(1:5 - 0.1, data1 - sd1, 1:5 + 0.1, data1 - sd1, col = 'red')
segments(1:5 - 0.1, data1 + sd1, 1:5 + 0.1, data1 + sd1, col = 'red')
The same five-point line chart with the same vertical error bars, but each bar now has a short horizontal cap at its top and bottom, forming a capital-I shape at every point.
Figure 5.3: Adding caps to the error bars with two more segments() calls

5.8.2 arrows()

arrows() draws a line with a head at one end. Use it when the reader’s eye needs to be sent somewhere specific.

data2 <- c(6.8, 7.2, 7.8, 7, 6.2)
plot(data1, type = 'b', col = 'blue', ylim = c(5, 9),
     xlab = 'Index', ylab = 'Data', main = 'Arrow points at the gap')
arrows(4.4, 6.1, 3, 7.6, col = 'red', lwd = 2)
Two line series crossing between the second and third points. A red arrow runs from the lower right up to the peak of the falling series, marking the point where that series turns downward.
Figure 5.4: Using arrows() to draw attention to a feature

The first two arguments are where the arrow starts, the next two where it ends. That is the opposite of the order you would guess, and it is worth remembering: arrows(x0, y0, x1, y1) reads “from here, to there”.

Useful arguments: length sets the head size (0.1 is the default; 0 leaves a plain line), angle opens the head, and code picks the head shape — 1 for a closed arrow, 2 for an open one.

plot(data1, type = 'b', col = 'blue', ylim = c(5, 9),
     xlab = 'Index', ylab = 'Data', main = 'Open head, thinner line')
arrows(4.4, 6.1, 3, 7.6, col = 'red', lwd = 1.5, length = 0.15,
       angle = 30, code = 2)
The same two crossing line series with the same arrow, but the arrow head is drawn as an open V rather than a solid triangle, is visibly longer, and is set at a wider angle. The shaft is also thinner.
Figure 5.5: The same annotation with an open arrow head and a longer head length

5.8.3 polygon()

polygon() draws a closed shape and fills it. On a line graph its use is a confidence band: shade the area between an upper and a lower bound, then draw the line on top.

The order of the points matters, because that is the order they get joined. For a band you want to walk along the top from left to right, then back along the bottom from right to left, so the shape closes on itself instead of crossing.

x <- 1:12
mid <- c(4.5, 5.2, 5.0, 6.1, 6.8, 7.4, 7.1, 8.0, 8.6, 9.1, 9.4, 10.2)
lo <- mid - 0.9
hi <- mid + 0.9

plot(x, mid, type = 'n', ylim = c(2, 12), col = 'blue',
     xlab = 'Month', ylab = 'Value')
polygon(c(x, rev(x)), c(hi, rev(lo)), col = '#DCE9F5', border = NA)
lines(x, mid, col = 'blue', lwd = 2)
points(x, mid, pch = 19, col = 'blue')
A line chart of twelve monthly points rising overall from about 4.5 to about 10.2. A pale blue band follows the line, about 0.9 units above and below it at every point, and the dark blue line with filled circular markers is drawn on top of the band so both remain visible.
Figure 5.6: A confidence band shaded with polygon() and the line drawn on top

Two details carry that figure. The first is type = 'n', which sets up the axes and the plot region without drawing anything — the standard way to draw a plot in layers when the first layer is not the data itself. The second is rev(), which reverses a vector so the polygon can be closed in one call instead of building the point list by hand.

Note that polygon() is drawn first and lines() second. Anything you fill will cover anything already plotted, so background bands belong underneath.

5.9 Putting it all together

Finally let us enhance the plot by adding a title and modifying the axis labels.

plot(data1, type = "b", col = "blue", 
     ylim = c(5, 9), main = 'Line Graph',
     xlab = 'Index', ylab = 'Data')
lines(data2, type = "b", col = "red")
segments(1:5, data1 - 0.5, 1:5, data1 + 0.5, col = "blue", lwd = 2)
# Approach the peak from above-right so the arrow crosses neither series.
arrows(4.1, 8.5, 3.15, 7.85, col = "grey30", length = 0.1)
Two line series as above, with a blue vertical error bar through each blue point and a grey arrow pointing down and to the left at the peak of the red series.
Figure 5.7: Layering error bars and an annotation onto a two-series line chart