在ggplot中添加多个图例

4

我有一个带有3个ribbon的ggplot。我可以使用以下代码生成此图:

library(ggplot2)
library(RColorBrewer)

data <- data.frame(
  date = seq.Date(as.Date("2018-01-01"), as.Date("2018-01-31"), by= "days"), 
  value = runif(min = 0, max = 1, n = 31) 
)


breaks <- c(0.1, 0.2, 0.3)
reds <- brewer.pal(3, "Reds")


pl <- ggplot2::ggplot(data = data,
                      aes(x = date, y = value)) +

  geom_ribbon(
    aes(
      x = date,
      ymin = value * (1 - breaks[1]),
      ymax = value * (1 + breaks[1])
    ),
    fill = reds[3],
    alpha = 0.4
  )  +

  geom_ribbon(
    aes(
      x = date,
      ymin = value * (1 - breaks[2]),
      ymax = value * (1 + breaks[2])
    ),
    fill = reds[2],
    alpha = 0.4
  )  +

  geom_ribbon(
    aes(
      x = date,
      ymin = value * (1 - breaks[2]),
      ymax = value * (1 + breaks[2])
    ),
    fill = reds[1],
    alpha = 0.4
  )  +

  geom_line(size = 1); pl

这个工作完美,做我想要的。

enter image description here

我的问题是如何将代码中的丝带数量进行概括。如果我想添加一个新的丝带,我可以复制/粘贴我的代码,但这不是我想要的...我只想扩展breaks-vector(c(0.1,0.2,0.3,0.4)),然后图应该自动包含4个丝带(甚至更多)。在我的情况下,后面的图将由一个函数生成。这个函数应该只包含断点(和数据)作为参数。

我认为我可以通过在geom_ribbon周围使用for循环,并将结果存储在列表中来实现这一点。但我没有成功:-(

有人有什么想法吗?提前谢谢大家!

3个回答

3
如果您将断点作为数据集的一部分,您可以重新组织数据,使得断点成为一个变量,然后将其分配给一个美学属性,在这种情况下是填充。我记得有一段时间以前回答了一个类似的问题,尽管那个问题在计算上实际上更加复杂。
为了使breaks向量成为数据框的一列,我只需将其添加为一个列表。每个观测值都有相同的这组断点。
data %>%
  mutate(brk = list(breaks))
#> # A tibble: 31 x 3
#>    date        value brk      
#>    <date>      <dbl> <list>   
#>  1 2018-01-01 0.0502 <dbl [3]>
#>  2 2018-01-02 0.190  <dbl [3]>
#>  3 2018-01-03 0.409  <dbl [3]>
#>  4 2018-01-04 0.453  <dbl [3]>
#>  5 2018-01-05 0.295  <dbl [3]>
#>  6 2018-01-06 0.170  <dbl [3]>
#>  7 2018-01-07 0.592  <dbl [3]>
#>  8 2018-01-08 0.315  <dbl [3]>
#>  9 2018-01-09 0.118  <dbl [3]>
#> 10 2018-01-10 0.374  <dbl [3]>
#> # ... with 21 more rows

拆解列表列会将其中的换行值分开,这样日期和数值的组合就会重复出现,每个换行都会产生一次重复。由于有3个换行,所以现在有3倍的行数。
data %>%
  mutate(brk = list(breaks)) %>%
  unnest()
#> # A tibble: 93 x 3
#>    date        value   brk
#>    <date>      <dbl> <dbl>
#>  1 2018-01-01 0.0502   0.1
#>  2 2018-01-01 0.0502   0.2
#>  3 2018-01-01 0.0502   0.3
#>  4 2018-01-02 0.190    0.1
#>  5 2018-01-02 0.190    0.2
#>  6 2018-01-02 0.190    0.3
#>  7 2018-01-03 0.409    0.1
#>  8 2018-01-03 0.409    0.2
#>  9 2018-01-03 0.409    0.3
#> 10 2018-01-04 0.453    0.1
#> # ... with 83 more rows

为了方便将这些断点作为离散变量使用,我创建了一个列,仅将断点值作为因子,并反转了它的顺序。这里(以及我链接到的前一个问题中)棘手的问题是排序。ggplot图层每层都建立在先前的图层之上,因此如果最宽的丝带最后绘制,它将阻挡所有较小的丝带。断点的默认顺序将按数字顺序排列,但由于我将它们设置为因子,所以我可以反转级别,使最宽的0.3首先绘制,从而位于下一层之下。
最后,要绘制线条:对此,您只需要日期和值,不需要像我从unnesting中重复那样,请在geom_line内取出日期和值的不同组合。您可以以其他方式执行此操作,包括创建两个数据框,一个重复一个不重复,但我通常更喜欢在一个管道中完成所有操作。
data %>%
  mutate(brk = list(breaks)) %>%
  unnest() %>%
  mutate(brk_fct = as.factor(brk) %>% fct_rev()) %>%
  ggplot(aes(x = date, y = value)) +
    geom_ribbon(aes(ymin = value * (1 - brk), ymax = value * (1 + brk), fill = brk_fct)) +
    geom_line(data = . %>% distinct(date, value)) +
    scale_fill_brewer(palette = "Reds")

这是由于 reprex package(v0.2.1)在2018年10月5日创建的。


嗨,Camille,非常感谢您的完美答案。那正是我在寻找的! - M. Schmid

0

我的第一反应是创建一个带有breaks的长数据集,就像其他答案中所示。但是,您可以在循环中添加图层。

使用循环添加图层可能会很棘手,因为ggplot2会延迟评估直到绘制图表(请参见此处的解释)。我们可以通过使用“tidyeval”代码中的“取消引用”来强制评估。

您将看到我循环遍历断点数并为每个断点添加一个图层,但通过使用!!进行取消引用来强制评估。

您将看到我还使用rev()来反转颜色板。

reds = brewer.pal(length(breaks), "Reds")

p1 = ggplot(data = data,
               aes(x = date, y = value))
for(i in 1:length(breaks)) {
    p1 = p1 + geom_ribbon( aes(ymin = value*(1 - !!breaks[i]),
                          ymax = value*(1 + !!breaks[i])),
                      fill = rev(reds)[i],
                      alpha = .4)
}
p1 + geom_line(size = 1)

enter image description here


也完美地工作了!我不知道取消引用的技巧...谢谢 - M. Schmid

0

这基本上是@camille答案的基础版本(在我撰写时没有看到)。无论如何,也可以发布它...

ggplot(data = data, aes(x = date, y = value)) +
  geom_line(size = 1) +
  geom_ribbon(data = merge(expand.grid(date = data$date, breaks = breaks), data), 
              aes(ymin = value * (1 - breaks), ymax = value * (1 + breaks),
                  fill = factor(breaks, levels = rev(unique(breaks)))), alpha = 0.4) +
 scale_fill_brewer(palette = "Reds")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接