Introduction to Graph Analysis with networkx
¶
Graph theory deals with various properties and algorithms concerned with
Graphs. Although it is very easy to implement a Graph ADT in Python, we
will use networkx
library for Graph Analysis as it has inbuilt
support for visualizing graphs. In future versions of networkx
,
graph visualization might be removed. When this happens, it is required
to modify some parts of this chapter
Standard import
statement¶
Throughout this tutorial, we assume that you have imported networkx
as follows
In [38]:
import networkx as nx
Creating Graphs¶
Create an empty graph with no nodes and no edges.
In [39]:
G = nx.Graph()
By definition, a Graph
is a collection of nodes (vertices) along
with identified pairs of nodes (called edges, links, etc). In NetworkX,
nodes can be any hashable object e.g. a text string, an image, an XML
object, another Graph, a customized node object, etc. (Note: Python’s
None object should not be used as a node as it determines whether
optional function arguments have been assigned in many functions.)
Nodes¶
The graph G
can be grown in several ways. NetworkX includes many
graph generator functions and facilities to read and write graphs in
many formats. To get started, we’ll look at simple manipulations. You
can add one node at a time,
In [40]:
G.add_node(1)
add a list of nodes,
In [41]:
G.add_nodes_from([2,3])
Edges¶
G can also be grown by adding one edge at a time,
In [42]:
G.add_edge(1,2)
e=(2,3)
G.add_edge(*e) # Unpacking tuple
by adding a list of edges,
In [43]:
G.add_edges_from([(1,2),(1,3)])
we add new nodes/edges and NetworkX quietly ignores any that are already present.
At this stage the graph G consists of 3 nodes and 3 edges, as can be seen by:
In [44]:
G.number_of_nodes()
Out[44]:
3
In [45]:
G.number_of_edges()
Out[45]:
3
Accessing edges¶
In addition to the methods Graph.nodes
, Graph.edges
, and
Graph.neighbors
, iterator versions (e.g. Graph.edges_iter
) can
save you from creating large lists when you are just going to iterate
through them anyway.
Fast direct access to the graph data structure is also possible using subscript notation.
Warning
Do not change the returned dict
–it is part of the graph data
structure and direct manipulation may leave the graph in an inconsistent
state.
In [46]:
G.nodes()
Out[46]:
[1, 2, 3]
In [47]:
G.edges()
Out[47]:
[(1, 2), (1, 3), (2, 3)]
In [48]:
G[1]
Out[48]:
{2: {}, 3: {}}
In [49]:
G[1][2]
Out[49]:
{}
You can safely set the attributes of an edge using subscript notation if the edge already exists.
In [50]:
G[1][2]['weight'] = 10
In [51]:
G[1][2]
Out[51]:
{'weight': 10}
Fast examination of all edges is achieved using adjacency iterators. Note that for undirected graphs this actually looks at each edge twice.
In [52]:
FG=nx.Graph()
FG.add_weighted_edges_from([(1,2,0.125),(1,3,0.75),(2,4,1.2),(3,4,0.375)])
for n,nbrs in FG.adjacency_iter():
for nbr,eattr in nbrs.items():
data=eattr['weight']
if data<0.5: print('(%d, %d, %.3f)' % (n,nbr,data))
(1, 2, 0.125)
(2, 1, 0.125)
(3, 4, 0.375)
(4, 3, 0.375)
In [53]:
list(FG.adjacency_iter())
Out[53]:
[(1, {2: {'weight': 0.125}, 3: {'weight': 0.75}}),
(2, {1: {'weight': 0.125}, 4: {'weight': 1.2}}),
(3, {1: {'weight': 0.75}, 4: {'weight': 0.375}}),
(4, {2: {'weight': 1.2}, 3: {'weight': 0.375}})]
Convenient access to all edges is achieved with the edges
method.
In [54]:
for (u,v,d) in FG.edges(data='weight'):
if d<0.5: print('(%d, %d, %.3f)'%(n,nbr,d))
(4, 3, 0.125)
(4, 3, 0.375)
Adding attributes to graphs, nodes, and edges¶
Attributes such as weights, labels, colors, or whatever Python object you like, can be attached to graphs, nodes, or edges.
Each graph, node, and edge can hold key/value attribute pairs in an
associated attribute dictionary (the keys must be hashable). By default
these are empty, but attributes can be added or changed using
add_edge
, add_node
or direct manipulation of the attribute
dictionaries named G.graph
, G.node
and G.edge
for a graph
G
.
Graph attributes¶
Assign graph attributes when creating a new graph
In [55]:
G = nx.Graph(day="Friday")
G.graph
Out[55]:
{'day': 'Friday'}
Or you can modify attributes later
In [56]:
G.graph['day']='Monday'
G.graph
Out[56]:
{'day': 'Monday'}
Node attributes¶
Add node attributes using add_node()
, add_nodes_from()
or G.node
In [57]:
G.add_node(1,time = '5pm')
In [58]:
G.add_nodes_from([3], time='2pm')
In [59]:
G.node[1]
Out[59]:
{'time': '5pm'}
In [60]:
G.node[1]['room'] = 714
In [61]:
G.nodes(data=True)
Out[61]:
[(1, {'room': 714, 'time': '5pm'}), (3, {'time': '2pm'})]
Note that adding a node to G.node
does not add it to the graph, use
G.add_node()
to add new nodes.
Edge Attributes¶
Add edge attributes using add_edge()
, add_edges_from()
,
subscript notation, or G.edge
.
In [62]:
G.add_edge(1, 2, weight=4.7 )
In [63]:
G[1][2]
Out[63]:
{'weight': 4.7}
In [64]:
G.add_edges_from([(3,4),(4,5)], color='red')
In [65]:
G.add_edges_from([(1,2,{'color':'blue'}), (2,3,{'weight':8})])
In [66]:
G[1][2]['weight'] = 4.7
In [67]:
G.edge[1][2]['weight'] = 4
In [68]:
G.edges(data=True)
Out[68]:
[(1, 2, {'color': 'blue', 'weight': 4}),
(2, 3, {'weight': 8}),
(3, 4, {'color': 'red'}),
(4, 5, {'color': 'red'})]
Converting Graph to Adjacency matrix¶
You can use nx.to_numpy_matrix(G)
to convert G
to numpy
matrix. If the graph is weighted, the elements of the matrix are
weights. If an edge doesn’t exsist, its value will be 0, not Infinity.
You have to manually modify those values to Infinity (float('inf')
)
In [69]:
nx.to_numpy_matrix(G)
Out[69]:
matrix([[ 0., 4., 0., 0., 0.],
[ 4., 0., 8., 0., 0.],
[ 0., 8., 0., 1., 0.],
[ 0., 0., 1., 0., 1.],
[ 0., 0., 0., 1., 0.]])
In [70]:
nx.to_numpy_matrix(FG)
Out[70]:
matrix([[ 0. , 0.125, 0.75 , 0. ],
[ 0.125, 0. , 0. , 1.2 ],
[ 0.75 , 0. , 0. , 0.375],
[ 0. , 1.2 , 0.375, 0. ]])
Drawing graphs¶
NetworkX is not primarily a graph drawing package but basic drawing with Matplotlib as well as an interface to use the open source Graphviz software package are included. These are part of the networkx.drawing package and will be imported if possible
In [71]:
%matplotlib inline
import matplotlib.pyplot as plt
In [72]:
nx.draw(FG)
Now we shall draw the graph using graphviz layout
In [73]:
from networkx.drawing.nx_agraph import graphviz_layout
pos = graphviz_layout(FG)
plt.axis('off')
nx.draw_networkx_nodes(FG,pos,node_color='g',alpha = 0.8) # draws nodes
nx.draw_networkx_edges(FG,pos,edge_color='b',alpha = 0.6) # draws edges
nx.draw_networkx_edge_labels(FG,pos,edge_labels = nx.get_edge_attributes(FG,'weight')) # edge lables
nx.draw_networkx_labels(FG,pos) # node lables
Out[73]:
{1: <matplotlib.text.Text at 0x7f2e2eecacc0>,
2: <matplotlib.text.Text at 0x7f2e2eecaba8>,
3: <matplotlib.text.Text at 0x7f2e2ee97e80>,
4: <matplotlib.text.Text at 0x7f2e2ee97be0>}
Going Further¶
We have only seen the basic graph functionalities. In addition to this, NetworkX provides many Graph Algorithms, and Many types of Graphs. Interested reader can look at Official Documentation