rigraphggraph

R automated procedure for placement of loops in network graphs


I aim to draw a circular network that contains loops, i.e., the beginning and the end of the edge are at the same node. If there are many connections between nodes, the problem arises that loops are always drawn on the right side. This leads to a very unpleasant graphical presentation. For small networks this could be worked out manually (see e.g., this post).

However, this becomes much more difficult for large networks that potentially do not always contain the same loops. Thus, I am looking for an automated way (rather than the mentioned manual procedure) to place the loops always on the outside of the network to ensure that they don't overlap with the edges within the plot.

An example of this issue could look as follows, where, in particular, the nodes "g", "h", "i", and to some extent "j" suffer from this issue. The plot is restricted to contain only edges with a weight above 5 to indicate that not necessarily all loops need to be available at all times.

Example network:

Example

Code to reproduce network:

# packages
library(ggraph)
library(igraph)

# create example data
set.seed(123)
adj_mat <- matrix(runif(100, 0, 10), nrow=10)
colnames(adj_mat) <- letters[1:10]

# create graph object and remove edges below threshold
g <- graph_from_adjacency_matrix(adj_mat, weighted = T, diag = T)
g <- delete.edges(g, which(E(g)$weight < 5))

# plot
ggraph(g, layout = "circle") +
  geom_edge_link() +
  geom_edge_loop() +
  geom_node_circle(aes(r=0.1), fill = "orange") +
  geom_node_text(aes(label = colnames(adj_mat))) +
  theme(aspect.ratio = 1)

Solution

  • When using geom_edge_loop, you can change the direction of the loops using the direction aesthetic. The default value is 45 degrees, but you can specify it using the from (or, equivalently the to) attributes of the edges. For your circular layout, these are numbered counter-clockwise from the 3 o'clock position, and the correct angle for each node would be (from - 1) * 360 / length(g):

    ggraph(g, layout = "circle") +
      geom_edge_link() +
      geom_edge_loop(aes(direction = (from - 1) * 360 / length(g))) +
      geom_node_circle(aes(r = 0.1), fill = "orange") +
      geom_node_text(aes(label = colnames(adj_mat))) +
      theme(aspect.ratio = 1)
    

    enter image description here