Monthly Archives: February 2014

Back to basics: Plotting two y-axes on the same plot

After another lengthy hiatus (PhD work isn’t all fun and games!), I’m back and decided to make a post about a particular problem that I keep coming across when I’m making my figures.  Many times, you want to plot two variables, say y1 and y2, on the same graph, against another variable (say x1), but the y variables have very different units/scales/etc.  This comes up a lot with time series data, when y1 and y2 span the same time but are measured on different days and/or represent different variables.

To overcome this issue, we can take advantage of a few built-in features of R, namely the par(new=T). This allows you to create a new plotting device (as R likes to call plots), without erasing the previous plot. Essentially what we are going to do is plot two figure on top of one another, but with a different y axis range specified in the second plot() function.

Let’s work through the code:

First we create or dummy data. For this example, both y1 and y2 are assumed to have the same x variable (“dates”):

### Create Dummy Data
dates <- seq.dates("1/1/2014", "3/1/2014", by = "days")
y1 <- seq(1, length(dates), 1) + rnorm (length(dates), mean =1, sd= 0.5)
y2 <- rev(seq(1+100, length(dates)+100, 1)) + rnorm (length(dates),
                                                     mean =1, sd= 0.5)

Next we calculate the range for our dates and y1 variable and plot it up. Note we are NOT actually plotting axes or points at this time (ie. plot(..., type = "n", ...)):

par(mar = c(5,4,3,4), mgp = c(1,0.3,0), font=2,
    font.axis = 2, tck = 0.01)
date.range = sort(dates)
y1.range <- c(min(floor(range(y1))), max(ceiling(range(y1))))
plot(dates, y1, ylim = c(y1.range), ann = F, axes = F, type = "n",
     xlim = c(date.range[1], date.range[length(date.range)]))

We then add the box around the plotting region, and both the x axis (which are dates) and the y axis. Wa also add the points (finally!):

### Add axes for dates and y1
box()
axis.Date(1, at = seq(date.range[1], date.range[length(date.range)],
                      4), format="%m/%d/%y", labels = F, tck = 0.01)
label=seq(date.range[1], date.range[length(date.range)],4)
x.len=length(label)
text(x=label[1:x.len],  par("usr")[3]-1.35, srt = 45, adj = 1,
     labels = label, xpd = T, cex.axis  = 1.5)
axis(2, at = round(seq(y1.range[1], y1.range[2], 5)), las = 2,
     cex.axis = 1.25)
mtext(side = 2, "Y variable 1", at = 35, line = 2.3, cex = 1.5,
      col = "black")
mtext(side = 1, "Date", cex = 1.5, line = 3.2)

### Add Y1 points
points(dates, y1, pch = 20, cex = 1.5, col = "black")

Finally, we can add the second variable. But first don’t forget to add par(new=T) BEFORE you add the second plot! Also note that the second plot uses the y2 variable, not y1, and the ylim is set to match the range of the y2 variable:

### Now we add second Y axis and points----------------------------------------#
par(new=T)
y2.range <- c(min(floor(range(y2))), max(ceiling(range(y2))))
plot(dates, y2, ylim = c(y2.range), ann = F, axes = F, type = "n",
     xlim = c(date.range[1], date.range[length(date.range)]))
points(dates, y2, col="red", pch = 20)

### Add 2nd y axis
axis(4, at = round(seq(y2.range[1], y2.range[2], 5)), las = 2,
     cex.axis = 1.25,
     col = "red", col.axis = "red")
mtext(side = 4, "Y variable 2", at = 135, line = 2.3, cex = 1.5,
      col = "red")
Example of xy-plot with two y variables that span different ranges

Example of xy-plot with two y variables that span different ranges

The above case works when both y1 and y2 have the same x variable. But, it’s not hard to imagine that y1 and y2 were measured on different days. This is actually quite easy to handle. The only difference from the above code is that when we add the points for the second y variable (in the second points() function above), we would use a the correct x variable. To add a 3rd variable to the plot above, which was measured on different days:

### add third Y variable----------------------------------------#
y3 <- rep(mean(y2),20) + rnorm (20, mean =10, sd= 5)
y3.dates <- dates[seq(2, 40, 2)] + 13
par(new=T)
y3.range <- c(min(floor(range(y3))), max(ceiling(range(y3))))
plot(dates, y3, ylim = c(y3.range), ann = F, axes = F, type = "n",
     xlim = c(date.range[1], date.range[length(date.range)]))
points(y3.dates, y3, col="magenta", pch = 20)
lines(y3.dates, y3, col="magenta", lwd=2, pch = 20)

Here the final product!

Added a 3rd y variable, but with a different length x variable.

Added a 3rd y variable, but with a different length x variable.