Search code examples
rggplot2overlaystacked-bar-chartgeom-point

Overlay dots on a stacked bar chart and include a legend key on blank background for the dots


I need a stacked bar chart showing the diamonds by cut and clarity with count on the primary y-axis (left-hand-side) and the average price on the secondary x-axis (right-hand-side). How do I include a legend item for the dots? The desired plot should have another key for the dots: just a black dot on a white background and reads "average price".

A dummy df for the dots

library(dplyr)

price.avg <- diamonds %>%
   group_by(cut, color) %>%
   summarise(price = mean(price))

Main plot with double y-axes

library(ggplot2)

diamonds %>%
ggplot() +
geom_bar(aes(x = cut, fill = clarity)) +
facet_wrap(~ color, scale="free_y") +
ylab("Count") +
  xlab("Cut") + 
  geom_point(data = price.avg, aes(x = cut, y = price)) +
    scale_y_continuous(sec.axis = sec_axis(~. * 1, name = "price"))

enter image description here

Using geom_point(data = price.avg, aes(x = cut, y = price), show.legend = TRUE) put a dot in each of the legend keys, which I don't want to have.

enter image description here

Thanks for stopping by.


Solution

  • You can include a dummy variable which has the same value in your price.avg dataframe, then use aes(color) on the dummy, and set scale_color_manual.

    library(dplyr)
    library(ggplot2)
    
    price.avg <- diamonds %>%
      group_by(cut, color) %>%
      summarise(price = mean(price), dummy = "1")
    
    diamonds %>%
      ggplot() +
      geom_bar(aes(x = cut, fill = clarity)) +
      facet_wrap(~ color, scale="free_y") +
      ylab("Count") +
      xlab("Cut") + 
      geom_point(data = price.avg, aes(x = cut, y = price, col = dummy)) +
      scale_y_continuous(sec.axis = sec_axis(~. * 1, name = "price")) +
      scale_color_manual(name = "", label = "Average price", values = "black")
    

    Or simply add a fake aes(color = "") in geom_point, and set scale_color_manual

    diamonds %>%
      ggplot() +
      geom_bar(aes(x = cut, fill = clarity)) +
      facet_wrap(~ color, scale="free_y") +
      ylab("Count") +
      xlab("Cut") + 
      geom_point(data = price.avg, aes(x = cut, y = price, col = "")) +
      scale_y_continuous(sec.axis = sec_axis(~. * 1, name = "price")) + 
      scale_color_manual(name = "", label = "Average price", values = "black")
    

    Output