以计数作为标签的二维汇总图

2D summary plot with counts as labels

我在特定点(lonlat)测量了一个数量(value),如下面的示例数据:

library(ggplot2)
set.seed(1)
dat <- data.frame(lon = runif(1000, 1, 15), 
                  lat = runif(1000, 40, 60), 
                  value = rnorm(1000))

我想用 space 中的颜色对测量值进行二维汇总(例如平均值),最重要的是我想将计数显示为标签。

我可以绘制标签和摘要图

## Left plot
ggplot(dat) +
  aes(x = lon, y = lat, z = value) +
  stat_summary_hex(bins = 5, fun = "mean", geom = "hex")
## Right plot
ggplot(dat) +
  aes(x = lon, y = lat, z = value) +
  stat_binhex(aes(label = ..count..), bins = 5, geom = "text")

但是当我将两者结合起来时,我失去了总结:

ggplot(dat) +
  aes(x = lon, y = lat, z = value) +
  stat_summary_hex(bins = 5, fun = "mean", geom = "hex") +
  stat_binhex(aes(label = ..count..), bins = 5, geom = "text")

我可以实现相反的,算作颜色和摘要算作标签:

ggplot(dat, aes(lon, lat, z = value)) +
  geom_hex(bins = 5) +
  stat_summary_hex(aes(label=..value..), bins = 5, 
                   fun = function(x) round(mean(x), 3), 
                   geom = "text")

在写问题时,花了几个小时的测试,我找到了一个解决方案:在文本中添加一个 fill=NULLfill=mean(value) 一个给了我我想要的。在代码及其结果图下方;唯一的区别是图例的标签。

但感觉非常hacky,所以我希望有更好的解决方案。

ggplot(dat) +
  aes(x = lon, y = lat, z = value)  +
  stat_summary_hex(bins = 5, fun = "mean", geom = "hex") +
  stat_binhex(aes(label = ..count.., fill = NULL), bins = 5, geom = "text") +
  theme_bw()



ggplot(dat) +
  aes(x = lon, y = lat, z = value)  +
  stat_summary_hex(bins = 5, fun = "mean", geom = "hex") +
  stat_binhex(aes(label = ..count.., fill = mean(value)), bins = 5, geom = "text") +
  theme_bw()

这里的问题是两个图共享相同的图例比例。

由于比例范围不同:0-40 vs -1.5 - 0.5,最大范围使最小范围的值以(几乎)相同的颜色出现。

这就是为什么将 count 显示为颜色有效,但反之似乎无效的原因。

作为示例,如果您重新缩放 mean 计算,颜色变化是可见的:

  rescaled_mean <- function(x) mean(x)*40
 
   ggplot(dat) +
    aes(x = lon, y = lat, z = value)  +
    stat_summary_hex(bins = 5, fun = "rescaled_mean", geom = "hex")+
    stat_binhex(aes(label = ..count..), bins = 5, geom = "text") +
    theme_bw()   

公平地说,我发现这是一种非常奇怪的行为。不过我喜欢你的解决方案——我真的不觉得添加 fill = NULL 很麻烦。相反,我觉得这很优雅。这是一种更 hacky 的方法,基本上结果相同,但多了一行。它正在使用 ggnewscale。

library(ggplot2)
set.seed(1)
dat <- data.frame(lon = runif(1000, 1, 15), 
                  lat = runif(1000, 40, 60), 
                  value = rnorm(1000))
ggplot(dat) +
  aes(x = lon, y = lat,z = value) +
  stat_summary_hex(bins = 5, fun = "mean", geom = "hex") +
  ggnewscale::new_scale_fill() +
  stat_binhex(aes(label = ..count..), bins = 5, geom = "text")

reprex package (v2.0.1)

创建于 2022-02-17

我提出了一个完全不同的方法来解决这个问题。但是,首先需要澄清一下。你写 “我在特定点(lonlat)测量了一个数量(值)” 但你没有指定 这些点正好。您的数据(生成的)包含 1000 lon 个点和相同数量的 lat 个点。

总之,你自己看看吧。

library(tidyverse)

set.seed(1)
dat <- 
  tibble(
    lon = runif(1000, 1, 15), 
    lat = runif(1000, 40, 60), 
    value = rnorm(1000)
  ) 

dat %>% distinct(lon) %>% nrow() #1000
dat %>% distinct(lat) %>% nrow() #1000

我的猜测是,对于真实数据,lonlat 的值集要小得多。 让我将其分解为 2 的精度。

grid = 2

dat %>% mutate(
    lon = round(lon/grid)*grid,
    lat = round(lat/grid)*grid,
  ) %>% 
  group_by(lon, lat) %>% 
  summarise(
    mean = mean(value),
    label = n()
  )

正如您在四舍五入后看到的那样,数据根据这两个变量进行了分组,然后我计算了您感兴趣的统计数据(平均值和观察次数)。

另请注意,这些统计数据是在 lon lat 的交点处生成的,因此我们有一个正方形网格。在您的解决方案中,情况完全不是这样。您没有获得这些点的观测值数量,并且您的网格不是正方形的。

所以让我们做一个图表。

dat %>% ggplot(aes(lon,lat,z=mean)) + 
  geom_contour_filled(binwidth = 0.25) + 
  geom_text(aes(label = label)) + 
  theme_bw()

没有什么能阻止您增加网格,比方说 4。

grid = 4

datg = dat %>% mutate(
  lon = round(lon/grid)*grid,
  lat = round(lat/grid)*grid,
) %>% 
  group_by(lon, lat) %>% 
  summarise(
    mean = mean(value),
    label = n()
  )

datg %>% ggplot(aes(lon,lat,z=mean)) + 
  geom_contour_filled(binwidth = 0.25) + 
  geom_text(aes(label = label)) + 
  theme_bw()

使用这样的解决方案,我们可以很容易地在我们感兴趣的地方补充标签,例如与平均值。这次我们将使用 grid = 1.5.

grid = 1.5

datg = dat %>% mutate(
  lon = round(lon/grid)*grid,
  lat = round(lat/grid)*grid,
) %>% 
  group_by(lon, lat) %>% 
  summarise(
    mean = mean(value),
    label = n(),
    lab2 = paste0("(", round(mean, 2), ")")
  )

datg %>% ggplot(aes(lon,lat,z=mean)) + 
  geom_contour_filled(binwidth = 0.25) + 
  geom_text(aes(label = label)) + 
  geom_text(aes(label = lab2), nudge_y = -.5, size = 3) + 
  theme_bw()

希望此解决方案比基于 stat_binhex 的解决方案更能满足您的需求。