3

I'm trying to create a ggplot2 graph showing a bar graph and a line graph overlaying each other. In excel this would be done by adding a second axis.

The x axis represents product type, the y values of the bar graph should represent revenue, and the line graph I want to represent profit margin as a percentage. The value of the line graph and the bar chart should be independent of each other, i.e. there is no such relationship.

require(ggplot2) df <- data.frame(x = c(1:5), y = abs(rnorm(5)*100)) df$y2 <- abs(rnorm(5)) ggplot(df, mapping= aes(x=as.factor(`x`), y = `y`)) + geom_col(aes(x=as.factor(`x`), y = `y`),fill = 'blue')+ geom_line(mapping= aes(x=as.factor(`x`), y = `y`),group=1) + geom_label(aes(label= round(y2,2))) + scale_y_continuous() + theme_bw() + theme(axis.text.x = element_text(angle = 20,hjust=1)) 

enter image description here

The image above produces almost what I want. However, the scaling is incorrect - I would need the 1.38 and 0.23 value to be ordered by magnitude, i.e. the point 0.23 should show below 1.38. I am also not sure how to add another axsis on the right hand side.

2
  • These type of graphs with two axis scales are usually frowned upon as bad practice: see here and here. Commented Dec 24, 2018 at 11:53
  • @VY - I think they're quite useful and especially for this it is quite easy to read and not misleading. Is there no way to get this achieved? Commented Dec 24, 2018 at 11:59

1 Answer 1

5

Starting with version 2.2.0 of ggplot2, it is possible to add a secondary axis - see this detailed demo. Also, some already answered questions with this approach: here, here, here or here. An interesting discussion about adding a second OY axis here.

The main idea is that one needs to apply a transformation for the second OY axis. In the example below, the transformation factor is the ratio between the max values of each OY axis.

# Prepare data library(ggplot2) set.seed(2018) df <- data.frame(x = c(1:5), y = abs(rnorm(5)*100)) df$y2 <- abs(rnorm(5)) # The transformation factor transf_fact <- max(df$y)/max(df$y2) # Plot ggplot(data = df, mapping = aes(x = as.factor(x), y = y)) + geom_col(fill = 'blue') + # Apply the factor on values appearing on second OY axis geom_line(aes(y = transf_fact * y2), group = 1) + # Add second OY axis; note the transformation back (division) scale_y_continuous(sec.axis = sec_axis(trans = ~ . / transf_fact, name = "Second axis")) + geom_label(aes(y = transf_fact * y2, label = round(y2, 2))) + theme_bw() + theme(axis.text.x = element_text(angle = 20, hjust = 1)) 

enter image description here

But if you have a particular wish for the one-to-one transformation, like, say value 100 from Y1 should correspond to value 1 from Y2 (200 to 2 and so on), then change the transformation (multiplication) factor to 100 (100/1): transf_fact <- 100/1 and you get this:

enter image description here

The advantage of transf_fact <- max(df$y)/max(df$y2) is using the plotting area in a optimum way when using two different scales - try something like transf_fact <- 1000/1 and I think you'll get the idea.

Sign up to request clarification or add additional context in comments.

4 Comments

Hi, thanks for your answer. Could you explain what you mean by the transformation factor? How did you think of using that particular one?
Is just a constant that you use to multiply the values from one scale to transform then to another scale. Is a one-to-one transformation as required by sec_axis (see ?sec_axis). Here is another demo. You can try other values for the factor, not only the max(y1)/max(y2) ratio (this one was sure to work for a fast example).
I updated my answer, adding an extra example, that maybe clarifies the importance of the transformation/multiplication factor.
Thanks Valentin, this was very helpful! I'm glad that it's possible to do this as I'm trying to recreate some of our excel features in R and one of them was graphs such as this

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.