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
)
Is there any way I can dodge the labels from the arrow heads? Thanks!