我有一个带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
这很完美,可以满足我的要求。
我的问题是如何在代码中概括功能区的数量。如果我想添加一个新的功能区,我可以复制/粘贴我的代码,但这不是我想要的...我只想扩展breaks-vector(c(0.1,0.2,0.3,0.4))然后绘制应该自动包含4个色带(甚至更多)。就我而言,后面的图将由一个函数产生。此函数应仅将中断(和数据)作为参数。
我想我可以使用围绕geom_ribbon的for循环来做到这一点,并将结果存储在列表中。但是我没有成功:-(
有人有想法吗?提前非常感谢!
答案 0 :(得分:2)
如果将断裂点作为数据集的一部分,则可以调整数据的形状,使断裂点成为变量,然后可以将其分配给美学(在这种情况下为填充)。我记得前一阵子回答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-05创建
答案 1 :(得分:0)
我的第一个直觉是用另一个其他答案将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)
答案 2 :(得分:0)
这基本上是@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")