Search code examples
rggplot2geom-text

geom_text() position change with different axis span or length, how to keep it fixed?


So, I am having an issue with ggplot2's geom_text(). I wasn't able to find a solution to this so I figured I ask it here. What I noticed is the following:

library("ggplot2")
dev.new()

ggplot(mtcars, aes(x=mpg, y=hp)) +
    geom_point() +
    geom_text(label=rownames(mtcars), nudge_x=5, nudge_y=5) 
    
mtcars_mod <- rbind.data.frame(mtcars, c(80, 6, 123.0, 111, 5.14, 1.7, 22.1, 2, 1, 3, 1))
rownames(mtcars_mod) <- c(rownames(mtcars), "Fake Car")


dev.new()

ggplot(mtcars_mod, aes(x=mpg, y=hp)) +
    geom_point() +
    geom_text(label=rownames(mtcars_mod), nudge_x=5, nudge_y=5)

By running this piece of code, I get two different plots. They are different because in the second table (mtcars_mod) I added a Fake Car which is an outlier and changes completely the span of the x axis, if compared to the first plot.

Problem is that if I use the same nudge for both plots, what I get is labelled dots which labels are in different position, with respect to the dots themselves. Indeed, it seems that the scale of the axis is affecting the position of the labels, with respect to the position of the dots. I expected that the value I am passing to nudge was somehow aware of the scale of the plot but it looks it is not, right? So my question is: is there a way of showing the labels, on different plots, in the same position, with respect to the dots, no matter what happens to x and/or y axes?


Solution

  • One option would be to use ggtext::geom_richtext. In contrast to nudging which is measured in units of your scale and is affected by the range of your data ggtext::geom_richtext allows to "shift" the labels by setting the margin in units like "mm" or "pt". So the distance between labels and datapoints is fixed in absolute units and independent of the scale. By default geom_richtext resembles geom_label so I set the fill and the label outline aka label.colour to NA to get rid of the box drawn around the label.

    library(ggplot2)
    library(ggtext)
    
    mtcars_mod <- rbind.data.frame(mtcars, c(80, 6, 123.0, 111, 5.14, 1.7, 22.1, 2, 1, 3, 1))
    rownames(mtcars_mod) <- c(rownames(mtcars), "Fake Car")
    
    ggplot(mtcars, aes(x = mpg, y = hp)) +
      geom_point() +
      geom_richtext(
        label = rownames(mtcars),
        label.margin = unit(c(0, 0, 5, 5), "mm"),
        hjust = 0,
        fill = NA, label.colour = NA
      )
    

    ggplot(mtcars_mod, aes(x = mpg, y = hp)) +
      geom_point() +
      geom_richtext(
        label = rownames(mtcars_mod),
        label.margin = unit(c(0, 0, 5, 5), "mm"),
        hjust = 0,
        fill = NA, label.colour = NA
      )