Próbuję narysować gładką krzywą R
. Mam następujące proste dane dotyczące zabawki:
> x
[1] 1 2 3 4 5 6 7 8 9 10
> y
[1] 2 4 6 8 7 12 14 16 18 20
Teraz, kiedy kreślę to za pomocą standardowego polecenia, wygląda to oczywiście nierówno i ostro:
> plot(x,y, type='l', lwd=2, col='red')
Jak mogę wygładzić krzywą, tak aby 3 krawędzie były zaokrąglone przy użyciu wartości szacunkowych? Wiem, że istnieje wiele metod dopasowania gładkiej krzywej, ale nie jestem pewien, która z nich byłaby najbardziej odpowiednia dla tego typu krzywej i jak byś ją zapisał R
.
r
plot
curve-fitting
Szczery
źródło
źródło
Odpowiedzi:
Bardzo lubię
loess()
wygładzanie: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)
Książka Venables and Ripley's MASS zawiera całą sekcję dotyczącą wygładzania, która obejmuje również splajny i wielomiany - ale
loess()
jest prawie ulubiona przez wszystkich.źródło
x
iy
są widoczne. Jeśli są kolumny o data.frame nazwiefoo
The dodaćdata=foo
opcję doloess(y ~ x. data=foo)
rozmowy - tak jak w prawie wszystkich innych funkcji modelowania w R.supsmu()
jako wygładzacz polo <- loess(count~day, data=logins_per_day)
), otrzymuję to:Error: NA/NaN/Inf in foreign function call (arg 2) In addition: Warning message: NAs introduced by coercion
Może smooth.spline jest opcją. Możesz tutaj ustawić parametr wygładzania (zazwyczaj od 0 do 1)
smoothingSpline = smooth.spline(x, y, spar=0.35) plot(x,y) lines(smoothingSpline)
możesz także użyć predykcji na obiektach smooth.spline. Funkcja jest dostarczana z podstawą R, patrz? Smooth.spline po szczegóły.
źródło
Aby uzyskać NAPRAWDĘ płynną ...
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)
Ten styl interpoluje wiele dodatkowych punktów i zapewnia bardzo płynną krzywą. Wydaje się również, że jest to podejście przyjęte przez ggplot. Jeśli standardowy poziom gładkości jest w porządku, możesz po prostu użyć.
źródło
funkcja qplot () w pakiecie ggplot2 jest bardzo prosta w użyciu i zapewnia eleganckie rozwiązanie, które zawiera pasma ufności. Na przykład,
qplot(x,y, geom='smooth', span =0.5)
produkuje
źródło
ggplot2
pomyślnie, bu nie może działać,qplot
ponieważ nie może znaleźć funkcji w Debianie 8.5.LOESS to bardzo dobre podejście, jak powiedział Dirk.
Inną opcją jest użycie splajnów Beziera, które w niektórych przypadkach mogą działać lepiej niż LOESS, jeśli nie masz wielu punktów danych.
Tutaj znajdziesz przykład: 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ódło
Pozostałe odpowiedzi to dobre podejścia. Jest jednak kilka innych opcji w R, o których nie wspomniano, w tym
lowess
iapprox
, które mogą zapewnić lepsze dopasowanie lub szybszą wydajność.Zalety można łatwiej wykazać za pomocą alternatywnego zestawu danych:
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)))
Oto dane nałożone na sigmoidalną krzywą, która ją wygenerowała:
Ten rodzaj danych jest powszechny, gdy patrzy się na zachowanie binarne wśród populacji. Na przykład może to być wykres przedstawiający, czy klient coś kupił (wartość binarna 1/0 na osi y) w porównaniu z czasem spędzonym w witrynie (oś x).
Aby lepiej pokazać różnice w działaniu tych funkcji, używa się dużej liczby punktów.
Smooth
,spline
ismooth.spline
wszystkie generują bełkot na takim zbiorze danych z dowolnym zestawem parametrów, które wypróbowałem, być może z powodu ich tendencji do mapowania do każdego punktu, co nie działa w przypadku zaszumionych danych.Te
loess
,lowess
orazapprox
funkcje produkują użytecznych wyników, chociaż ledwo zaapprox
. Oto kod dla każdego z lekko zoptymalizowanymi parametrami: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))
A wyniki:
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"))
Jak widać,
lowess
daje prawie idealne dopasowanie do oryginalnej krzywej generowania.Loess
jest blisko, ale doświadcza dziwnego odchylenia na obu ogonach.Chociaż Twój zestaw danych będzie bardzo różny, odkryłem, że inne zestawy danych działają podobnie, z obydwoma
loess
ilowess
zdolnymi do generowania dobrych wyników. Różnice stają się bardziej znaczące, gdy spojrzysz na testy porównawcze:> 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
jest niezwykle powolny, trwa 100 razy dłużejapprox
.Lowess
daje lepsze wyniki niżapprox
, podczas gdy nadal działa dość szybko (15x szybciej niż less).Loess
również staje się coraz bardziej grzęznąć w miarę wzrostu liczby punktów, a około 50 000 staje się bezużytecznych.EDYTUJ: Dodatkowe badania pokazują, że
loess
zapewnia lepsze dopasowanie do niektórych zestawów danych. Jeśli masz do czynienia z małym zestawem danych lub wydajność nie jest brana pod uwagę, wypróbuj obie funkcje i porównaj wyniki.źródło
W ggplot2 możesz wykonywać wygładzanie na wiele sposobów, na przykład:
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)
źródło
Nie widziałem tej metody pokazanej, więc jeśli ktoś inny chce to zrobić, stwierdziłem, że dokumentacja ggplot zasugerowała technikę użycia
gam
metody, która dała podobne wyniki doloess
pracy z małymi zbiorami danych.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
Najpierw metodą lessu i formułą auto Drugą metodą gam z sugerowaną formułą
źródło