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

drop = FALSE does not maintain unused factors #5996

Closed
brianmsm opened this issue Jul 11, 2024 · 1 comment
Closed

drop = FALSE does not maintain unused factors #5996

brianmsm opened this issue Jul 11, 2024 · 1 comment

Comments

@brianmsm
Copy link

brianmsm commented Jul 11, 2024

This problem is associated with the scale_fill_ and scale_color functions, including their manual versions, which contain the drop argument. What is happening is that if you have data where any of the factors are not in use, and you are using drop = FALSE, these factors appear in the legend, but without any associated color.

library(dplyr, warn.conflicts = FALSE)
library(ggplot2)

mtcars_tbl <- mtcars %>% 
  as_tibble() %>% 
  mutate(
    cyl = factor(cyl,
                 levels = c("4", "6", "8", "10"))
  ) 

mtcars_tbl %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl)) +
  scale_color_brewer(drop = FALSE, palette = "Dark2")

Created on 2024-07-11 with reprex v2.1.1

I understand that the operation of associated with drop = FALSE has had continuous changes over time (#4511 , #4619 , #4723 , #5214), so I have tried different iterations on the above issues, but none of them work.

library(dplyr, warn.conflicts = FALSE)
library(ggplot2)

mtcars_tbl <- mtcars %>% 
  as_tibble() %>% 
  mutate(
    cyl = factor(cyl,
                 levels = c("4", "6", "8", "10"))
  ) 


# Colors manuals
colors <- RColorBrewer::brewer.pal(n = 4, name = "Dark2")
colors
#> [1] "#1B9E77" "#D95F02" "#7570B3" "#E7298A"

# With scale_color_manual
mtcars_tbl %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl)) +
  scale_color_manual(
    values = colors,
    drop = FALSE
  ) +
  theme_bw()

# With limits
mtcars_tbl %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl)) +
  scale_color_manual(
    values = colors,
    drop = FALSE,
    limits = c("4", "6", "8", "10")
  ) +
  theme_bw()

# With limits and breaks
mtcars_tbl %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl)) +
  scale_color_manual(
    values = colors,
    drop = FALSE,
    breaks = c("4", "6", "8", "10"),
    limits = c("4", "6", "8", "10")
  ) +
  theme_bw()

# With limits on force
mtcars_tbl %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl)) +
  scale_color_manual(
    values = colors,
    drop = FALSE,
    limits = force
  ) +
  theme_bw()

# With limits on identity
mtcars_tbl %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl)) +
  scale_color_manual(
    values = colors,
    drop = FALSE,
    limits = identity
  ) +
  theme_bw()

Created on 2024-07-11 with reprex v2.1.1

The only way to get what I am looking for is to add an empty row where the missing factors are mentioned, and plot waiting for them to be eliminated.

library(dplyr, warn.conflicts = FALSE)
library(ggplot2)

mtcars_tbl <- mtcars %>% 
  as_tibble() %>% 
  mutate(
    cyl = factor(cyl,
                 levels = c("4", "6", "8", "10"))
  ) 

mtcars_tbl %>% 
  tibble::add_case(
    cyl = factor("10",
                 levels = c("4", "6", "8", "10"))
  ) %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl)) +
  scale_color_brewer(drop = FALSE, palette = "Dark2")
#> Warning: Removed 1 row containing missing values or values outside the scale range
#> (`geom_point()`).

Created on 2024-07-11 with reprex v2.1.1

However, I think it is not an ideal solution, since add_case() or bind_rows() may have limitations when you have tibbles with list or different data type.

packageVersion("ggplot2")
#> [1] '3.5.1'

Created on 2024-07-11 with reprex v2.1.1

@teunbrand
Copy link
Collaborator

This is a duplicate of #5728.

The solution is to add show.legend = TRUE to the points layer. This is mentioned in the documentation of the drop argument in ?discrete_scale:

Should unused factor levels be omitted from the scale? The default, TRUE, uses the levels that appear in the data; FALSE includes the levels in the factor. Please note that to display every level in a legend, the layer should use show.legend = TRUE.

library(dplyr, warn.conflicts = FALSE)
library(ggplot2)

mtcars_tbl <- mtcars %>% 
  as_tibble() %>% 
  mutate(
    cyl = factor(cyl,
                 levels = c("4", "6", "8", "10"))
  ) 

mtcars_tbl %>% 
  ggplot(aes(mpg, wt)) +
  geom_point(aes(colour = cyl), show.legend = TRUE) +
  scale_color_brewer(drop = FALSE, palette = "Dark2")

Created on 2024-07-12 with reprex v2.1.1

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

2 participants