Starting bars and histograms at zero in ggplot2

Image credit: MalditoBarbudo (CC-BY 4.0)

Starting bars and histograms at zero in ggplot2

MalditoBarbudo bio photo By MalditoBarbudo

When creating histograms or barplots in ggplot2 we found that the data is placed at some distance from the x axis, which means the y axis starts below zero:

# libraries
library(ggplot2)
library(ggthemes)

# histogram with gap example
ggplot(iris, aes(x = Petal.Length, fill = Species)) +
  geom_histogram(position = 'dodge') +
  scale_fill_solarized() +
  theme_solarized(light = FALSE, base_family = 'Inconsolata')

Histogram with gap at the bottom

This is because, internally, ggplot2 is expanding x and y axes by a multiplicative or additive constant1. This makes sense in almost all plots, except for the bar and histogram ones, as we see above.

Workaround

To avoid this behaviour, we have to modify the y scale indicating the expand value as well as the limits value:

# histogram without bottom gap
ggplot(iris, aes(x = Petal.Length, fill = Species)) +
  geom_histogram(position = 'dodge') +
  scale_y_continuous(expand = c(0,0),
                     limits = c(0,30)) +
  scale_fill_solarized() +
  theme_solarized(light = FALSE, base_family = 'Inconsolata')

Histogram without bottom gap

As you can see, here we add the following layer to the gpplot call:

scale_y_continuous(expand = c(0,0),
                   limits = c(0,30)) +

This way we avoid the y axis expand, but we need to set (harcode) the y max limit to allow for a space above the data (the y max value depends on the count range, so you have to take a look at the plot first).
This way has some disadventages2:

  • You have to provide a hardcoded limit value, so you have to generate the plot one time and modify the code to generate the final version. So no automatization here, in case you need it.

  • It does not play well with free y faceted plots.

Barplots

For barplots is the same:

# histogram without bottom gap
ggplot(iris, aes(x = Species, fill = Species)) +
  geom_bar(stat = 'count') +
  scale_y_continuous(expand = c(0,0),
                     limits = c(0,55)) +
  scale_fill_solarized() +
  theme_solarized(light = FALSE, base_family = 'Inconsolata')

Bar plot without bottom gap

Summary

Summary plot. Default plot, without expand plot and without expand and limits plot

As you can see, default plot adds a gap at both ends, up and down. Setting expand to zero removes both gaps, no ideal. Finally setting expand to zero and setting the optimal y max limit does the trick.

Bits of code

Code for the summary cowplot:

# install cowplot if needed
# install.packages('cowplot')

# libraries
library(cowplot)

# plots
default <- ggplot(iris, aes(x = Petal.Length, fill = Species)) +
  geom_histogram(position = 'dodge') +
  scale_fill_solarized() +
  labs(title = 'default') +
  theme_solarized(light = FALSE, base_family = 'Inconsolata') +
  theme(legend.position = 'none')

wo_expand <- default +
  scale_y_continuous(expand = c(0,0)) +
  labs(title = 'wo expand')

wo_expand_w_limits <- default +
  scale_y_continuous(expand = c(0,0),
                     limits = c(0,30)) +
  labs(title = 'wo expand + limits') +
  theme(legend.position = c(.8, .7))

# cowplot
plot_grid(default, wo_expand, wo_expand_w_limits,
          align = 'h', nrow = 1,
          labels = NULL)

Sources


  1. See the expand parameter in ?scale_y_continuous for details

  2. It seems an asymmetrical expand argument is on its way