合并单独不同的大小并在 ggplot 中填充(或颜色)图例,显示绝对大小与大小比例

jar*_*_sk 16 r ggplot2

我正在绘制一些多变量数据,其中有 3 个离散变量和一个连续变量。我希望每个点的大小代表变化的幅度而不是实际的数值。我认为我可以通过使用绝对值来实现这一点。考虑到这一点,我希望负值用蓝色表示,正值用红色表示,零值用白色表示。比制作​​一个图例看起来像这样的情节:

示例.png

我想出了一个与我的数据集具有相同结构的虚拟数据集,以获得一个可重现的示例:

a1 <- c(-2, 2, 1, 0, 0.5, -0.5)
a2 <- c(-2, -2, -1.5, 2, 1, 0)
a3 <- c(1.5, 2, 1, 2, 0.5, 0)
a4 <- c(2, 0.5, 0, 1, -1.5, 0.5)
cond1 <- c("A", "B", "A", "B", "A", "B")
cond2 <- c("L", "L", "H", "H", "S", "S")
df <- data.frame(cond1, cond2, a1, a2, a3, a4)

#some data munging
df <- df %>%
  pivot_longer(names_to = "animal",
                          values_to = "FC",
                          cols = c(a1:a4)) %>%
  mutate(across(c("cond1", "cond2", "animal"),
                as.factor)) %>% 
  mutate(fillCol = case_when(FC < 0 ~ "decrease",
                             FC > 0 ~ "increase",
                             FC == 0 ~ "no_change"))

# plot 1
plt1 <- ggplot(df, aes(x = cond2, y = animal)) +
  geom_point(aes(size = abs(FC), color = FC)) +
  scale_color_gradient2(low='blue',
                        mid='white',
                        high='red',
                        limits=c(-2,2),
                        breaks=c(-2, -1, 0, 1, 2))+
  facet_wrap(~cond1)
plt1

#plot 2
plt2 <- ggplot(df, aes(x = cond2, y = animal)) +
  geom_point(aes(size = abs(FC), color = factor(FC))) +
  facet_wrap(~cond1)
plt2

#plot 3
cols <- c("decrease" = "blue", "no_change" = "white", "increase" = "red")
plt3 <- ggplot(df, aes(x = cond2, y = animal)) +
  geom_point(aes(size = abs(FC), color = fillCol)) +
  scale_color_manual(name = "FC",
                     values = cols,
                     labels = c("< 0", "0", "> 0"),
                     guide = "legend") +
  facet_wrap(~cond1)
plt3
Run Code Online (Sandbox Code Playgroud)

因此,结果应该看起来基本上像 plt3,但图例应该看起来像是将这两个图例合并到 plt2 中。最小的点在中间为零,越来越大的点指向负和正方向,颜色红色=正,蓝色=负,白色=零,图例上的标签显示实际数字。我的任务是完成这个任务,但我无法弄清楚。这是我在 Stackoverflow 上的第一个问题,所以没有图片:(。我对 r 比较陌生。

谢谢你!

编辑 12/08/2021 根据下面@jared_mamrot 的回复,只有当 FC 变量中的值在某种程度上是规则的时,它才有效。但是当我更改一些数字时,它会显示为警告,并且不会显示绘图上的点。是否可以使用值范围定义手动比例或以某种方式对其进行分类?更改值的示例:

a1 <- c(-2, 2, 1.4, 0, 0.8, -0.5)
a2 <- c(-2, -2, -1.5, 2, 1, 0)
a3 <- c(1.8, 2, 1, 2, 0.6, 0.4)
a4 <- c(2, 0.2, 0, 1, -1.2, 0.5)
cond1 <- c("A", "B", "A", "B", "A", "B")
cond2 <- c("L", "L", "H", "H", "S", "S")
df <- data.frame(cond1, cond2, a1, a2, a3, a4)

df <- df %>% pivot_longer(names_to = "animal",
                          values_to = "FC",
                          cols = c(a1:a4)) %>%
  mutate(across(everything(),
                as.factor))
 
plt4 <- ggplot(df, aes(x = cond2, y = animal, color = FC, size = FC)) +
  geom_point() +
  scale_size_manual(values = c(10,8,6,4,3,4,6,8,10),
                    breaks = seq(-2, 2, 0.5),
                    limits = factor(seq(-2, 2, 0.5),
                                    levels = seq(-2, 2, 0.5))) +
  scale_color_manual(values = c("-2" = "#03254C",
                                "-1.5" = "#1167B1",
                                "-1" = "#187BCD",
                                "-0.5" = "#2A9DF4",
                                "0" = "white",
                                "0.5" = "#FAD65F",
                                "1" = "#F88E2A",
                                "1.5" = "#FC6400",
                                "2" = "#B72C0A"),
                     breaks = seq(-2, 2, 0.5),
                     limits = factor(seq(-2, 2, 0.5),
                                     levels = seq(-2, 2, 0.5))) +
  facet_wrap(~cond1)

plt4
Run Code Online (Sandbox Code Playgroud)
> Warning message:
> Removed 7 rows containing missing values (geom_point). 
Run Code Online (Sandbox Code Playgroud)

Tje*_*ebo 7

问题是您想要将绝对值映射到大小,并将真实值映射到颜色(不同比例)。我认为对数据进行分箱是一个好主意,但它不是我的,所以我不会走这条路(我鼓励用户 Skaqqs 根据他们的建议尝试答案)。

我个人更愿意将您的大小保留为连续变量,因此您仍然可以使用scale_size_continuous. 这需要:

  • 将数据分为负值、正值和“零”值,并根据您的填充或颜色美感使用不同的比例(使用 {ggnewscale} 即可轻松实现)
  • 使用绝对值来达到尺寸美观

尝试使用指南做一些奇特的事情很快就会变得非常棘手。我真的更喜欢将图例创建分离到一个新图(“假图例”)中,并将图例添加到其他图(例如,使用 {patchwork}),而不是用指南功能等做疯狂的事情。

外观/相对尺寸显然可以根据您的审美愿望进行更改,我认为这比处理真实指南时更容易。

library(tidyverse)
library(patchwork)

a1 <- c(-2, 2, 1.4, 0, 0.8, -0.5)
a2 <- c(-2, -2, -1.5, 2, 1, 0)
a3 <- c(1.8, 2, 1, 2, 0.6, 0.4)
a4 <- c(2, 0.2, 0, 1, -1.2, 0.5)
cond1 <- c("A", "B", "A", "B", "A", "B")
cond2 <- c("L", "L", "H", "H", "S", "S")
df <- data.frame(cond1, cond2, a1, a2, a3, a4)

df <- 
  df %>% pivot_longer(names_to = "animal", values_to = "FC", cols = c(a1:a4)) %>%
  ## keep your continuous variable continuous:
  ## make a new column which tells you what is negative and positve and zero
  ## turn FC into absolute values 
  mutate(across(-FC, as.factor), 
         signFC = ifelse(FC == 0, 0, sign(FC)), 
         FC = abs(FC)) 

## move data and certain aesthetics from main call to layers
## I am also using fillable points, in order to be able to show "zero" in white
p <- ggplot(mapping = aes(x = cond2, y = animal, size = FC)) +
  geom_point(data = filter(df, signFC == -1), aes(fill = FC), shape = 21) +
  scale_fill_fermenter(palette = "Blues", direction = 1) +
  ## to show negative and positives differently, but size information still 
  ## mapped to continuous scale
  ggnewscale::new_scale_fill()+
  geom_point(data = filter(df, signFC == 1), aes(fill = FC), shape = 21, show.legend = FALSE) +
  scale_fill_fermenter(palette = "Reds", direction = 1) +
  geom_point(data = filter(df, signFC == 0), fill = "white", shape = 21) +
  scale_size_continuous(limits = c(0, 2)) +
  facet_wrap(~cond1) +
  theme(legend.position = "none")

## When dealing with guides gets too messy, I prefer to cleanly build the legend
## as a different plot
leg_df <- 
  data.frame(breaks = seq(-2, 2, 0.5)) %>%
  mutate(br_sign = ifelse(breaks == 0, 0, sign(breaks)), 
         vals = abs(breaks), 
         y = seq_along(vals)) 

## Do all the above, again :)
p_leg <-
  ggplot(mapping = aes(x = 1, y = y, size = vals)) +
  geom_text(data = leg_df, aes(x = 1, label = breaks, y = y), inherit.aes = FALSE,
            nudge_x = .01, hjust = 0) +
  geom_point(data = filter(leg_df, br_sign == -1), aes(fill = vals), shape = 21) +
  scale_fill_fermenter(palette = "Blues", direction = 1) +
  ## to show negative and positives differently, but size information still 
  ## mapped to continuous scale
  ggnewscale::new_scale_fill()+
  geom_point(data = filter(leg_df, br_sign == 1), aes(fill = vals), shape = 21, show.legend = FALSE) +
  scale_fill_fermenter(palette = "Reds", direction = 1) +
  geom_point(data = filter(leg_df, br_sign == 0), fill = "white", shape = 21) +
  scale_size_continuous(limits = c(0, 2)) +
  theme_void() +
  theme(legend.position = "none",
        plot.margin = margin(l = 10, r = 15, unit = "pt")) +
  coord_cartesian(clip = "off")

p + p_leg + plot_layout(widths = c(1, .05)) 
Run Code Online (Sandbox Code Playgroud)

由reprex 包于 2021 年 12 月 10 日创建(v2.0.1)


jar*_*rot 6

一种可能的解决方案是手动指定每个比例的值,例如

library(tidyverse)
a1 <- c(-2, 2, 1, 0, 0.5, -0.5)
a2 <- c(-2, -2, -1.5, 2, 1, 0)
a3 <- c(1.5, 2, 1, 2, 0.5, 0)
a4 <- c(2, 0.5, 0, 1, -1.5, 0.5)
cond1 <- c("A", "B", "A", "B", "A", "B")
cond2 <- c("L", "L", "H", "H", "S", "S")
df <- data.frame(cond1, cond2, a1, a2, a3, a4)

#some data munging
df %>%
  pivot_longer(names_to = "animal",
               values_to = "FC",
               cols = c(a1:a4)) %>%
  mutate(across(everything(),
                as.factor)) %>%
  ggplot(aes(x = cond2, y = animal, color = FC, size = FC)) +
  geom_point() +
  scale_size_manual(values = c(10,8,6,4,3,4,6,8,10),
                    breaks = seq(-2, 2, 0.5),
                    limits = factor(seq(-2, 2, 0.5),
                                    levels = seq(-2, 2, 0.5))) +
  scale_color_manual(values = c("-2" = "#03254C",
                                "-1.5" = "#1167B1",
                                "-1" = "#187BCD",
                                "-0.5" = "#2A9DF4",
                                "0" = "white",
                                "0.5" = "#FAD65F",
                                "1" = "#F88E2A",
                                "1.5" = "#FC6400",
                                "2" = "#B72C0A"),
                     breaks = seq(-2, 2, 0.5),
                     limits = factor(seq(-2, 2, 0.5),
                                     levels = seq(-2, 2, 0.5))) +
  facet_wrap(~cond1)
Run Code Online (Sandbox Code Playgroud)

由reprex 包于 2021 年 12 月 8 日创建(v2.0.1)

  • Jared,值得我花两分钱 - 尽管OP已经接受了我的答案(这对于他们的用例来说似乎更方便),但我相信用户@Skaqqs的答案更简单,在我看来值得赏金。(我仍然不明白为什么这个解决方案会起作用,但它确实有效!) (2认同)
  • 谢谢@tjebo,我也喜欢你的方法,但我同意 Skaqqs 的答案是清晰/聪明的,值得赏金(喜欢学习新事物)。感谢您为 SO 做出如此杰出的贡献。另外,根据OP的变量名称,他们正在处理RNA-Seq数据,所以我怀疑多面涂抹和火山图将是“最佳”解决方案([XY问题](https://meta.stackexchange.com/questions/ 66377/what-is-the-xy-problem))所以总的来说,我认为赏金带来了两个出色的答案,这很棒。 (2认同)

Ska*_*qqs 6

我的理解是,如果比例由相同的变量(FC_num)、中断和标签定义,则 ggplot 将自动组合图例中的比例。这意味着我们不必使用scale...manual(),这应该使我们的代码更加灵活和简洁(!)。

这里有两个选项:

library(ggplot2)
library(dplyr)
library(tidyr)

a1 <- c(-2, 2, 1.4, 0, 0.8, -0.5)
a2 <- c(-2, -2, -1.5, 2, 1, 0)
a3 <- c(1.8, 2, 1, 2, 0.6, 0.4)
a4 <- c(2, 0.2, 0, 1, -1.2, 0.5)
cond1 <- c("A", "B", "A", "B", "A", "B")
cond2 <- c("L", "L", "H", "H", "S", "S")
dff <- data.frame(cond1, cond2, a1, a2, a3, a4)

#some data munging
df <- dff %>%
  pivot_longer(names_to = "animal",
               values_to = "FC",
               cols = c(a1:a4)) %>%
  mutate(across(everything(),
                as.factor))

# Make focal variable numeric
df$FC_num <- as.numeric(paste(df$FC))

# Define breaks based on focal variable
breaks <- seq(min(df$FC_num), max(df$FC_num), 0.5)

# Option 1
transAbs <- scales::trans_new(name="abs", transform=abs, inverse=abs)
ggplot(data=df, aes(x=cond2, y=animal, fill=FC_num, size=FC_num)) +
  geom_point(pch=21) +
  scale_size_continuous(range=c(3,10), trans=transAbs, breaks=breaks, labels=breaks) +
  scale_fill_distiller(palette="RdBu", breaks=breaks, labels=breaks) +
  guides(fill=guide_legend(reverse=TRUE), size=guide_legend(reverse=TRUE)) +
  facet_wrap(~cond1)

# Option 2
ggplot(data=df, aes(x=cond2, y=animal, fill=FC_num, size=FC_num)) +
  geom_point(pch=21) +
  scale_size_binned_area(max_size=10, breaks=breaks, labels=breaks) +
  scale_fill_distiller(palette="RdBu", breaks=breaks, labels=breaks) +
  guides(fill=guide_legend(reverse=TRUE), size=guide_legend(reverse=TRUE)) +
  facet_wrap(~cond1)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述