簡體   English   中英

如何在 R 中為我的數據擬合平滑曲線?

[英]How to fit a smooth curve to my data in R?

我正在嘗試在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 它看起來很顛簸和前衛時,當然:

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

如何使曲線平滑,以便使用估計值對 3 個邊緣進行舍入? 我知道有很多方法可以擬合平滑曲線,但我不確定哪種方法最適合這種類型的曲線,以及如何在R中編寫它。

我喜歡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()幾乎是每個人的最愛。

也許smooth.spline是一個選項,你可以在這里設置一個平滑參數(通常在0和1之間)

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

你也可以在smooth.spline對象上使用predict。 該功能附帶基礎R,詳情請見?smooth.spline。

為了得到真正的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)

ggplot2包中的qplot()函數使用起來非常簡單,並提供了一個包含置信帶的優雅解決方案。 例如,

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

產生 在此輸入圖像描述

德克說,黃土是一種非常好的方法。

另一個選擇是使用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")

其他答案都是好方法。 但是,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曲線覆蓋的數據:

數據

在查看總體中的二元行為時,這種數據很常見。 例如,這可能是客戶是否購買了某些東西(y軸上的二進制1/0)與他們在網站上花費的時間(x軸)的關系圖。

大量的點用於更好地展示這些功能的性能差異。

使用我嘗試的任何參數集, Smoothsplinesmooth.spline都會在這樣的數據集上產生亂碼,可能是因為它們傾向於映射到每個點,這對於噪聲數據不起作用。

loesslowessapprox函數都可以產生可用的結果,盡管只是lowess 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非常慢, approx需要100倍。 Lowessapprox產生更好的結果,同時仍然運行得相當快(比黃土快15倍)。

隨着點數的增加, Loess也越來越陷入困境,大約在50,000點左右無法使用。

編輯:其他研究表明, loess更好地適應某些數據集。 如果您正在處理小型數據集或性能不是考慮因素,請嘗試兩種功能並比較結果。

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

在此輸入圖像描述 在此輸入圖像描述

我沒有看到這個方法顯示,所以如果其他人想要這樣做,我發現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方法

另一種選擇是使用來自ggpubr package 的ggpubr function。 通過指定add="loess" ,您將在數據中獲得一條平滑線。 在上面的鏈接中,您可以使用此 function 找到更多可能性。 這是使用mtcars數據集的可重現示例:

library(ggpubr)
ggscatter(data = mtcars,
          x = "wt",
          y = "mpg",
          add = "loess",
          conf.int = TRUE)
#> `geom_smooth()` using formula 'y ~ x'

使用reprex v2.0.2創建於 2022-08-28

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM