Reputation: 111
I wanted to visualize a network with the data I have and would like to graph them with specific edge lengths. I use Python, and I've tried networkx and igraph to plot but all seem to assign fixed edge lengths.
a.) I wonder if I did the codes wrong or the packages aren't really capable. How do you properly implement specified edge lengths for networkx or igraph?
b.) If networkx and igraph can't do it, what package could you possibly suggest? (Preferably one that can carry over 80 thousand nodes.)
Thanks!
Upvotes: 11
Views: 13288
Reputation: 13031
AFAIK, networkx and igraph do not have a layout functions that infers node positions based on a given set of edge lengths. However, netgraph, which is a python library for making better network visualisations, does implement the desired functionality in the geometric node layout. In the example below, I am using an edge list to represent the network, but netgraph also accepts networkx, igraph, and graph-tool Graph objects.
#!/usr/bin/env python
import matplotlib.pyplot as plt
from netgraph import Graph # pip install netgraph OR conda install -c conda-forge netgraph
# right triangle
edge_length = {
(0, 1) : 0.3,
(1, 2) : 0.4,
(2, 0) : 0.5,
}
edges = list(edge_length.keys())
fig, ax = plt.subplots()
Graph(edges, edge_labels=edge_length, node_layout='geometric',
node_layout_kwargs=dict(edge_length=edge_length), ax=ax)
ax.set_aspect('equal')
plt.show()
If you only want the node positions but you don't want to use netgraph for plotting, you can compute the node positions using the get_geometric_layout
function:
from netgraph import get_geometric_layout
pos = get_geometric_layout(edges, edge_length)
However, netgraph uses a non-linear optimisation to infer the node positions from the edge lengths. This computation scales with the square of the number of nodes. For networks that can reasonably represented as link diagrams (i.e. up to a few hundred nodes) the computation time is reasonably short (<2 seconds) but I have never tried running the procedure on 80k nodes and I suspect it would take days to finish.
Upvotes: 3
Reputation: 70038
This should work:
import networkx as NX
import pygraphviz as PG
G = PG.AGraph()
nlist = "A B C D E".split()
a, b = "A A B", "B C D"
elist = zip(a.split(), b.split())
G.add_nodes_from(nlist)
G.add_edges_from(elist)
G.node_attr.update(color="red", style="filled")
G.edge_attr.update(color="blue", len="2.0", width="2.0")
print(G.edge_attr)
# returns {'color': 'red', 'width': '', 'len': '2.0'}
# add new edge with custom length (all others have length=2.0):
G.add_edge("C", "E", len="3.0", color="blue", width="2.0")
edge = G.get_edge("C", "E")
print(edge_attr)
# returns {'color': 'blue', 'width': '2.0', 'len': '3.0'}
# and you can confirm that introspection by drawing & printing this graph:
G.draw('somefolderandfilename.png', format='png', prog='neato')
Most graph drawing algorithms use some version of SMACOF, which of course varies the edge length; however, the graphviz layout engine 'neato' (supplied as the 2nd argument to 'draw' above) ought to preserve, if at all possible, user-set edge lengths.
The library i used here is certainly sturdy enough to handle 80,000 nodes.
Upvotes: 8