In this Jupyter Notebook we plot the graph representing the Couauthorship Network of Scientists, with Kamada-Kawada (KK) layout, implemented in the Python packages, igraph
, py/graphviz
, respectively networkx
. The idea of this comparison was suggested by a conversation on twitter.
import plotly.graph_objects as go
from plotly.offline import download_plotlyjs, init_notebook_mode, iplot
init_notebook_mode(connected=True)
Define data for the Plotly plot:
def get_plotly_data(E, coords):
# E is the list of tuples representing the graph edges
# coords is the list of node coordinates
N = len(coords)
Xnodes = [coords[k][0] for k in range(N)] # x-coordinates of nodes
Ynodes = [coords[k][1] for k in range(N)] # y-coordnates of nodes
Xedges = []
Yedges = []
for e in E:
Xedges.extend([coords[e[0]][0], coords[e[1]][0], None])
Yedges.extend([coords[e[0]][1], coords[e[1]][1], None])
return Xnodes, Ynodes, Xedges, Yedges
def get_node_trace(x, y, labels, marker_size=5, marker_color='#6959CD',
line_color='rgb(50,50,50)', line_width=0.5):
return go.Scatter(
x=x,
y=y,
mode='markers',
marker=dict(
size=marker_size,
color=marker_color,
line=dict(color=line_color, width=line_width)
),
text=labels,
hoverinfo='text'
)
def get_edge_trace(x, y, line_color='rgb(210,210,210)', line_width=1):
return go.Scatter(
x=x,
y=y,
mode='lines',
line_color=line_color,
line_width=line_width,
hoverinfo='none'
)
Set the plot layout (don't confuse with graph layout!!!):
title1= "Coauthorship network"+\
"<br>Data source: <a href='https://networkdata.ics.uci.edu/data.php?id=11'> [1]</a>"
width = 800
height = 800
layout=go.Layout(
title_text=title1,
title_x=0.5,
font_size=12,
showlegend=False,
width=width,
height=height,
xaxis_visible=False,
yaxis_visible=False,
template='none',
hovermode='closest',
paper_bgcolor='#eeeeee',
annotations=[dict(showarrow=False,
text='igraph Kamada-Kawai layout',
xref='paper',
yref='paper',
x=0,
y=-0.1,
xanchor='left',
yanchor='bottom',
font=dict(size=14)
)
]
)
import igraph as ig
ig.__version__
'0.7.1'
The graph data are read from a gml
file, posted at UC Irvine Network Data Repository:
G = ig.Graph.Read_GML('netscience.gml')
labels = list(G.vs['label'])
N = len(labels)
V = range(N)
E = [e.tuple for e in G.es]# list of edges as tuples of node indices
Get node positions according to Kamada-Kawai graph layout:
ig_position = G.layout('kk')
Xn, Yn, Xe, Ye = get_plotly_data(E, ig_position)
trace1 = get_edge_trace(Xe, Ye)
trace2 = get_node_trace(Xn, Yn, labels)
fig1 = go.Figure(data=[trace1, trace2], layout=layout)
iplot(fig1)
Display the number of weak connected components in this graph:
len(G.clusters(mode='weak'))
396
The graphviz layout, neato
, implements the KK-algorithm (see https://graphviz.gitlab.io/_pages/pdf/libguide.pdf, page 22: "The model used by neato (layout) comes from Kamada and Kawai[KK89], though it was first introduced by
Kruskal and Seely[KS80] in a different format")
import pygraphviz
pygraphviz.__version__ #python 3.7 compatible
'1.5'
import pygraphviz as pgv
from ast import literal_eval
g = pgv.AGraph(strict=True, directed=False)
g.add_nodes_from(V)
g.add_edges_from(E)
g.layout(prog='neato')
g.get_node(0).attr['pos']
'-888.25,-354.74'
N = len(g.nodes())
pgv_position = [literal_eval(g.get_node(k).attr['pos']) for k in range(N)]
Xnode, Ynode, Xedge, Yedge = get_plotly_data(E, pgv_position)
trace3 = get_edge_trace(Xedge, Yedge)
trace4 =get_node_trace(Xnode, Ynode, labels)
annot2 = "Graphviz neato layout"
fig2 = go.Figure(data=[trace3, trace4], layout=layout)
fig2['layout']['annotations'][0]['text'] = annot2
iplot(fig2)
import networkx as nx
nx.__version__
'2.4'
We don't read the gml file again, because we instantiate the class nx.Graph
with the lists of nodes V,
and edges, E, already set up.
#H=nx.read_gml('netscience.gml')#
H = nx.Graph()
H.add_nodes_from(V)
H.add_edges_from(E)
Get the node positions:
nx_position = nx.kamada_kawai_layout(H)
Define data for the Plotly plot:
Xv, Yv, Xed, Yed = get_plotly_data(E, nx_position)
trace5 = get_edge_trace(Xed, Yed)
trace6 = get_node_trace(Xv, Yv, labels)
annot3 = "Networkx Kamada-Kawai layout"
fig3 = go.Figure(data=[trace5, trace6], layout=layout)
fig3['layout'].update(width=900, height=900)
fig3['layout']['annotations'][0]['text']= annot3
iplot(fig3)
Inspect the number of connected components computed via networkx:
print (nx.number_connected_components(H))
396
Let us try the nx.spring_layout
:
position = nx.spring_layout(H)
Xv, Yv, Xed, Yed = get_plotly_data(E, position)
trace7 = get_edge_trace(Xed, Yed)
trace8 = get_node_trace(Xv, Yv, labels)
annot4 = "Networkx spring layout"
fig4 = go.Figure(data=[trace7, trace8], layout=layout)
fig4['layout'].update(width=900, height=900)
fig4['layout']['annotations'][0]['text'] = annot4
iplot(fig4)
Conclusion
The networks defined as instances of the graph classes provided by the three Python packages, and displayed with the same KK layout, are quite different. To avoid any doubt, we instantiated the class pygraphvix.AGraph
, and the networkx.Graph
from the same lists of nodes, V, and edges, E, extracted from the igraph.Graph
.
surrounding most of the complementary nodes.
The pygraphviz
neato
layout seems to display the multi-node connected components in the similar way, but here the singleton nodes are placed around a square, bounding the graph.
At the first sight the networkx
with KK layout displays a connected graph. We cannot see any singleton in the graph plot.
The multi-node connected components are indistinguishable, although the total number of reported connected components is the same as the number found via igraph
.