3
votes

There are existing questions asking about labeling a single geom_abline() in ggplot2:

None of these get at a use-case where I wanted to add multiple reference lines to a scatter plot, with the intent of allowing easy categorization of points within slope ranges. Here is a reproducible example of the plot:

library(ggplot2)

set.seed(123)
df <- data.frame(
  x = runif(100, 0, 1),
  y = runif(100, 0, 1))

lines <- data.frame(
  intercept = rep(0, 5),
  slope = c(0.1, 0.25, 0.5, 1, 2))

p <- ggplot(df, aes(x = x, y = y)) +
  geom_point() +
  geom_abline(aes(intercept = intercept, slope = slope),
              linetype = "dashed", data = lines)
p

scatter plot with dashed ablines added at various slopes

As I found no way to do this programmatically via the other questions, I "scaled" the manual approach via a data frame, using trial and error to figure out reasonable label positions.

labels <- data.frame(
  x = c(rep(1, 3), 0.95, 0.47),
  y = c(0.12, 0.28, 0.53, 1, 1),
  label = lines$slope)

p + geom_text(aes(label = label), color = "red", data = labels)

plot with ablines labeled with their slope value in red text

Is there a better way than trial and error? While this wasn't too bad with 5 lines, I still had to redo my tweaking further upon export, as the plot aspect ratios and spacing were not the same between prototyping in an R session vs. the generated image. Programmatic labeling would be a huge help.

For some thoughts:

  • I wondered if the parameter could be along a range of c(0, 1), to correspond to the position along the line
  • could the min/max x/y positions be extracted from the ggplot2 object internals (which I'm not familiar with) as a "cheat" for figuring out the position? Essentially if I know the pixel location of (0, intercept), I already know the slope, so for this example, I just need to know the pixel position of max(x) or max(y), depending on where we hit the perimeter
  • this struck me as similar to ggrepel, which figures out how to label points while trying to avoid overlaps
Wrt your 2nd point, if you set x = Inf or y = -Inf, the positions are translated to the xmax and ymin positions respectively.teunbrand
geomtextpath is a fairly new package that I haven't had a chance to try out yet, but seems like it should do thiscamille