Come tracciare due istogrammi insieme in R?

Sto usando R e ho due frame di dati: carote e cetrioli. Ogni frame di dati ha una singola colonna numerica che elenca la lunghezza di tutte le carote misurate (totale: 100k di carote) e di cetrioli (totale: 50k di cetrioli).

Vorrei tracciare due istogrammi – lunghezza della carota e lunghezza dei cetrioli – sulla stessa trama. Si sovrappongono, quindi suppongo di aver bisogno anche di trasparenza. Devo anche usare le frequenze relative non i numeri assoluti poiché il numero di istanze in ciascun gruppo è diverso.

qualcosa del genere sarebbe bello ma non capisco come crearlo dai miei due tavoli:

densità sovrapposta

Quell’immagine a cui ti sei collegato era per le curve di densità, non per gli istogrammi.

Se hai letto su ggplot, forse l’unica cosa che ti manca è combinare i tuoi due frame di dati in uno lungo.

Quindi, iniziamo con qualcosa di simile a ciò che hai, due insiemi di dati separati e combinali.

carrots <- data.frame(length = rnorm(100000, 6, 2)) cukes <- data.frame(length = rnorm(50000, 7, 2.5)) #Now, combine your two dataframes into one. First make a new column in each that will be a variable to identify where they came from later. carrots$veg <- 'carrot' cukes$veg <- 'cuke' #and combine into your new data frame vegLengths vegLengths <- rbind(carrots, cukes) 

Dopodiché, che non è necessario se i tuoi dati sono già in formato formale, è sufficiente una sola riga per creare la tua trama.

 ggplot(vegLengths, aes(length, fill = veg)) + geom_density(alpha = 0.2) 

inserisci la descrizione dell'immagine qui

Ora, se davvero volevi degli istogrammi, il seguente funzionerà. Nota che devi cambiare posizione dall'argomento "stack" predefinito. Potresti non accorgertene se non hai davvero un'idea di come dovrebbero essere i tuoi dati. Un alfa più alto sembra meglio lì. Si noti inoltre che ho creato istogrammi di densità. È facile rimuovere la y = ..density.. per riportarla ai conteggi.

 ggplot(vegLengths, aes(length, fill = veg)) + geom_histogram(alpha = 0.5, aes(y = ..density..), position = 'identity') 

inserisci la descrizione dell'immagine qui

Ecco una soluzione ancora più semplice che utilizza la grafica di base e alpha-blending (che non funziona su tutti i dispositivi grafici):

 set.seed(42) p1 <- hist(rnorm(500,4)) # centered at 4 p2 <- hist(rnorm(500,6)) # centered at 6 plot( p1, col=rgb(0,0,1,1/4), xlim=c(0,10)) # first histogram plot( p2, col=rgb(1,0,0,1/4), xlim=c(0,10), add=T) # second 

La chiave è che i colors sono semi-trasparenti.

Modifica, più di due anni dopo : Poiché questo ha appena ottenuto un upvote, immagino che possa anche aggiungere una visualizzazione di ciò che il codice produce come alpha-blending è così dannatamente utile:

inserisci la descrizione dell'immagine qui

Ecco una funzione che ho scritto che utilizza la pseudo-trasparenza per rappresentare istogrammi sovrapposti

 plotOverlappingHist <- function(a, b, colors=c("white","gray20","gray50"), breaks=NULL, xlim=NULL, ylim=NULL){ ahist=NULL bhist=NULL if(!(is.null(breaks))){ ahist=hist(a,breaks=breaks,plot=F) bhist=hist(b,breaks=breaks,plot=F) } else { ahist=hist(a,plot=F) bhist=hist(b,plot=F) dist = ahist$breaks[2]-ahist$breaks[1] breaks = seq(min(ahist$breaks,bhist$breaks),max(ahist$breaks,bhist$breaks),dist) ahist=hist(a,breaks=breaks,plot=F) bhist=hist(b,breaks=breaks,plot=F) } if(is.null(xlim)){ xlim = c(min(ahist$breaks,bhist$breaks),max(ahist$breaks,bhist$breaks)) } if(is.null(ylim)){ ylim = c(0,max(ahist$counts,bhist$counts)) } overlap = ahist for(i in 1:length(overlap$counts)){ if(ahist$counts[i] > 0 & bhist$counts[i] > 0){ overlap$counts[i] = min(ahist$counts[i],bhist$counts[i]) } else { overlap$counts[i] = 0 } } plot(ahist, xlim=xlim, ylim=ylim, col=colors[1]) plot(bhist, xlim=xlim, ylim=ylim, col=colors[2], add=T) plot(overlap, xlim=xlim, ylim=ylim, col=colors[3], add=T) } 

Ecco un altro modo per farlo usando il supporto di R per i colors trasparenti

 a=rnorm(1000, 3, 1) b=rnorm(1000, 6, 1) hist(a, xlim=c(0,10), col="red") hist(b, add=T, col=rgb(0, 1, 0, 0.5) ) 

I risultati finiscono per sembrare qualcosa del genere: alt text

Ecco un esempio di come puoi farlo nella grafica R “classica”:

 ## generate some random data carrotLengths <- rnorm(1000,15,5) cucumberLengths <- rnorm(200,20,7) ## calculate the histograms - don't plot yet histCarrot <- hist(carrotLengths,plot = FALSE) histCucumber <- hist(cucumberLengths,plot = FALSE) ## calculate the range of the graph xlim <- range(histCucumber$breaks,histCarrot$breaks) ylim <- range(0,histCucumber$density, histCarrot$density) ## plot the first graph plot(histCarrot,xlim = xlim, ylim = ylim, col = rgb(1,0,0,0.4),xlab = 'Lengths', freq = FALSE, ## relative, not absolute frequency main = 'Distribution of carrots and cucumbers') ## plot the second graph on top of this opar <- par(new = FALSE) plot(histCucumber,xlim = xlim, ylim = ylim, xaxt = 'n', yaxt = 'n', ## don't add axes col = rgb(0,0,1,0.4), add = TRUE, freq = FALSE) ## relative, not absolute frequency ## add a legend in the corner legend('topleft',c('Carrots','Cucumbers'), fill = rgb(1:0,0,0:1,0.4), bty = 'n', border = NA) par(opar) 

L'unico problema è che sembra molto meglio se le interruzioni degli istogrammi sono allineate, il che può essere fatto manualmente (negli argomenti passati a hist ).

Ci sono già delle belle risposte, ma ho pensato di aggiungere questo. Mi sembra buono. (Numeri casuali copiati da @Dirk). library(scales) »

 set.seed(42) hist(rnorm(500,4),xlim=c(0,10),col='skyblue',border=F) hist(rnorm(500,6),add=T,col=scales::alpha('red',.5),border=F) 

Il risultato è …

inserisci la descrizione dell'immagine qui

Aggiornamento: questa funzione di sovrapposizione potrebbe anche essere utile per alcuni.

 hist0 <- function(...,col='skyblue',border=T) hist(...,col=col,border=border) 

Sento che il risultato di hist0 è più bello da guardare che da un hist0

 hist2 <- function(var1, var2,name1='',name2='', breaks = min(max(length(var1), length(var2)),20), main0 = "", alpha0 = 0.5,grey=0,border=F,...) { library(scales) colh <- c(rgb(0, 1, 0, alpha0), rgb(1, 0, 0, alpha0)) if(grey) colh <- c(alpha(grey(0.1,alpha0)), alpha(grey(0.9,alpha0))) max0 = max(var1, var2) min0 = min(var1, var2) den1_max <- hist(var1, breaks = breaks, plot = F)$density %>% max den2_max <- hist(var2, breaks = breaks, plot = F)$density %>% max den_max <- max(den2_max, den1_max)*1.2 var1 %>% hist0(xlim = c(min0 , max0) , breaks = breaks, freq = F, col = colh[1], ylim = c(0, den_max), main = main0,border=border,...) var2 %>% hist0(xlim = c(min0 , max0), breaks = breaks, freq = F, col = colh[2], ylim = c(0, den_max), add = T,border=border,...) legend(min0,den_max, legend = c( ifelse(nchar(name1)==0,substitute(var1) %>% deparse,name1), ifelse(nchar(name2)==0,substitute(var2) %>% deparse,name2), "Overlap"), fill = c('white','white', colh[1]), bty = "n", cex=1,ncol=3) legend(min0,den_max, legend = c( ifelse(nchar(name1)==0,substitute(var1) %>% deparse,name1), ifelse(nchar(name2)==0,substitute(var2) %>% deparse,name2), "Overlap"), fill = c(colh, colh[2]), bty = "n", cex=1,ncol=3) } 

Il risultato di

 par(mar=c(3, 4, 3, 2) + 0.1) set.seed(100) hist2(rnorm(10000,2),rnorm(10000,3),breaks = 50) 

è

inserisci la descrizione dell'immagine qui

Ecco la versione come ggplot2 che ho dato solo nella base R. Ho copiato alcuni da @nullglob.

generare i dati

 carrots <- rnorm(100000,5,2) cukes <- rnorm(50000,7,2.5) 

Non è necessario inserirlo in una cornice dati come con ggplot2. Lo svantaggio di questo metodo è che devi scrivere molti più dettagli della trama. Il vantaggio è che hai il controllo su più dettagli della trama.

 ## calculate the density - don't plot yet densCarrot <- density(carrots) densCuke <- density(cukes) ## calculate the range of the graph xlim <- range(densCuke$x,densCarrot$x) ylim <- range(0,densCuke$y, densCarrot$y) #pick the colours carrotCol <- rgb(1,0,0,0.2) cukeCol <- rgb(0,0,1,0.2) ## plot the carrots and set up most of the plot parameters plot(densCarrot, xlim = xlim, ylim = ylim, xlab = 'Lengths', main = 'Distribution of carrots and cucumbers', panel.first = grid()) #put our density plots in polygon(densCarrot, density = -1, col = carrotCol) polygon(densCuke, density = -1, col = cukeCol) ## add a legend in the corner legend('topleft',c('Carrots','Cucumbers'), fill = c(carrotCol, cukeCol), bty = 'n', border = NA) 

inserisci la descrizione dell'immagine qui

@Dirk Eddelbuettel: L’idea di base è eccellente ma il codice mostrato può essere migliorato. [Ci vuole molto per spiegare, quindi una risposta separata e non un commento.]

La funzione hist() per default disegna i diagrammi, quindi è necessario aggiungere l’opzione plot=FALSE . Inoltre, è più chiaro stabilire l’area di trama con una chiamata plot(0,0,type="n",...) in cui è ansible aggiungere le etichette degli assi, il titolo della trama, ecc. Infine, vorrei ricordare che si potrebbe anche usare l’ombreggiatura per distinguere tra i due istogrammi. Ecco il codice:

 set.seed(42) p1 <- hist(rnorm(500,4),plot=FALSE) p2 <- hist(rnorm(500,6),plot=FALSE) plot(0,0,type="n",xlim=c(0,10),ylim=c(0,100),xlab="x",ylab="freq",main="Two histograms") plot(p1,col="green",density=10,angle=135,add=TRUE) plot(p2,col="blue",density=10,angle=45,add=TRUE) 

Ed ecco il risultato (un po 'troppo ampio a causa di RStudio :-)):

inserisci la descrizione dell'immagine qui

L’API R di Plotly potrebbe essere utile per te. Il grafico qui sotto è qui .

 library(plotly) #add username and key p <- plotly(username="Username", key="API_KEY") #generate data x0 = rnorm(500) x1 = rnorm(500)+1 #arrange your graph data0 = list(x=x0, name = "Carrots", type='histogramx', opacity = 0.8) data1 = list(x=x1, name = "Cukes", type='histogramx', opacity = 0.8) #specify type as 'overlay' layout <- list(barmode='overlay', plot_bgcolor = 'rgba(249,249,251,.85)') #format response, and use 'browseURL' to open graph tab in your browser. response = p$plotly(data0, data1, kwargs=list(layout=layout)) url = response$url filename = response$filename browseURL(response$url) 

Full disclosure: sono nella squadra.

Grafico