我正试图在R
中绘制一条平滑的曲线。我有以下简单的玩具数据:
> x
[1] 1 2 3 4 5 6 7 8 9 10
> y
[1] 2 4 6 8 7 12 14 16 18 20
现在,当我使用标准命令绘制它时,它看起来很崎岖和前卫,当然:
> plot(x,y, type='l', lwd=2, col='red')
如何使曲线平滑,以便使用估计值对3条边进行舍入?我知道有很多方法可以拟合平滑曲线,但我不确定哪种方法最适合这种类型的曲线以及如何在R
中编写它。
答案 0 :(得分:95)
我非常喜欢loess()
平滑:
x <- 1:10
y <- c(2,4,6,8,7,12,14,16,18,20)
lo <- loess(y~x)
plot(x,y)
lines(predict(lo), col='red', lwd=2)
Venables和Ripley的MASS书中有关于平滑的整个部分,也涵盖了样条和多项式 - 但loess()
几乎是每个人的最爱。
答案 1 :(得分:57)
也许smooth.spline是一个选项,你可以在这里设置一个平滑参数(通常在0和1之间)
smoothingSpline = smooth.spline(x, y, spar=0.35)
plot(x,y)
lines(smoothingSpline)
你也可以在smooth.spline对象上使用predict。该功能附带基础R,请参阅 ?smooth.spline了解详情。
答案 2 :(得分:26)
为了得到它真的很棒...
x <- 1:10
y <- c(2,4,6,8,7,8,14,16,18,20)
lo <- loess(y~x)
plot(x,y)
xl <- seq(min(x),max(x), (max(x) - min(x))/1000)
lines(xl, predict(lo,xl), col='red', lwd=2)
此样式会插入大量额外点,并为您提供非常平滑的曲线。它似乎也是ggplot采用的方法。如果标准的平滑度很好,你可以使用。
scatter.smooth(x, y)
答案 3 :(得分:24)
ggplot2包中的 qplot()函数使用起来非常简单,并提供了一个包含置信区间的优雅解决方案。例如,
qplot(x,y, geom='smooth', span =0.5)
产生
答案 4 :(得分:12)
如德克所说,黄土是一种非常好的方法。
另一种选择是使用Bezier样条曲线,如果没有很多数据点,在某些情况下可能比LOESS更好。
您可以在这里找到一个示例:http://rosettacode.org/wiki/Cubic_bezier_curves#R
# x, y: the x and y coordinates of the hull points
# n: the number of points in the curve.
bezierCurve <- function(x, y, n=10)
{
outx <- NULL
outy <- NULL
i <- 1
for (t in seq(0, 1, length.out=n))
{
b <- bez(x, y, t)
outx[i] <- b$x
outy[i] <- b$y
i <- i+1
}
return (list(x=outx, y=outy))
}
bez <- function(x, y, t)
{
outx <- 0
outy <- 0
n <- length(x)-1
for (i in 0:n)
{
outx <- outx + choose(n, i)*((1-t)^(n-i))*t^i*x[i+1]
outy <- outy + choose(n, i)*((1-t)^(n-i))*t^i*y[i+1]
}
return (list(x=outx, y=outy))
}
# Example usage
x <- c(4,6,4,5,6,7)
y <- 1:6
plot(x, y, "o", pch=20)
points(bezierCurve(x,y,20), type="l", col="red")
答案 5 :(得分:9)
其他答案都是好方法。但是,R中还有一些未提及的选项,包括lowess
和approx
,这可能会提供更好的拟合或更快的性能。
使用备用数据集可以更轻松地证明这些优势:
sigmoid <- function(x)
{
y<-1/(1+exp(-.15*(x-100)))
return(y)
}
dat<-data.frame(x=rnorm(5000)*30+100)
dat$y<-as.numeric(as.logical(round(sigmoid(dat$x)+rnorm(5000)*.3,0)))
这是用生成它的sigmoid曲线覆盖的数据:
当查看总体中的二元行为时,这种数据很常见。例如,这可能是客户是否购买了某些东西(y轴上的二进制1/0)与他们在网站上花费的时间(x轴)的关系图。
大量的点用于更好地展示这些功能的性能差异。
Smooth
,spline
和smooth.spline
都会在我尝试的任何参数集上对这样的数据集产生乱码,可能是因为它们倾向于映射到每个点,不适用于噪音数据。
loess
,lowess
和approx
函数都会产生可用的结果,尽管只有approx
。这是每个使用轻微优化参数的代码:
loessFit <- loess(y~x, dat, span = 0.6)
loessFit <- data.frame(x=loessFit$x,y=loessFit$fitted)
loessFit <- loessFit[order(loessFit$x),]
approxFit <- approx(dat,n = 15)
lowessFit <-data.frame(lowess(dat,f = .6,iter=1))
结果:
plot(dat,col='gray')
curve(sigmoid,0,200,add=TRUE,col='blue',)
lines(lowessFit,col='red')
lines(loessFit,col='green')
lines(approxFit,col='purple')
legend(150,.6,
legend=c("Sigmoid","Loess","Lowess",'Approx'),
lty=c(1,1),
lwd=c(2.5,2.5),col=c("blue","green","red","purple"))
如您所见,lowess
与原始生成曲线几乎完美契合。 Loess
很接近,但两条尾巴都有一个奇怪的偏差。
虽然您的数据集会有很大差异,但我发现其他数据集的表现相似,loess
和lowess
都能产生良好的效果。当您查看基准时,差异变得更加显着:
> microbenchmark::microbenchmark(loess(y~x, dat, span = 0.6),approx(dat,n = 20),lowess(dat,f = .6,iter=1),times=20)
Unit: milliseconds
expr min lq mean median uq max neval cld
loess(y ~ x, dat, span = 0.6) 153.034810 154.450750 156.794257 156.004357 159.23183 163.117746 20 c
approx(dat, n = 20) 1.297685 1.346773 1.689133 1.441823 1.86018 4.281735 20 a
lowess(dat, f = 0.6, iter = 1) 9.637583 10.085613 11.270911 11.350722 12.33046 12.495343 20 b
Loess
速度非常慢,只需approx
的100倍。 Lowess
产生的结果比approx
更好,但仍然运行得相当快(比黄土快15倍)。
Loess
也随着点数的增加而变得越来越困难,在50,000左右变得无法使用。
编辑:其他研究表明loess
更适合某些数据集。如果您正在处理小型数据集或性能不是考虑因素,请尝试两种功能并比较结果。
答案 6 :(得分:3)
在ggplot2中,您可以通过多种方式进行平滑处理,例如:
library(ggplot2)
ggplot(mtcars, aes(wt, mpg)) + geom_point() +
geom_smooth(method = "gam", formula = y ~ poly(x, 2))
ggplot(mtcars, aes(wt, mpg)) + geom_point() +
geom_smooth(method = "loess", span = 0.3, se = FALSE)
答案 7 :(得分:0)
我没有看到显示此方法,因此,如果其他人正在寻找这样做,我发现ggplot文档建议了一种使用gam
方法的技术,该方法在工作时会产生与loess
类似的结果与小数据集。
library(ggplot2)
x <- 1:10
y <- c(2,4,6,8,7,8,14,16,18,20)
df <- data.frame(x,y)
r <- ggplot(df, aes(x = x, y = y)) + geom_smooth(method = "gam", formula = y ~ s(x, bs = "cs"))+geom_point()
r
First with the loess method and auto formula Second with the gam method with suggested formula