如何将平滑曲线拟合到R中的数据?


87

我正在尝试在中绘制平滑曲线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

现在,当我使用标准命令对其进行绘制时,它当然看起来很坎and和锋利:

> plot(x,y, type='l', lwd=2, col='red')

如何使曲线平滑,以便使用估计值对3个边进行舍入?我知道有很多方法可以拟合平滑曲线,但是我不确定哪种方法最适合这种曲线,以及如何编写R


3
这完全取决于您的数据是什么以及为什么要对其进行平滑处理!数据计数吗?密度?测量?可能会有什么样的测量误差?您想通过图表告诉读者什么故事?所有这些问题都会影响是否以及如何平滑数据。
哈兰

这些是测量数据。在x值为1、2、3,...,10时,某些系统出现2、4、6,...,20个错误。拟合算法可能不应更改这些坐标。但是我想在缺失的x值处模拟错误(y),例如在数据f(4)= 8和f(5)= 7中,所以大概f(4.5)在7到8之间一些多项式或其他平滑处理。
弗兰克(Frank)

2
在那种情况下,对于每个x值只有一个数据点,我将一无所获。对于测量的数据点,我只有一个大点,并用细线连接它们。其他任何事情都向查看者暗示,您对数据的了解要比您了解的多。
哈兰

您可能适合此示例。很高兴知道如何做,以后我可能想在其他一些数据上使用它,例如,如果您有成千上万个非常尖刻的数据点在上下波动,这是有道理的,但总的趋势是,例如,像下面这样向上:plot(seq(1,100)+ runif(100,0,10),type ='l')。
Frank

这里是一个很好的方式,stats.stackexchange.com/a/278666/134555
Belter

Answers:


104

我非常喜欢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()几乎每个人都喜欢。


您如何将其应用于此数据?我不确定如何操作,因为它需要公式。谢谢!
Frank

7
正如我在示例中向您展示的,ifx和ify是可见变量。如果他们是一个名为data.frame列foo中,添加一个data=foo选项来loess(y ~ x. data=foo)调用-就像在河几乎所有其他建模功能
德克Eddelbuettel

4
我也喜欢supsmu()开箱即用的平滑器
apeescape

4
如果x是日期参数怎么办?如果我使用将日期映射到数字的数据表(使用lo <- loess(count~day, data=logins_per_day) )进行尝试,则会得到以下信息:Error: NA/NaN/Inf in foreign function call (arg 2) In addition: Warning message: NAs introduced by coercion
Wichert Akkerman

1
@Wichert Akkerman似乎大多数R函数都讨厌日期格式。我通常会执行类似new $ date = as.numeric(new $ date,as.Date(“ 2015-01-01”),units =“ days”)之类的操作(如stat.ethz.ch/pipermail/r- help / 2008-May / 162719.html
减少活动

58

也许smooth.spline是一个选项,您可以在此处设置平滑参数(通常在0和1之间)

smoothingSpline = smooth.spline(x, y, spar=0.35)
plot(x,y)
lines(smoothingSpline)

您还可以在smooth.spline对象上使用预测。该函数随R一起提供,有关详细信息,请参见?smooth.spline。


27

为了让它真正光滑...

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)

25

ggplot2软件包中的qplot()函数非常易于使用,并提供了一个优雅的解决方案,其中包括置信带。例如,

qplot(x,y, geom='smooth', span =0.5)

产生 在此处输入图片说明


不要回避这个问题,但是我发现平滑拟合的R ^ 2(或伪R ^ 2)值的报告是可疑的。随着带宽的减少,平滑器必定更适合数据。
Underminer


嗯,我最终无法在R 3.3.1中运行您的代码。我已ggplot2成功安装bu无法运行,qplot因为它无法在Debian 8.5中找到该功能。
莱奥波德·赫兹(LéoLéopoldHertz)2016年

13

正如Dirk所说,黄土是一种非常好的方法。

另一种选择是使用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")

11

其他答案都是好的方法。但是,R中还有其他一些未提及的选项,包括lowessapprox,它们可能会提供更好的契合度或更快的性能。

使用备用数据集更容易证明这些优点:

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)))

以下是生成该数据的S型曲线所覆盖的数据:

数据

当查看总体中的二进制行为时,此类数据很常见。例如,这可能是客户是否购买了某些商品(y轴为二进制1/0)与他们在网站上花费的时间(x轴)之间的关系图。

使用大量的观点可以更好地证明这些功能的性能差异。

Smoothsplinesmooth.spline都会在我尝试过的任何一组参数的数据集上产生乱码,这可能是因为它们倾向于映射到每个点,但不适用于嘈杂的数据。

loesslowessapprox功能都得到可用的结果,虽然只是勉强的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距离很近,但尾巴处都有奇怪的偏差。

虽然你的数据集将有很大的不同,我发现,其他数据集执行类似的,既loesslowess能够产生良好的效果。当您查看基准时,差异变得更加明显:

> 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非常慢,需要100倍的时间approxLowess产生比以下更好的结果approx,同时仍保持相当快的运行速度(比黄土快15倍)。

Loess 随着点数的增加,也变得越来越陷入困境,在50,000左右无法使用。

编辑:其他研究表明,loess它可以更好地拟合某些数据集。如果要处理的数据集较小或不考虑性能,请尝试使用两个函数并比较结果。


8

在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) 

在此处输入图片说明 在此处输入图片说明


是否可以将此geom_smooth用于后续处理?

2

我没有看到该方法,所以如果有人要这样做,我发现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

首先使用黄土法和自动公式 ,然后使用建议方法的gam方法

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.