Bagaimana cara menyesuaikan kurva halus ke data saya di R?

88

Saya mencoba menggambar kurva yang mulus R. Saya memiliki data mainan sederhana berikut:

> x
 [1]  1  2  3  4  5  6  7  8  9 10
> y
 [1]  2  4  6  8  7 12 14 16 18 20

Sekarang ketika saya memplotnya dengan perintah standar terlihat bergelombang dan tegang, tentu saja:

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

Bagaimana cara membuat kurva halus sehingga 3 tepinya dibulatkan menggunakan nilai perkiraan? Saya tahu ada banyak metode untuk menyesuaikan kurva yang mulus, tetapi saya tidak yakin mana yang paling sesuai untuk jenis kurva ini dan bagaimana Anda akan menulisnya R.

jujur
sumber
3
Itu sepenuhnya tergantung pada apa data Anda dan mengapa Anda menghaluskannya! Apakah datanya penting? Kepadatan? Pengukuran? Jenis kesalahan pengukuran apa yang mungkin ada? Cerita apa yang ingin Anda sampaikan kepada pembaca dengan grafik Anda? Semua masalah ini memengaruhi apakah dan bagaimana Anda harus menghaluskan data Anda.
Harlan
Ini adalah data terukur. Pada nilai x 1, 2, 3, ..., 10 beberapa sistem membuat 2, 4, 6, ..., 20 kesalahan. Koordinat ini mungkin tidak boleh diubah oleh algoritme pemasangan. Tapi saya ingin mensimulasikan kesalahan (y) pada nilai x yang hilang, misalnya dalam data, f (4) = 8 dan f (5) = 7, jadi mungkin f (4,5) adalah antara 7 dan 8, menggunakan beberapa penghalusan polinomial atau lainnya.
Frank
2
Dalam hal ini, dengan satu titik data untuk setiap nilai x, saya tidak akan memuluskan sama sekali. Saya hanya memiliki titik-titik besar untuk titik data yang saya ukur, dengan garis tipis yang menghubungkannya. Ada lagi yang menyarankan kepada pemirsa bahwa Anda tahu lebih banyak tentang data Anda daripada Anda sendiri.
Harlan
Anda mungkin benar untuk contoh ini. Ada baiknya mengetahui cara melakukannya, dan saya mungkin ingin menggunakannya pada beberapa data lain nanti, misalnya masuk akal jika Anda memiliki ribuan titik data yang sangat tajam yang naik dan turun, tetapi ada kecenderungan umum , misalnya naik ke atas seperti di sini: plot (seq (1.100) + runif (100, 0,10), ketik = 'l').
Frank
Ini cara yang baik, stats.stackexchange.com/a/278666/134555
Belter

Jawaban:

105

Saya sangat suka loess()menghaluskan:

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 dan buku MASS Ripley memiliki keseluruhan bagian tentang menghaluskan yang juga mencakup splines dan polinomial - tetapi loess()hampir semua orang favorit.

Dirk Eddelbuettel
sumber
Bagaimana Anda menerapkannya pada data ini? Saya tidak yakin bagaimana karena mengharapkan formula. Terima kasih!
Frank
7
Seperti yang saya tunjukkan dalam contoh ketika variabel if xdan yare visible. Jika mereka adalah kolom dengan nama data.frame foo, Anda menambahkan data=fooopsi ke loess(y ~ x. data=foo)panggilan - seperti di hampir semua fungsi pemodelan lainnya di R.
Dirk Eddelbuettel
4
saya juga suka supsmu()sebagai out-of-the-box smooth
apeescape
4
bagaimana cara kerjanya jika x adalah parameter tanggal? Jika saya mencobanya dengan tabel data yang memetakan tanggal ke sebuah nomor (menggunakan lo <- loess(count~day, data=logins_per_day) ), saya mendapatkan ini:Error: NA/NaN/Inf in foreign function call (arg 2) In addition: Warning message: NAs introduced by coercion
Wichert Akkerman
1
@Wichert Akkerman Tampaknya format tanggal dibenci oleh sebagian besar fungsi R. Saya biasanya melakukan sesuatu seperti $ date = as.numeric baru (new $ date, as.Date ("2015-01-01"), units = "days") (seperti yang dijelaskan di stat.ethz.ch/pipermail/r- help / 2008-May / 162719.html )
mengurangi aktivitas
59

Mungkin smooth.spline adalah sebuah opsi, Anda dapat mengatur parameter penghalusan (biasanya antara 0 dan 1) di sini

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

Anda juga dapat menggunakan prediksi pada objek smooth.spline. Fungsi ini dilengkapi dengan basis R, lihat? Smooth.spline untuk detailnya.

Karsten W.
sumber
27

Untuk mendapatkannya BENAR-BENAR 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)

Gaya ini menginterpolasi banyak poin ekstra dan memberi Anda kurva yang sangat halus. Ini juga tampaknya menjadi pendekatan yang diambil ggplot. Jika tingkat kehalusan standar baik-baik saja Anda bisa menggunakan.

scatter.smooth(x, y)
John
sumber
25

fungsi qplot () dalam paket ggplot2 sangat mudah digunakan dan memberikan solusi elegan yang mencakup pita kepercayaan. Misalnya,

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

menghasilkan masukkan deskripsi gambar di sini

Underminer
sumber
Bukan untuk mengelak dari pertanyaan, tetapi saya menemukan pelaporan nilai R ^ 2 (atau pseudo R ^ 2) untuk kecocokan yang dihaluskan menjadi meragukan. Yang lebih halus akan muat lebih dekat ke data karena bandwidth berkurang.
Underminer
Ini mungkin membantu: stackoverflow.com/questions/7549694/…
Underminer
Hmm, akhirnya saya tidak dapat menjalankan kode Anda di R 3.3.1. Saya ggplot2berhasil menginstal bu tidak dapat berjalan qplotkarena tidak dapat menemukan fungsi di Debian 8.5.
Léo Léopold Hertz 준영
14

LOESS adalah pendekatan yang sangat bagus, seperti yang dikatakan Dirk.

Pilihan lainnya adalah menggunakan Bezier splines, yang dalam beberapa kasus dapat bekerja lebih baik daripada LOESS jika Anda tidak memiliki banyak titik data.

Di sini Anda akan menemukan contoh: 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")
nico
sumber
11

Jawaban lainnya adalah pendekatan yang baik. Namun, ada beberapa opsi lain di R yang belum disebutkan, termasuk lowessdanapprox , yang mungkin memberikan kesesuaian yang lebih baik atau kinerja yang lebih cepat.

Keuntungannya lebih mudah ditunjukkan dengan kumpulan data alternatif:

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

Berikut adalah data yang dihamparkan dengan kurva sigmoid yang menghasilkannya:

Data

Jenis data ini biasa terjadi saat melihat perilaku biner di antara suatu populasi. Misalnya, ini mungkin plot tentang apakah pelanggan membeli sesuatu (biner 1/0 pada sumbu y) versus jumlah waktu yang mereka habiskan di situs (sumbu x).

Sejumlah besar poin digunakan untuk mendemonstrasikan perbedaan kinerja fungsi-fungsi ini dengan lebih baik.

Smooth,, splinedansmooth.spline semuanya menghasilkan omong kosong pada kumpulan data seperti ini dengan kumpulan parameter apa pun yang telah saya coba, mungkin karena kecenderungannya untuk memetakan ke setiap titik, yang tidak berfungsi untuk data yang berisik.

Fungsi loess,, lowessdan approxsemuanya menghasilkan hasil yang dapat digunakan, meskipun hanya untuk approx. Ini adalah kode untuk masing-masing menggunakan parameter yang dioptimalkan ringan:

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

Dan hasilnya:

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

Cocok

Seperti yang Anda lihat, lowessmenghasilkan kesesuaian yang hampir sempurna dengan kurva pembangkit asli. Loessdekat, tetapi mengalami penyimpangan yang aneh di kedua ekor.

Meskipun kumpulan data Anda akan sangat berbeda, saya menemukan bahwa kumpulan data lain memiliki kinerja yang sama, dengan keduanya loessdan lowessmampu memberikan hasil yang baik. Perbedaan menjadi lebih signifikan saat Anda melihat tolok ukur:

> 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 

Loesssangat lambat, memakan waktu 100x lebih lama approx. Lowessmenghasilkan hasil yang lebih baik daripada approx, sambil tetap berlari dengan cukup cepat (15x lebih cepat dari loess).

Loess juga menjadi semakin macet karena jumlah poin meningkat, menjadi tidak dapat digunakan sekitar 50.000.

EDIT: Penelitian tambahan menunjukkan bahwa loesslebih cocok untuk set data tertentu. Jika Anda berurusan dengan set data kecil atau kinerja tidak menjadi pertimbangan, coba kedua fungsi dan bandingkan hasilnya.

Craig
sumber
8

Di ggplot2 Anda dapat melakukan pemulusan dalam beberapa cara, misalnya:

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) 

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

jsb
sumber
apakah mungkin menggunakan geom_smooth ini untuk proses lebih lanjut?
Ben
3

Saya tidak melihat metode ini ditampilkan, jadi jika orang lain ingin melakukan ini, saya menemukan bahwa dokumentasi ggplot menyarankan teknik untuk menggunakan gammetode yang menghasilkan hasil yang serupa loessketika bekerja dengan kumpulan data kecil.

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

Pertama dengan metode loess dan rumus otomatis Kedua dengan metode gam dengan rumus yang disarankan

Adam Bunn
sumber