Skip to content
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

Legends duplicated when collected #170

Closed
robjhyndman opened this issue Apr 27, 2020 · 14 comments
Closed

Legends duplicated when collected #170

robjhyndman opened this issue Apr 27, 2020 · 14 comments

Comments

@robjhyndman
Copy link

This was working in R3.6.3, but not in R4.0.

library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp, col=gear))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear, col=gear))
(p1 | p2 ) +
  plot_layout(guides = "collect") & theme(legend.position = 'bottom')

Created on 2020-04-27 by the reprex package (v0.3.0)

@bling1000
Copy link

The same happened to me ! I updated my R version to 4.0.0 recently.

tomjemmett added a commit to The-Strategy-Unit/617_eol_project that referenced this issue Apr 30, 2020
temporary fix due to a bug in patchwork duplicating collected legends (thomasp85/patchwork#170)
@mikecuoco
Copy link

Same happened to me after I updated to R 4.0.0

@teng-gao
Copy link

teng-gao commented May 9, 2020

+1 My R is 3.6.1

@hexuebao
Copy link

Same happened to me after I updated to R 4.0.0

@MMJansen
Copy link

Anyone know if a solution is to be expected soon? If I can help in anyway, please let me know.

@llrs
Copy link

llrs commented May 18, 2020

No idea, but it would help if someone could find the source of the problem. At least then it would be easier to fix it (via PR or Thomas himself).

@thomasp85
Copy link
Owner

I will look into this within a week or two

@mcanouil
Copy link

mcanouil commented Jun 1, 2020

So I took a look into that issue and the behaviour of all.equal is not the same in R 4.0.0 on grobs (https://github.com/thomasp85/patchwork/blob/master/R/guides.R#L31).
Although, I don't know (yet) how to fix this.

library(patchwork)
library(ggplot2)
library(gtable)

p <- ggplot(iris, aes(Sepal.Length, Petal.Length, colour = Species)) +
  geom_point()

guides <- list(
  gtable_filter(ggplotGrob(p), "guide-box"), 
  gtable_filter(ggplotGrob(p), "guide-box")
)

unnamed <- lapply(guides, patchwork:::unname_grob)

i <- 2
j <- 1
res <- all.equal(unnamed[[i]], unnamed[[j]], check.names = FALSE, check.attributes = FALSE)
head(res)
#> [1] "Component \"grobs\": Component 1: Component 1: Component 1: Component 1: Component 2: Component 3: Component 3: Component 1: Component 2: Component 1: Component 2: Component 2: Component 1: Component 2: Component 9: 1 string mismatch"                                        
#> [2] "Component \"grobs\": Component 1: Component 1: Component 1: Component 1: Component 2: Component 3: Component 3: Component 1: Component 2: Component 2: Component 2: Component 2: Component 1: Component 2: Component 9: 1 string mismatch"                                        
#> [3] "Component \"grobs\": Component 1: Component 1: Component 1: Component 1: Component 2: Component 3: Component 3: Component 1: Component 2: Component 3: 1 string mismatch"                                                                                                         
#> [4] "Component \"grobs\": Component 1: Component 1: Component 1: Component 1: Component 2: Component 3: Component 3: Component 1: Component 2: Component 5: Component 1: Component 11: Component 3: Component 2: Component 2: Component 1: Component 2: Component 9: 1 string mismatch"
#> [5] "Component \"grobs\": Component 1: Component 1: Component 1: Component 1: Component 2: Component 3: Component 3: Component 1: Component 2: Component 5: Component 1: Component 11: Component 4: Component 2: Component 2: Component 1: Component 2: Component 9: 1 string mismatch"
#> [6] "Component \"grobs\": Component 1: Component 1: Component 1: Component 1: Component 2: Component 3: Component 3: Component 1: Component 2: Component 5: Component 1: Component 17: 1 string mismatch"

@thomasp85
Copy link
Owner

thanks - yeah, I suspected some change in all.equal() was the root cause

@mcanouil
Copy link

mcanouil commented Jun 2, 2020

For the ones more expert in grid than me, the challenge to solve the issue is to return TRUE with the following code (in R 4.0.0):

library("grid")
x <- grob()
y <- grob()
all.equal(x, y)
#> [1] "Component \"name\": 1 string mismatch"

EDIT:

library("grid")
x <- patchwork:::unname_grob(grob())
y <- patchwork:::unname_grob(grob())
all.equal(x, y)
#> [1] TRUE

Maybe unname_grob needs to be called recursively on the guides.

EDIT2: Following my previous reprex, I think the issue comes from the new unit class used in grid.

# R 4.0.0
x <- unnamed[[1]]$grobs[[1]]$grobs[[1]]$grobs[[2]]$children[[1]]$widths[[2]]
y <- unnamed[[2]]$grobs[[1]]$grobs[[1]]$grobs[[2]]$children[[1]]$widths[[2]]
all.equal(x, y, check.names = FALSE, check.attributes = FALSE)
#> [1] "Component 1: Component 2: Component 1: Component 2: Component 9: 1 string mismatch" 
# this is the location of the component '$name         : chr "GRID.text.509"',  in the sub element of classes "text", "grob" and "gDesc"
class(x)
#> [1] "unit"    "unit_v2"
class(y)
#> [1] "unit"    "unit_v2"
# R 3.6.3
x <- unnamed[[1]]$grobs[[1]]$grobs[[1]]$grobs[[2]]$children[[1]]$widths[[2]]
y <- unnamed[[2]]$grobs[[1]]$grobs[[1]]$grobs[[2]]$children[[1]]$widths[[2]]
all.equal(x, y, check.names = FALSE, check.attributes = FALSE)
#> [1] TRUE
class(x)
#> [1] "unit.arithmetic" "unit"
class(y)
#> [1] "unit.arithmetic" "unit"

@RobinM92
Copy link

RobinM92 commented Jun 9, 2020

Is there a workaround for now that we can use?

@mcanouil
Copy link

mcanouil commented Jun 9, 2020

Yes, to manually remove the duplicated legends.

library(patchwork)
library(ggplot2)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, colour = Species)) +
  geom_point()

p + p + plot_layout(guides = "collect")

(p + theme(legend.position = "none")) + p + plot_layout(guides = "collect")

@RobinM92
Copy link

RobinM92 commented Jun 9, 2020

Thanks, logical! To combine this with the legend positions new problems arise of course.
E.g.

library(patchwork)
library(ggplot2)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, colour = Species)) +
  geom_point()

(p + theme(legend.position = "none")) + p + plot_layout(guides = "collect") & theme(legend.position = "bottom")

image

vs

library(patchwork)
library(ggplot2)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, colour = Species)) +
  geom_point()

(p + theme(legend.position = "none")) + p + plot_layout(guides = "collect") & theme(legend.position = "bottom")

image

Both are not ideal, but if you define the position for the first legend and then remove the later ones it works as we want to:

library(patchwork)
library(ggplot2)
p <- ggplot(iris, aes(Sepal.Length, Petal.Length, colour = Species)) +
  geom_point()

(p + plot_layout(guides = "collect") & theme(legend.position = "bottom")) + (p + theme(legend.position = "none")) 

image

@mcanouil
Copy link

mcanouil commented Jun 9, 2020

Use guides to remove the legend then

library(patchwork)
library(ggplot2)

p <- ggplot(iris, aes(Sepal.Length, Petal.Length, colour = Species)) +
  geom_point()

p_theme <- p + theme(legend.position = "none")
p_guide <- p + guides(colour = "none")
p_theme + p + 
  plot_layout(guides = "collect") & theme(legend.position = "bottom")

p_guide + p + 
  plot_layout(guides = "collect") & theme(legend.position = "bottom")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants