Search code examples
rplotggplot2data-visualizationhexagonal-tiles

ggplot2 stat_binhex(): keep bin radius while changing plot size


I'd like to a find a way to keep regular hexagons (all sides have equal length) when resizing hexbin plots in ggplot2 without manually adjusting the binwidth parameter.

To illustrate:

d <- ggplot(diamonds, aes(carat, price))+ 
  stat_binhex(colour="white")
try(ggsave(plot=d,filename=<some file>,height=6,width=8))

yields hexagons that at least look regular to the eye:ggplot2 stat_binhex plot1

And

try(ggsave(plot=d,filename=<some other file>,height=6,width=12))

yields irregular hexagons:ggplot2 stat_binhex plot2

The documentation describes the binwidth parameter (e.g. binwidth = c(1, 1000)) which specifies bin width. I'd like a function which, when given any plot size, returns the right binwidth settings to create regular hexagons.


Solution

  • Here's the solution to adjust binwidth dynamically. I've included handling for portrait aspect ratios and explicitly stated axis limits.

    bins <- function(xMin,xMax,yMin,yMax,height,width,minBins) {
      if(width > height) {
        hbins = ((width/height)*minBins)
        vbins = minBins
      } else if (width < height) { 
        vbins = ((height/width)*minBins)
        hbins = minBins
      } else { 
        vbins = hbins = minBins
        }
      binwidths <- c(((xMax-xMin)/hbins),((yMax-yMin)/vbins))
      return(binwidths)
    }
    

    For example this code:

    h = 5
    w = 5
    yMin = min(diamonds$price)
    yMax = max(diamonds$price)
    xMin = min(diamonds$carat)
    xMax = max(diamonds$carat)
    minBins = 30
    
    d <- ggplot(diamonds, aes(x = carat, y = price))+ 
      stat_binhex(colour="white", binwidth = bins(xMin,xMax,yMin,yMax,h,w,minBins))+
      ylim(yMin,yMax)+
      xlim(xMin,xMax)
    try(ggsave(plot=d,filename=<some file>,height=h,width=w))
    

    Yields: graham jeffries - hexbin plot 1 And when we change the width:

    w = 8
    d <- ggplot(diamonds, aes(x = carat, y = price))+ 
      stat_binhex(colour="white", binwidth = bins(xMin,xMax,yMin,yMax,h,w,minBins))+
      ylim(yMin,yMax)+
      xlim(xMin,xMax)
    try(ggsave(plot=d,filename=<some file>,height=h,width=w))
    

    graham jeffries - hexbin plot 2

    Or change the height:

    h = 8
    w = 5
    d <- ggplot(diamonds, aes(x = carat, y = price))+ 
      stat_binhex(colour="white", binwidth = bins(xMin,xMax,yMin,yMax,h,w,minBins))+
      ylim(yMin,yMax)+
      xlim(xMin,xMax)
    try(ggsave(plot=d,filename=<some file>,height=h,width=w))
    

    graham jeffries - hexbin plot 3

    We can also change the x and y limits:

    h = 5
    w = 5
    xMin = -2
    
    d <- ggplot(diamonds, aes(x = carat, y = price))+ 
      stat_binhex(colour="white", binwidth = bins(xMin,xMax,yMin,yMax,h,w,minBins))+
      ylim(yMin,yMax)+
      xlim(xMin,xMax)
    try(ggsave(plot=d,filename=<some file>,height=h,width=w))
    

    graham jeffries - hexbin plot 4