Как подогнать плавную кривую к моим данным в 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

Теперь, когда я рисую его стандартной командой, он, конечно же, выглядит неровно и резко:

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

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

Фрэнк
источник
3
Это полностью зависит от того, какие у вас данные и почему вы их сглаживаете! Подсчитываются ли данные? Плотности? Измерения? Какая может быть ошибка измерения? Какую историю вы пытаетесь рассказать своим читателям с помощью своего графика? Все эти проблемы влияют на то, следует ли и как выполнять сглаживание данных.
Харлан
Это измеренные данные. При значениях x 1, 2, 3, ..., 10 некоторая система допустила 2, 4, 6, ..., 20 ошибок. Эти координаты, вероятно, не должны изменяться алгоритмом подбора. Но я хочу смоделировать ошибки (y) при недостающих значениях x, например, в данных, f (4) = 8 и f (5) = 7, поэтому, предположительно, f (4.5) находится между 7 и 8, используя какое-то полиномиальное или другое сглаживание.
Фрэнк
2
В этом случае с одной точкой данных для каждого значения x я бы вообще не стал сглаживать. У меня были бы просто большие точки для моих измеренных точек данных с тонкими линиями, соединяющими их. Все остальное указывает зрителю, что вы знаете о своих данных больше, чем вы.
Харлан,
Возможно, вы правы в этом примере. Тем не менее, хорошо знать, как это сделать, и я, возможно, захочу использовать его для некоторых других данных позже, например, это имеет смысл, если у вас есть тысячи очень острых точек данных, которые как бы поднимаются и опускаются, но есть общая тенденция , например, вверх, как здесь: plot (seq (1,100) + runif (100, 0,10), type = 'l').
Фрэнк
Вот хороший способ, stats.stackexchange.com/a/278666/134555
Belter

Ответы:

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)

В книге MASS Венейбла и Рипли есть целый раздел о сглаживании, который также охватывает сплайны и полиномы, но loess()он почти всем нравится.

Дирк Эддельбюттель
источник
Как вы примените это к этим данным? Я не уверен, как это сделать, потому что он требует формулы. Благодарность!
Фрэнк
7
Как я показал вам в примере, когда if xи yявляются видимыми переменными. Если они являются столбцами названного data.frame foo, вы добавляете data=fooпараметр к loess(y ~ x. data=foo)вызову - точно так же, как почти во всех других функциях моделирования в R.
Дирк Эддельбюттель,
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

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

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

производит введите описание изображения здесь

Подрывник
источник
Чтобы не уклоняться от вопроса, я считаю, что сообщение о значениях R ^ 2 (или псевдо R ^ 2) для сглаженной подгонки сомнительно. Более плавный обязательно будет соответствовать данным, поскольку пропускная способность уменьшается.
Underminer
Это может помочь: stackoverflow.com/questions/7549694/…
Underminer
Хм, я не смог наконец запустить ваш код на R 3.3.1. Я ggplot2успешно установил bu не может работать, qplotпотому что он не может найти функцию в Debian 8.5.
Лео Леопольд Герц 준영
13

Как сказал Дирк, 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")
Нико
источник
11

Все остальные ответы - хорошие подходы. Однако в 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очень медленно, занимает в 100 раз больше approx. Lowessдает лучшие результаты, чем 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) 

введите описание изображения здесь введите описание изображения здесь

jsb
источник
можно ли использовать 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

Сначала с методом лесса и автоматической формулой Второй с методом гаммы с предложенной формулой

Адам Банн
источник