Search code examples
rggplot2ggproto

Hollow histogram or binning for geom_step


I would like to draw a hollow histogram that has no vertical bars drawn inside of it, but just an outline. I couldn't find any way to do it with geom_histogram. The geom_step+stat_bin combination seemed like it could do the job. However, the bins of geom_step+stat_bin are shifted by a half bin either to the right or to the left, depending on the step's direction= parameter value. It seems like it is doing its "steps" WRT bin centers. Is there any way to change this behavior so it would do the "steps" at bin edges?

Here's an illustration:

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

enter image description here


Solution

  • I propose making a new Geom like so:

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

    This also works for non-uniform breaks. To illustrate the usage:

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

    plot