Jak wykreślić dwa histogramy razem w R?

221

Używam R i mam dwie ramki danych: marchew i ogórki. Każda ramka danych ma pojedynczą kolumnę numeryczną, która podaje długość wszystkich zmierzonych marchwi (łącznie: 100 tys. Marchwi) i ogórków (łącznie: 50 tys. Ogórków).

Chciałbym narysować dwa histogramy - długość marchwi i długości ogórków - na tej samej działce. Nakładają się na siebie, więc potrzebuję też pewnej przejrzystości. Muszę również użyć częstotliwości względnych, a nie liczb bezwzględnych, ponieważ liczba wystąpień w każdej grupie jest inna.

coś takiego byłoby fajne, ale nie rozumiem, jak to zrobić z moich dwóch tabel:

nakładająca się gęstość

David B.
źródło
Przy okazji, z jakiego oprogramowania zamierzasz korzystać? W przypadku oprogramowania typu open source polecam gnuplot.info [gnuplot]. Wierzę, że w jego dokumentacji znajdziesz pewną technikę i przykładowe skrypty do robienia tego, co chcesz.
noel aye
1
Używam R, jak sugeruje tag (edytowany post, aby to wyjaśnić)
David B
1
ktoś opublikował fragment kodu, aby to zrobić w tym wątku: stackoverflow.com/questions/3485456/…
nico

Odpowiedzi:

194

Ten obraz, z którym się łączyłeś, dotyczył krzywych gęstości, a nie histogramów.

Jeśli czytasz na ggplot, być może jedyne, czego brakuje, to połączenie dwóch ramek danych w jedną długą.

Zacznijmy więc od tego, co masz, dwóch oddzielnych zestawów danych i połącz je.

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)

Po tym, co nie jest konieczne, jeśli dane są już w długim formacie, wystarczy tylko jeden wiersz, aby utworzyć wykres.

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

wprowadź opis zdjęcia tutaj

Teraz, jeśli naprawdę chcesz histogramy, następujące działania będą działać. Zauważ, że musisz zmienić pozycję z domyślnego argumentu „stos”. Możesz tego przegapić, jeśli tak naprawdę nie masz pojęcia, jak powinny wyglądać Twoje dane. Wyższa alfa wygląda tam lepiej. Zauważ też, że zrobiłem to histogramy gęstości. Łatwo jest go usunąć, y = ..density..aby przywrócić go do zliczeń.

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

wprowadź opis zdjęcia tutaj

Jan
źródło
8
Jeśli chcesz pozostać przy histogramach, użyj ggplot(vegLengths, aes(length, fill = veg)) + geom_bar(pos="dodge"). Spowoduje to utworzenie histogramów z przeplotem, jak w MATLAB.
Mbq
1
Dziękuję za odpowiedź! Część „pozycja =„ tożsamość ”” jest w rzeczywistości ważna, ponieważ w przeciwnym razie pręty są ułożone w stos, co wprowadza w błąd w połączeniu z gęstością, która domyślnie wydaje się być „tożsamością”, tj. Nakładana w przeciwieństwie do stosu.
Shadow
265

Oto jeszcze prostsze rozwiązanie z wykorzystaniem podstawowej grafiki i mieszania alfa (które nie działa na wszystkich urządzeniach graficznych):

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

Kluczem jest to, że kolory są półprzezroczyste.

Edytuj, ponad dwa lata później : Ponieważ otrzymałem właśnie opinię, sądzę, że równie dobrze mogę dodać grafikę tego, co wytwarza kod, gdy mieszanie alfa jest tak przydatne:

wprowadź opis zdjęcia tutaj

Dirk Eddelbuettel
źródło
6
+1 dziękuję wszystkim, czy można to przekształcić w płynniejszy gistogram (jak had.co.nz/ggplot2/graphics/55078149a733dd1a0b42a57faf847036.png )?
David B,
3
Dlaczego rozdzieliłeś plotpolecenia? Możesz umieścić wszystkie te opcje w histpoleceniach i tylko dwie w dwóch liniach.
Jan
@John Jak byś to zrobił?
HelloWorld,
Umieść opcje w plotpoleceniu bezpośrednio w poleceniu hist, tak jak powiedziałem. Publikowanie kodu nie jest tym, do czego służą komentarze.
Jan
44

Oto funkcja, którą napisałem, która używa pseudo-przezroczystości do reprezentowania nakładających się histogramów

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

Oto inny sposób, aby to zrobić za pomocą obsługi R dla przezroczystych kolorów

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

Wyniki wyglądają mniej więcej tak: alternatywny tekst

chrisamiller
źródło
+1 za opcję dostępną na wszystkich urządzeniach graficznych (np. postscript)
Lenna
31

Są już piękne odpowiedzi, ale pomyślałem o dodaniu tego. Dla mnie wygląda dobrze. (Skopiowano losowe liczby z @Dirk). library(scales)jest potrzebny ”

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)

Wynik to...

wprowadź opis zdjęcia tutaj

Aktualizacja: Ta funkcja nakładania się może być również przydatna dla niektórych.

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

Wydaje mi się, że wynik hist0jest ładniejszy niż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) }

Wynik

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

jest

wprowadź opis zdjęcia tutaj

Stat-R
źródło
24

Oto przykład, jak to zrobić w „klasycznej” grafice R:

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

Jedynym problemem jest to, że wygląda znacznie lepiej, jeśli podział histogramu jest wyrównany, co może wymagać ręcznego wykonania (w przekazanych argumentach hist).

nullglob
źródło
Bardzo dobrze. Przypomniało mi to także o jednym stackoverflow.com/questions/3485456/…
George Dontas
Zwiększenie tego, ponieważ ta odpowiedź jest jedyną (oprócz tych w ggplot), która bezpośrednio bierze pod uwagę, jeśli twoje dwa histogramy mają zasadniczo różne rozmiary próbek.
MichaelChirico
Podoba mi się ta metoda, pamiętaj, że możesz synchronizować przerwy, definiując je za pomocą seq (). Na przykład:breaks=seq(min(data$some_property), max(data$some_property), by=(max_prop - min_prop)/20)
Deruijter
17

Oto wersja podobna do ggplot2, którą podałem tylko w bazie R. Skopiowałem niektóre z @nullglob.

generować dane

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

Nie musisz umieszczać go w ramce danych, jak w przypadku ggplot2. Wadą tej metody jest to, że musisz napisać o wiele więcej szczegółów fabuły. Zaletą jest to, że masz kontrolę nad większą ilością szczegółów fabuły.

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

wprowadź opis zdjęcia tutaj

Jan
źródło
9

@Dirk Eddelbuettel: Podstawowa idea jest doskonała, ale przedstawiony kod można ulepszyć. [Trzeba długo wyjaśniać, stąd osobna odpowiedź, a nie komentarz.]

hist()Funkcja domyślnie zwraca działek, więc trzeba dodać plot=FALSEopcję. Co więcej, plot(0,0,type="n",...)łatwiej jest ustalić obszar wydruku za pomocą wywołania, w którym można dodać etykiety osi, tytuł wykresu itp. Na koniec chciałbym wspomnieć, że można również użyć cieniowania, aby rozróżnić dwa histogramy. Oto kod:

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)

A oto wynik (nieco za szeroki z powodu RStudio :-)):

wprowadź opis zdjęcia tutaj

Laryx Decidua
źródło
podniesienie tego, ponieważ jest to bardzo prosta opcja przy użyciu bazy i opłacalna na postscripturządzeniach.
MichaelChirico
6

API R Plotly może być dla Ciebie przydatne. Poniższy wykres jest tutaj .

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)

Pełne ujawnienie: jestem w zespole.

Wykres

Mateo Sanchez
źródło
1

Tyle świetnych odpowiedzi, ale skoro właśnie napisałem funkcję function ( plotMultipleHistograms()), aby to zrobić, pomyślałem, że dodam inną odpowiedź.

Zaletą tej funkcji jest to, że automatycznie ustawia odpowiednie ograniczenia osi X i Y oraz definiuje wspólny zestaw pojemników, których używa we wszystkich dystrybucjach.

Oto jak go użyć:

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

wprowadź opis zdjęcia tutaj

plotMultipleHistograms()Funkcja może mieć dowolną liczbę rozkładów, a wszystkie parametry ogólne kreślenia powinna pracować z nim (na przykład las, mainitd.)

Joseph Crispell
źródło