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

How to assign colors to multicolor scatter plot with multicolor fitted lines in ggplot2

问题

我在 data.frame 中存储了一些数据点,其中包含三个变量,xygender。我的目标是在散点图上画出几条一般拟合的线和专门适合 male/female 的线,点按性别着色。听起来很简单,但仍然存在一些问题。

我目前所做的是使用一组新的 x 并为每个模型预测 y,将拟合线组合在 data.frame 中,然后然后将 wide 转换为 long,将它们的型号名称作为第三个 var(从这个 post: ggplot2: how to add the legend for a line added to a scatter plot? and this: Add legend to ggplot2 line plot 我了解到应该使用映射而不是单独设置 colours/legends)。然而,虽然我可以获得多色线图,但正如我从我引用的 post 中预期的那样,这些点没有 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"))

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

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

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

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

模拟数据

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 创建

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

在我看来,您真正想做的是使用 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"))