Wie zeichnet man zwei Histogramme in R zusammen?

221

Ich benutze R und habe zwei Datenrahmen: Karotten und Gurken. Jeder Datenrahmen hat eine einzelne numerische Spalte, in der die Länge aller gemessenen Karotten (insgesamt: 100.000 Karotten) und Gurken (insgesamt: 50.000 Gurken) aufgeführt ist.

Ich möchte zwei Histogramme - Karottenlänge und Gurkenlänge - auf derselben Handlung zeichnen. Sie überschneiden sich, also brauche ich wohl auch etwas Transparenz. Ich muss auch relative Häufigkeiten verwenden, keine absoluten Zahlen, da die Anzahl der Instanzen in jeder Gruppe unterschiedlich ist.

so etwas wäre schön, aber ich verstehe nicht, wie ich es aus meinen beiden Tabellen erstellen soll:

überlappende Dichte

David B.
quelle
Übrigens, welche Software planen Sie zu verwenden? Für Open Source würde ich gnuplot.info [gnuplot] empfehlen . Ich glaube, dass Sie in der Dokumentation bestimmte Techniken und Beispielskripte finden, um das zu tun, was Sie wollen.
Noel Aye
1
Ich benutze R, wie das Tag vorschlägt (bearbeiteter Beitrag, um dies klar zu machen)
David B
1
Jemand hat ein Code-Snippet in diesem Thread gepostet
nico

Antworten:

194

Das Bild, mit dem Sie verknüpft haben, war für Dichtekurven gedacht, nicht für Histogramme.

Wenn Sie auf ggplot gelesen haben, fehlt Ihnen möglicherweise nur die Kombination Ihrer beiden Datenrahmen zu einem langen.

Beginnen wir also mit so etwas wie dem, was Sie haben, zwei getrennten Datensätzen und kombinieren sie.

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)

Danach, was nicht erforderlich ist, wenn Ihre Daten bereits im Langformat vorliegen, benötigen Sie nur noch eine Zeile, um Ihren Plot zu erstellen.

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

Geben Sie hier die Bildbeschreibung ein

Wenn Sie nun wirklich Histogramme wollten, funktioniert Folgendes. Beachten Sie, dass Sie die Position gegenüber dem Standardargument "Stapel" ändern müssen. Sie könnten das vermissen, wenn Sie nicht wirklich eine Vorstellung davon haben, wie Ihre Daten aussehen sollten. Ein höheres Alpha sieht dort besser aus. Beachten Sie auch, dass ich es Dichtehistogramme gemacht habe. Es ist einfach, das zu entfernen y = ..density.., um es wieder zu zählen.

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

Geben Sie hier die Bildbeschreibung ein

John
quelle
8
Wenn Sie bei Histogrammen bleiben möchten, verwenden Sie ggplot(vegLengths, aes(length, fill = veg)) + geom_bar(pos="dodge"). Dadurch werden Interlaced-Histogramme erstellt, wie in MATLAB.
mbq
1
Danke für die Antwort! Der Teil 'position = "identity"' ist tatsächlich wichtig, da ansonsten die Balken gestapelt werden, was irreführend ist, wenn sie mit einer Dichte kombiniert werden, die standardmäßig "Identität" zu sein scheint, dh überlagert im Gegensatz zu gestapelt.
Schatten
265

Hier ist eine noch einfachere Lösung mit Basisgrafiken und Alpha-Blending (die nicht auf allen Grafikgeräten funktioniert):

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

Der Schlüssel ist, dass die Farben halbtransparent sind.

Bearbeiten, mehr als zwei Jahre später : Da dies gerade positiv bewertet wurde, kann ich auch ein Bild davon hinzufügen, was der Code erzeugt, da Alpha-Blending so verdammt nützlich ist:

Geben Sie hier die Bildbeschreibung ein

Dirk Eddelbuettel
quelle
6
+1 Vielen Dank an alle, kann dies in ein flüssigeres Gistogramm umgewandelt werden (wie had.co.nz/ggplot2/graphics/55078149a733dd1a0b42a57faf847036.png )?
David B
3
Warum haben Sie die plotBefehle getrennt? Sie können alle diese Optionen in die histBefehle einfügen und nur zwei in die beiden Zeilen.
John
@ John Wie würdest du das machen?
HelloWorld
Fügen Sie die Optionen im plotBefehl wie gesagt direkt in den Befehl hist ein. Das Posten des Codes ist nicht das, wofür Kommentare gedacht sind.
John
44

Hier ist eine Funktion, die ich geschrieben habe und die Pseudotransparenz verwendet, um überlappende Histogramme darzustellen

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

Hier ist eine andere Möglichkeit, die Unterstützung von R für transparente Farben zu verwenden

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

Die Ergebnisse sehen ungefähr so ​​aus: Alt-Text

Chrisamiller
quelle
+1 für eine Option, die auf allen Grafikgeräten verfügbar ist (z. B. postscript)
Lenna
31

Es gibt bereits schöne Antworten, aber ich dachte darüber nach, diese hinzuzufügen. Sieht gut für mich aus. (Kopierte Zufallszahlen von @Dirk). library(scales)wird benötigt`

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)

Das Ergebnis ist...

Geben Sie hier die Bildbeschreibung ein

Update: Diese überlappende Funktion kann auch für einige nützlich sein.

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

Ich finde das Ergebnis von hist0ist schöner alshist

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

Das Ergebnis von

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

ist

Geben Sie hier die Bildbeschreibung ein

Stat-R
quelle
24

Hier ist ein Beispiel, wie Sie dies in "klassischen" R-Grafiken tun können:

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

Das einzige Problem dabei ist, dass es viel besser aussieht, wenn die Histogrammumbrüche ausgerichtet sind, was möglicherweise manuell erfolgen muss (in den an übergebenen Argumenten hist).

nullglob
quelle
Sehr schön. Es erinnerte mich auch an diesen einen stackoverflow.com/questions/3485456/…
George Dontas
Dies zu erhöhen, da diese Antwort die einzige ist (neben den Antworten in ggplot), die direkt erklärt, ob Ihre beiden Histogramme wesentlich unterschiedliche Stichprobengrößen aufweisen.
MichaelChirico
Ich mag diese Methode. Beachten Sie, dass Sie Pausen synchronisieren können, indem Sie sie mit seq () definieren. Zum Beispiel:breaks=seq(min(data$some_property), max(data$some_property), by=(max_prop - min_prop)/20)
Deruijter
17

Hier ist die Version wie die ggplot2, die ich nur in Base R gegeben habe. Ich habe einige von @nullglob kopiert.

Generieren Sie die Daten

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

Sie müssen es nicht wie bei ggplot2 in einen Datenrahmen einfügen. Der Nachteil dieser Methode ist, dass Sie viel mehr Details der Handlung aufschreiben müssen. Der Vorteil ist, dass Sie die Kontrolle über weitere Details des Diagramms haben.

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

Geben Sie hier die Bildbeschreibung ein

John
quelle
9

@ Kirk Eddelbuettel: Die Grundidee ist ausgezeichnet, aber der gezeigte Code kann verbessert werden. [Die Erklärung dauert lange, daher eine separate Antwort und kein Kommentar.]

Die hist()Funktion zeichnet standardmäßig Diagramme, daher müssen Sie die plot=FALSEOption hinzufügen . Darüber hinaus ist es klarer, den Plotbereich durch einen plot(0,0,type="n",...)Aufruf festzulegen , in dem Sie die Achsenbeschriftungen, den Plottitel usw. hinzufügen können. Abschließend möchte ich erwähnen, dass man auch Schattierungen verwenden kann, um zwischen den beiden Histogrammen zu unterscheiden. Hier ist der Code:

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)

Und hier ist das Ergebnis (etwas zu breit wegen RStudio :-)):

Geben Sie hier die Bildbeschreibung ein

Kehlkopf Decidua
quelle
Dies zu verbessern, da es eine sehr einfache Option ist, die Basis verwendet und auf postscriptGeräten funktioniert .
MichaelChirico
6

Die R-API von Plotly könnte für Sie nützlich sein. Die Grafik unten ist hier .

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)

Vollständige Offenlegung: Ich bin im Team.

Graph

Mateo Sanchez
quelle
1

So viele gute Antworten, aber da ich gerade eine function ( plotMultipleHistograms()) -Funktion geschrieben habe, dachte ich, ich würde eine weitere Antwort hinzufügen.

Der Vorteil dieser Funktion besteht darin, dass sie automatisch die entsprechenden Grenzwerte für die X- und Y-Achse festlegt und einen gemeinsamen Satz von Bins definiert, die für alle Verteilungen verwendet werden.

So verwenden Sie es:

# Install the plotteR package
install.packages("devtools")
devtools::install_github("JosephCrispell/basicPlotteR")
library(basicPlotteR)

# Set the seed
set.seed(254534)

# Create random samples from a normal distribution
distributions <- list(rnorm(500, mean=5, sd=0.5), 
                      rnorm(500, mean=8, sd=5), 
                      rnorm(500, mean=20, sd=2))

# Plot overlapping histograms
plotMultipleHistograms(distributions, nBins=20, 
                       colours=c(rgb(1,0,0, 0.5), rgb(0,0,1, 0.5), rgb(0,1,0, 0.5)), 
                       las=1, main="Samples from normal distribution", xlab="Value")

Geben Sie hier die Bildbeschreibung ein

Die plotMultipleHistograms()Funktion kann eine beliebige Anzahl von Verteilungen nehmen und alle allgemeinen Plotten Parameter sollen mit ihm arbeiten (zum Beispiel: las, main, etc.).

Joseph Crispell
quelle