Как поместить гладкую кривую в мои данные в 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(x,y, type='l', lwd=2, col='red')

Как сделать кривую гладкой, чтобы 3 края округлились с использованием оценочных значений? Я знаю, что есть много методов, чтобы соответствовать гладкой кривой, но я не уверен, какой из них наиболее подходит для этого типа кривой и как вы напишете ее в R.

Ответ 1

Мне нравится 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() - это почти все любимые.

Ответ 2

Возможно, smooth.spline является опцией, здесь вы можете установить параметр сглаживания (обычно между 0 и 1)

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

вы также можете использовать прогноз для объектов smooth.spline. Функция поставляется с базой R, см. ? smooth.spline для деталей.

Ответ 3

Чтобы получить это ДЕЙСТВИТЕЛЬНО 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)

Ответ 4

Функция qplot() в пакете ggplot2 очень проста в использовании и представляет собой элегантное решение, которое включает в себя доверительные диапазоны. Например,

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

производит enter image description here

Ответ 5

LOESS - очень хороший подход, как сказал Дирк.

Другой вариант - использовать сплайны Безье, которые в некоторых случаях могут работать лучше, чем 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")

Ответ 6

Другие ответы - все хорошие подходы. Однако есть несколько других опций в 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)))

Вот данные, наложенные на сигмовидную кривую, которые сгенерировали ее:

Данные

Подобные данные распространены при рассмотрении бинарного поведения среди населения. Например, это может быть график того, покупает ли покупатель что-то (двоичный код 1/0 по оси Y) и количество времени, которое они потратили на сайт (ось 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 чрезвычайно медленный, принимая 100x до тех пор, пока approx. lowess дает лучшие результаты, чем approx, но все еще работает довольно быстро (15 раз быстрее, чем лесс).

loess также становится все более увязшим, поскольку количество очков увеличивается, становясь непригодным для использования около 50 000.

EDIT: дополнительные исследования показывают, что loess дает лучшие приемы для некоторых наборов данных. Если вы имеете дело с небольшим набором данных или производительности, это не соображение, попробуйте обе функции и сравните результаты.

Ответ 7

В 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

Ответ 8

Я не видел этот метод, показанный, поэтому, если кто-то еще хочет это сделать, я обнаружил, что в документации 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

Во-первых, с помощью метода Лёсса и авто-формулы. Во-вторых, с помощью метода гам с предложенной формулой