如何在ggplot2中使用多色拟合线将颜色分配给多色散点图

Posted

技术标签:

【中文标题】如何在ggplot2中使用多色拟合线将颜色分配给多色散点图【英文标题】:How to assign colors to multicolor scatter plot with multicolor fitted lines in ggplot2 【发布时间】:2021-11-13 13:29:50 【问题描述】:

问题

我有一些数据点存储在data.frame 中,其中包含三个变量xygender。我的目标是在散点图上绘制几条一般拟合的线和专门适合男性/女性的线,并按性别着色点。这听起来很容易,但有些问题仍然存在。

我目前所做的是使用一组新的x 并为每个模型预测y,将拟合线组合在data.frame 中,然后将宽转换为长,用它们的模型名称作为第三个变量(来自这篇文章:ggplot2: how to add the legend for a line added to a scatter plot? 和这个:Add legend to ggplot2 line plot 我了解到应该使用映射而不是单独设置颜色/图例)。然而,虽然我可以得到一个多色线图,但这些点没有gender(已经是factor)的特定颜色,正如我从我引用的帖子中所预期的那样。

我也知道可以使用aes=(y=predict(model)),但我遇到了其他问题。我还尝试直接在aes 中为点着色,并为每条线分别分配颜色,但除非我使用lty,否则无法生成图例,这使得图例具有相同的颜色

将不胜感激任何想法,也欢迎改变整个方法。

代码

请注意,两对线重叠。所以它似乎只有两条线。我想在数据中添加一些jitter 可能会使它看起来不同。

slrmen<-lm(tc~x+I(x^2),data=data[data['gender']==0,])
slrwomen<-lm(tc~x+I(x^2),data=data[data['gender']==1,])
prdf <- data.frame(x = seq(from = range(data$x)[1], 
                  to = range(data$x)[2], length.out = 100),
                  gender = as.factor(rep(1,100)))
prdm <- data.frame(x = seq(from = range(data$x)[1], 
                  to = range(data$x)[2], length.out = 100),
                  gender = as.factor(rep(0,100)))
prdf$fit <- predict(fullmodel, newdata = prdf)
prdm$fit <- predict(fullmodel, newdata = prdm)
rawplotdata<-data.frame(x=prdf$x, fullf=prdf$fit, fullm=prdm$fit, 
                     linf=predict(slrwomen, newdata = prdf),
                     linm=predict(slrmen, newdata = prdm))
plotdata<-reshape2::melt(rawplotdata,id.vars="x",
                         measure.vars=c("fullf","fullm","linf","linm"),
                         variable.name="fitmethod", value.name="y")
plotdata$fitmethod<-as.factor(plotdata$fitmethod)

plt <- ggplot() + 
       geom_line(data = plotdata, aes(x = x, y = y, group = fitmethod, 
                                      colour=fitmethod)) +
       scale_colour_manual(name = "Fit Methods", 
                           values = c("fullf" = "lightskyblue", 
                                      "linf" = "cornflowerblue",
                                      "fullm"="darkseagreen", "linm" = "olivedrab")) +
       geom_point(data = data, aes(x = x, y = y, fill = gender)) +
       scale_fill_manual(values=c("blue","green"))  ## This does not work as I expected...
show(plt)

另一种方法的代码(省略两行),生成同色图例和多色图:

ggplot(data = prdf, aes(x = x, y = fit)) +  # prdf and prdm are just data frames containing the x's and fitted values for different models
       geom_line(aes(lty="Female"),colour = "chocolate") +
       geom_line(data = prdm, aes(x = x, y = fit, lty="Male"), colour = "darkblue") + 
       geom_point(data = data, aes(x = x, y = y, colour = gender)) +
       scale_colour_discrete(name="Gender", breaks=c(0,1), 
                             labels=c("Male","Female"))

【问题讨论】:

【参考方案1】:

这与在您自己的(第一个)示例中将colour 美学用于线条和将fill 美学用于点有关。在第二个示例中,它之所以有效,是因为 colour 美学用于线条和点。

默认情况下,geom_point 无法将变量映射到fill,因为默认点形状 (19) 没有填充。

要使fill 处理点,您必须在geom_point() 中指定shape = 21:25,在aes() 之外。

也许这个可重复的小例子有助于说明这一点:

模拟数据

set.seed(4821)
x1 <- rnorm(100, mean = 5)

set.seed(4821)
x2 <- rnorm(100, mean = 6)

data <- data.frame(x = rep(seq(20,80,length.out = 100),2),
                   tc = c(x1, x2),
                   gender = factor(c(rep("Female", 100), rep("Male", 100))))

适合模型

slrmen <-lm(tc~x+I(x^2), data = data[data["gender"]=="Male",])
slrwomen <-lm(tc~x+I(x^2),data = data[data["gender"]=="Female",])

newdat <- data.frame(x = seq(20,80,length.out = 200))

fitted.male <- data.frame(x = newdat,
                          gender = "Male",
                          tc = predict(object = slrmen, newdata = newdat))
fitted.female <- data.frame(x = newdat,
                           gender = "Female",
                           tc = predict(object = slrwomen, newdata = newdat))

使用colour 美学进行绘图

对点和线使用colour 美学(在ggplot 中指定,以便它在整个过程中得到继承)。默认情况下,geom_point 可以将变量映射到colour

library(ggplot2)

ggplot(data, aes(x = x, y = tc, colour = gender)) +
  geom_point() +
  geom_line(data = fitted.male) +
  geom_line(data = fitted.female) +
  scale_colour_manual(values = c("tomato","blue")) +
  theme_bw()

使用colourfill 美学进行绘图

对点使用fill 美学,对线条使用colour 美学(在geom_* 中指定美学以防止它们被继承)。这将重现问题。

ggplot(data, aes(x = x, y = tc)) +
  geom_point(aes(fill = gender)) +
  geom_line(data = fitted.male, aes(colour = gender)) +
  geom_line(data = fitted.female, aes(colour = gender)) +
  scale_colour_manual(values = c("tomato","blue")) +
  scale_fill_manual(values = c("tomato","blue")) +
  theme_bw()

要解决此问题,请将 geom_point 中的 shape 参数更改为可以填充的点形状 (21:25)。

ggplot(data, aes(x = x, y = tc)) +
  geom_point(aes(fill = gender), shape = 21) +
  geom_line(data = fitted.male, aes(colour = gender)) +
  geom_line(data = fitted.female, aes(colour = gender)) +
  scale_colour_manual(values = c("tomato","blue")) +
  scale_fill_manual(values = c("tomato","blue")) +
  theme_bw()

由reprex package (v2.0.1) 于 2021-09-19 创建

请注意,如果将相同的变量映射到两种美学,颜色和填充的比例会自动合并。

【讨论】:

非常感谢您!我没有意识到 geom_points 不是“可填充的”。我想我会使用最后一种方法,将点更改为另一种形状,因为我需要绘制多条线并分别分配颜色(例如,4 条拟合线和 2 个不同的点),所以前两种方法可能不适用. 你也知道为什么在我的第二种方法中,即使我在aes 之外但在geom_line 内手动为colour 设置了不同的颜色,我也无法为两条线分配两种颜色? (其实我并不是真的期待虚线) 嗨 Jasper :) 我不确定你的线型到底发生了什么,但你可能不应该将诸如“男性”或“女性”之类的字符映射到线型美学。您是否尝试指定geom_line(colour = "chocolate", linetype = 1)geom_line(data = prdm, aes(x = x, y = fit), linetype = 2, colour = "darkblue") 或者,您可以 rbind 所有 data.frames 使用 x 并将值拟合到 prdm,并添加其他变量 gendermodel 指定模型类型,然后使用所有内容一次性映射到线型和颜色美学:geom_line(data = prdm, aes(x = x, y = fit, linetype = type, colour = gender) 我自己只是在aes 中使用了一个字符(不是变量名)。这种方法类似于将变量从 data.frame 映射到审美:在您的示例中,您首先将 lty 映射到“女性”,这会产生实线,然后将 lty 映射到“男性” ,然后将其解释为某个因素的第二级。但那个因素不是prdfprdm 中的gender 变量。要将gender 映射到linetype (lty),您可以使用aes(linetype = gender)。如果您不想要不同的线型,则不应在 aes 内指定 linetype。希望这会有所帮助!【参考方案2】:

在我看来,你真正想做的是使用ggplot2::stat_smooth,而不是试图预测自己。

从@scrameri 借用数据:

ggplot(data, aes(x = x, y = tc, color = gender)) +
   geom_point() +
   stat_smooth(aes(linetype = "X^2"), method = 'lm',formula = y~x + I(x^2)) +
   stat_smooth(aes(linetype = "X^3"), method = 'lm',formula = y~x + I(x^2) + I(x^3)) +
   scale_color_manual(values = c("darkseagreen","lightskyblue"))

【讨论】:

谢谢伊恩!直到现在我才知道我可以直接加入ggplot2。有趣的功能。然而,简单回归可能只是一个特例,而且我经常认为需要在ggplot() 之外进行更高级的拟合,只输入拟合点。在这种情况下,我猜通常需要更精确的手动控制。仍然感谢您的出色回答!

以上是关于如何在ggplot2中使用多色拟合线将颜色分配给多色散点图的主要内容,如果未能解决你的问题,请参考以下文章

如何为ggplot2中具有稳定映射的分类变量分配颜色?

为线条 ggplot2 分配颜色

使用背景颜色的多色圆形div?

使用背景颜色的多色圆形 div?

在按钮控件的文本上设置多色

如何在swift 3中制作多色标签栏