首页 文章

如何在R中为我的数据拟合平滑曲线?

提问于
浏览
76

我想在 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 中编写它 .

7 回答

  • 11

    我很喜欢 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() 几乎是每个人的最爱 .

  • 8

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

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

    你也可以在smooth.spline对象上使用predict . 该功能附带基础R,详情请见?smooth.spline .

  • 55

    为了得到真正的smoooth ...

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

    ggplot2包中的qplot()函数使用起来非常简单,并提供了包含置信带的优雅解决方案 . 例如,

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

    产生
    enter image description here

  • 23

    德克说,黄土是一种非常好的方法 .

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

    其他答案都是好方法 . 但是,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)))
    

    这是用生成它的sigmoid曲线覆盖的数据:

    Data

    在查看总体中的二元行为时,这种数据很常见 . 例如,这可能是客户是否购买了某些东西(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"))
    

    Fits

    如您所见, lowess 产生与原始生成曲线近似完美的拟合 . Loess 很接近,但两条尾巴都经历了一次奇怪的偏差 .

    虽然您的数据集将非常不同,但我发现其他数据集的表现相似, loess 和_622710都能够产生良好的效果 . 当您查看基准时,差异变得更加显着:

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

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

    编辑:其他研究表明, loess 更适合某些数据集 . 如果您正在处理小型数据集或性能不是考虑因素,请尝试两种功能并比较结果 .

  • 87

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

    enter image description here

    enter image description here

相关问题