As outras respostas são boas abordagens. No entanto, existem algumas outras opções em R que não foram mencionadas, incluindo lowess
e approx
, que podem fornecer ajustes melhores ou desempenho mais rápido.
As vantagens são demonstradas mais facilmente com um conjunto de dados alternativo:
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)))
Aqui estão os dados sobrepostos pela curva sigmóide que os gerou:
Esse tipo de dado é comum ao observar um comportamento binário entre uma população. Por exemplo, isso pode ser um gráfico que mostra se um cliente comprou ou não algo (um binário 1/0 no eixo y) versus a quantidade de tempo que ele passou no site (eixo x).
Um grande número de pontos é usado para demonstrar melhor as diferenças de desempenho dessas funções.
Smooth
,, spline
esmooth.spline
todos produzem gibberish em um conjunto de dados como este com qualquer conjunto de parâmetros que eu tentei, talvez devido à sua tendência de mapear para todos os pontos, o que não funciona para dados com ruído.
Os loess
, lowess
e approx
funções de todos os produzir resultados utilizáveis, embora apenas um pouco para approx
. Este é o código para cada um usando parâmetros levemente otimizados:
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))
E os resultados:
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"))
Como você pode ver, lowess
produz um ajuste quase perfeito à curva de geração original. Loess
está perto, mas experimenta um estranho desvio em ambas as caudas.
Embora seu conjunto de dados seja muito diferente, descobri que outros conjuntos de dados têm desempenho semelhante, com ambos loess
e lowess
capazes de produzir bons resultados. As diferenças se tornam mais significativas quando você olha para os benchmarks:
> 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
é extremamente lento, levando 100x mais tempo approx
. Lowess
produz melhores resultados do que approx
, enquanto ainda é executado com bastante rapidez (15x mais rápido do que loess).
Loess
também fica cada vez mais atolado à medida que o número de pontos aumenta, tornando-se inutilizável por volta de 50.000.
EDIT: Pesquisas adicionais mostram que loess
oferece melhores ajustes para determinados conjuntos de dados. Se você estiver lidando com um pequeno conjunto de dados ou se o desempenho não for levado em consideração, tente as duas funções e compare os resultados.