Search code examples
rggplot2ggrepel

Getting coordinates for the label locations from ggrepel


Below is an example of "label location optimization" using ggplot2 and ggrepel:

x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 
  0.9055, 1.3307)
y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 
  0.9717, 0.9357)
ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
         "SaxRub", "TurMer", "TurPil", "TurPhi")

df <- data.frame(x = x, y = y, z = ShortSci)
library(ggplot2)
library(ggrepel)
ggplot(data = df, aes(x = x, y = y)) + theme_bw() + 
  geom_text_repel(aes(label = z), 
                  box.padding = unit(0.45, "lines")) +
  geom_point(colour = "green", size = 3)

Question: is it at all possible to somehow grab the coordinates for the label locations as vectors?

Thank you very much!


Solution

  • At the link suggested by @Henrik there many interesting ideas for getting ggrepel label locations.
    Here is a personal reworking:

    x = c(0.8846, 1.1554, 0.9317, 0.9703, 0.9053, 0.9454, 1.0146, 0.9012, 0.9055, 1.3307)
    y = c(0.9828, 1.0329, 0.931, 1.3794, 0.9273, 0.9605, 1.0259, 0.9542, 0.9717, 0.9357)
    ShortSci = c("MotAlb", "PruMod", "EriRub", "LusMeg", "PhoOch", "PhoPho", 
             "SaxRub", "TurMer", "TurPil", "TurPhi")
    df <- data.frame(x = x, y = y, z = ShortSci)
    
    library(ggplot2)
    library(ggrepel)
    p1 <- ggplot(data = df, aes(x = x, y = y)) + theme_bw() + 
      geom_text_repel(aes(label = z), 
                      box.padding = unit(0.45, "lines")) +
      geom_point(colour = "green", size = 3)
    x11(width=7,height=7)
    p1
    
    # Get x and y plot ranges 
    xrg <- ggplot_build(p1)$layout$panel_ranges[[1]]$x.range
    yrg <- ggplot_build(p1)$layout$panel_ranges[[1]]$y.range
    
    library(grid)
    grid.force()
    kids <- childNames(grid.get("textrepeltree", grep = TRUE))
    
    # Function: get the x and y positions of a single ggrepel label
    get.xy.pos.labs <- function(n) {
      grb <- grid.get(n)
      data.frame(
      x = xrg[1]+diff(xrg)*convertX(grb$x, "native", valueOnly = TRUE),
      y = yrg[1]+diff(yrg)*convertY(grb$y, "native", valueOnly = TRUE)
      )
    }
    # Get positions of all ggrepel labels
    dts <- do.call(rbind, lapply(kids, get.xy.pos.labs))
    dts$lab <- df$z
    print(dts)
    
    #            x         y    lab
    # 1  0.8871681 0.9945523 MotAlb
    # 2  1.1441033 1.0214568 PruMod
    # 3  0.9431081 0.9194218 EriRub
    # 4  0.9977394 1.3906067 LusMeg
    # 5  0.8900197 0.9160821 PhoOch
    # 6  0.9597605 0.9471191 PhoPho
    # 7  1.0033184 1.0144658 SaxRub
    # 8  0.8898377 0.9424059 TurMer
    # 9  0.9340703 0.9722663 TurPil
    # 10 1.3063754 0.9358787 TurPhi
    
    # Plot labels using the positions calculated using get.xy.pos.labs      
    p2 <- ggplot(data = df, aes(x = x, y = y)) + theme_bw() + 
      geom_text(aes(x=x,y=y, label=lab), data=dts, col="red")+
      geom_point(colour = "green", size = 3)
    x11(width=7,height=7)
    p2
    

    UPDATE December 2018
    With ggplot2 version 3.1.0 (and newer) you need to use the following code to get the x- and y-range (see the answer of @VilmantasGegzna here for details):

    # Get x and y plot ranges 
    ggp1 <- ggplot_build(p1)
    xrg <- ggp1$layout$panel_params[[1]]$x.range
    yrg <- ggp1$layout$panel_params[[1]]$y.range
    

    Here are the plot with labels given by geom_text_repel and the plot with the labels positioned using the calculated positions.

    enter image description here