如何将直方图的条与x轴对齐?

24

考虑这个简单的例子

library(ggplot2)
dat <- data.frame(number = c(5, 10, 11 ,12,12,12,13,15,15))
ggplot(dat, aes(x = number)) + geom_histogram()

enter image description here

柱状图的条与x轴奇怪地对齐了,为什么左边第一根条在 5.0 的左边而 10.0 上的条居中?我该如何控制它呢?例如,将条开始位置放在标签右侧会更有意义(对我来说)。


1
data_frame?这是从哪里来的? - Sotos
1
如果您更改为geom_bar,则不会出现此行为,这可能是由于直方图引起的。 - Hardik Gupta
@Sotos library(tidyverse) 让我补充一下 - ℕʘʘḆḽḘ
1
@Noobie 我怀疑你能不能移动它。 - Hardik Gupta
3
我猜这只是由计算布局的算法所决定的结果。在 ggplot(data, aes(x = number)) + geom_histogram(boundary = 0) 中,直方图的中心会落在 5 和 15 上,但同时也会导致 10 的直方条不在中心位置上。 - nrussell
显示剩余3条评论
5个回答

52

为什么柱状图"奇怪地对齐"?

让我先解释一下,为什么您的代码会导致柱子奇怪地对齐。这与直方图的构建方式有关。首先,将x轴分成间隔,然后计算每个间隔中的值的数量。

默认情况下,ggplot将数据分成30个bin。它甚至输出了一个消息,指出如此:

stat_bin()使用bins = 30。使用binwidth选择更好的值。

默认值并不总是一个好选择。在您的情况下,所有数据点都是整数,可能希望选择边界为5、6、7、8...4.5、5.5、6.5...的bin,以便每个bin都包含一个整数值。您可以按如下方式获取绘图中使用的bin的边界:

data <- data.frame(number = c(5, 10, 11 ,12, 12, 12, 13, 15, 15))
p <- ggplot(data, aes(x = number)) + geom_histogram()
ggplot_build(p)$data[[1]]$xmin
##  [1]  4.655172  5.000000  5.344828  5.689655  6.034483  6.379310  6.724138  7.068966  7.413793
## [10]  7.758621  8.103448  8.448276  8.793103  9.137931  9.482759  9.827586 10.172414 10.517241
## [19] 10.862069 11.206897 11.551724 11.896552 12.241379 12.586207 12.931034 13.275862 13.620690
## [28] 13.965517 14.310345 14.655172

正如你所看到的,这些柱子的边界没有选择一种方式,可以使柱子与整数对齐。

简而言之,造成奇怪对齐的原因是,ggplot 简单地使用了默认的30个柱子,这并不适合您的情况,无法使柱子与整数对齐。

至少有两种方法可以获得漂亮的对齐柱子,我将在下面讨论。

改用条形图

由于您拥有整数数据,直方图可能不是适当的可视化选择。您可以改用 geom_bar(),这将导致柱子居中于整数:

ggplot(data, aes(x = number)) + geom_bar() + scale_x_continuous(breaks = 1:16)

输入图片说明

如果你想将条形图放在整数右侧,只需在number中加上0.5

ggplot(data, aes(x = number + 0.5)) + geom_bar() + scale_x_continuous(breaks = 1:16)

使用合适的区间创建直方图

如果您仍然想使用直方图,可以通过以下方式使ggplot使用更合理的区间:

此处输入图片描述

ggplot(data, aes(x = number)) +
  geom_histogram(binwidth = 1, boundary = 0, closed = "left") +
  scale_x_continuous(breaks = 1:16)

enter image description here

使用binwidth = 1可以覆盖默认选择的30个条形,并明确要求条形宽度为1。 boundary = 0确保分箱从整数值开始,如果希望整数在条形左侧,则需要这样做。(如果省略它,则会选择条形居中于整数上)。

参数closed = "left"有点棘手。如上所述,现在选择箱子的边界是5, 6, 7, ...。现在的问题是,例如6应该在哪个箱子里?它可能是第一个也可能是第二个。这是由closed控制的选择:如果将其设置为"right"(默认值),则箱子在右侧关闭,这意味着箱子的右边界将被包含,而左边界属于左边的箱子。因此,6将在第一个箱子中。另一方面,如果选择了"left",则左边界将成为箱子的一部分,6将在第二个箱子中。

由于希望条形在整数左侧,因此需要选择closed = "left"

两种解决方案的比较

如果比较直方图和条形图,您将注意到两个差异:

  • 在条形图中,条之间有一小段间隙,而在直方图中它们相邻。您可以通过使用geom_bar(width = 1)使条之间相邻。
  • 对于条形图中最右侧的条,它在15和16之间,而在直方图中它在14和15之间。原因是对于所有箱子,只有左边界属于箱子,而对于最右侧的箱子,两个边界都包括在内。

你的精彩示例应该被包含在官方文档中! - Sean Yun-Shiuan Chuang
1
没有通用解决方案吗? 每次更改x变量、bin大小都需要手动微调此解决方案。 - skan

7

这将使条形图在数值上居中显示

data <- data.frame(number = c(5, 10, 11 ,12,12,12,13,15,15))
ggplot(data,aes(x = number)) + geom_histogram(binwidth = 0.5)

这里有一个技巧,可以使用刻度标签将柱状图对齐到左侧。 但如果添加其他数据,需要将它们一起移动。

ggplot(data,aes(x = number)) + 
  geom_histogram(binwidth = 0.5) + 
  scale_x_continuous(
    breaks=seq(0.75,15.75,1), #show x-ticks align on the bar (0.25 before the value, half of the binwidth) 
    labels = 1:16 #change tick label to get the bar x-value
    )

其他选项:binwidth = 1, breaks=seq(0.5,15.5,1)(适用于整数可能更有意义)


谢谢Timat。是否可以获得左对齐而不是吗? - ℕʘʘḆḽḘ

1

除了@Stibu的好答案之外,需要注意的是自ggplot2 3.4.0以来,geom_colgeom_bar现在可以使用新的just参数将条形/柱形图放置在x轴的左侧或右侧。0.5(默认值)将把列放在中心位置,0在右侧,1在左侧:

library(patchwork)
library(ggplot2)
plot1 <- ggplot(dat, aes(x = number)) + 
  geom_bar(just = 0) + 
  labs(title = "with just = 0") +
  scale_x_continuous(breaks = 1:16)
plot2 <- ggplot(dat, aes(x = number)) + 
  geom_bar(just = 1) + 
  labs(title = "with just = 1") +
  scale_x_continuous(breaks = 1:16)

plot1 + plot2

enter image description here


-1
library(ggplot2)
dat <- data.frame(number = c(5, 10, 11 ,12,12,12,13,15,15))
#I have added bins=10 to control too many bins, by default it takes 30
#then it is difficult to read the labels
p1 <- ggplot(dat, aes(x = number)) + geom_histogram(bins = 10, color="black")
#use ggplot_build to get access to bin details, subsetting to [5] is used to 
#get max of each bin, you can use 3 to get centre, 4 to get left edge etc
#to see all the coponent of this chart, you can just run
#ggplot_build(p1)$data[[1]]
binDetails <- round(ggplot_build(p1)$data[[1]][5], digits = 3)
Scalexx <- scale_x_continuous(breaks = binDetails$xmax)

#final chart
p1+Scalexx

enter image description here

请访问下方链接,观看视频并且如果有帮助的话,请点赞: https://www.youtube.com/watch?v=Za8bTDvmPLk

通过使用这种方法,我们不需要手动计算存储箱的详细信息。如有任何问题,请留言。


-2

这对我有用

+ scale_x_continuous(limits = c(0, NA)) 

?scale_x_continuouslimits是:

其中之一:

  • NULL表示使用默认比例范围

  • 长度为两个的数字向量,提供比例的限制。使用NA来引用现有的最小值或最大值

  • 接受现有(自动)限制并返回新限制的函数。请注意,在位置比例上设置限制将删除超出限制范围的数据。如果目的是缩放,请在坐标系中使用limit参数(参见coord_cartesian())。


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