# 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