karldw
karldw

Reputation: 371

How do I fix an aspect ratio in ggplot2's geom_hex?

I'd like to use geom_hex to represent my counts, but I want the hexagons to be "square", with an aspect ratio 1:1.

I've seen coord_fixed (and its alias coord_equal), but these change the aspect ratio of the whole plotted area, while I'm interested in changing the aspect ratio of the hexagons themselves.

library(ggplot2)

# Here, in plot1.pdf, the hexagon aspect ratio is determined by 
# the saved plot aspect ratio
plt1 <- ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length)) +
  geom_hex(bins = 10)
ggsave("plot1.pdf", plt1, width = 5, height = 4)


# Here, in plot2.pdf, the hexagon aspect ratio is 1:1, but the
# plot doesn't fill the space well, particularly if the data change
ratio <- 2
plt2 <- ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length)) +
  geom_hex(bins = 10 * c(1, ratio)) +
  coord_fixed(ratio = ratio)
ggsave("plot2.pdf", plt2, width = 5, height = 4)


# In plot3.pdf, the only thing that's changed is the y-axis data,
# but the plot is unreadable because the x-axis is so compressed
ratio <- 2
plt3 <- ggplot(iris, aes(x = Sepal.Width, y = 5 * Sepal.Length)) +
  geom_hex(bins = 10 * c(1, ratio)) +
  coord_fixed(ratio = ratio)
ggsave("plot3.pdf", plt3, width = 5, height = 4)

In plot2.pdf and plot3.pdf above, the hexagons have a 1:1 aspect ratio, but the plots don't look good, because coord_fixed has scaled the entire plotted area, not just the hexagons.

In each plot, I could tweak the bins argument to get hexagons with an aspect ratio near 1:1, but I'd like to have the code pick automatically for me. Is there a way to say something like "pick 15 bins on the x-axis and however many bins are necessary on the y-axis to make the hexagons have a 1:1 aspect ratio"?

Upvotes: 1

Views: 536

Answers (1)

dww
dww

Reputation: 31452

One option is to extract the size of the plotting area (in axis units) from the ggplot, then scale the hexagons (using binwidth rather than bins argument) based on the ratio.

plt1 = ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length)) +
  geom_hex() 

xrange = diff(ggplot_build(plt1)$layout$panel_params[[1]]$x.range)
yrange = diff(ggplot_build(plt1)$layout$panel_params[[1]]$y.range)
ratio = xrange/yrange
xbins = 10
xwidth = xrange/xbins
ywidth = xwidth/ratio
plt1 = ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length)) +
  geom_hex(binwidth = c(xwidth,ywidth)) +
  coord_fixed(ratio = ratio)
ggsave("plot1.pdf", plt1, width = 5, height = 4)

enter image description here

Or, if you prefer the plotting area to have an aspect ratio the same as the page, rather than square, then you can adjust the ratio accordingly:

width = 5 
height = 4
ratio = (xrange/yrange) * (height/width)

xwidth = xrange/xbins
ywidth = xwidth/ratio
plt1 = ggplot(iris, aes(x = Sepal.Width, y = Sepal.Length)) +
  geom_hex(binwidth = c(xwidth,ywidth)) +
  coord_fixed(ratio = ratio)

ggsave("plot1.pdf", plt1, width = width, height = height)

enter image description here

Upvotes: 2

Related Questions