R语言 根据是否满足条件对绘图区域进行着色

zc0qhyus  于 2023-01-06  发布在  其他
关注(0)|答案(2)|浏览(186)

我正在使用ggplot()geom_line()为随时间推移而变化的值走廊创建线图。
有时可能会发生上限低于下限的情况(我称之为"反转"),我希望在图中突出显示发生这种情况的区域,比如使用不同的背景颜色。
搜索谷歌和StackOverflow都没有让我找到任何地方。
下面是一个人为的例子:

library(tidyverse)
library(RcppRoll)

set.seed(42)
N <- 100
l <- 5
a <- rgamma(n = N, shape = 2)
d <- tibble(x = 1:N, upper = roll_maxr(a, n = l), lower = roll_minr(a + lag(a), n = l)) %>% mutate(inversion = upper < lower)
dl <- pivot_longer(d, cols = c("upper", "lower"), names_to = "Bounds", values_to = "bound_vals")

ggplot(dl, mapping = aes(x = x, y = bound_vals, color = Bounds)) + geom_line(linewidth = 1) + theme_light()

这将生成以下图:

正如你所看到的,反转发生在一些地方,例如在x = 50附近。我希望图有一个较暗的背景(比如灰色),基于inversion列已经在tibble中。我该怎么做?
非常感谢您的帮助!

dced5bon

dced5bon1#

一个实现你想要的结果的方法是使用ggh4x::stat_difference,注意,为此我们必须使用宽数据集,并相应地通过两个geom_line添加行。

library(ggplot2)
library(ggh4x)

ggplot(d, mapping = aes(x = x)) + 
  stat_difference(aes(ymin = lower, ymax = upper)) +
  geom_line(aes(y = lower, color = "lower"), linewidth = 1) + 
  geom_line(aes(y = upper, color = "upper"), linewidth = 1) + 
  scale_fill_manual(values = c("+" = "transparent", "-" = "darkgrey"),
                    breaks = "-",
                    labels = "Inversion") +
  theme_light() +
  labs(color = "Bounds")

EDIT当然也可以为相交区域绘制背景矩形。但是我不知道有任何现成的选项,也就是说,棘手的部分是计算线相交处的x值,这需要一些努力和近似。这里有一种方法,但可能不是最有效的。

library(tidyverse)

# Compute intersection points and prepare data to draw rects
n <- 20 # Increase for a better approximation 
rect <- data.frame(
  x = seq(1, N, length.out = N * n)
)

# Shamefully stolen from ggh4x
rle_id <- function(x) with(rle(x), rep.int(seq_along(values), lengths))

rect <- rect |> 
  mutate(lower = approx(d$x, d$lower, x)[["y"]],
         upper = approx(d$x, d$upper, x)[["y"]],
         inversion = upper < lower,
         rle = with(rle(inversion & !is.na(inversion)), rep.int(seq_along(values), lengths))
        ) |>
  filter(inversion) |> 
  group_by(rle) |> 
  slice(c(1, n())) |>
  mutate(label = c("xmin", "xmax")) |> 
  ungroup() |> 
  select(x, rle, label) |> 
  pivot_wider(names_from = label, values_from = x)

ggplot(dl, mapping = aes(x = x, y = bound_vals, color = Bounds)) + 
  geom_line(linewidth = 1) + 
  geom_rect(data = rect, aes(xmin = xmin, xmax = xmax, group = rle), 
            ymin = -Inf, ymax = Inf, fill = "darkgrey", alpha = .3, inherit.aes = FALSE) +
  theme_light()
#> Warning: Removed 9 rows containing missing values (`geom_line()`).

fbcarpbf

fbcarpbf2#

回答我自己,下面的方法最后对我起作用了(也使用了实际数据和用facet_wrap()分组的图); h/t到@stefan,其对geom_rect() I的方法被回收:

library(tidyverse)
library(RcppRoll)

set.seed(42)
N <- 100
l <- 5
a <- rgamma(n = N, shape = 2)
d <- tibble(x = 1:N, upper = roll_maxr(a, n = l), lower = roll_minr(a + lag(a), n = l)) %>%
    mutate(inversion = upper < lower,
           inversionLag = if_else(is.na(lag(inversion)), FALSE, lag(inversion)),
            inversionLead = if_else(is.na(lead(inversion)), FALSE, lead(inversion)),
        inversionStart = inversion & !inversionLag,
        inversionEnd = inversion & !inversionLead
    )
dl <- pivot_longer(d, cols = c("upper", "lower"), names_to = "Bounds", values_to = "bound_vals")

iS <- d %>% filter(inversionStart) %>% select(x) %>% rowid_to_column() %>% rename(iS = x)
iE <- d %>% filter(inversionEnd) %>% select(x) %>% rowid_to_column() %>% rename(iE = x)
iD <- iS %>% full_join(iE, by = c("rowid"))

g <- ggplot(dl, mapping = aes(x = x, y = bound_vals, color = Bounds)) +
    geom_line(linewidth = 1) +
    geom_rect(data = iD, mapping = aes(xmin = iS, xmax = iE, fill = "Inversion"), ymin = -Inf, ymax = Inf, alpha = 0.3, inherit.aes = FALSE) +
    scale_fill_manual(name = "Inversions", values = "darkgray") +
    theme_light()
g

这给出了

这正是我想要的。

相关问题