ggplot:在图例中删除NA因子水平

41

如何在图例中省略因子的NA水平?

Pesky NA legend value.....

我从nycflights13数据库创建了一个新的连续变量tot_delay,然后创建了一个具有4个水平的因子delay_class。当我绘制图形时,过滤掉NA值,但它们仍然出现在图例中。这是我的代码:

library(nycflights13); library(ggplot2)

flights$tot_delay = flights$dep_delay + flights$arr_delay
flights$delay_class <- cut(flights$tot_delay,                                   
                           c(min(flights$tot_delay, na.rm = TRUE), 0, 20 , 120,
                             max(flights$tot_delay, na.rm = TRUE)),   
                           labels = c("none", "short","medium","long"))     

filter(flights, !is.na(tot_delay)) %>% 
  ggplot() +
  geom_bar(mapping = aes(x = carrier, fill = delay_class), position = "fill")
4个回答

68

这个例子并不能很好地说明问题(当然,应该追踪和消除意外的NA值),但这是谷歌上的首要结果,因此需要注意的是,在scale_XXX_XXX中现在有一个选项可以通过设置na.translate = F来防止在图例中显示NA级别。例如:

# default    
ggplot(data = data.frame(x = c(1,2,NA), y = c(1,1,NA), a = c("A","B",NA)),
           aes(x, y, colour = a)) + geom_point(size = 4)

输入图像说明

# with na.translate = F    
ggplot(data = data.frame(x = c(1,2,NA), y = c(1,1,NA), a = c("A","B",NA)),
           aes(x, y, colour = a)) + geom_point(size = 4) + 
           scale_colour_discrete(na.translate = F)

enter image description here

这在ggplot2 3.1.0中可行。


34

你有一个数据点,其中delay_classNA,但tot_delay不是。你的过滤器没有捕捉到这个点。将您的代码更改为:

filter(flights, !is.na(delay_class)) %>% 
  ggplot() +
  geom_bar(mapping = aes(x = carrier, fill = delay_class), position = "fill")

起作用:

enter image description here

或者,如果你一定要那个额外的点,你可以按照以下方式覆盖fill图例:

filter(flights, !is.na(tot_delay)) %>% 
  ggplot() +
  geom_bar(mapping = aes(x = carrier, fill = delay_class), position = "fill") +
  scale_fill_manual( breaks = c("none","short","medium","long"),
                    values = scales::hue_pal()(4) )

更新:正如@gatsky的回答中指出的那样,所有离散比例也包括na.translate参数。该特性实际上从ggplot 2.2.0版本开始存在;我在发布回答时并不知道这一点。为了完整起见,它在原问题中的使用方式如下:

filter(flights, !is.na(tot_delay)) %>% 
  ggplot() +
  geom_bar(mapping = aes(x = carrier, fill = delay_class), position = "fill") +
  scale_fill_discrete(na.translate=FALSE)

1
啊!太简单了。我一开始就应该按照delay_class进行过滤。由于图表上有一个额外的点,很难在图表上看到NA,尽管它在图例中显示。你的scale_fill_manual()方法覆盖图例非常好用,我投你一票。 - Rich Pauloo

2

如果你知道NA的颜色是grey50,你也可以使用scale_color_manualscale_fill_manual。这样你就可以指定所需的values和只有breaks的绘图项。这里是一些可重复的代码(数据来自@gatsky):

df = data.frame(x = c(1,2,NA), y = c(1,1,NA), a = c("A","B",NA))
library(ggplot2)
ggplot(data = df, aes(x, y, colour = a)) + 
  geom_point(size = 4) +
  scale_color_manual(values = c("red", "blue", "grey50"),
                     breaks = c("A", "B"))

如您所见,通过在values中指定所有颜色,并仅对A和B指定breaks,将从图例中去除NA值。
如果您想在图例中使用标准颜色,可以像这样使用包中的函数:
df = data.frame(x = c(1,2,NA), y = c(1,1,NA), a = c("A","B",NA))
library(ggplot2)
library(scales)
ggplot(data = df, aes(x, y, colour = a)) + 
  geom_point(size = 4) +
  scale_color_manual(values = c(hue_pal()(2), "grey50"),
                     breaks = c("A", "B"))


最后,使用来自 OP 的数据与像下面这样的 scale_fill_manual 示例:
library(nycflights13) 
library(ggplot2)
library(dplyr)
library(scales)

filter(flights, !is.na(tot_delay)) %>% 
  ggplot() +
  geom_bar(mapping = aes(x = carrier, fill = delay_class), position = "fill") +
  scale_fill_manual(values = c(hue_pal()(4), "grey50"),
                    breaks = c("none", "short", 'medium', 'long'))

使用reprex v2.0.2于2023年3月25日创建


1

我喜欢 @Artem 上面提到的方法,即找出数据框中为什么存在 NA 值的原因。然而有时候你知道存在 NA 值,而且你只想把它们排除在外。这种情况下,简单地使用 'na.omit' 应该就可以了:

na.omit(flights) %>% ggplot() +
geom_bar(mapping = aes(x = carrier, fill = delay_class), position = "fill")

3
问题在于它会删除所有包含任何列中NA值的行,而不仅仅是我在用ggplot填充的那一列。这可能会过滤掉一些需要保留的观察结果。 - Rich Pauloo
1
@RichPauloo -- 你的问题摘自上文,是“如何轻松地从图例中省略NA值?”如果你因为某种原因而难以找到导致问题的变量或变量,则使用'na.omit'将是方便的。然而,正如我在回复中提到的那样,我*始终鼓励人们弄清楚他们为什么有NA值。所以,这里有一个问题要问你(因为在发布之前我检查了我的*代码)。如果您使用'na.omit(flights)'与'filter(flights, !is.na(tot_delay))'有什么不同?在航班文件/案例/行中的差异是什么? - Woodstock
区别在于,如果您使用不同的数据集,则可能会意外丢失使用na.omit()删除缺失值的数据。dplyr方法更加健壮。 - WojciechF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接