ggplot2: Is there a fix for jagged, poor-quality text produced by geom_text()?

Solution 1:

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)

enter image description here

Solution 2:

Try geom_text(..., check_overlap = TRUE)*

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!