6 min read

Adding customized legends for multiple line graphs

Line graphs with more than one line, representing more than one variable, are quite common in any kind of data analysis. In this recipe we will learn how to create and customize legends for such graphs.

Getting ready

We will use the base graphics library for this recipe, so all you need to do is run the recipe at the R prompt. It is good practice to save your code as a script to use again later.

How to do it…

First we need to load the cityrain.csv example data file, which contains monthly rainfall data for four major cities across the world. You can download this file from here.

We will use the cityrain.csv example dataset.

rain<-read.csv(“cityrain.csv”)
plot(rain$Tokyo,type=”b”,lwd=2,
xaxt=”n”,ylim=c(0,300),col=”black”,
xlab=”Month”,ylab=”Rainfall (mm)”,
main=”Monthly Rainfall in major cities”)
axis(1,at=1:length(rain$Month),labels=rain$Month)
lines(rain$Berlin,col=”red”,type=”b”,lwd=2)
lines(rain$NewYork,col=”orange”,type=”b”,lwd=2)
lines(rain$London,col=”purple”,type=”b”,lwd=2)

legend(“topright”,legend=c(“Tokyo”,”Berlin”,”New York”,”London”),
lty=1,lwd=2,pch=21,col=c(“black”,”red”,”orange”,”purple”),
ncol=2,bty=”n”,cex=0.8,
text.col=c(“black”,”red”,”orange”,”purple”),
inset=0.01)


How it works…

We used the legend() function. It is quite a flexible function and allows us to adjust the placement and styling of the legend in many ways.

The first argument we passed to legend() specifies the position of the legend within the plot region. We used “topright“; other possible values are “bottomright“, “bottom“, “bottomleft“, “left“, “topleft“, “top“, “right“, and “center“. We can also specify the location of legend with x and y co-ordinates as we will soon see.

The other important arguments specific to lines are lwd and lty which specify the line width and type drawn in the legend box respectively. It is important to keep these the same as the corresponding values in the plot() and lines() commands. We also set pch to 21 to replicate the type=”b” argument in the plot() command. cex and text.col set the size and colors of the legend text. Note that we set the text colors to the same colors as the lines they represent. Setting bty (box type) to “n” ensures no box is drawn around the legend. This is good practice as it keeps the look of the graph clean. ncol sets the number of columns over which the legend labels are spread and inset sets the inset distance from the margins as a fraction of the plot region.

There’s more…

Let’s experiment by changing some of the arguments discussed:

legend(1,300,legend=c(“Tokyo”,”Berlin”,”New York”,”London”),
lty=1,lwd=2,pch=21,col=c(“black”,”red”,”orange”,”purple”),
horiz=TRUE,bty=”n”,bg=”yellow”,cex=1,
text.col=c(“black”,”red”,”orange”,”purple”))


This time we used x and y co-ordinates instead of a keyword to position the legend. We also set the horiz argument to TRUE. As the name suggests, horiz makes the legend labels horizontal instead of the default vertical. Specifying horiz overrides the ncol argument. Finally, we made the legend text bigger by setting cex to 1 and did not use the inset argument.

An alternative way of creating the previous plot without having to call plot() and lines() multiple times is to use the matplot() function. To see details on how to use this function, please see the help file by running ?matplot or help(matplot) at the R prompt.

Using margin labels instead of legends for multiple line graphs

While legends are the most commonly used method of providing a key to read multiple variable graphs, they are often not the easiest to read. Labelling lines directly is one way of getting around that problem.

Getting ready

We will use the base graphics library for this recipe, so all you need to do is run the recipe at the R prompt. It is good practice to save your code as a script to use again later.

How to do it…

Let’s use the gdp.txt example dataset to look at the trends in the annual GDP of five countries:

gdp<-read.table(“gdp_long.txt”,header=T)

library(RColorBrewer)
pal<-brewer.pal(5,”Set1″)

par(mar=par()$mar+c(0,0,0,2),bty=”l”)

plot(Canada~Year,data=gdp,type=”l”,lwd=2,lty=1,ylim=c(30,60),
col=pal[1],main=”Percentage change in GDP”,ylab=””)

mtext(side=4,at=gdp$Canada[length(gdp$Canada)],text=”Canada”,
col=pal[1],line=0.3,las=2)
lines(gdp$France~gdp$Year,col=pal[2],lwd=2)

mtext(side=4,at=gdp$France[length(gdp$France)],text=”France”,
col=pal[2],line=0.3,las=2)

lines(gdp$Germany~gdp$Year,col=pal[3],lwd=2)

mtext(side=4,at=gdp$Germany[length(gdp$Germany)],text=”Germany”,
col=pal[3],line=0.3,las=2)

lines(gdp$Britain~gdp$Year,col=pal[4],lwd=2)

mtext(side=4,at=gdp$Britain[length(gdp$Britain)],text=”Britain”,
col=pal[4],line=0.3,las=2)

lines(gdp$USA~gdp$Year,col=pal[5],lwd=2)

mtext(side=4,at=gdp$USA[length(gdp$USA)]-2,
text=”USA”,col=pal[5],line=0.3,las=2)


How it works…

We first read the gdp.txt data file using the read.table() function. Next we loaded the RColorBrewer color palette library and set our color palette pal to “Set1” (with five colors).

Before drawing the graph, we used the par() command to add extra space to the right margin, so that we have enough space for the labels. Depending on the size of the text labels you may have to experiment with this margin until you get it right. Finally, we set the box type (bty) to an L-shape (“l“) so that there is no line on the right margin. We can also set it to “c” if we want to keep the top line.

We used the mtext() function to label each of the lines individually in the right margin. The first argument we passed to the function is the side where we want the label to be placed. Sides (margins) are numbered starting from 1 for the bottom side and going round in a clockwise direction so that 2 is left, 3 is top, and 4 is right.

The at argument was used to specify the Y co-ordinate of the label. This is a bit tricky because we have to make sure we place the label as close to the corresponding line as possible. So, here we have used the last value of each line. For example, gdp$France[length(gdp$France) picks the last value in the France vector by using its length as the index. Note that we had to adjust the value for USA by subtracting 2 from its last value so that it doesn’t overlap the label for Canada.

We used the text argument to set the text of the labels as country names. We set the col argument to the appropriate element of the pal vector by using a number index. The line argument sets an offset in terms of margin lines, starting at 0 counting outwards. Finally, setting las to 2 rotates the labels to be perpendicular to the axis, instead of the default value of 1 which makes them parallel to the axis.

Sometimes, simply using the last value of a set of values may not work because the value may be missing. In that case we can use the second last value or visually choose a value that places the label closest to the line. Also, the size of the plot window and the proximity of the final values may cause overlapping of labels. So, we may need to iterate a few times before we get the placement right. We can write functions to automate this process but it is still good to visually inspect the outcome.

LEAVE A REPLY

Please enter your comment!
Please enter your name here