Repel labels in ggrepel away from arrowheads

Viewed 188

I'm creating some DAGs using the ggdag package, which is a wrapper around ggplot and ggrepel. Because the text doesn't fit into the nodes I'm using geom_dag_label_repel, which is a wrapper around ggrepel's geom_label_repel.

Unfortunately the labels cover the arrowheads and this will not do. I need the labels to repel in a way that shows the arrowheads clearly. I've tried using the force_pull argument but this gives very capricious results. I've tried a bunch of other things as well: nudge_x/y doesn't seem to nudge the labels at all, dodging using position = 'dodge' also doesn't work.

The catch is that I'm generating the graphs using a custom function and so I would like any repelling to be automated within the function, so that when I supply coordinates and etc, the labels come out at the right place without me having to specify exactly where I want them for each individual graph. Here's a reprex:

library(latex2exp)
library(tidyverse)
library(dagitty)
library(ggdag)
library(repr)

hj_ggdag <- function(x,
                     y,
                     names,
                     arcs = cbind(0,0),
                     title = "",
                     contraction = .1, #unused
                     add_functions = 0, #unused
                     add_functions_text = NULL, #unused
                     text_shift = .2*add_points, #unused
                     padding = 0, # padding around box if labels = T
                     length = 0.2, #unused
                     cex = 1, #unused
                     adj = .5, #unused
                     box = TRUE,   #unused
                     model = NULL, #accepts causal model objects and returns ggdag
                     labels = FALSE,
                     textcol = 'white', # text colour (not label)
                     textsize = 3.88, #text size (not label)
                     force = 0, #repelling force between labels
                     ...) { # other arguments passed to ggdag and geom_dag_label_repel, e.g. force_pull, node = T/F
  
  # Step 1: First make the df
  if (!is.null(model)) {
    df <- tidy_dagitty(paste0('dag{',model$statement,'}'))
    df$data <- df$data %>%
      mutate(label = name)
  }  else {
    
    # Operates on other arguments
    # Placeholders
    nodes     <- LETTERS[1:length(names)]
    
    # Coordinates
    names(x) <- nodes  
    names(y) <- nodes  
    
    coords_df <- list(x = x , y = y) %>% coords2df
    
    # Statement  
    statement <-  paste(nodes[arcs[,1]], " -> ", nodes[arcs[,2]], collapse = "; ")
    
    # DAG df  
    df <-  paste("dag{", statement, "}") %>% dagitty
    coordinates(df) <- coords2list(coords_df)
    # imputing labels as an additional column so geom_dag_label_repel can accept it within the same aes
    df <- df %>% tidy_dagitty
    df$data <- df$data %>%
      mutate(label = names %>% as.character %>% .[match(df$data$name,LETTERS)])
  }
  # Step 2: Format and export
  p <- df %>% 
    ggdag(text = FALSE,...) + theme_dag() +
    labs(title = TeX(repr_text(title) %>% str_remove_all('\\"')))
  if (labels==TRUE){
    parse <- ifelse(class(names)=='expression',TRUE,FALSE)
    p + 
      geom_dag_label_repel(aes_string(label = 'label'),
                           show.legend = FALSE,
                           parse = parse,
                           box.padding = padding,
                           hjust = 0,
                           segment.color = 'red',
                           segment.size = 1,
                           min.segment.length=0,
                           ...)
  } else {
    p + 
      geom_dag_text_repel(aes_string(label = 'label'),
                          show.legend = FALSE,
                          parse = TRUE,
                          color=textcol,
                          size=textsize,
                          box.padding = 0,
                          force = 0
      )
  }
}

hj_ggdag(x = c(1,1,2),
       y = c(2,0,1),
       names = c(
         expression(paste(D[t-1]: "Democracy, last period")),
         expression(paste(Y[t]: "GDP per capita, this period")),  
         expression(paste(D[t]: "Democracy, this period"))
         ),
       arcs = cbind( c(1,2),
                     c(3,3)),
       title = "(c) Democratization (Przeworski and Limongi, 1997)",
       add_functions = 0, 
       contraction = .16, 
       padding = 0,
       labels = T,
       force_pull=0
)

Label still covers arrows

Is there any way I can dodge the labels from the arrow heads? Thanks!

0 Answers
Related