Consider this simple example
library(ggplot2)
dat <- data.frame(number = c(5, 10, 11 ,12,12,12,13,15,15))
ggplot(dat, aes(x = number)) + geom_histogram()
See how the bars are weirdly aligned with the x axis? Why is the first bar on the left of 5.0
while the bar at 10.0
is centered? How can I get control over that? For instance, it would make more sense (to me) to have the bar starting on the right of the label.
Let me start by explaining, why your code leads to weirdly aligned bars. This has to do with the way a histogram is constructed. First, the x-axis is split up into intervals and then, the number of values in each interval is counted.
By default, ggplot
splits the data up into 30 bins. It even spits out a message that says so:
stat_bin()
usingbins = 30
. Pick better value withbinwidth
.
The default number of is not always a good choice. In your case, where all the data points are integers, one might want to choose the boundaries of the bins as 5, 6, 7, 8, ...
or 4.5, 5.5, 6.5, ...
, such that each bin contains exactly one integer value. You can obtain the boundaries of the bins that have been used in the plot as follows:
data <- data.frame(number = c(5, 10, 11 ,12, 12, 12, 13, 15, 15))
p <- ggplot(data, aes(x = number)) + geom_histogram()
ggplot_build(p)$data[[1]]$xmin
## [1] 4.655172 5.000000 5.344828 5.689655 6.034483 6.379310 6.724138 7.068966 7.413793
## [10] 7.758621 8.103448 8.448276 8.793103 9.137931 9.482759 9.827586 10.172414 10.517241
## [19] 10.862069 11.206897 11.551724 11.896552 12.241379 12.586207 12.931034 13.275862 13.620690
## [28] 13.965517 14.310345 14.655172
As you can see, the boundaries of the bins are not chosen in a way that would lead to a nice alignment of the bars with integers.
So, in short, the reason for the weird alignment is that ggplot
simply uses a default number of 30 bins, which is not suitable, in your case, to have bars that are nicely aligned with integers.
There are (at least) two ways to get nicely aligned bars that I will discuss in the following
Since you have integer data, a histogram may just not be the appropriate choice of visualisation. You could instead use geom_bar()
, which will lead to bars that are centered on integers:
ggplot(data, aes(x = number)) + geom_bar() + scale_x_continuous(breaks = 1:16)
You could move the bars to the right of the integers by adding 0.5
to number
:
ggplot(data, aes(x = number + 0.5)) + geom_bar() + scale_x_continuous(breaks = 1:16)
If you nevertheless want to use a histogram, you can make ggplot to use more reasonable bins as follows:
ggplot(data, aes(x = number)) +
geom_histogram(binwidth = 1, boundary = 0, closed = "left") +
scale_x_continuous(breaks = 1:16)
With binwidth = 1
, you override the default choice of 30 bins and explicitly require that bins should have a width of 1. boundary = 0
ensures that the binning starts at an integer value, which is what you need, if you want the integers to be to the left of the bars. (If you omit it, bins are chosen such that the bars are centered on integers.)
The argument closed = "left"
is a bit more tricky to explain. As I described above, the boundaries of the bins are now chosen to be 5, 6, 7, ...
. The question is now, in which bin, e.g., 6 should be? It could be either the first or second one. This is the choice that is controlled by closed
: if you set it to "right"
(the default), then the bins are closed on the right, meaning that the right boundary of the bin will be included, while the left boundary belongs to the bin to the left. So, 6 would be in the first bin. On the other hand, if you chose "left"
, the left boundary will be part of the bin and 6 would be in the second bin.
Since you want the bars to be to the left of the integers, you need to pick closed = "left"
.
If you compare the histogram with the bar plot, you will notice two differences:
geom_bar(width = 1)
.This will center the bar on the value
data <- data.frame(number = c(5, 10, 11 ,12,12,12,13,15,15))
ggplot(data,aes(x = number)) + geom_histogram(binwidth = 0.5)
Here is a trick with the tick label to get the bar align on the left.. But if you add other data, you need to shift them also
ggplot(data,aes(x = number)) +
geom_histogram(binwidth = 0.5) +
scale_x_continuous(
breaks=seq(0.75,15.75,1), #show x-ticks align on the bar (0.25 before the value, half of the binwidth)
labels = 1:16 #change tick label to get the bar x-value
)
other option: binwidth = 1, breaks=seq(0.5,15.5,1)
(might make more sense for integer)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With