Hoe teken je twee histogrammen samen in R?

Ik gebruik R en ik heb twee dataframes: wortelen en komkommers. Elk gegevensframe heeft een enkele numerieke kolom met de lengte van alle gemeten wortelen (totaal: 100k wortelen) en komkommers (totaal: 50k komkommers).

Ik wil twee histogrammen – wortellengte en komkommerlengte – op dezelfde plot plotten. Ze overlappen elkaar, dus ik denk dat ik ook wat transparantie nodig heb. Ik moet ook relatieve frequenties gebruiken en geen absolute getallen, aangezien het aantal instanties in elke groep anders is.

Zoiets zou leuk zijn, maar ik begrijp niet hoe ik het van mijn twee tabellen moet maken:


Antwoord 1, autoriteit 100%

De afbeelding waarnaar je linkte was voor dichtheidscurves, niet voor histogrammen.

Als je op ggplot hebt gelezen, is het enige dat je misschien mist het combineren van je twee dataframes in één lange.

Dus, laten we beginnen met zoiets als wat je hebt, twee afzonderlijke gegevenssets en deze combineren.

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)

Daarna, wat niet nodig is als uw gegevens al in lang formaat zijn, heeft u slechts één regel nodig om uw plot te maken.

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

Nu, als je echt wel histogrammen wilden, zal het volgende werken. Merk op dat u de positie van het standaard “Stack” -argument moet wijzigen. Misschien mis je dat als je niet echt een idee hebt van hoe je gegevens eruit moeten zien. Een hogere alpha ziet er beter uit. Merk ook op dat ik de dichtheid histogrammen heb gemaakt. Het is eenvoudig om de y = ..density..om het terug te krijgen naar tellen.

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


Antwoord 2, Autoriteit 140%

Hier is een nog eenvoudiger oplossing met behulp van basisafbeeldingen en alfa-blending (die niet werkt op alle grafische apparaten):

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

De sleutel is dat de kleuren semi-transparant zijn.

Bewerken, meer dan twee jaar later : Omdat dit net een upvote kreeg, moet ik figuur ik kan net zo goed een visueel toevoegen van wat de code produceert als alfa-blending is zo’n dartel nuttig:


Antwoord 3, Autoriteit 21%

Hier is een functie die ik heb geschreven die gebruikt pseudo- transparantie om overlappende histogrammen

te vertegenwoordigen

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’s een andere manier om het te doen met R’s ondersteuning voor transparante kleuren

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

De resultaten eindigen op zoek iets als dit:


Antwoord 4, Autoriteit 17%

Reeds mooie antwoorden zijn er, maar ik dacht eraan dit toe te voegen. Het lijkt me goed.
(Gekopieerde willekeurige getallen van @Dirk). library(scales)is nodig`

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)

Het resultaat is…

Update:Deze overlappendefunctie kan ook voor sommigen nuttig zijn.

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

Ik vind het resultaat van hist0mooier om te zien dan hist

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

Het resultaat van

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

is


Antwoord 5, Autoriteit 12%

Hier is een voorbeeld van hoe u het kunt doen in “Classic” R-afbeeldingen:

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

Het enige probleem hiermee is dat het er veel beter uitziet als de histogramonderbrekingen worden uitgelijnd, wat mogelijk handmatig moet worden gedaan (in de argumenten die worden doorgegeven aan hist).


Antwoord 6, autoriteit 9%

Hier is de versie zoals de ggplot2 die ik alleen in basis R gaf. Ik heb er een paar gekopieerd van @nullglob.

de gegevens genereren

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

Je hoeft het niet in een dataframe te plaatsen zoals bij ggplot2. Het nadeel van deze methode is dat je veel meer details van de plot moet uitschrijven. Het voordeel is dat je controle hebt over meer details van de plot.

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


Antwoord 7, autoriteit 5%

@Dirk Eddelbuettel: Het basisidee is uitstekend, maar de getoonde code kan worden verbeterd. [Duurt lang om uit te leggen, vandaar een apart antwoord en geen commentaar.]

De functie hist()tekent standaard plots, dus u moet de optie plot=FALSEtoevoegen. Bovendien is het duidelijker om het plotgebied vast te stellen door een plot(0,0,type="n",...)-aanroep waarin u de aslabels, plottitel enz. kunt toevoegen. , zou ik willen vermelden dat men ook arcering zou kunnen gebruiken om onderscheid te maken tussen de twee histogrammen. Hier is de 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)

En hier is het resultaat (een beetje te breed vanwege RStudio 🙂 ):


Antwoord 8, autoriteit 3%

Plotly’s R APIkan nuttig voor je zijn. De onderstaande grafiek is 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)

Volledige openbaarmaking: ik ben in het team.


Antwoord 9

Zoveel geweldige antwoorden, maar sinds ik zojuist een functie heb geschreven (plotMultipleHistograms()in ‘Basicplotter’ pakket) functie om dit te doen, ik dacht dat ik een ander antwoord zou toevoegen.

Het voordeel van deze functie is dat het automatisch de juiste X- en Y-aslimieten instelt en een gemeenschappelijke set bakken definieert die het gebruikt in alle distributies.

HOE HOE TE GEBRUIKEN:

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

De plotMultipleHistograms()-functie kan een willekeurig aantal distributies ondernemen en alle algemene plotparameters moeten ermee werken (bijvoorbeeld: las, main, enz.).

Other episodes