7

I am automatically generating graphs whose nodes need to be in fixed positions. For example:

Graph with fixed nodes

There is actually an arc from node V4 to node V16, but we annot see it because there are also arcs from V4 to V10 and from V10 to V16.

Note that both the nodes and the arcs are generated automatically, and that the positions may vary, so I would need an automated way to curve arcs that are hidden behind other arcs.


Also note that none of these solutions are valid: igraph: Resolving tight overlapping nodes ; Using igraph, how to force curvature when arrows point in opposite directions. The first one simply places de nodes in a certain way, but my nodes need to be fixed. The second one simply deals with pairs of nodes that have two arcs connecting them going in the opposite direction.


UPDATE: The construction of the graph is the result of the learning process of the graph that forms a Bayesian Network using bnlearn library, so I am not very sure how could I produce a reproducible example. The positions of the nodes are fixed because they represent positions. I actually need some magic, some kind of detection of overlapping arcs: If two arcs overlap, curve one of them slightly so that it can be seen. I know from the linked questions that curving an arc is an option, so I thought maybe this kind of magic could be achieved

D1X
  • 5,025
  • 5
  • 21
  • 36
  • 4
    It's easier to help if you provide a [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input data so we can run and test code. Also clearly describe the desired output. What exactly do you want to have happen in the final plot (other than just "magic")? – MrFlick Aug 16 '17 at 19:54
  • @MrFlick The construction of the graph is actually the result of the learning process of the graph that forms a [Bayesian Network](https://en.wikipedia.org/wiki/Bayesian_network) using bnlearn, so I am not very sure how could I produce a reproducible example. The positions of the nodes are fixed because they represent positions. I actually need some magic, some kind of detection of overlapping arcs: If two arcs overlap, curve one of them slightly so that it can be seen. I know from the linked questions that curving an arc is an option, so I thought maybe this kind of magic could be achieved. – D1X Aug 16 '17 at 20:02
  • We do not need the data from bnlearn just the graph: e.g. `as.data.frame(get.edgelist(gr))` and the layout that creates overlapping edges. – emilliman5 Aug 18 '17 at 18:42
  • @emilliman5, I don't get what you mean, neither the nodes nor the edges are always the same – D1X Aug 19 '17 at 19:29
  • 1
    if you are not set on igraph.. you could probably do this using graphviz (which you likely have as bnlearn loads it). With a little bit of text manipulation you could set positions and the render using neato, which (i think( would curve the edges. – user20650 Aug 21 '17 at 00:11
  • @user20650, I decided to switch to igraph due to my inability to set the positions using graphviz, I didn't make it... – D1X Aug 21 '17 at 10:06
  • hmmm, yes, I cant seem to set the pos attribute using rgraphviz either. It can be done by messing about / gsubing with the .dot file to add in the positions, but i doubt it will be that robust. I maybe add a question specific to this to see if anyone has a solution. – user20650 Aug 21 '17 at 15:08
  • @D1X ; added a question on node positions specific to rgraphviz https://stackoverflow.com/questions/45801817/use-the-pos-argument-in-rgraphviz-to-fix-node-position – user20650 Aug 21 '17 at 16:38
  • This may give a way to proceed using igraph. Idea is to find which edges to pass to the edge.curved argument: so using the graph at the link^^, and layout lay <- layout.grid(ig) , you can do `xx = dist(lay[,1], "manhattan") ; yy = dist(lay[,2], "manhattan") ; curveME <- (xx == 0) & (yy > 1) | (xx > 1) & (yy == 0) | ( (xx+yy) > 3) ; plot(ig, layout=lay, edge.curved=as.integer(curveME)/5)` (if i have time i'll try to generalise) – user20650 Aug 21 '17 at 18:23

1 Answers1

4

One solution would be to use the qgraph package. In the example below it automatically curves the bidirectional edges:

library(igraph)
library(qgraph)

# the raster layout
layout <- cbind(1:3, rep(1:3, each = 3))
# fully connected network
adj <- matrix(1, 9, 9)

# plot directed and undirected network
layout(matrix(1:2, 1, 2))
qgraph(adj, layout = layout, directed = FALSE, title = "undirected")
qgraph(adj, layout = layout, directed = TRUE, title = "directed") # automatically curves the bidirectional arrows

enter image description here

To convert an igraph object to something qgraph can use, all you need is an edgelist or adjacency matrix:

g <- make_ring(9)
edgeList <- do.call(rbind, igraph::get.adjedgelist(g))
qgraph(edgeList)

If you also want to include the axes, you can do so using axis() since qgraph uses base graphics. However, you probably have to tinker with par() as well to make it look nice.

Vandenman
  • 3,046
  • 20
  • 33
  • This is cool, but is there an easy way to get edges curved if they are not bidirectional. for example, with adj matrix `adj[lower.tri(adj, diag=TRUE)] <- 0`, which *I think* represent the op's use case - the edge from 1 to 7 can't be seen – user20650 Aug 26 '17 at 20:10
  • 1
    There is, using the argument `curveAll`: `adj <- matrix(0, 9, 9); adj[cbind(1, 2:3)] <- 1; adj[cbind(2:3, 1)] <- 1; qgraph(adj, layout = layout, curveAll = -1)`. Different methods using the arguments `edge.color` and `arrows` also exist but are a bit more hacky. – Vandenman Aug 27 '17 at 10:39
  • thanks Vanderman, this gives similar results as edge.curved using igraph. It would be nice to only curve the edges that are *hidden* when they are not curved and keep the others straight, but I guess it is non trivial to identify these.( ie 1 to 4 and 4 to 7 straight, but 1 to 7 curved) – user20650 Aug 27 '17 at 11:33
  • Ah didn't get that, sorry. If you have an edgelist and a layout were the distance between adjacent nodes is 1, couldn't you just curve all edges with length > 1? Edges passing underneath other nodes could also be confusing. – Vandenman Aug 27 '17 at 11:49
  • I thought of doing something like [that](https://stackoverflow.com/questions/45721802/automatically-curving-an-arc-when-it-is-overlapping-with-another-one/45897286?noredirect=1#comment78564669_45721802), but an added difficulty is , that not all edges with distance > 1 need to be curved - ie you would still want edge 1 to 8 straight. Probably some fancy graph search algorithm but ??? (just hoping you would have some inspiration ;)) – user20650 Aug 27 '17 at 12:09