9

Please consider the following minimal example:

library(ggplot2)
library(ggrepel)
ggplot(mtcars) +
  aes(x = mpg, y = qsec) +
  geom_line() +
  geom_text(x = 20, y = 20, label = "(20,20)")

overplotted text

I guess you can see pretty easily that the text "(20,20)" is heavily overplotted (actually, I don't know whether that's the correct word. I mean that the text is plotted several times at one location).

If I use annotate(), this does not happen:

ggplot(mtcars) +
  aes(x = mpg, y = qsec) +
  geom_line() +
  annotate("text", x = 20, y = 20, label = "(20,20)")

no overplotted text

"So, why don't you use annotate() then?" you might ask. Actually, I don't want to use text for annotation but labels. And I also want to use the {ggrepel} package to avoid overplotting. But look what happens, when I try this:

ggplot(mtcars) +
  aes(x = mpg, y = qsec) +
  geom_line() +
  geom_label_repel(x = 20, y = 20, label = "(20,20)")

many many labels

Again, many labels are plotted and {ggrepel} does a good job at preventing them from overlapping. But I want only one label pointing at a specific location. I really don't understand why this happens. I only supplied one value for x, y and label each. I also tried data = NULL and inherit.aes = F and putting the values into aes() within geom_label_repel() to no effect. I suspect that there are as many labels as there are rows in mtcars. For my real application that's really bad because I have a lot of rows in the respective dataset.

Could you help me out here and maybe give a short explanation why this happens and why your solution works? Thanks a lot!

pogibas
  • 27,303
  • 19
  • 84
  • 117
swolf
  • 1,020
  • 7
  • 20
  • 1
    `geom_text` adds `(20,20)` for each row in `mtcars` – pogibas Feb 27 '19 at 08:05
  • Yeah, I suspected this. But why does this happen when I specified only one value for x, y, and label? And how do I resolve this in the geom_label_repel case? – swolf Feb 27 '19 at 08:06
  • 1
    You should pass different data for such annotation, for example: `geom_text(aes(20, 20, label = "(20,20)"), data.frame())` or `geom_label_repel(aes(20, 20, label = "(20,20)"), data.frame())` – pogibas Feb 27 '19 at 08:07
  • Holy s..., this works! And also for the case of ggplot(mtcars) + aes(x = mpg, y = qsec) + geom_line() + geom_label_repel(aes(x = 20, y = 20, label = "(20,20)"), data.frame()) – swolf Feb 27 '19 at 08:08
  • Thanks! If you want, write it up as an answer and I'll accept it. Still don't fully understand why `data = NULL` doesn't work. – swolf Feb 27 '19 at 08:10
  • 2
    In documentation it says that data is "A data frame. If specified, overrides the default data frame defined at the top level of the plot". NULL doesn't override it. – pogibas Feb 27 '19 at 08:14

2 Answers2

8

Add "check_overlap = TRUE" to geom_text to prevent overplotting.

library(ggplot2) 

ggplot(mtcars) +
        aes(x = mpg, y = qsec) +
        geom_line() +
        geom_text(x = 20, y = 20, label = "(20,20)", check_overlap = TRUE)

enter image description here

tjebo
  • 21,977
  • 7
  • 58
  • 94
PeteRlearner
  • 154
  • 2
  • 7
  • Tjebo, it works, adding "check_overlap = TRUE" to geom_text, as shown above, prevents overplotting. – PeteRlearner May 13 '20 at 00:38
  • My bad! I tested it yesterday using R online (me not being at home) and it somehow did not work. The downvote is not mine btw. – tjebo May 13 '20 at 08:02
4

geom_text or geom_label_repel adds one label per row. Therefore you can submit a separate dataset for annotation geom. For example:

library(ggplot2)
library(ggrepel)
ggplot(mtcars, aes(mpg, qsec)) +
    geom_line() +
    geom_label_repel(aes(20, 20, label = "(20,20)"), data.frame())

enter image description here

pogibas
  • 27,303
  • 19
  • 84
  • 117