首页 > 解决方案 > 在 tidybayes 中自定义半眼图的透明度

问题描述

我正在使用 tidybayes 生成半眼图来说明后验图的分布,遵循此处的示例。

使用以下代码:

library(magrittr)
library(dplyr)
library(purrr)
library(forcats)
library(tidyr)
library(modelr)
library(tidybayes)
library(ggplot2)
library(ggstance)
library(ggridges)
library(cowplot)
library(rstan)
library(brms)
library(ggrepel)
library(RColorBrewer)
library(gganimate)

theme_set(theme_tidybayes() + panel_border())

rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())

set.seed(5)
n = 10
n_condition = 5
ABC =
  tibble(
    condition = rep(c("A","B","C","D","E"), n),
    response = rnorm(n * 5, c(0,1,2,1,-1), 0.5)
  )

m = brm(response ~ (1|condition), data = ABC, control = list(adapt_delta = .99),
  prior = c(
    prior(normal(0, 1), class = Intercept),
    prior(student_t(3, 0, 1), class = sd),
    prior(student_t(3, 0, 1), class = sigma)
  ))

m %>%
  spread_draws(b_Intercept, r_condition[condition,]) %>%
  mutate(condition_mean = b_Intercept + r_condition) %>%
  ggplot(aes(y = condition, x = condition_mean, fill = stat(x < 0))) +
  stat_halfeyeh() +
  geom_vline(xintercept = 0, linetype = "dashed") +
  scale_fill_manual(values = c("gray80", "skyblue"))

我能够生成下图,其中每个分布的负区域为蓝色,而正区域为灰色: 在此处输入图像描述

我想要的最终数字应满足以下标准:

(1) 条件 A 到 E 的分布曲线下区域依次用“#009B9F”、“#5EBCBF”、“#C6DFE0”、“#E9D4E2”、“#D99BC5”着色;

(2)对于后验均值为正的分布(即条件A到D),将较大的alpha值分配给曲线下位于0线右侧的区域,并分配较低的alpha值(更透明)到曲线下位于 0 线左侧的区域;

(3) 对于后均值为负的分布(即条件 E),将较低的 alpha 值(更透明)分配给曲线下位于 0 线右侧的区域,并将较高的 alpha 值(更不透明)分配给曲线下位于 0 线左侧的区域。

我用 ggplot 函数中的 alpha 参数进行了调整,但总是失败。欢迎任何建议。

标签: rggplot2graphicsdplyrtidyverse

解决方案


让我们一一解决:

(1) 条件 A 到 E 的分布曲线下区域依次用“#009B9F”、“#5EBCBF”、“#C6DFE0”、“#E9D4E2”、“#D99BC5”着色;

将其转换为 ggplot-speak,您想要condition映射到fill美学上,然后您想要一个自定义的填充色标与 values c("#009B9F", "#5EBCBF", "#C6DFE0", "#E9D4E2", "#D99BC5")

(我会在每个发生变化的块中添加评论)

m %>%
  spread_draws(b_Intercept, r_condition[condition,]) %>%
  mutate(condition_mean = b_Intercept + r_condition) %>%
  # map condition onto fill here ------------------------\/
  ggplot(aes(y = condition, x = condition_mean, fill = condition)) +
  stat_halfeyeh() +
  geom_vline(xintercept = 0, linetype = "dashed") +
  # change the fill scale to use your values ------------\/
  scale_fill_manual(values = c("#009B9F", "#5EBCBF", "#C6DFE0", "#E9D4E2", "#D99BC5"))

使用手动填充比例绘制

侧边栏:我不确定我是否会为此使用手动色标:使用ViridisColorBrewerHCL Wizard等地方的预制色标通常更容易、更好——这些色标是为诸如此类的事情而设计的色盲安全、感知均匀性、良好的去饱和度等。此外,此示例使用分类数据,并且您提供的色标具有自然顺序;我希望你的真实数据是有序的而不是分类的,否则色阶意味着数据中不存在的排序。

无论如何,下一个问题:

(2)对于后验均值为正的分布(即条件A到D),将较大的alpha值分配给曲线下位于0线右侧的区域,并分配较低的alpha值(更透明)到曲线下位于 0 线左侧的区域;

(3) 对于后均值为负的分布(即条件 E),将较低的 alpha 值(更透明)分配给曲线下位于 0 线右侧的区域,并将较高的 alpha 值(更不透明)分配给曲线下位于 0 线左侧的区域。

我不确定你在这里的意思,所以我将尝试两种不同的东西。

一种可能性是:您想将x值映射到美学上,但如果平均值小于 0 alpha,您想反转这种关系(我实际上将使用美学,它专门针对此几何的平板部分并离开仅间隔)。我将从做简单的映射开始:condition_meanslab_alphaslab_alpha

m %>%
  spread_draws(b_Intercept, r_condition[condition,]) %>%
  mutate(condition_mean = b_Intercept + r_condition) %>%
  # map x value onto alpha -------------------------------------------\/
  ggplot(aes(y = condition, x = condition_mean, fill = condition, slab_alpha = stat(x))) +
  stat_halfeyeh() +
  geom_vline(xintercept = 0, linetype = "dashed") +
  scale_fill_manual(values = c("#009B9F", "#5EBCBF", "#C6DFE0", "#E9D4E2", "#D99BC5"))

简单的slab_alpha映射

马上您应该会看到一个问题:您的手动色标也使用了 alpha 通道,而这两个映射以一种令人困惑的方式组合在一起。所以我要从不使用 alpha 通道的 ColorBrewer 切换到调色板:

m %>%
  spread_draws(b_Intercept, r_condition[condition,]) %>%
  mutate(condition_mean = b_Intercept + r_condition) %>%
  ggplot(aes(y = condition, x = condition_mean, fill = condition, slab_alpha = stat(x))) +
  stat_halfeyeh() +
  geom_vline(xintercept = 0, linetype = "dashed") +
  # categorical palette that doesn't vary alpha much:
  scale_color_brewer(palette = "Set1")

带有颜色 brewer 调色板的 alpha

对于下一部分,您必须将 alpha 映射一分为二(我认为不拆分数据就不可能做到这一点,因为 ggplot 目前不支持将原始数据中的列与统计数据计算的列混合) . 您可以将函数(包括 purrr 样式的~函数)传递给datastats 和 geoms 的参数,这些函数将应用于数据,这使您可以轻松地在 ggplot 中拆分数据。此外,由于spread_draws按您传递的表达式中的所有索引(在本例中为conditions)分组,数据已经按 分组condition,因此类似表达式mean(condition_mean)将计算 的condition_mean每个级别内的平均值condition。这使您可以执行以下操作:

m %>%
  spread_draws(b_Intercept, r_condition[condition,]) %>%
  mutate(condition_mean = b_Intercept + r_condition) %>%
  ggplot(aes(y = condition, x = condition_mean, fill = condition)) +
  # move alpha mapping here and split halfeye spec in two
  stat_halfeyeh(aes(slab_alpha = stat(x)), data = ~ filter(., mean(condition_mean) > 0)) +
  stat_halfeyeh(aes(slab_alpha = -stat(x)), data = ~ filter(., mean(condition_mean) < 0)) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  scale_color_brewer(palette = "Set1")

分割阿尔法,连续

或者,您可能一直要求对 alpha 值进行硬性更改。为此,您只需要类似stat(x < 0)orstat(x > 0)而不是stat(x)or -stat(x)

m %>%
  spread_draws(b_Intercept, r_condition[condition,]) %>%
  mutate(condition_mean = b_Intercept + r_condition) %>%
  ggplot(aes(y = condition, x = condition_mean, fill = condition)) +
  # use binary alpha mapping
  stat_halfeyeh(aes(slab_alpha = stat(x > 0)), data = ~ filter(., mean(condition_mean) > 0)) +
  stat_halfeyeh(aes(slab_alpha = stat(x < 0)), data = ~ filter(., mean(condition_mean) < 0)) +
  geom_vline(xintercept = 0, linetype = "dashed") +
  scale_color_brewer(palette = "Set1")

拆分 alpha,二进制

最后,一般而言,如果您尝试使用 alpha 来强调任何一方更可能远离 0 的事物,我倾向于不根据均值在哪一侧来定义该映射——这似乎是一点点倒退,以忽略仅根据均值决定它在哪一边的不确定性。

abs(x)这是一个更简单的替代方案,它仅在 alpha 美学上编码距 0 (即)的距离:

m %>%
  spread_draws(b_Intercept, r_condition[condition,]) %>%
  mutate(condition_mean = b_Intercept + r_condition) %>%
  ggplot(aes(y = condition, x = condition_mean, fill = condition)) +

  # use alpha mapping with abs(x)
  stat_halfeyeh(aes(slab_alpha = stat(abs(x)))) +

  geom_vline(xintercept = 0, linetype = "dashed") +
  scale_color_brewer(palette = "Set1")

距离 0 的 Alpha 通道


推荐阅读