2009-09-04 9 views
19

Sto provando a tracciare dati di tipo reticolo con GGPLOT2 e quindi sovrapporre una distribuzione normale sui dati di esempio per illustrare quanto lontano siano normali i dati sottostanti. Mi piacerebbe avere il dist normale in cima per avere la stessa media e stdev del pannello.utilizzando stat_function e facet_wrap insieme in GGPLOT2 in R

Ecco un esempio:

library(ggplot2) 

#make some example data 
dd<-data.frame(matrix(rnorm(144, mean=2, sd=2),72,2),c(rep("A",24),rep("B",24),rep("C",24))) 
colnames(dd) <- c("x_value", "Predicted_value", "State_CD") 

#This works 
pg <- ggplot(dd) + geom_density(aes(x=Predicted_value)) + facet_wrap(~State_CD) 
print(pg) 

che tutte le opere grandi e produce un bel grafico a tre panel di dati. Come aggiungo il dist normale alla parte superiore? Sembra che io userei stat_function, ma questo non riesce:

#this fails 
pg <- ggplot(dd) + geom_density(aes(x=Predicted_value)) + stat_function(fun=dnorm) + facet_wrap(~State_CD) 
print(pg) 

Sembra che lo stat_function non è sempre insieme con la funzione facet_wrap. Come faccio a far giocare bene questi due?

------------ EDIT ---------

ho cercato di integrare le idee da due delle risposte qui sotto e io sono ancora non c'è:

utilizzando una combinazione di entrambe le risposte che posso incidere insieme questa:

library(ggplot) 
library(plyr) 

#make some example data 
dd<-data.frame(matrix(rnorm(108, mean=2, sd=2),36,2),c(rep("A",24),rep("B",24),rep("C",24))) 
colnames(dd) <- c("x_value", "Predicted_value", "State_CD") 

DevMeanSt <- ddply(dd, c("State_CD"), function(df)mean(df$Predicted_value)) 
colnames(DevMeanSt) <- c("State_CD", "mean") 
DevSdSt <- ddply(dd, c("State_CD"), function(df)sd(df$Predicted_value)) 
colnames(DevSdSt) <- c("State_CD", "sd") 
DevStatsSt <- merge(DevMeanSt, DevSdSt) 

pg <- ggplot(dd, aes(x=Predicted_value)) 
pg <- pg + geom_density() 
pg <- pg + stat_function(fun=dnorm, colour='red', args=list(mean=DevStatsSt$mean, sd=DevStatsSt$sd)) 
pg <- pg + facet_wrap(~State_CD) 
print(pg) 

che è davvero vicino ... tranne che qualcosa non va con il normale tracciato dist:

enter image description here

cosa sto facendo di sbagliato qui?

+6

In futuro, la prego di usare nomi di variabili sia con lettere maiuscole e minuscole _oppure_ sottolinea, ma non entrambi. Mi sta uccidendo! – hadley

+0

ok ok, questo è un buon punto. :) –

+0

Ho spostato la mia "risposta" nell'area delle domande. Avrei dovuto metterlo lì per cominciare. Le mie scuse a coloro che hanno fatto commenti in quanto non hanno trasferito. Sarò più riflessivo su come lo faccio in futuro. –

risposta

34

stat_function è progettato per sovrapporre la stessa funzione in ogni pannello. (Non esiste un modo ovvio per abbinare i parametri della funzione con i diversi pannelli).

Come suggerisce Ian, il modo migliore è quello di generare le curve normali te stesso, e li tracciare come un separato dataset (questo è dove si andavano sbagliato prima - la fusione semplicemente non ha senso per questo esempio e se guardi attentamente vedrai che è per questo che ottieni lo strano motivo a dente di sega).

Ecco come mi piacerebbe andare di risolvere il problema:

dd <- data.frame(
    predicted = rnorm(72, mean = 2, sd = 2), 
    state = rep(c("A", "B", "C"), each = 24) 
) 

grid <- with(dd, seq(min(predicted), max(predicted), length = 100)) 
normaldens <- ddply(dd, "state", function(df) { 
    data.frame( 
    predicted = grid, 
    density = dnorm(grid, mean(df$predicted), sd(df$predicted)) 
) 
}) 

ggplot(dd, aes(predicted)) + 
    geom_density() + 
    geom_line(aes(y = density), data = normaldens, colour = "red") + 
    facet_wrap(~ state) 

enter image description here

+0

Questo ha perfettamente senso dopo averlo spiegato. Non era intuitivo per me che stat_function fosse progettato per curve singole. Ho solo dato per scontato che stavo sbagliando. Grazie per il tempo dedicato a dare un esempio, è straordinariamente utile. –

+0

E 'ancora il caso che 'stat_function' non possa fornire una curva diversa per ciascun pannello? Sembra che si voglia semplicemente passare una lista di funzioni nominate, e abbinare quei nomi alla variabile categoriale data a 'facet_wrap', oppure fornire la funzione come argomento nella tabella di dati originale? – cboettig

+0

@cboettig no ed è improbabile che lo sarà mai. Una lista nominata non funzionerebbe per più variabili di sfaccettatura. – hadley

3

Penso che sia necessario fornire ulteriori informazioni. Questo sembra funzionare:

pg <- ggplot(dd, aes(Predicted_value)) ## need aesthetics in the ggplot 
pg <- pg + geom_density() 
## gotta provide the arguments of the dnorm 
pg <- pg + stat_function(fun=dnorm, colour='red',    
      args=list(mean=mean(dd$Predicted_value), sd=sd(dd$Predicted_value))) 
## wrap it! 
pg <- pg + facet_wrap(~State_CD) 
pg 

Forniamo lo stesso parametro di media e sd per ogni pannello. Ottenere pannello specifico medie e deviazioni standard viene lasciato come esercizio al lettore *;)

'*' In altre parole, non so come si può fare ...

+1

I mezzi specifici del pannello e le deviazioni standard possono essere ottenuti usando la libreria (plyr) e ddply (dd,. (State_CD), riepilogo, ...) – Nova

+1

Un buon sforzo, ma ... OP dichiarato in alto '' "Vorrei Mi piace avere il dist normale in cima per avere lo stesso mean e stdev del pannello. "" – PatrickT

1

penso che la soluzione migliore è quella di disegna la linea manualmente con geom_line.

dd<-data.frame(matrix(rnorm(144, mean=2, sd=2),72,2),c(rep("A",24),rep("B",24),rep("C",24))) 
colnames(dd) <- c("x_value", "Predicted_value", "State_CD") 
dd$Predicted_value<-dd$Predicted_value*as.numeric(dd$State_CD) #make different by state 

##Calculate means and standard deviations by level 
means<-as.numeric(by(dd[,2],dd$State_CD,mean)) 
sds<-as.numeric(by(dd[,2],dd$State_CD,sd)) 

##Create evenly spaced evaluation points +/- 3 standard deviations away from the mean 
dd$vals<-0 
for(i in 1:length(levels(dd$State_CD))){ 
    dd$vals[dd$State_CD==levels(dd$State_CD)[i]]<-seq(from=means[i]-3*sds[i], 
          to=means[i]+3*sds[i], 
          length.out=sum(dd$State_CD==levels(dd$State_CD)[i])) 
} 
##Create normal density points 
dd$norm<-with(dd,dnorm(vals,means[as.numeric(State_CD)], 
         sds[as.numeric(State_CD)])) 


pg <- ggplot(dd, aes(Predicted_value)) 
pg <- pg + geom_density() 
pg <- pg + geom_line(aes(x=vals,y=norm),colour="red") #Add in normal distribution 
pg <- pg + facet_wrap(~State_CD,scales="free") 
pg 
1

Se non si desidera generare la distribuzione normale linea-grafico "a mano", usano ancora stat_function, e mostra i grafici fianco a fianco - quindi potresti prendere in considerazione l'utilizzo della funzione "multiplot" pubblicata su "Cookbook for R" come alternativa a facet_wrap. È possibile copiare il codice multiplot sul progetto from here.

Dopo aver copiato il codice, effettuare le seguenti operazioni:

# Some fake data (copied from hadley's answer) 
dd <- data.frame(
    predicted = rnorm(72, mean = 2, sd = 2), 
    state = rep(c("A", "B", "C"), each = 24) 
) 

# Split the data by state, apply a function on each member that converts it into a 
# plot object, and return the result as a vector. 
plots <- lapply(split(dd,dd$state),FUN=function(state_slice){ 
    # The code here is the plot code generation. You can do anything you would 
    # normally do for a single plot, such as calling stat_function, and you do this 
    # one slice at a time. 
    ggplot(state_slice, aes(predicted)) + 
    geom_density() + 
    stat_function(fun=dnorm, 
        args=list(mean=mean(state_slice$predicted), 
          sd=sd(state_slice$predicted)), 
        color="red") 
}) 

# Finally, present the plots on 3 columns. 
multiplot(plotlist = plots, cols=3) 

enter image description here