【问题标题】:How to plot two histograms together in R?如何在R中将两个直方图一起绘制?
【发布时间】:2011-04-02 06:52:24
【问题描述】:

我正在使用 R,我有两个数据框:胡萝卜和黄瓜。每个数据框都有一个数字列,列出了所有测量的胡萝卜(总计:100k 胡萝卜)和黄瓜(总计:50k 黄瓜)的长度。

我希望在同一个图上绘制两个直方图——胡萝卜长度和黄瓜长度。它们重叠,所以我想我也需要一些透明度。我还需要使用相对频率而不是绝对数字,因为每组中的实例数不同。

这样的东西会很好,但我不明白如何从我的两个表中创建它:

【问题讨论】:

  • 顺便说一句,您打算使用哪个软件?对于开源,我推荐gnuplot.info [gnuplot]。在它的文档中,我相信您会找到某些技术和示例脚本来做您想做的事情。
  • 我正在使用 R 作为标签建议(编辑帖子以明确这一点)
  • 有人在此线程中发布了一些代码 sn-p 来执行此操作:stackoverflow.com/questions/3485456/…

标签: r plot histogram


【解决方案1】:

这是一个使用基本图形和 alpha 混合(不适用于所有图形设备)的更简单的解决方案:

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

关键是颜色是半透明的。

两年多后编辑:由于这刚刚获得了赞成票,我想我不妨添加代码生成的视觉效果,因为 alpha 混合非常有用: p>

【讨论】:

  • +1 谢谢大家,这可以转换成更平滑的直方图(如had.co.nz/ggplot2/graphics/55078149a733dd1a0b42a57faf847036.png)吗?
  • 为什么要分离出plot 命令?您可以将所有这些选项放入 hist 命令中,然后将其放在两行中。
  • @John 你会怎么做?
  • 如我所说,将plot命令中的选项直接放入hist命令中。发布代码不是 cmets 的用途。
  • @John 为什么要分开?我无法读懂 Dirk 的想法,但我会这样写,因为这样代码更清晰易读。计算(hist)用一行,图形表示(plot)用一行。
【解决方案2】:

您链接到的图像是密度曲线,而不是直方图。

如果您一直在阅读 ggplot,那么您可能唯一缺少的就是将两个数据框组合成一个长数据框。

所以,让我们从你所拥有的东西开始,两组独立的数据并将它们组合起来。

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)

之后,如果您的数据已经是长格式,则无需这样做,您只需要一行即可绘制图表。

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

现在,如果您确实需要直方图,则以下内容将起作用。请注意,您必须从默认的“堆栈”参数更改位置。如果您真的不知道您的数据应该是什么样子,您可能会错过这一点。更高的 alpha 看起来更好。另请注意,我将其设为密度直方图。删除y = ..density.. 使其恢复计数很容易。

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

【讨论】:

  • 如果您想保留直方图,请使用ggplot(vegLengths, aes(length, fill = veg)) + geom_bar(pos="dodge")。这将制作交错的直方图,就像在 MATLAB 中一样。
  • 感谢您的回答! 'position="identity"' 部分实际上很重要,否则条形图是堆叠的,当与默认情况下似乎是“身份”的密度相结合时会产生误导,即叠加而不是堆叠。
【解决方案3】:

这是我写的一个函数uses pseudo-transparency to represent overlapping histograms

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

这里是another way to do it using R's support for transparent colors

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

结果最终看起来像这样:

【讨论】:

  • +1 表示可在所有图形设备上使用的选项(例如 postscript
【解决方案4】:

已经有了漂亮的答案,但我想添加这个。在我看来很好。 (从@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)

结果是……

更新:这个重叠功能也可能对某些人有用。

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

我觉得hist0 的结果比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) }

结果

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

【讨论】:

    【解决方案5】:

    以下是如何在“经典”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)
    

    唯一的问题是,如果直方图中断对齐,它看起来会更好,这可能必须手动完成(在传递给 hist 的参数中)。

    【讨论】:

    • 非常好。这也让我想起了那个stackoverflow.com/questions/3485456/…
    • 提高这一点,因为这个答案是唯一一个(除了ggplot 中的答案),它直接说明您的两个直方图是否具有显着不同的样本量。
    • 我喜欢这种方法,请注意,您可以通过使用 seq() 定义它们来同步中断。例如:breaks=seq(min(data$some_property), max(data$some_property), by=(max_prop - min_prop)/20)
    【解决方案6】:

    这是我只在基本 R 中提供的 ggplot2 版本。我从@nullglob 复制了一些。

    生成数据

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

    您不需要像 ggplot2 那样将其放入数据框中。这种方法的缺点是你必须写出更多的情节细节。优点是您可以控制情节的更多细节。

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

    【讨论】:

      【解决方案7】:

      @Dirk Eddelbuettel:基本思想非常好,但所示代码可以改进。 [需要很长时间才能解释,因此单独回答而不是评论。]

      hist() 函数默认绘制绘图,因此您需要添加plot=FALSE 选项。此外,通过plot(0,0,type="n",...) 调用建立绘图区域更清晰,您可以在其中添加轴标签、绘图标题等。最后,我想提一下,还可以使用阴影来区分两个直方图。代码如下:

      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)
      

      这是结果(由于 RStudio 有点太宽了 :-)):

      【讨论】:

      • 提高这一点,因为它是一个非常简单的选项,使用 base 并且在 postscript 设备上可行。
      【解决方案8】:

      Plotly's R API 可能对你有用。下图为here

      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)
      

      全面披露:我在团队中。

      【讨论】:

        【解决方案9】:

        这么多很棒的答案,但由于我刚刚编写了一个函数('basicPlotteR' 包中的plotMultipleHistograms())函数来执行此操作,所以我想我会添加另一个答案。

        此函数的优点是它会自动设置适当的 X 和 Y 轴限制,并定义一组通用的 bin 集合,用于所有分布。

        使用方法如下:

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

        plotMultipleHistograms() 函数可以采用任意数量的分布,并且所有常规绘图参数都应使用它(例如:lasmain 等)。

        【讨论】:

          猜你喜欢
          • 2015-01-14
          • 2015-05-31
          • 2012-06-26
          • 2014-05-30
          • 1970-01-01
          • 2016-11-23
          • 1970-01-01
          相关资源
          最近更新 更多