【问题标题】:Hollow histogram or binning for geom_stepgeom_step 的空心直方图或分箱
【发布时间】:2014-05-15 17:59:55
【问题描述】:

我想绘制一个空心直方图,其中没有绘制垂直条,而只是一个轮廓。我找不到任何使用geom_histogram 的方法。 geom_step+stat_bin 组合似乎可以完成这项工作。但是,geom_step+stat_bin 的 bin 会向右或向左移动半个 bin,具体取决于步骤的 direction= 参数值。似乎它正在做它的“步骤”WRT bin 中心。有什么办法可以改变这种行为,让它在 bin 边缘执行“步骤”?

这是一个插图:

d <- data.frame(x=rnorm(1000))
qplot(x, data=d, geom="histogram",
      breaks=seq(-4,4,by=.5), color=I("red"), fill = I("transparent")) +
geom_step(stat="bin", breaks=seq(-4,4,by=.5), color="black", direction="vh")

【问题讨论】:

  • 现在有direction = "mid" 可以做到这一点(参见my answer below

标签: r ggplot2 ggproto


【解决方案1】:

我建议像这样制作一个新的 Geom:

library(ggplot2)
library(proto)

geom_stephist <- function(mapping = NULL, data = NULL, stat="bin", position="identity", ...) {
  GeomStepHist$new(mapping=mapping, data=data, stat=stat, position=position, ...)
}

GeomStepHist <- proto(ggplot2:::Geom, {
  objname <- "stephist"

  default_stat <- function(.) StatBin
  default_aes <- function(.) aes(colour="black", size=0.5, linetype=1, alpha = NA)

  reparameterise <- function(., df, params) {
    transform(df,
              ymin = pmin(y, 0), ymax = pmax(y, 0),
              xmin = x - width / 2, xmax = x + width / 2, width = NULL
    )
  }

  draw <- function(., data, scales, coordinates, ...) {
    data <- as.data.frame(data)[order(data$x), ]

    n <- nrow(data)
    i <- rep(1:n, each=2)
    newdata <- rbind(
      transform(data[1, ], x=xmin, y=0),
      transform(data[i, ], x=c(rbind(data$xmin, data$xmax))),
      transform(data[n, ], x=xmax, y=0)
    )
    rownames(newdata) <- NULL

    GeomPath$draw(newdata, scales, coordinates, ...)
  }
  guide_geom <- function(.) "path"
})

这也适用于非均匀休息。为了说明用法:

d <- data.frame(x=runif(1000, -5, 5))
ggplot(d, aes(x)) +
  geom_histogram(breaks=seq(-4,4,by=.5), color="red", fill=NA) +
  geom_stephist(breaks=seq(-4,4,by=.5), color="black")

【讨论】:

  • 这是一个不错的无缝破解!它甚至允许通常的简单分面和默认分箱。但最自然的解决方案可能是向 geom_histogram 添加一个参数以禁用内部垂直条。
  • @VadimKhotilovich 我认为参数选项很难,因为geom_histogram 是围绕stat_bingeom_bargeom_bar 构建的,并没有真正设置为选择性地仅包含/排除部分它的垂直边缘。
  • @joran:这样的技术困难无法推翻“直方图不是条形图”的事实(这是直接来自“图形语法”一书的引述)。一般来说,直方图表示分布,条形图用于比较类别。虽然 ggplot2 将直方图实现为 bar+bin 上的一个微不足道的别名,但它不必保持这种状态。我还要补充一点,直方图也不是阶梯图。
  • @VadimKhotilovich 没问题。事实上,我应该道歉,我是在一些非常烦人的事情发生在离线的云层下写的,这对我的影响太大了。
  • 我过去非常依赖 geom_stephist,但它不再适用于 ggplot2 的 v2(又名 ggplot2_2.0.0)的 ggproto。如果有人可以以此为例说明在 ggplot2_2.0.0 中创建新 gem,那将非常有帮助,谢谢!
【解决方案2】:

这并不理想,但这是我能想到的最好的:

h <- hist(d$x,breaks=seq(-4,4,by=.5))
d1 <- data.frame(x = h$breaks,y = c(h$counts,NA))

ggplot() + 
    geom_histogram(data = d,aes(x = x),breaks = seq(-4,4,by=.5),
                                 color = "red",fill = "transparent") + 
    geom_step(data = d1,aes(x = x,y = y),stat = "identity")

【讨论】:

  • @Henrik 坦率地说,我喜欢这三个解决方案。
【解决方案3】:

又一个。使用ggplot_build 构建直方图的绘图对象进行渲染。从此对象中提取xy 值,用于geom_step。使用by 偏移x 值。

by <- 0.5
p1 <- ggplot(data = d, aes(x = x)) +
  geom_histogram(breaks = seq(from = -4, to = 4, by = by),
                 color = "red", fill = "transparent")

df <- ggplot_build(p1)$data[[1]][ , c("x", "y")]

p1 +
  geom_step(data = df, aes(x = x - by/2, y = y))

编辑来自@Vadim Khotilovich 的评论(谢谢!)

可以使用绘图对象中的xmin 代替(-> 无需调整偏移量)

df <- ggplot_build(p1)$data[[1]][ , c("xmin", "y")]

p1 +
  geom_step(data = df, aes(x = xmin, y = y))   

【讨论】:

  • 感谢您将我指向 ggplot_build。它提供了许多潜在有用的数据!不过,在这种特殊情况下,我将通过 [ , c("xmin", "y")] 对其进行子集化,以直接获取下边缘。
  • 不客气。是的,当您用完“正常”ggplot 选项时,走ggplot_build 路径可能会很有成效。您还可以操作绘图对象中的数据,然后使用grid 函数对其进行绘图。
【解决方案4】:

另一种选择,也不太理想:

qplot(x, data=d, geom="histogram", breaks=seq(-4,4,by=.5), color=I("red"), fill = I("transparent")) +
  stat_summary(aes(x=round(x * 2 - .5) / 2, y=1), fun.y=length, geom="step")

缺少一些垃圾箱,如果您弄乱了一点,您可能会添加回来。唯一(有点毫无意义)的优势是ggplot 比@Joran 的答案更多,尽管这也是有争议的。

【讨论】:

    【解决方案5】:

    我今天早些时候回答了我自己的评论:这是使用 ggproto 为 v2 (ggplot2_2.0.0) 更新的@RosenMatev 答案的修改版本:

    GeomStepHist <- ggproto("GeomStepHist", GeomPath,
                            required_aes = c("x"),
    
                            draw_panel = function(data, panel_scales, coord, direction) {
                              data <- as.data.frame(data)[order(data$x), ]
    
                              n <- nrow(data)
                              i <- rep(1:n, each=2)
                              newdata <- rbind(
                                transform(data[1, ], x=x - width/2, y=0),
                                transform(data[i, ], x=c(rbind(data$x-data$width/2, data$x+data$width/2))),
                                transform(data[n, ], x=x + width/2, y=0)
                              )
                              rownames(newdata) <- NULL
    
                              GeomPath$draw_panel(newdata, panel_scales, coord)
                            }
    )
    
    
    geom_step_hist <- function(mapping = NULL, data = NULL, stat = "bin",
                               direction = "hv", position = "stack", na.rm = FALSE, 
                               show.legend = NA, inherit.aes = TRUE, ...) {
      layer(
        data = data,
        mapping = mapping,
        stat = stat,
        geom = GeomStepHist,
        position = position,
        show.legend = show.legend,
        inherit.aes = inherit.aes,
        params = list(
          direction = direction,
          na.rm = na.rm,
          ...
        )
      )
    }
    

    【讨论】:

      【解决方案6】:

      TLDR:使用geom_step(..., direction = "mid")

      自从 Daniel Mastropietro 和 Dewey Dunnington implemented 将“中间”作为 direction 参数的附加选项 geom_step for ggplot2 v3.3.0 以来,这变得容易多了:

      library(ggplot2)
      
      set.seed(1)
      d <- data.frame(x = rnorm(1000))
      ggplot(d, aes(x)) + 
        geom_histogram(breaks = seq(-4, 4, by=.5), color="red", fill = "transparent") +
        geom_step(stat="bin", breaks=seq(-4, 4, by=.5), color = "black", direction = "mid")
      

      下面,作为参考,问题中的代码格式类似于上面的答案:

      ggplot(d, aes(x)) + 
        geom_histogram(breaks = seq(-4, 4, by=.5), color = "red", fill = "transparent") +
        geom_step(stat="bin", breaks = seq(-4, 4, by=.5), color = "black", direction = "vh")
      

      reprex package (v0.3.0) 于 2020 年 9 月 2 日创建

      【讨论】:

        【解决方案7】:

        一种类似于@Rosen Matev 的简单方法(不适用于@julou 提到的ggplot2_2.0.0),我只想 1)手动计算bin的值(使用如下所示的小函数) 2) 使用 geom_step() 希望这会有所帮助!

        geom_step_hist<- function(d,binw){
          dd=NULL
          bin=min(d$y) # this enables having a first value that is = 0 (to have the left vertical bar of the plot when using geom_step)
          max=max(d$y)+binw*2 # this enables having a last value that is = 0 (to have the right vertical bar of the plot when using geom_step)
          xx=NULL
          yy=NULL
          while(bin<=max){
            n=length(temp$y[which(temp$y<bin & temp$y>=(bin-binw))])
            yy=c(yy,n)
            xx=c(xx,bin-binw)
            bin=bin+binw
            rm(n)
          }
          dd=data.frame(xx,yy)
          return(dd)
        }
        hist=ggplot(dd,aes(x=xx,y=yy))+
        geom_step()
        

        【讨论】:

          猜你喜欢
          • 2021-10-29
          • 2023-03-09
          • 1970-01-01
          • 2018-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-02-21
          • 2013-08-15
          相关资源
          最近更新 更多