NetworkX Tutorial

In this tutorial we will take a look at ways of combining the analysis tools provided by NetworkX with the visualization capailities of AlgorithmX.

Simple Graph

Let’s start by creating a simple NetworkX graph. We will use add_path to quickly add both nodes and edges.

import networkx as nx

G = nx.Graph()

nx.add_path(G, [1, 2, 3])
nx.add_path(G, [4, 2, 5])

print('Nodes:', G.nodes)
print('Edges:', G.edges)
Nodes: [1, 2, 3, 4, 5]
Edges: [(1, 2), (2, 3), (2, 4), (2, 5)]

Now that we have all the data we need, we can create an AlgorithmX canvas to display our nodes and edges.

from algorithmx import jupyter_canvas

canvas = jupyter_canvas()

canvas.nodes(G.nodes).add()
canvas.edges(G.edges).add()

canvas

Finally, let’s make it look a little more interesting.

canvas = jupyter_canvas()

node_colors = {1: 'red', 2: 'green', 3: 'blue', 4: 'orange', 5: 'purple'}

canvas.nodes(G.nodes).add(
    shape='rect',
    size=(20, 12),
    color=lambda n: node_colors[n]
)
canvas.edges(G.edges).add()

canvas

Weighted and Directed Graphs

To create a directed graph, all we need to do is use a NetworkX DiGraph, and tell AlgrithmX that the edges should be rendered with an arrow.

G = nx.DiGraph()

G.add_nodes_from([1, 2, 3])
G.add_edges_from([(1, 2), (2, 3), (3, 1)])

canvas = jupyter_canvas()

canvas.nodes(G.nodes).add()
canvas.edges(G.edges).add(directed=True)

canvas

To create wighted graph, we will first ensure that our NetworkX edges have a ‘weight’ attribute. Then, we will add a label to each edge displaying the attribute.

G = nx.Graph()

G.add_nodes_from([1, 2, 3])
G.add_weighted_edges_from([(1, 2, 0.4), (2, 3, 0.2), (3, 1, 0.3)])

canvas = jupyter_canvas()

canvas.nodes(G.nodes).add()
canvas.edges(G.edges).add(
    labels=lambda e: {0: {'text': G.edges[e]['weight']}}
)

canvas

Finally, AlgorithmX provides a uility to simplify this process.

from algorithmx.networkx import add_graph

canvas = jupyter_canvas()
add_graph(canvas, G)

Random Graph

NetworkX provides a range of functions for generating graphs. For generating a random graph, we will use the basic gnp_random_graph function. By providing a seed, we can ensure that we get the same graph every time (otherwise there is no guarantee of it being connected!).

G = nx.gnp_random_graph(10, 0.3, 138)

canvas = jupyter_canvas()
canvas.nodes(G.nodes).add()
canvas.edges(G.edges).add()

canvas

To make the graph directed, we will simply use G.to_directed. To make the graph weighted, we will need to configure a weight attribute for each edge. Since our graph is random, we’ll make our edge weights random as well.

from random import randint

G = G.to_directed()
nx.set_edge_attributes(G, {e: {'weight': randint(1, 10)} for e in G.edges})

Finally, we display the graph.

canvas = jupyter_canvas()
add_graph(canvas, G)

Detailed Graph

Now we are going to create a graph that displays a range of interesting properties. Let’s begin by generating a random weighted graph, as before.

G = nx.gnp_random_graph(10, 0.3, 201)
nx.set_edge_attributes(G, {e: {'weight': randint(1, 10)} for e in G.edges})

Next, we will use NetworkX to calculate the graph’s coloring and edge centrality.

coloring = nx.greedy_color(G)
centrality = nx.edge_betweenness_centrality(G, weight='weight', normalized=True)

We can now begin displaying the graph. First, we will add the nodes and assign them a color based on their calculated priority. We happen to know that any planar graph requires at most 4 different colors, and so we prepare these beforehand.

canvas = jupyter_canvas()

color_priority = {0: 'red', 1: 'orange', 2: 'yellow', 3: 'green'}

canvas.nodes(G.nodes).add() \
    .color(lambda n: color_priority[coloring[n]])

print(coloring)
{4: 0, 2: 1, 3: 2, 0: 1, 1: 2, 6: 0, 8: 1, 7: 2, 9: 2, 5: 0}

Afterwards, we will add the edges. Each one will have two labels; one to display it’s weight, and another to display it’s calculated centrality.

formatted_centrality = {k: '{0:.2f}'.format(v) for k, v in centrality.items()}

init_edges = canvas.edges(G.edges).add()

init_edges.label().add(
     text=lambda e: G.edges[e]['weight']
)
init_edges.label('centrality').add(
     color='blue',
     text=lambda e: formatted_centrality[e]
)

print(formatted_centrality)
{(0, 1): '0.02', (0, 3): '0.07', (0, 4): '0.11', (1, 4): '0.18', (1, 8): '0.00', (2, 3): '0.04', (2, 4): '0.40', (2, 5): '0.20', (2, 6): '0.20', (2, 7): '0.07', (3, 4): '0.09', (3, 6): '0.09', (4, 8): '0.00', (4, 9): '0.36', (6, 7): '0.13', (8, 9): '0.20'}

Finally, we display the whole graph.

canvas