ggplot2: Is there a fix for jagged, poor-quality text produced by geom_text()?
RGgplot2R Problem Overview
While adding annotation text to a plot I noticed that geom_text()
produced unsightly, jagged text, while annotate()
produced smooth, nice-looking text. Does anyone know why this happens and if there's any way to fix it? I know I could just use annotate()
here, but there are probably cases where geom_text()
is preferable, and I'd like to find a fix. Also, geom_text()
can't be intended to give poor-looking text, so either I'm doing something wrong, or I've run into some sort of subtle side effect.
Here's some fake data and the code to produce the graph, plus an image showing the results.
library(ggplot2)
age = structure(list(age = c(41L, 40L, 43L, 44L, 40L, 42L, 44L, 45L,
44L, 41L, 43L, 40L, 43L, 43L, 40L, 42L, 43L, 44L, 43L, 41L)),
.Names = "age", row.names = c(NA, -20L), class = "data.frame")
ggplot(age, aes(age)) +
geom_histogram() +
scale_x_continuous(breaks=seq(40,45,1)) +
stat_bin(binwidth=1, color="black", fill="blue") +
geom_text(aes(41, 5.2,
label=paste("Average = ", round(mean(age),1))), size=12) +
annotate("text", x=41, y=4.5,
label=paste("Average = ", round(mean(age$age),1)), size=12)
R Solutions
Solution 1 - R
geom_text
, despite not using anything directly from the age
data.frame, is still using it for its data source. Therefore, it is putting 20 copies of "Average=42.3" on the plot, one for each row. It is that multiple overwriting that makes it look so bad. geom_text
is designed to put text on a plot where the information comes from a data.frame (which it is given, either directly or indirectly in the original ggplot
call). annotate
is designed for simple one-off additions like you have (it creates a geom_text
, taking care of the data source).
If you really want to use geom_text()
, just reset the data source:
ggplot(age, aes(age)) +
scale_x_continuous(breaks=seq(40,45,1)) +
stat_bin(binwidth=1, color="black", fill="blue") +
geom_text(aes(41, 5.2,
label=paste("Average = ", round(mean(age$age),1))), size=12,
data = data.frame()) +
annotate("text", x=41, y=4.5,
label=paste("Average = ", round(mean(age$age),1)), size=12)
Solution 2 - R
geom_text(..., check_overlap = TRUE)
*
Try From the docs ?geom_text
, check_overlap
says:
> If TRUE, text that overlaps previous text in the same layer will not be plotted.
library(ggplot2)
age = structure(list(age = c(41L, 40L, 43L, 44L, 40L, 42L, 44L, 45L,
44L, 41L, 43L, 40L, 43L, 43L, 40L, 42L, 43L, 44L, 43L, 41L)),
.Names = "age", row.names = c(NA, -20L), class = "data.frame")
ggplot(age, aes(age)) +
geom_histogram() +
stat_bin(binwidth=1) +
geom_text(aes(41, 5.2, label=paste("Average = ", round(mean(age),1))),
size=12,
check_overlap = TRUE)
*This is essentially the answer that Dave Gruenewald posted in a comment to Brian's excellent answer. I'm just trying to make that answer more visible!