19

我正在尝试对多个类别的数据进行十六进制表示。问题是,对这些垃圾箱进行刻面似乎会使它们的大小都不同。

set.seed(1) #Create data
bindata <- data.frame(x=rnorm(100), y=rnorm(100))
fac_probs <- dnorm(seq(-3, 3, length.out=26))
fac_probs <- fac_probs/sum(fac_probs)
bindata$factor <- sample(letters, 100, replace=TRUE, prob=fac_probs)

library(ggplot2) #Actual plotting
library(hexbin)

ggplot(bindata, aes(x=x, y=y)) +
  geom_hex() +
  facet_wrap(~factor)

在此处输入图像描述

是否可以设置一些东西以使所有这些垃圾箱在物理上具有相同的大小?

4

4 回答 4

19

正如 Julius 所说,问题在于hexGrob它没有获得有关 bin 大小的信息,而是从它在 facet中发现的差异中猜测它。

显然,没有六边形的宽度dx和高度就像指定dy一个hexGrob圆而不给出半径一样。

解决方法:

解决方法

resolution如果刻面包含两个在 x 和 y 上都不同的相邻六边形,则该策略有效。因此,作为一种解决方法,我将手动构建一个 data.frame,其中包含单元格的 x 和 y 中心坐标,以及分面因子和计数:

除了问题中指定的库之外,我还需要

library (reshape2)

并且bindata$factor实际上也需要成为一个因素:

bindata$factor <- as.factor (bindata$factor)

现在,计算基本的六边形网格

h <- hexbin (bindata, xbins = 5, IDs = TRUE, 
             xbnds = range (bindata$x), 
             ybnds = range (bindata$y))

接下来,我们需要根据bindata$factor

counts <- hexTapply (h, bindata$factor, table)
counts <- t (simplify2array (counts))
counts <- melt (counts)
colnames (counts)  <- c ("ID", "factor", "counts")

由于我们有单元格 ID,我们可以将此 data.frame 与适当的坐标合并:

hexdf <- data.frame (hcell2xy (h),  ID = h@cell)
hexdf <- merge (counts, hexdf)

这是 data.frame 的样子:

> head (hexdf)
  ID factor counts          x         y
1  3      e      0 -0.3681728 -1.914359
2  3      s      0 -0.3681728 -1.914359
3  3      y      0 -0.3681728 -1.914359
4  3      r      0 -0.3681728 -1.914359
5  3      p      0 -0.3681728 -1.914359
6  3      o      0 -0.3681728 -1.914359

ggplotting(使用下面的命令)这会产生正确的 bin 大小,但该图的外观有点奇怪:绘制了 0 个六边形,但仅在其他一些方面填充了该 bin 的地方。为了抑制绘图,我们可以将计数设置为NA并使na.value完全透明(默认为 gray50):

hexdf$counts [hexdf$counts == 0] <- NA

ggplot(hexdf, aes(x=x, y=y, fill = counts)) +
  geom_hex(stat="identity") +
  facet_wrap(~factor) +
  coord_equal () +
  scale_fill_continuous (low = "grey80", high = "#000040", na.value = "#00000000")

产生帖子顶部的数字。

只要 binwidth 正确且没有分面,此策略就有效。如果 binwidths 设置得非常小,resolution可能仍然会产生太大dxdy. 在这种情况下,我们可以hexGrob为两个相邻的 bin(但 x 和 y 都不同)提供NA每个方面的计数。

dummy <- hgridcent (xbins = 5, 
                    xbnds = range (bindata$x),  
                    ybnds = range (bindata$y),  
                    shape = 1)

dummy <- data.frame (ID = 0,
                     factor = rep (levels (bindata$factor), each = 2),
                     counts = NA,
                     x = rep (dummy$x [1] + c (0, dummy$dx/2), 
                              nlevels (bindata$factor)),
                     y = rep (dummy$y [1] + c (0, dummy$dy  ), 
                              nlevels (bindata$factor)))

这种方法的另一个优点是我们可以删除所有计数为 0 的行counts,在这种情况下将大小减少hexdf大约 3/4(122 行而不是 520 行):

counts <- counts [counts$counts > 0 ,]
hexdf <- data.frame (hcell2xy (h),  ID = h@cell)
hexdf <- merge (counts, hexdf)
hexdf <- rbind (hexdf, dummy)

该图看起来与上面完全相同,但您可以在na.value不完全透明的情况下可视化差异。


更多关于这个问题

这个问题并不是刻面所独有的,而是总是在太少的 bin 被占用时发生,因此没有“对角线”相邻的 bin 被填充。

这是一系列显示问题的最小数据:

首先,我进行跟踪hexBin,以便获得ggplot2:::hexBin与返回的对象相同的六边形网格的所有中心坐标hexbin

trace (ggplot2:::hexBin, exit = quote ({trace.grid <<- as.data.frame (hgridcent (xbins = xbins, xbnds = xbnds, ybnds = ybnds, shape = ybins/xbins) [1:2]); trace.h <<- hb}))

建立一个非常小的数据集:

df <- data.frame (x = 3 : 1, y = 1 : 3)

和情节:

p <- ggplot(df, aes(x=x, y=y)) +  geom_hex(binwidth=c(1, 1)) +          
     coord_fixed (xlim = c (0, 4), ylim = c (0,4))

p # needed for the tracing to occur
p + geom_point (data = trace.grid, size = 4) + 
    geom_point (data = df, col = "red") # data pts

str (trace.h)

Formal class 'hexbin' [package "hexbin"] with 16 slots
  ..@ cell  : int [1:3] 3 5 7
  ..@ count : int [1:3] 1 1 1
  ..@ xcm   : num [1:3] 3 2 1
  ..@ ycm   : num [1:3] 1 2 3
  ..@ xbins : num 2
  ..@ shape : num 1
  ..@ xbnds : num [1:2] 1 3
  ..@ ybnds : num [1:2] 1 3
  ..@ dimen : num [1:2] 4 3
  ..@ n     : int 3
  ..@ ncells: int 3
  ..@ call  : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds)
  ..@ xlab  : chr "x"
  ..@ ylab  : chr "y"
  ..@ cID   : NULL
  ..@ cAtt  : int(0) 

我重复这个情节,省略数据点 2:

p <- ggplot(df [-2,], aes(x=x, y=y)) +  geom_hex(binwidth=c(1, 1)) +          coord_fixed (xlim = c (0, 4), ylim = c (0,4))
p
p + geom_point (data = trace.grid, size = 4) + geom_point (data = df, col = "red")
str (trace.h)

Formal class 'hexbin' [package "hexbin"] with 16 slots
  ..@ cell  : int [1:2] 3 7
  ..@ count : int [1:2] 1 1
  ..@ xcm   : num [1:2] 3 1
  ..@ ycm   : num [1:2] 1 3
  ..@ xbins : num 2
  ..@ shape : num 1
  ..@ xbnds : num [1:2] 1 3
  ..@ ybnds : num [1:2] 1 3
  ..@ dimen : num [1:2] 4 3
  ..@ n     : int 2
  ..@ ncells: int 2
  ..@ call  : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds)
  ..@ xlab  : chr "x"
  ..@ ylab  : chr "y"
  ..@ cID   : NULL
  ..@ cAtt  : int(0) 

一切都很好 六边形绘图搞砸了

  • 请注意,来自hexbin同一网格的结果(单元格编号没有改变,只是单元格 5 不再填充,因此没有列出),网格尺寸和范围没有改变。但是绘制的六边形确实发生了巨大变化。

  • 另请注意,hgridcent忘记返回第一个单元格的中心坐标(左下角)。

虽然它被填充:

df <- data.frame (x = 1 : 3, y = 1 : 3)

p <- ggplot(df, aes(x=x, y=y)) +  geom_hex(binwidth=c(0.5, 0.8)) +          
     coord_fixed (xlim = c (0, 4), ylim = c (0,4))

p # needed for the tracing to occur
p + geom_point (data = trace.grid, size = 4) + 
    geom_point (data = df, col = "red") + # data pts
    geom_point (data = as.data.frame (hcell2xy (trace.h)), shape = 1, size = 6)

一切都搞砸了

在这里,六边形的渲染不可能是正确的——它们不属于一个六边形网格。

于 2013-01-29T15:58:27.037 回答
12

我尝试使用 lattice 使用相同的数据集复制您的解决方案hexbinplot。最初,它给了我一个错误xbnds[1] < xbnds[2] is not fulfilled。此错误是由于错误的数值向量指定了分箱应涵盖的值范围。我在 中更改了这些参数hexbinplot,并且它以某种方式起作用。不确定它是否可以帮助您使用 ggplot 解决它,但这可能是一个起点。

library(lattice)
library(hexbin)
hexbinplot(y ~ x | factor, bindata, xbnds = "panel", ybnds = "panel", xbins=5, 
           layout=c(7,3))

在此处输入图像描述

编辑

虽然矩形垃圾箱stat_bin2d()工作得很好:

ggplot(bindata, aes(x=x, y=y, group=factor)) + 
    facet_wrap(~factor) +
    stat_bin2d(binwidth=c(0.6, 0.6))

在此处输入图像描述

于 2013-01-26T19:58:38.580 回答
5

有两个我们感兴趣的源文件:stat-binhex.rgeom-hex.r,主要是hexBinhexGrob函数。

正如@Dinre 提到的,这个问题与刻面无关。我们可以看到的是,它binwidth并没有被忽略,并且在 中以特殊的方式使用hexBin,这个函数分别应用于每个方面。之后,hexGrob应用于每个方面。确保您可以使用例如检查它们

trace(ggplot2:::hexGrob, quote(browser()))
trace(ggplot2:::hexBin, quote(browser()))

因此,这就解释了为什么大小不同 - 它们取决于binwidth每个方面本身的数据和数据。

由于各种坐标变换,很难跟踪该过程,但请注意hexBin

data.frame(
  hcell2xy(hb),
  count = hb@count,
  density = hb@count / sum(hb@count, na.rm=TRUE)
)

似乎总是看起来很普通,这hexGrob负责绘制十六进制垃圾箱,失真,即它有polygonGrob. 如果刻面中只有一个 hex bin,则会出现更严重的异常。

dx <- resolution(x, FALSE)
dy <- resolution(y, FALSE) / sqrt(3) / 2 * 1.15

?resolution我们可以看到

描述

 The resolution is is the smallest non-zero distance between adjacent
 values. If there is only one unique value, then the resolution is
 defined to be one.

出于这个原因(resolution(x, FALSE) == 1和) ,您的示例中第一个方面resolution(y, FALSE) == 1的 x 坐标是polygonGrob

[1] 1.5native  1.5native  0.5native  -0.5native -0.5native 0.5native 

如果我没记错的话,在这种情况下,本地单位就像 npc,所以它们应该在 0 和 1 之间。也就是说,如果是单个 hex bin,它会超出范围,因为resolution(). 这个功能也是@Dinre 提到的失真的原因,即使有多达几个十六进制垃圾箱。

因此,目前似乎没有相同大小的十六进制箱的选项。一个临时的(并且对于大量因素非常不方便)解决方案可以从以下内容开始:

library(gridExtra)
set.seed(2)
bindata <- data.frame(x = rnorm(100), y = rnorm(100))
fac_probs <- c(10, 40, 40, 10)
bindata$factor <- sample(letters[1:4], 100, 
                         replace = TRUE, prob = fac_probs)

binwidths <- list(c(0.4, 0.4), c(0.5, 0.5),
                  c(0.5, 0.5), c(0.4, 0.4))

plots <- mapply(function(w,z){
  ggplot(bindata[bindata$factor == w, ], aes(x = x, y = y)) +
    geom_hex(binwidth = z) + theme(legend.position = 'none')
}, letters[1:4], binwidths, SIMPLIFY = FALSE)

do.call(grid.arrange, plots)

在此处输入图像描述

于 2013-01-29T19:27:06.043 回答
2

我还对“ggplot2”中的十六进制图进行了一些摆弄,当一个因子的人口减少到 8 或以下时,我能够始终如一地产生明显的 bin 失真。如果不深入研究包源(我不愿意这样做),我无法解释为什么会发生这种情况,但我可以告诉你,稀疏因素似乎一直在破坏 'ggplot2' 中的十六进制 bin 绘图。

这向我表明,“ggplot2”中特定十六进制 bin 的大小和形状与每个方面唯一的计算有关,而不是对组进行一次计算并随后绘制数据。我可以通过仅绘制单个因素来重现任何给定方面的失真,这在一定程度上加强了这一点,如下所示:

ggplot(bindata[bindata$factor=="e",], aes(x=x, y=y)) +
geom_hex()

这感觉像是应该提升给包维护者 Hadley Wickham(h.wickham at gmail.com)。 此信息可从 CRAN 公开获得。

更新:我向 Hadley Wickham 发送了一封电子邮件,询问他是否会看一下这个问题,他确认这种行为确实是一个错误。

于 2013-01-29T14:35:50.307 回答