Search code examples
rggplot2plotbar-chartggh4x

Multiple Legends in ggplot2/ggh4x do interfere with each other


I have a rather complex plot that requires multiple legends that I create with ggh4x::scale_listed. Everything works fine but the second overlay barchart creates an additional legend for the fill variable.

target: IN OUT

Is there an other way to solve this than my very hacky solution to basically "hide" the unneeded legend?

Note that my actual plot is much more complex and I would like to keep the general structure, including the custom legends.

library(ggplot2)
library(ggh4x)
my_data1 <- data.frame(case = "UK",
                       target = rep(c("IN", "OUT"), 5),
                       values = 1:10,
                       step = as.character(rep(1:2, 5)),
                       label = rep(c("A", "B"), 5))

p <- ggplot() +
  geom_bar(data = my_data1,
           position = position_stack(reverse = TRUE),
           mapping = aes(x = step, 
                         y = values, 
                         fill = target,
                         my_target_scale = target),
           stat = "identity",
           na.rm = TRUE) +
  
  # overwite prior data
  geom_bar(data = data.frame(step = 1:2,
                             purpose = "TOTAL",
                             values = c(20, 40)),
           
           # do not inherit easthetics
           inherit.aes = FALSE,
           mapping = aes(x = step,
                         y = values, 
                         # doesnt work
                         # color = purpose,
                         my_total_scale = purpose),
           show.legend = TRUE,
           position = "stack", 
           stat = "identity",
           # doesnt work
           # color = "black", 
           fill = NA,
           na.rm = TRUE) +
  ggh4x::scale_listed(scalelist = list(
    scale_fill_manual(aesthetics = "my_target_scale",
                      name = "GROUP:",
                      values = c("IN" = "red",
                                 "OUT" = "green"),
                      breaks = c("IN", "OUT"),
                      labels = c("Incoming", "Outgoing")
    ),
    scale_color_manual(aesthetics = "my_total_scale",
                       name = "Label:",
                       values = c("TOTAL" = "black"), 
                       breaks = c("TOTAL"),
                       labels = c("EVErYTHING")
    )
    # very hacky "solution"
    # ,scale_color_manual(aesthetics = "fill",
    #                    name = "",
    #                    values = c("IN", "OUT"), 
    #                    breaks = c("IN"),
    #                    labels = c(""))
  ),
  replaces = c("fill", 
               "color"
               # part of the hacky solution
               # ,"fill"
  )) +
  theme_bw() +
  theme(panel.spacing = unit(.01, "lines"),
        legend.position = "bottom",
        legend.box = "horizontal")
#> Warning: Ignoring unknown aesthetics: my_target_scale
#> Warning: Ignoring unknown aesthetics: my_total_scale

Created on 2021-10-06 by the reprex package (v2.0.0)


Solution

  • If I understand the question correctly, you don't want the legend that has the 'target: -in -out' bit? What about if you just omit the fill = target line in the first layer?

    library(ggplot2)
    library(ggh4x)
    my_data1 <- data.frame(case = "UK",
                           target = rep(c("IN", "OUT"), 5),
                           values = 1:10,
                           step = as.character(rep(1:2, 5)),
                           label = rep(c("A", "B"), 5))
    
    ggplot() +
      geom_bar(data = my_data1,
               position = position_stack(reverse = TRUE),
               mapping = aes(x = step, 
                             y = values, 
                             my_target_scale = target),
               stat = "identity",
               na.rm = TRUE) +
      
      # overwite prior data
      geom_bar(data = data.frame(step = 1:2,
                                 purpose = "TOTAL",
                                 values = c(20, 40)),
               
               # do not inherit easthetics
               inherit.aes = FALSE,
               mapping = aes(x = step,
                             y = values, 
                             my_total_scale = purpose),
               show.legend = TRUE,
               position = "stack", 
               stat = "identity",
               fill = NA,
               na.rm = TRUE) +
      ggh4x::scale_listed(scalelist = list(
        scale_fill_manual(aesthetics = "my_target_scale",
                          name = "GROUP:",
                          values = c("IN" = "red",
                                     "OUT" = "green"),
                          breaks = c("IN", "OUT"),
                          labels = c("Incoming", "Outgoing")
        ),
        scale_color_manual(aesthetics = "my_total_scale",
                           name = "Label:",
                           values = c("TOTAL" = "black"), 
                           breaks = c("TOTAL"),
                           labels = c("EVErYTHING")
        )
      ),
      replaces = c("fill", 
                   "color"
      )) +
      theme_bw() +
      theme(panel.spacing = unit(.01, "lines"),
            legend.position = "bottom",
            legend.box = "horizontal")
    #> Warning: Ignoring unknown aesthetics: my_target_scale
    #> Warning: Ignoring unknown aesthetics: my_total_scale
    

    Created on 2021-10-07 by the reprex package (v2.0.0)