ggplot2 geom_bar位置错误

10
我正在使用geom_bar中的..count..转换,并且当我的某些类别计数很少时,会收到警告position_stack需要不重叠的x间隔
最好使用一些模拟数据来解释这个问题(我的数据涉及方向和风速,我保留了相关名称)。
#make data
set.seed(12345)
FF=rweibull(100,1.7,1)*20  #mock speeds
FF[FF>60]=59
dir=sample.int(10,size=100,replace=TRUE) # mock directions

#group into speed classes
FFcut=cut(FF,breaks=seq(0,60,by=20),ordered_result=TRUE,right=FALSE,drop=FALSE)

# stuff into data frame & plot
df=data.frame(dir=dir,grp=FFcut)
ggplot(data=df,aes(x=dir,y=(..count..)/sum(..count..),fill=grp)) + geom_bar()

这个很好用,生成的图表显示了按照速度分组后方向频率。值得注意的是,最少计数的速度分类(此处为"[40,60)")将有5个计数。

每个大小为20的三个类别

然而,更多的速度分类会导致警告。例如,使用...

FFcut=cut(FF,breaks=seq(0,60,by=15),ordered_result=TRUE,right=FALSE,drop=FALSE)
 

计数最少的速度类别(现在是“ [45,60)”)仅有3个计数,ggplot2会发出警告: position_stack需要具有不重叠的x间隔 图表将显示该类别数据分散在x轴上。如下图所示: 四个大小为15的类别,现在最后一个只有三个元素未添加到相应的条形上 看起来,要使其正常工作,组的最小大小应为5。
我想知道这是stat_bingeom_bar使用的)中的特性还是错误,或者我是否过度滥用了geom_bar。同时,如果您有任何关于如何解决此问题的建议,我将不胜感激。
谨此。

也许是这样的?ggplot(data=df,aes(dir, fill=grp)) + geom_histogram(aes(y=(..count..)/sum(..count..))) - Roman
2个回答

17

这是因为df $ dir是数字型的,所以 ggplot 对象会认为x轴是连续的,并且美学参数group是基于唯一已知的离散变量 (fill = grp)。

因此,当在grp=[45,60)中没有太多的dir值时,ggplot就会混淆每个条形图应该有多宽。如果我们将图表分成不同的面板,这会更加明显:

ggplot(data=df,
            aes(x=dir,y=(..count..)/sum(..count..),
                fill = grp)) + 
  geom_bar() + 
  facet_wrap(~ grp)

facet view

> for(l in levels(df$grp)) print(sort(unique(df$dir[df$grp == l])))
[1]  1  2  3  4  6  7  8  9 10
[1]  1  2  3  4  5  6  7  8  9 10
[1]  2  3  4  5  7  9 10
[1] 2 4 7

我们还可以手动检查排序后的df$dir值之间的最小差异,前三个grp值的差异为1,而最后一个为2。默认的柱形图宽度因此更宽。

以下解决方案都应该能够达到相同的结果:

1. 明确指定在geom_bar()中所有组使用相同的条形宽度

ggplot(data=df,
       aes(x=dir,y=(..count..)/sum(..count..),
           fill = grp)) + 
  geom_bar(width = 0.9)

2. 在将 dir 传递给 aes(x = ...) 之前,将其转换为分类变量:

ggplot(data=df,
       aes(x=factor(dir), y=(..count..)/sum(..count..),
           fill = grp)) + 
  geom_bar()

3. 指定group参数应基于df$dirdf$grp两者:

ggplot(data=df,
       aes(x=dir,
           y=(..count..)/sum(..count..),
           group = interaction(dir, grp),
           fill = grp)) + 
  geom_bar()

plot


非常感谢。顺便提一下,我的原始代码中有一个分类变量dir,但我正在处理的图表更加复杂,还添加了coord_polar()。在圆形图中,x轴上的离散性在我添加其他层到图表时会引起问题。拥有连续的x轴可以解决这些问题,但也许这个解决方案过早了..... - stuttungr
旁注:我在尝试向Plotly传递标签时遇到了这个问题。虽然通过指定宽度可以解决ggplot(如上面的解决方案#1),但传递给Plotly的标签变成了NA。解决方案#3完美地解决了这个问题。 - Dubukay

1

这并没有直接解决问题,因为我也不知道重叠值的情况,但这是一个基于dplyr的解决方法,可能会更加灵活。

我们可以不再依赖于geom_bar通过..count../sum(..count..)来获取分布比例,而是可以自己预先计算这些比例,然后绘制柱状图。我个人喜欢对我的数据和绘图有这种类型的控制。

首先,我将dirFF放入一个数据框/tbl_df中,并对FF进行切割。然后使用count函数让我按dirgrp分组数据,并统计每个变量组合的观测值数量,然后计算每个n相对于所有n之和的占比。我使用的是geom_col,它类似于geom_bar,但当你在aes中有一个y值时使用。

library(tidyverse)

set.seed(12345)
FF <- rweibull(100,1.7,1) * 20  #mock speeds
FF[FF > 60] <- 59
dir <- sample.int(10, size = 100, replace = TRUE) # mock directions

shares <- tibble(dir = dir, FF = FF) %>%
  mutate(grp = cut(FF, breaks = seq(0, 60, by = 15), ordered_result = T, right = F, drop = F)) %>%
  count(dir, grp) %>%
  mutate(share = n / sum(n))

shares
#> # A tibble: 29 x 4
#>      dir grp         n share
#>    <int> <ord>   <int> <dbl>
#>  1     1 [0,15)      3  0.03
#>  2     1 [15,30)     2  0.02
#>  3     2 [0,15)      4  0.04
#>  4     2 [15,30)     3  0.03
#>  5     2 [30,45)     1  0.01
#>  6     2 [45,60)     1  0.01
#>  7     3 [0,15)      6  0.06
#>  8     3 [15,30)     1  0.01
#>  9     3 [30,45)     2  0.02
#> 10     4 [0,15)      6  0.06
#> # ... with 19 more rows

ggplot(shares, aes(x = dir, y = share, fill = grp)) +
  geom_col()


1
谢谢Camille。这非常有用。我一直在考虑让代码更加明确的方向。问题是,我正在更新我在2001年用基本R编写的绘图例程(自那时以来一直在使用),当时所有东西都是明确计算的。虽然麻烦,但我知道我拥有什么。你的解决方案非常优雅,而且一点也不麻烦... - stuttungr

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