如何在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
中,其中包含三个变量x
、y
和gender
。我的目标是在散点图上绘制几条一般拟合的线和专门适合男性/女性的线,并按性别着色点。这听起来很容易,但有些问题仍然存在。
我目前所做的是使用一组新的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()
使用colour
和fill
美学进行绘图
对点使用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
,并添加其他变量 gender
和 model
指定模型类型,然后使用所有内容一次性映射到线型和颜色美学:geom_line(data = prdm, aes(x = x, y = fit, linetype = type, colour = gender)
我自己只是在aes
中使用了一个字符(不是变量名)。这种方法类似于将变量从 data.frame 映射到审美:在您的示例中,您首先将 lty
映射到“女性”,这会产生实线,然后将 lty
映射到“男性” ,然后将其解释为某个因素的第二级。但那个因素不是prdf
和prdm
中的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中使用多色拟合线将颜色分配给多色散点图的主要内容,如果未能解决你的问题,请参考以下文章