Search code examples
rggplot2legendscatter-plotaxis

How do I seperate two overlapping legends for a dual axis ggplot, one legend representing variables by shape and another representing groups by color?


I have the following dataframe as a reprex:

weather <- data.frame(time= c("12:19","12:15","13:20","13:22","16:19"), sensor_rh = c("A","B","C","A","A"), sensor_t = c("A","B","C","C","A"), t = c(30.4,29.4,29.4,30.3,28.9), rh = c(66.8,67.1,67.6,67.1,68.0))

I want the plot to show me two different shapes, distinguishing the rh and t variables, and three colors, distinguishing the sensors for rh and t.

I am managing to plot the points as desired in a scatterplot with a primary axis for the variable relative humidity (rh) and a secondary axis for the variable temperature (temp). However, as an example, the following code does not give me two seperate legends; one for the variables and one for the sensors.

scale <- 2
ggplot(weather, aes(x = time)) +
  geom_point(aes(y = rh, color = sensor_rh), shape = 1) +
  geom_point(aes(y = t*scale, color = sensor_t), shape = 3) +
  scale_color_manual(name = "sensor", values = c("A" = "red", "B" = "blue", "C" = "green"), guide = guide_legend(title = "Sensor", title.position = "top", title.hjust = 0.5 )) +
  scale_shape_manual(name = "variable", values = c("rh" =1, "t" = 3), guide = guide_legend(title = "variable", title.position = "top", title.hjust = 0.5)) +
  scale_y_continuous(name = "rh(%)", sec.axis = sec_axis(~./scale, name = "t(°C)")) + 
  labs(x= "time")

scatterplot with dual axis only one (overlapping)legend

Can someone help me figure out how to plot the desired graph? I feel I have played around with the code in so many ways, with questions that seem almost duplicate to mine, but mostly I either do not get correctly plotted scatter points or I dont get a seperate legend.

It seems that the shapes are overlapping for the two variables.

I know there is controversy in the use of dual axis, but please lets not get into that discussion here.

Thank you in advance!


Solution

  • Instead of setting the shape as an argument map on the shape aesthetic, i.e. inside aes(). As you already included scale_shape_manual you could use shape="rh" and shape="t" in your geom_point layers:

    library(ggplot2)
    
    scale <- 2
    ggplot(weather, aes(x = time)) +
      geom_point(aes(y = rh, color = sensor_rh, shape = "rh")) +
      geom_point(aes(y = t * scale, color = sensor_t, shape = "t")) +
      scale_color_manual(
        name = "sensor", values = c("A" = "red", "B" = "blue", "C" = "green"),
        guide = guide_legend(title = "Sensor", title.position = "top", title.hjust = 0.5)
      ) +
      scale_shape_manual(
        name = "variable", values = c("rh" = 1, "t" = 3),
        guide = guide_legend(title = "variable", title.position = "top", title.hjust = 0.5)
      ) +
      scale_y_continuous(name = "rh(%)", sec.axis = sec_axis(~ . / scale, name = "t(°C)")) +
      labs(x = "time")