2009-09-04 13 views
19

Próbuję wykreślić dane typu sieci z GGPLOT2, a następnie nakładam normalny rozkład na dane próbki, aby zilustrować, jak daleko od normalnych danych podstawowych. Chciałbym mieć normalny dystans na górze, aby mieć ten sam średni i stdev co panel.przy użyciu funkcji stat_function i facet_wrap razem w GGPLOT2 w R

oto przykład:

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) 

To wszystko działa świetnie i daje piękny wykres trzy panelu danych. Jak dodać normalny dystans na górze? Wydaje użyłbym stat_function, ale to się nie powiedzie:

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

Wydaje się, że nie jest coraz stat_function wraz z funkcją facet_wrap. Jak sprawić, by ci dwaj grali ładnie?

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

Próbowałem zintegrować pomysłów z dwóch odpowiedzi poniżej i jestem nadal nie istnieje:

stosując kombinację obu odpowiedziach mogę włamywanie razem to:

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) 

który jest bardzo blisko ... chyba coś jest nie tak z normalnego dist wydrukiem:

enter image description here

co ja tu robię źle?

+6

W przyszłości można proszę użyć nazwy zmiennych z obu mieszanych przypadku _or_ podkreślenia, ale nie jednocześnie. To mnie zabija! – hadley

+0

OK ok, to dobra uwaga. :) –

+0

Przesunąłem moją "odpowiedź" w pole pytania. Powinienem to tam umieścić na początek. Moje przeprosiny dla tych, którzy napisali komentarze, ponieważ nie przekazali. Będę bardziej zamyślony, jak to zrobię w przyszłości. –

Odpowiedz

34

stat_function jest przeznaczony do nakładania tej samej funkcji w każdym panelu. (Nie ma oczywistego sposobu na dopasowanie parametrów funkcji do różnych paneli).

Jak sugeruje Ian, najlepszym sposobem jest wygenerowanie normalnych krzywych i wydrukowanie ich jako oddzielnego zbioru danych (w tym miejscu było źle, wcześniej - scalanie nie ma sensu dla tego przykładu i jeśli patrzysz uważnie, zobaczysz, dlaczego otrzymujesz dziwny wzór piłokształtny).

Oto jak pójdę o rozwiązywaniu problemu:

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

To ma sens po tym, jak to wyjaśnisz. Nie było dla mnie intuicyjne, że funkcja stat_ została zaprojektowana dla pojedynczych krzywych. Po prostu założyłem, że robię to źle. Dzięki za poświęcenie czasu, aby dać przykład, jest to fantastycznie pomocne. –

+0

Czy nadal jest tak, że 'stat_function' nie może zapewnić innej krzywej dla każdego panelu? Wygląda na to, że po prostu chcesz przekazać nazwaną listę funkcji i dopasować te nazwy do zmiennej kategorialnej przypisanej do 'facet_wrap', czy też podać tę funkcję jako argument w oryginalnej tabeli danych? – cboettig

+0

@cboettig no i jest mało prawdopodobne, że kiedykolwiek będzie. Lista nazwana nie działałaby dla wielu zmiennych fasetowania. – hadley

3

Myślę, że musisz podać więcej informacji. Wydaje się, że działa:

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 

Dostarczamy ten sam parametr mean i sd dla każdego panelu. Pierwsze panelu specyficzny średnie i odchylenia standardowe pozostawiono jako ćwiczenie dla czytelnika *;)

„*” Innymi słowy, nie wiem, jak można to zrobić ...

+1

Specyficzne dla panelu sposoby i standardowe odchylenia można uzyskać, używając biblioteki (plyr) i ddply (dd,. (State_CD), podsumowując, ...) – Nova

+1

Dobry wysiłek, ale ... OP stwierdził na górze '' "Ja lubię mieć normalny dystans na górze, aby mieć ten sam średni i stdev co panel. "' ' – PatrickT

1

Myślę, że najlepiej jest narysuj linię ręcznie za pomocą 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

Jeśli nie chcesz, aby wygenerować rozkładu normalnego line-graph „ręcznie”, nadal używać stat_function, i wyświetlać wykresy obok siebie - wtedy możesz rozważyć użycie funkcji "multiplot" opublikowanej w "Cookbook for R" jako alternatywy dla facet_wrap. Możesz skopiować kod mnożnika do swojego projektu from here.

Po skopiowaniu kodu, wykonaj następujące czynności:

# 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