【问题标题】:How to fill a rectangle with smaller rectangles coloured by averaged value如何用平均值着色的较小矩形填充矩形
【发布时间】:2019-02-24 12:50:41
【问题描述】:

我有如下数据:

data_ex <- data.frame(x = runif(1000, 0, 10),
                      y = runif(1000, 0, 10),
                      z = runif(1000, 0, 1))

所以基本上这些点(x, y) 在一个正方形(但也可能是一个矩形)内具有一些值z。我想将此平面划分为 100 个较小的正方形(矩形)并在其中平均 z 值。所以我做了以下事情:

data_ex <- data_ex %>% 
  mutate(x2 = cut(x, breaks = 0:10), 
         y2 = cut(y, breaks = 0:10)) %>%
  group_by(x2, y2) %>%
  mutate(z = mean(z)) %>% 
  ungroup()

现在我想绘制它并使用平均 z 值作为每个小正方形(矩形)的颜色。潜在地,我可以使用geom_tile 来执行此操作(如下所示),但它需要图块的中心作为输入。

data_ex %>% 
  ggplot() +
  geom_rect(aes(xmin = 0, xmax = 10, ymin = 0, ymax = 10), fill = 'white') +
  geom_tile(aes(x_center, y_center, fill = z))

我大概可以将它提取为x2y2 的中心,但看起来有点麻烦。因此,我想知道是否有更快的方法来执行适当的计算或以不同的方式制作所需的绘图。

【问题讨论】:

标签: r ggplot2


【解决方案1】:

您可以使用floorceiling 函数来创建任意大小的矩形,然后计算这些区间的中点。我稍微修改了你的第二个代码块:

data_ex <- data_ex %>% 
  mutate(x2 = cut(x, breaks = 0:10), 
         y2 = cut(y, breaks = 0:10)) %>%
  group_by(x2, y2) %>%
  mutate(mean_z = mean(z),
         x_mid = floor(x) + (ceiling(x) - floor(x))/2,
         y_mid = floor(y) + (ceiling(y) - floor(y))/2,
         height = ceiling(y) - floor(y),
         width = ceiling(x) - floor(x)) %>%
  ungroup()

然后为geom_tile()绘制并指定高度和宽度参数到aes()

data_ex %>% 
  ggplot() +
  geom_rect(aes(xmin = 0, xmax = 10, ymin = 0, ymax = 10), fill = 'white') +
  geom_tile(aes(x = x_mid, y = y_mid,height = height, width = width, fill = mean_z))

这也适用于非方形矩形,如果您将cut 应用于xy 并具有不同的中断。

data_ex <- data_ex %>% 
      mutate(x2 = cut(x, breaks = 0:10), 
             y2 = cut(y, breaks = c(0,2,4,6,8))) %>%
      group_by(x2, y2) %>%
      mutate(mean_z = mean(z),
             x_mid = floor(x) + (ceiling(x) - floor(x))/2,
             y_mid = floor(y) + (ceiling(y) - floor(y))/2,
             height = ceiling(y) - floor(y),
             width = ceiling(x) - floor(x)) %>%
      ungroup()

data_ex %>% 
      ggplot() +
      geom_rect(aes(xmin = 0, xmax = 10, ymin = 0, ymax = 10), fill = 'white') +
      geom_tile(aes(x = x_mid, y = y_mid,height = height, width = width, fill = mean_z))

【讨论】:

  • 你检查输出了吗?这是我得到的:imgur.com/a/98EgkxI 小方块没有完全覆盖大方块,并且稍微向左下角移动。这是因为geom_tile 需要瓷砖中心的xy
  • 是的,我现在明白了。我更新了代码来计算间隔的中点。
  • 谢谢。第二个问题是它似乎绘制了每一个点,仅由瓷砖隶属关系着色,这对于大型数据集可能很耗时(请参阅imgur.com/a/oOKrWvl)。我更喜欢总结值并因此限制要绘制的观察数量的解决方案。
  • 看起来您的数据是来自足球场的实际空间数据,在某些位置具有稀疏性,而不是像您提供的示例中那样跨绘图域更统一完整的数据。您是否尝试在mutatecut 调用中使用不同的breaks 值?这将使矩形的大小不同,但允许更广泛的覆盖范围并可以消除图形中的空白。在我回答的第三个代码块中尝试一下。
【解决方案2】:

编辑:OP 要求使分箱适用于任意比例和分箱大小的方法。

可以通过一个函数使分箱变得灵活:

library(tidyverse)
bin_df <- function(df, x_binwidth, y_binwidth) {
  df %>%
    mutate(x2 = x_binwidth * (floor(x/x_binwidth) + 0.5), 
           y2 = y_binwidth * (floor(y/y_binwidth) + 0.5)) %>%
    group_by(x2, y2) %>%
    summarize(z = mean(z)) %>% 
    ungroup()
}

data_ex %>%
  bin_df(x_binwidth = 1, y_binwidth = 1) %>%
  ggplot() +
  geom_tile(aes(x2, y2, fill = z)) +
  scale_x_continuous(breaks = 0:10)

data_ex %>%
  bin_df(x_binwidth = 2, y_binwidth = 2) %>%
  ggplot() +
  geom_tile(aes(x2, y2, fill = z)) +
  scale_x_continuous(breaks = 0:10)

【讨论】:

  • 对于虚假数据,没有 10 的值(或者至少出现任何值的可能性非常低);如果您的真实数据在边缘有值,您可能希望在 mutategroup_by 之间添加一个特殊情况,将 10 的值分配给 9.5 bin,例如mutate(x2 = pmin(9.5, x2), y2 = pmin(9.5, y2)),或使用 if_else 来执行此操作。
  • 这很好,但floor 只使用整数和 0-10 的比例。如果比例不同,并且您希望拥有任意大小的网格,则此方法将失败。
  • 您能否更改 x 和 y 值的单位以使它们成为整数?例如将 0 到 1 之间的米值转换为 0 到 100 之间的厘米值?
  • 更新了适用于任意 bin 尺寸的答案。
【解决方案3】:

由于并非所有内容都应该是 ggplot2,我将添加基于 spraster 包的替代解决方案。

代码如下:

library(sp)
library(raster)

set.seed(2222)

# Lets create 10 x 15 tiles
NCOLS = 10
NROWS = 15

data_ex <- data.frame(x = runif(1000, 0, 10),
                      y = runif(1000, 0, 10),
                      z = runif(1000, 0, 1))

# Create spatial points
dat_sp <- SpatialPointsDataFrame(data_ex[, 1:2], data = data_ex["z"])

# Create reference raster
r <- raster(ncols = NCOLS, nrows = NROWS, ext = extent(c(0, 10, 0, 10))) 

# Convert to a raster with z averaging
# Also could be any aggregation function like min, max, etc.
dat_rast <- rasterize(dat_sp, r, field = "z", fun = mean)

# Plot with base graphics
plot(dat_rast)

这是一个结果:

如果您仍想使用ggplot 绘制它,可以使用graphVis 包:

# Plot with ggplot2
library(ggplot2)
library(rasterVis)

gplot(dat_rast) + geom_tile(aes(fill = value))

结果:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    • 2021-02-21
    • 2019-03-01
    • 1970-01-01
    相关资源
    最近更新 更多