The problem#
I stumbled across this problem as a TA for an introductory R course. It was a minor question on a Problem Set focussed on creating a Shiny app that the instructor had seemingly dismissed as trivial and not bothered to include in his solution guide. Anyways, I needed to be able to grade the problem set, so I gave it a shot.
Like any good programmer, I Googled the question and clicked on the most appealing Stack Overflow response. Here is a link to the post I pulled up: “How to display only integer values on an axis using ggplot2.” The question was essentially that and the accepted answer was:
With
scale_y_continuous()
and argumentbreaks=
you can set the breaking points for y axis (sic) to integers you want to display.
ggplot(data2, aes(x =factor(IR), y = value, fill = Legend, width=.15)) +
geom_bar(position='dodge', colour='black')+
scale_y_continuous(breaks=c(1,3,7,10))
This was simple solution, but having to define the axes values with some magic numbers seemed wrong. Further, it would be clunky to implement in the Shiny app as the axes are being adjusted based on user input.
The next answer was a bit more promising.
If you have the ‘scales’ package, you can use
pretty_breaks()
without having to manually specify the breaks.
q + geom_bar(position='dodge', colour='black') +
scale_y_continuous(breaks= pretty_breaks())
Still though, this doesn’t actually solve the issue - at a small enough scale, this does not force integers.
Here is an example using the classic iris
data set that comes with R.
iris %>%
ggplot(aes(x = Petal.Width, y = Sepal.Width)) +
geom_point(aes(color = Species)) +
scale_y_continuous(breaks = scales::pretty_breaks()) +
theme_bw()
Looking through the next few answers gave a hint as to what I needed to do.
I needed to pass a function, to the breaks
argument that takes a vector of values to make the axes for and a number indicating the number of ticks.
I decided to take a look at what pretty_breaks()
does and see if I could augment that.
scales::pretty_breaks
#> function (n = 5, ...)
#> {
#> force_all(n, ...)
#> function(x) {
#> breaks <- pretty(x, n, ...)
#> names(breaks) <- attr(breaks, "labels")
#> breaks
#> }
#> }
#> <bytecode: 0x7ff8a81061d8>
#> <environment: namespace:scales>
It was such a simple function that I was pretty quickly able to see how I could make it only return integer values.
Therefore, I created integer_breaks()
.
# A function factory for getting integer y-axis values.
integer_breaks <- function(n = 5, ...) {
fxn <- function(x) {
breaks <- floor(pretty(x, n, ...))
names(breaks) <- attr(breaks, "labels")
breaks
}
return(fxn)
}
And here is an example of it being used in practice.
iris %>%
ggplot(aes(x = Petal.Width, y = Sepal.Width)) +
geom_point(aes(color = Species)) +
scale_y_continuous(breaks = integer_breaks()) +
theme_bw()
As a quick comparison, I used ‘cowplot’ to show the same plot with and without the integer axes.
The code to generate all of the above plots is linked at the top.
I also created a Gist on GitHub with the integer_breaks()
function.