Bagaimana cara memplot dua histogram bersama di R?

221

Saya menggunakan R dan saya memiliki dua frame data: wortel dan mentimun. Setiap kerangka data memiliki kolom numerik tunggal yang mencantumkan panjang semua wortel yang diukur (total: 100k wortel) dan mentimun (total: 50k mentimun).

Saya ingin membuat dua histogram - panjang wortel dan panjang mentimun - pada plot yang sama. Mereka tumpang tindih, jadi saya kira saya juga perlu transparansi. Saya juga perlu menggunakan frekuensi relatif bukan angka absolut karena jumlah instance dalam setiap kelompok berbeda.

sesuatu seperti ini akan menyenangkan tetapi saya tidak mengerti cara membuatnya dari dua tabel saya:

kepadatan tumpang tindih

David B
sumber
Btw, perangkat lunak apa yang Anda rencanakan untuk digunakan? Untuk open source, saya akan merekomendasikan gnuplot.info [gnuplot]. Dalam dokumentasinya, saya yakin Anda akan menemukan teknik dan skrip sampel tertentu untuk melakukan apa yang Anda inginkan.
noel aye
1
Saya menggunakan R sebagai tag yang disarankan (pos yang diedit untuk memperjelas ini)
David B
1
seseorang memposting beberapa cuplikan kode untuk melakukannya di utas ini: stackoverflow.com/questions/3485456/…
nico

Jawaban:

194

Gambar yang Anda tautkan adalah untuk kurva kerapatan, bukan histogram.

Jika Anda membaca di ggplot maka mungkin satu-satunya hal yang Anda lewatkan adalah menggabungkan dua frame data Anda menjadi satu yang panjang.

Jadi, mari kita mulai dengan sesuatu seperti apa yang Anda miliki, dua set data yang terpisah dan menggabungkannya.

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)

Setelah itu, yang tidak perlu jika data Anda sudah dalam format panjang, Anda hanya perlu satu baris untuk membuat plot Anda.

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

masukkan deskripsi gambar di sini

Sekarang, jika Anda benar-benar ingin histogram, berikut ini akan berfungsi. Perhatikan bahwa Anda harus mengubah posisi dari argumen "tumpukan" default. Anda mungkin kehilangan itu jika Anda tidak benar-benar tahu seperti apa data Anda seharusnya. Alfa yang lebih tinggi terlihat lebih baik di sana. Juga perhatikan bahwa saya membuatnya kepadatan histogram. Sangat mudah untuk menghapus y = ..density..untuk mendapatkannya kembali ke hitungan.

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

masukkan deskripsi gambar di sini

John
sumber
8
Jika Anda ingin tetap menggunakan histogram, gunakan ggplot(vegLengths, aes(length, fill = veg)) + geom_bar(pos="dodge"). Ini akan membuat histogram interlaced, seperti di MATLAB.
mbq
1
Terima kasih untuk jawabannya! Bagian 'position = "identity"' sebenarnya penting karena jika tidak, bilah ditumpuk yang menyesatkan ketika dikombinasikan dengan kepadatan yang secara default tampaknya "identitas", yaitu, dilapis sebagai lawan ditumpuk.
Bayangan
265

Berikut ini adalah solusi yang lebih sederhana menggunakan grafis dasar dan alpha-blending (yang tidak bekerja pada semua perangkat grafis):

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

Kuncinya adalah bahwa warnanya semi-transparan.

Sunting, lebih dari dua tahun kemudian : Karena ini baru saja mendapat upvote, saya kira saya bisa menambahkan visual dari apa yang dihasilkan kode sebagai alpha-blending sangat berguna:

masukkan deskripsi gambar di sini

Dirk Eddelbuettel
sumber
6
+1 terima kasih semua, dapatkah ini dikonversi menjadi gistogram yang lebih halus (seperti had.co.nz/ggplot2/graphics/55078149a733dd1a0b42a57faf847036.png )?
David B
3
Mengapa Anda memisahkan plotperintah? Anda dapat menempatkan semua opsi itu ke dalam histperintah dan hanya dua di dua baris.
John
@ John Bagaimana Anda melakukannya?
HelloWorld
Masukkan opsi dalam plotperintah langsung ke perintah hist seperti yang saya katakan. Memposting kode bukanlah untuk apa komentar.
John
44

Inilah fungsi yang saya tulis yang menggunakan pseudo-transparansi untuk mewakili histogram yang tumpang tindih

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

Berikut cara lain untuk melakukannya menggunakan dukungan R untuk warna transparan

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

Hasilnya akhirnya terlihat seperti ini: teks alternatif

chrisamiller
sumber
+1 untuk opsi yang tersedia di semua perangkat grafis (mis. postscript)
Lenna
31

Sudah ada jawaban yang indah, tetapi saya berpikir untuk menambahkan ini. Terlihat bagus untukku. (Menyalin nomor acak dari @Dirk). library(scales)diperlukan`

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)

Hasilnya adalah...

masukkan deskripsi gambar di sini

Pembaruan: Fungsi yang tumpang tindih ini mungkin juga bermanfaat bagi sebagian orang.

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

Saya merasa hasil dari hist0lebih cantik untuk dilihat daripadahist

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

Hasil dari

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

adalah

masukkan deskripsi gambar di sini

Stat-R
sumber
24

Berikut adalah contoh bagaimana Anda dapat melakukannya dalam grafik "klasik" 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)

Satu-satunya masalah dengan ini adalah bahwa itu terlihat jauh lebih baik jika istirahat histogram disejajarkan, yang mungkin harus dilakukan secara manual (dalam argumen yang diteruskan ke hist).

nullglob
sumber
Sangat bagus. Itu juga mengingatkan saya pada satu stackoverflow.com/questions/3485456/…
George Dontas
Meningkatkan ini karena jawaban ini adalah satu-satunya (selain yang masuk ggplot) yang secara langsung menjelaskan jika dua histogram Anda memiliki ukuran sampel yang sangat berbeda.
MichaelChirico
Saya suka metode ini, perhatikan bahwa Anda dapat menyinkronkan jeda dengan mendefinisikannya dengan seq (). Misalnya:breaks=seq(min(data$some_property), max(data$some_property), by=(max_prop - min_prop)/20)
Deruijter
17

Inilah versi seperti ggplot2 yang saya berikan hanya di basis R. Saya menyalin beberapa dari @nullglob.

menghasilkan data

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

Anda tidak perlu memasukkannya ke dalam bingkai data seperti dengan ggplot2. Kelemahan dari metode ini adalah Anda harus menulis lebih banyak detail dari alur cerita. Keuntungannya adalah Anda memiliki kendali atas detail plot yang lebih banyak.

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

masukkan deskripsi gambar di sini

John
sumber
9

@Dirk Eddelbuettel: Ide dasarnya sangat bagus tetapi kode seperti yang ditunjukkan dapat ditingkatkan. [Butuh waktu lama untuk menjelaskan, karena itu jawaban yang terpisah dan bukan komentar.]

The hist()Fungsi secara default menarik plot, sehingga Anda perlu menambahkan plot=FALSEopsi. Selain itu, lebih jelas untuk menetapkan area plot dengan plot(0,0,type="n",...)panggilan di mana Anda dapat menambahkan label sumbu, judul plot dll. Akhirnya, saya ingin menyebutkan bahwa orang juga dapat menggunakan shading untuk membedakan antara dua histogram. Ini kodenya:

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)

Dan inilah hasilnya (agak terlalu lebar karena RStudio :-)):

masukkan deskripsi gambar di sini

Laryx Decidua
sumber
meningkatkan ini karena ini adalah pilihan yang sangat sederhana menggunakan basis dan layak pada postscriptperangkat.
MichaelChirico
6

API R Plotly mungkin berguna bagi Anda. Grafik di bawah ini ada di sini .

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)

Pengungkapan penuh: Saya di tim.

Grafik

Mateo Sanchez
sumber
1

Begitu banyak jawaban yang bagus tetapi karena saya baru saja menulis fungsi ( plotMultipleHistograms()) berfungsi untuk melakukan ini, saya pikir saya akan menambahkan jawaban lain.

Keuntungan dari fungsi ini adalah bahwa ia secara otomatis menetapkan batas sumbu X dan Y yang sesuai dan mendefinisikan satu set tempat sampah yang umum digunakan di semua distribusi.

Berikut cara menggunakannya:

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

masukkan deskripsi gambar di sini

The plotMultipleHistograms()Fungsi dapat mengambil sejumlah distribusi, dan semua parameter memplot umum harus bekerja dengan itu (misalnya: las, main, dll).

Joseph Crispell
sumber