Skip to content

Guides with duplicate labels do not get merged properly #3573

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
MartinEarle opened this issue Oct 16, 2019 · 6 comments · Fixed by #3599
Closed

Guides with duplicate labels do not get merged properly #3573

MartinEarle opened this issue Oct 16, 2019 · 6 comments · Fixed by #3599
Labels
bug an unexpected problem or unintended behavior guides 📏

Comments

@MartinEarle
Copy link

I have come across a small issue with manually selecting colour and linetype for my data. I want to manually select values for these aesthetics and relabel them. In this instance, I am showing the "treatment" from a factorial experiment with the label, and an additional factor with the colour. The linetype distinguishes the experiment from the control. Below is my code:

ggplot(df,aes(date,mean))+ 
  geom_line(size=0.5,aes(linetype=sample_name,colour=sample_name))+
  theme_classic()+
  theme(legend.margin=margin(0,0,0,0),legend.box.margin=margin(0,-10,-8,-10),legend.text=element_text(size=11),legend.position="top")+
  scale_x_date(expand=c(0,0),labels=date_format("%m/%d"),breaks = seq.Date(as.Date(date_min),as.Date(date_max),
                                                                              by = "month"),limits = as.Date(c("2018-1-1","2018-9-26")))+
  scale_y_continuous(limits=c(6,9),expand = c(0,0),breaks = seq(6,9.,0.5))+
  scale_colour_manual(labels = c("A", "B", "C", "D", "E", "F"),breaks=c("Inflow","Filter 3","Filter 7","Filter 8", "Filter 9", "Filter 10"),values = c('Black','#004f71','#4c839b','#99b8c6','#C0504D','#d28482'))+
  scale_linetype_manual(labels = c("A", "B", "C", "D", "E", "F"),breaks=c("Inflow","Filter 3","Filter 7","Filter 8", "Filter 9", "Filter 10"),values=c("dashed","solid","solid","solid","solid","solid"))+
  labs(color="", linetype="")+
  xlab("Date")+ylab("Data")

This results in the following plot:

Ex1

As you can see, the labels for C & D are both duplicated. This does not occur if I rename the second "C" and "D" to "E" and "F", as shown below, or if I hide one of the guides (not shown).

Ex2

I can provide some data to recreate this, if desired.

Thank you!

@paleolimbot
Copy link
Member

Welcome @MartinEarle!

I can't seem to reproduce this...perhaps you could pair down your example and render it using the reprex package? The reprex I was using to try to replicate your error is here:

library(ggplot2)

ggplot(mpg, aes(cty, hwy, col = drv, linetype = drv)) +
  geom_line() +
  scale_colour_manual(
    breaks = c("4", "f", "r"),
    labels = c("4", "f", "r"),
    values = c("red", "blue", "green")
  ) +
  scale_linetype_manual(
    breaks = c("4", "f", "r"),
    labels = c("4", "f", "r"),
    values = c("dashed", "solid", "solid")
  ) +
  labs(color = "", linetype = "")

@MartinEarle
Copy link
Author

MartinEarle commented Oct 17, 2019

Hi @paleolimbot!

Sorry, I did not understand how Reprex worked before, but I have now recreated my issue now with the mpg dataset and have copied it below:

library(ggplot2)
#> Warning: package 'ggplot2' was built under R version 3.6.1

mpg$class <- factor(mpg$class, levels = c("pickup", "midsize","compact", "subcompact",  "minivan",  "suv", "2seater"))

ggplot(subset(mpg, class != "2seater"), aes(cty, hwy, col = class, linetype = class)) +
  geom_line() +
  scale_colour_manual(
    breaks = c("pickup", "midsize","compact", "subcompact",  "minivan",  "suv"),
    labels = c("pickup","medium", "small", "small",  "large", "large"),
    values = c("red", "blue", "green", "black", "yellow", "orange")
  ) +
  scale_linetype_manual(
    breaks = c("pickup", "midsize","compact", "subcompact",  "minivan",  "suv"),
    labels = c("pickup","medium", "small", "small",  "large", "large"),
    values = c("dashed", "solid", "solid", "solid", "solid", "solid")
  ) +
  theme(legend.position = "top")+
  labs(color = "", linetype = "")

Created on 2019-10-17 by the reprex package (v0.3.0)

I am using R 3.6.0.

@paleolimbot
Copy link
Member

Just simplifying this a tiny bit...the determining factor looks to be that there are two manual scales that are trying to be merged where any(breaks != labels).

library(ggplot2)

breaks <- c("pickup", "midsize","compact", "subcompact",  "minivan",  "suv", "2seater")
labels <- c("pickup","medium", "small", "small",  "large", "large", "2seater")

ggplot(mpg, aes(cty, hwy, col = class, linetype = class)) +
  geom_line() +
  scale_colour_manual(
    breaks = breaks,
    labels = labels,
    values = c("red", "blue", "green", "black", "yellow", "orange", "purple")
  ) +
  scale_linetype_manual(
    breaks = breaks,
    labels = labels,
    values = c("dashed", "solid", "solid", "solid", "solid", "solid", "solid")
  )

Created on 2019-10-17 by the reprex package (v0.2.1)

@paleolimbot paleolimbot added bug an unexpected problem or unintended behavior guides 📏 labels Oct 17, 2019
@paleolimbot
Copy link
Member

Upon closer inspection, it looks like it's a general problem with duplicate labels and how guides are merged (using .$labels as an implicit key for merge() doesn't work when there are duplicates):

ggplot2/R/guide-legend.r

Lines 232 to 233 in 115c396

guide_merge.legend <- function(guide, new_guide) {
guide$key <- merge(guide$key, new_guide$key, sort = FALSE)

guide1 <- list(
  key = tibble::tibble(
    colour = 1:4, 
    .label = c("low", "low", "high", "high")
  )
)

guide2 <- list(
  key = tibble::tibble(
    linetype = 1:4, 
    .label = c("low", "low", "high", "high")
  )
)

ggplot2:::guide_merge.legend(guide1, guide2)$key
#>   .label colour linetype
#> 1    low      1        1
#> 2    low      1        2
#> 3    low      2        1
#> 4    low      2        2
#> 5   high      3        3
#> 6   high      3        4
#> 7   high      4        3
#> 8   high      4        4

Created on 2019-10-17 by the reprex package (v0.2.1)

We could (1) change the way merging happens (cbind() should work, given that the breaks and labels are hashed to determine if the guides ever get merged), or (2) enforce unique labels.

As a workaround, one could recode the values before they get to ggplot2 (e.g., using fct_recode()).

@paleolimbot paleolimbot changed the title Multiple scale_xxx_manual do not properly label in legend with duplicate labels. Guides with duplicate labels do not get merged properly Oct 17, 2019
@clauswilke
Copy link
Member

Please don't enforce unique labels. There are valid cases for duplicated labels, e.g. making multiple labels equal to "" to zero them out. Wouldn't the correct approach merge on breaks rather than labels? Breaks have to be unique by definition (I think).

paleolimbot added a commit that referenced this issue Nov 4, 2019
* add tests for guide merging

* fix guide merging when there are duplicated labels
@lock
Copy link

lock bot commented May 5, 2020

This old issue has been automatically locked. If you believe you have found a related problem, please file a new issue (with reprex) and link to this issue. https://reprex.tidyverse.org/

@lock lock bot locked and limited conversation to collaborators May 5, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug an unexpected problem or unintended behavior guides 📏
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants