【问题标题】:adding multiple ribbons in ggplot在ggplot中添加多个功能区
【发布时间】:2019-03-11 07:22:36
【问题描述】:

我有一个带有 3 条丝带的 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

这完美地工作并且做我想要的。

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

我想我可以用围绕 geom_ribbon 的 for 循环来做到这一点,并将结果存储在一个列表中。但我没有成功:-(

有人知道吗?提前非常感谢!

【问题讨论】:

    标签: r ggplot2


    【解决方案1】:

    这基本上是@camille 答案的base 版本(我在撰写时没有看到)。反正也可以发一下...

    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")
    

    【讨论】:

      【解决方案2】:

      我的第一个直觉是制作一个长数据集,其中 breaks 作为新列作为另一个答案。但是,您可以在循环中添加层。

      添加带有循环的层可能会很棘手,因为 ggplot2 会延迟评估直到绘图被渲染(参见解释 here)。我们可以通过 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)
      

      【讨论】:

      • 也可以完美运行!我不知道取消引用的诀窍...谢谢
      【解决方案3】:

      如果您将中断作为数据集的一部分,您可以重新调整数据的形状,使中断成为一个变量,然后您可以将其分配给美学——在本例中为填充。我记得不久前回答过a similar question,虽然那个计算实际上更复杂。

      为了使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 - ,然后在下一层下方分层。

      最后,划线:为此,您只需要日期和值,并且不需要像我从取消嵌套中所做的那样重复它们,因此我在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 日创建

      【讨论】:

      • 嗨卡米尔,非常感谢您的完美回答。这正是我想要的!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-27
      • 2011-07-25
      • 1970-01-01
      • 2015-10-27
      • 2019-06-08
      • 1970-01-01
      相关资源
      最近更新 更多