2010-08-13 15 views
66

Sto provando a disegnare una curva uniforme in R. Ho il seguente semplice dei dati giocattolo:Come adattare una curva morbida ai miei dati in 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 

Ora, quando ho tracciare con un comando standard sembra irregolare e tagliente, naturalmente:

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

Come posso fare la curva liscia in modo che il 3 spigoli vengono arrotondati utilizzando i valori stimati? So che ci sono molti metodi per adattare una curva liscia, ma non sono sicuro quale sia il più appropriato per questo tipo di curva e come lo si scriverà in R.

+1

Dipende interamente su ciò che i vostri dati è e perché si sta lisciandolo! I dati sono importanti? Densità?Misure? Che tipo di errore di misura potrebbe esserci? Che storia stai cercando di dire ai tuoi lettori con il tuo grafico? Tutti questi problemi influenzano se e in che modo è necessario uniformare i dati. – Harlan

+0

Questi sono dati misurati. Ai valori x 1, 2, 3, ..., 10 alcuni sistemi hanno fatto 2, 4, 6, ..., 20 errori. Probabilmente queste coordinate non dovrebbero essere modificate dall'algoritmo di adattamento. Ma voglio simulare gli errori (y) ai valori x mancanti, per esempio nei dati, f (4) = 8 e f (5) = 7, quindi presumibilmente f (4.5) è qualcosa tra 7 e 8, usando qualche polinomio o altra levigatura. – Frank

+1

In tal caso, con un singolo punto di dati per ogni valore di x, non mi andrebbe affatto bene. Avrei solo dei punti grandi per i miei punti dati misurati, con linee sottili che li collegano. Qualcos'altro suggerisce allo spettatore che sai di più sui tuoi dati di te. – Harlan

risposta

81

mi piace loess() molto per lisciare:

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 e Ripley del libro di massa ha un'intera sezione sul smoothing che copre anche spline e polinomi -, ma si tratta solo di loess() preferito di tutti.

+0

Come si applica a questi dati? Non sono sicuro di come si aspetta una formula. Grazie! – Frank

+6

Come ti ho mostrato nell'esempio quando if 'x' e' y' sono variabili visibili. Se sono colonne di un data.frame chiamato 'foo', si aggiunge l'opzione' data = foo' alla chiamata 'loess (y ~ x. Data = foo)' - come in quasi tutte le altre funzioni di modellazione in R. –

+4

Mi piace anche 'supsmu()' come un più semplice – apeescape

8

LOESS rappresenta un ottimo approccio, come ha affermato Dirk.

Un'altra opzione utilizza le spline di Bezier, che in alcuni casi possono funzionare meglio di LOESS se non si dispone di molti punti dati.

Qui troverete un esempio: 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") 
48

Forse smooth.spline è un'opzione, è possibile impostare un parametro di smoothing (tipicamente tra 0 e 1) qui

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

può anche usare la previsione sugli oggetti smooth.spline. La funzione viene fornita con la base R, vedere ? Smooth.spline per i dettagli.

23

Per ottenerlo davvero 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) 

Questo stile interpola un sacco di punti extra e si ottiene una curva che è molto liscia. Sembra anche essere l'approccio che ggplot prende. Se il livello standard di scorrevolezza va bene, puoi semplicemente usarlo.

scatter.smooth(x, y) 
19

il qplot() funzione nel pacchetto ggplot2 è molto semplice da usare e fornisce una soluzione elegante che include bande di confidenza. Per esempio,

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

produce enter image description here

+0

Non per schivare la domanda, ma trovo che la segnalazione di valori R^2 (o pseudo R^2) per un adattamento regolare sia dubbia. Un attenuatore si adatta necessariamente più vicino ai dati man mano che la larghezza di banda diminuisce. – Underminer

+0

Questo può aiutare: http://stackoverflow.com/questions/7549694/ggplot2-adding-regression-line-equation-and-r2-on-graph – Underminer

+0

Hmm, non ho potuto finalmente eseguire il codice in R 3.3.1. Ho installato 'ggplot2' correttamente bu non può eseguire' qplot' perché non riesce a trovare la funzione in Debian 8.5. –

6

Le altre risposte sono tutte buone approcci. Tuttavia, ci sono alcune altre opzioni in R che non sono state menzionate, tra cui lowess e approx, che possono offrire migliori adattamenti o prestazioni più veloci.

I vantaggi sono più facilmente dimostrati con un set di dati 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))) 

Ecco i dati sovrapposto con la curva sigmoidale che lo ha generato:

Data

Questo tipo di dati è comune quando si guarda ad un comportamento binario tra una popolazione. Ad esempio, questo potrebbe essere un complotto sul fatto che un cliente abbia acquistato o meno qualcosa (un binario 1/0 sull'asse delle ordinate) rispetto alla quantità di tempo trascorso sul sito (asse x).

Un numero elevato di punti viene utilizzato per dimostrare meglio le differenze di prestazioni di queste funzioni.

Smooth, spline e smooth.spline tutti producono senza senso su un set di dati come questo con qualsiasi insieme di parametri che ho provato, forse a causa della loro tendenza a mappare ad ogni punto, che non funziona per i dati rumorosi.

I loess, lowess, e approx funzioni tutti producono risultati utili, anche se appena per approx. Questo è il codice per ogni utilizzando parametri leggermente ottimizzati:

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 i risultati:

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

Fits

Come si può vedere, lowess produce una misura quasi perfetta alla curva generatrice originale . Loess è vicino, ma subisce una strana deviazione su entrambe le code.

Sebbene il set di dati sia molto diverso, ho riscontrato che altri set di dati funzionano in modo simile, con loess e lowess in grado di produrre buoni risultati. Le differenze diventano più significativo se si guarda a parametri di riferimento:

> 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 è estremamente lento, prendendo 100x finché approx. Lowess produce risultati migliori rispetto a approx, mentre è ancora abbastanza veloce (15 volte più veloce di loess).

Loess diventa sempre più impantanato quando il numero di punti aumenta, diventando inutilizzabile intorno a 50.000.

MODIFICA: ulteriori ricerche mostrano che loess offre adattamenti migliori per determinati set di dati. Se hai a che fare con un set di dati di piccole dimensioni o le prestazioni non sono una considerazione, prova entrambe le funzioni e confronta i risultati.

0

In ggplot2 si può fare leviga in vari modi, ad esempio:

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