如何删除堆叠的geom_col之间的空白

时间:2018-10-30 15:42:54

标签: r ggplot2

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 = '')

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

我需要做的是删除列之间的空白。我发现了一些类似的主题,但是他们建议使用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 = '')

enter image description here

编辑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 = '')

enter image description here

2 个答案:

答案 0 :(得分:4)

geom_col的文档比我上面的评论要具体。 width参数的更详细含义:

  

条形宽度。默认情况下,设置为数据分辨率的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)

答案 1 :(得分:3)

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

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

作为示例,在这里,我通过按日期分组,按category2排序并获取累积总和来预先计算每个条的累积y坐标。我还通过获取下一个日期来确定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 = "")

enter image description here

或者您可以使用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 = '')

enter image description here