【问题标题】:How to remove white spaces between stacked geom_col如何删除堆叠的 geom_col 之间的空白
【发布时间】:2018-10-30 15:42:53
【问题描述】:
library(tidyverse)
library(lubridate)

date <- seq(ymd('2018-08-01'), ymd('2018-08-31'), by = '1 day')
c <- 21.30
x1 <- runif(length(date), 0, 20)
x2 <- rnorm(length(date), 10, 3)
x3 <- abs(rnorm(length(date), 40, 10))
data <- data.frame(c, x1, x2, x3) %>% 
  t() %>% as.data.frame() %>% rownames_to_column('var')
data <- data %>%
  mutate(category1 = c('catA', 'catB', 'catB', 'catC') %>% as.factor(),
         category2 = c('catAA', 'catBA', 'catBB', 'catCA') %>% as.factor())
names(data) <- c('var', as.character(date), 'category1', 'category2')
data_long <- data %>% 
  gather(date, value, -var, -category1, -category2) %>% 
  mutate(date = ymd(date))

data_long %>%
  ggplot(aes(date, value, fill = category1)) +
  geom_col(position = 'stack') +
  scale_x_date(breaks = '1 week', date_labels = '%Y-%m-%d', expand = c(.01, .01)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = .4)) +
  labs(fill = '')

使用上面的示例数据和代码,我生成了以下图:

我需要做的是删除列之间的空格。我发现了一些类似的主题,但他们建议使用position_dodge(),而在我的情况下不能使用它,因为我已经有position = 'stack',无法替换。那我怎样才能使列彼此相邻呢?

编辑

@camille 提议的设置 width = 1 似乎可以处理原始数据,但不能汇总到几周或几个月 - 请参阅下面的代码:

data_long %>%
  mutate(date = floor_date(date, unit = 'week', week_start = 1)) %>% 
  group_by(category1, date) %>% 
  summarise(value = sum(value, na.rm = TRUE)) %>% 
  ungroup() %>% 
  ggplot(aes(date, value, fill = category1, width = 1)) +
  geom_col(position = 'stack') +
  scale_x_date(breaks = '1 month', date_labels = '%Y-%m', expand = c(.01, .01)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = .4)) +
  labs(fill = '')

编辑 2。

正如@Camille 所指出的,在日期刻度的情况下,1 的宽度可能指的是 1 天。但是,以下内容不会产生预期的输出并返回警告消息:position_stack requires non-overlapping x intervals

 data_long %>%
    mutate(date = floor_date(date, unit = 'month', week_start = 1)) %>% 
    group_by(category1, date) %>% 
    summarise(value = sum(value, na.rm = TRUE),
              n = n()) %>% 
    ungroup() %>% 
    ggplot(aes(date, value, fill = category1, width = n)) +
    geom_col(position = 'stack') +
    scale_x_date(breaks = '1 month', date_labels = '%Y-%m', expand = c(.01, .01)) +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 90, vjust = .4)) +
    labs(fill = '')

【问题讨论】:

  • width = 1?默认情况下,根据 ?geom_col,宽度 = 0.9
  • 似乎可以处理原始数据,但不能处理汇总数据 - 请参阅我的编辑。
  • 嗨,我如何摆脱警告position_stack requires non-overlapping x intervals?这真是脖子疼。

标签: r ggplot2


【解决方案1】:

geom_col 的文档比我在上面的评论中更具体。宽度参数更详细的含义:

条形宽度。默认情况下,设置为数据分辨率的 90%。

在一般情况下,例如您的第一个案例,这可能仅表示一个离散案例与另一个案例之间的距离。但在日期的情况下, 有一个真正的分辨率,这似乎是指天。我不确定是否有不同的方法来设置日期的分辨率,例如一个单位指的是一周,而不是一天。

我正在降低 alpha 值只是为了查看条形是否重叠。

因此,如果不设置宽度,则默认为观察之间距离的 90%,即一周的 90%。

library(tidyverse)
library(lubridate)
...

summarized <- data_long %>%
  mutate(date = floor_date(date, unit = 'week', week_start = 1)) %>% 
  group_by(category1, date) %>% 
  summarise(value = sum(value, na.rm = TRUE)) %>% 
  ungroup()

ggplot(summarized, aes(date, value, fill = category1)) +
  geom_col(alpha = 0.6) +
  scale_x_date(breaks = '1 week', expand = c(.01, .01))

将宽度设置为 1 表示宽度为 1 天。我觉得这里有一个其他人可能能够解释的差异,为什么这被解读为 1 天而不是 100% 的分辨率。

ggplot(summarized, aes(date, value, fill = category1)) +
  geom_col(alpha = 0.6, width = 1) +
  scale_x_date(breaks = '1 week', expand = c(.01, .01))

所以要获得 1 周的宽度,也就是 7 天,请将宽度设置为 7。同样,我认为其他人可以在这里填写一些解释。

ggplot(summarized, aes(date, value, fill = category1)) +
  geom_col(alpha = 0.6, width = 7) +
  scale_x_date(breaks = '1 week', expand = c(.01, .01))

编辑:基于link in my comment,最好的方法可能只是将日期转换为字符串,这样您就可以像往常一样在离散的 x 比例上绘图。在致电as.character 之前,您可以进行任何您想要的格式设置。

summarized %>%
  mutate(date = as.character(date)) %>%
  ggplot(aes(x = date, y = value, fill = category1)) +
    geom_col(width = 1)

【讨论】:

  • 好像有点奇怪。那几个月呢?有些月份是 30 天,有些是 31 天,当然还有 2 月,它可能有 28 或 29 天。添加 n = n() 进行总结,然后设置 width = n 并没有多大帮助(请参阅编辑 2.)。那么我该如何处理呢?
  • 老实说不知道,虽然我认为你的聚合版本会扔掉一些东西,因为你通过说width = n而不是一个静态数字来为宽度分配一个向量跨度>
  • Here's an answer 类似的东西,他们的建议只是将 x 变量转换为一个因子(或字符)并删除日期缩放。这可能是要走的路,因为您仍然可以将字符串或因子标签格式化为看起来像日期
  • 堆叠需要更多的数据准备,但您也可以考虑使用geom_rect 来完全避免处理宽度。然后它会等效地工作几天/几周/几个月,甚至在不均匀间隔的时间段内。
【解决方案2】:

(顺便说一句,在顶部包含set.seed() 会很有帮助,这样我们都能得出相同的数据。我使用set.seed(42) 来表示这些。)

一种可以带来更多灵活性的替代方法是使用geom_rectgeom_tile 而不是geom_col。然后,您可以根据需要使每个条形图的天数/周/月宽。但这需要更多的准备工作。

例如,这里我预先计算了每个条的累积 y 坐标,方法是按日期分组,按类别 2 排序,并获得累积总和。我还通过获取以下日期来确定日期的 x 范围。 (最后我确实有一个手动位,我假设图表右侧的最后一列应该是一个“天”宽。如果使用周/月进行调整。可能有一个聪明的方法来使用 padr::pad 或其他东西否则自动直觉该增量应该是什么。)

data_long2 <- data_long %>%
  group_by(date) %>%
  arrange(desc(category2)) %>%
  mutate(top = cumsum(value),
         bottom = top - value) %>%
  ungroup() %>%
  group_by(category2) %>%
  mutate(next_date = lead(date, default = max(date) + 1)) %>%
  ungroup()

有了这个,您可以使用geom_rectgeom_tile 来获取您的图表。它们是可互换的,但它们使用不同的坐标系,分别基于角或中心。

这是一个使用 geom_rect 的示例,其中每个条形的左边缘与日期对齐。

ggplot(data_long2) +
  geom_rect(aes(xmin = date, xmax = next_date,
                ymin = bottom, ymax = top,
                fill = category1)) +
  scale_x_date(breaks = '1 week', date_labels = '%Y-%m-%d', expand = c(.01, .01)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = .4)) +
  labs(fill = '', y = "")

或者您可以使用geom_tile,在这种情况下,我将与每个栏中间的日期对齐。

ggplot(data_long2) +
  geom_tile(aes(x = date, width = as.numeric(next_date - date),
                y = (top + bottom)/2, height = (top - bottom),
                fill = category1)) +
  scale_x_date(breaks = '1 week', date_labels = '%Y-%m-%d', expand = c(.01, .01)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = .4)) +
  labs(fill = '')

【讨论】:

    猜你喜欢
    • 2012-06-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-09
    • 2012-09-25
    • 1970-01-01
    • 1970-01-01
    • 2013-05-01
    • 1970-01-01
    相关资源
    最近更新 更多