# Visualizing Graphs with Plotly Python

In this post, I will show working examples of the Plotly Python library.

## Why Plotly Python?

Plotly Python is a free, open-source graphing library for Python. I think it is more powerful than Matplotlib for the following reasons.

- Fully integrated with JupyterLab.
- Interactive interface: useful for large graphs and 3D visualization.
- Simple and rich APIs.

*Note*: Matplotlib is still useful for simple, on-the-fly graphing and repetitive tasks, such as generating a number of images.

## Installation

In addition to Plotly Python, I am using NetworkX and JupyterLab for visualizing graphs. Those tools can be installed by `pip`

.

```
$ pip install networkx jupyterlab plotly
```

Then, install the following JupyterLab extensions. See JupyterLab Support.

```
$ jupyter labextension install jupyterlab-plotly
```

Versions used for this post:

```
$ python --version
Python 3.8.5
$ pip list |egrep 'networkx|jupyterlab|plotly'
jupyterlab 3.0.6
jupyterlab-pygments 0.1.1
jupyterlab-server 2.1.3
networkx 2.5
plotly 4.14.3
```

## Visualizing 2D Graphs

First, create a new Jupyter Notebook.

- Launch JupyterLab:
`jupyter-lab`

- Create a new Jupyter Notebook with Python 3.

With Plotly, we represent nodes as scattered markers and edges as a set of line graphs with gaps. To facilitate this process, I have written a thin wrapper class specialized in NetworkX graphs.

Download and run this file in a Jupyter Notebook cell.

```
%run visualize.py
```

Next, I want to visualize the Petersen graph. To get better visualization, I manually set the position of each vertex. Refer to NetworkX’s document if you want an automatic layout.

```
G = nx.petersen_graph()
# set 2D positions
import math
pos2d = {}
for i in range(5):
theta = 2 * math.pi * i / 5 + math.pi / 2
pos2d[i] = (2 * math.cos(theta), 2 * math.sin(theta)) # outer vertices
pos2d[5 + i] = (math.cos(theta), math.sin(theta)) # inner vertices
for v in G:
print('%d: (%6s, %6s)' % (v, '%.3f' % pos2d[v][0], '%.3f' % pos2d[v][1]))
```

Output:

```
0: ( 0.000, 2.000)
1: (-1.902, 0.618)
2: (-1.176, -1.618)
3: ( 1.176, -1.618)
4: ( 1.902, 0.618)
5: ( 0.000, 1.000)
6: (-0.951, 0.309)
7: (-0.588, -0.809)
8: ( 0.588, -0.809)
9: ( 0.951, 0.309)
```

Then, create an instance of the `GraphVisualization`

class defined in `visualize.py`

with the 2D positions.

```
vis = GraphVisualization(G, pos2d)
fig = vis.create_figure()
fig.show()
```

With the `GraphVisualization`

class, one can specify various options, such as vertex color and edge width.

```
vis = GraphVisualization(
G,
pos2d,
node_text_position='top center',
node_size=40,
node_color=int, # color vertices based on their IDs
node_text_font_size={4: 30},
edge_color={(0, 1): '#ff0000'},
edge_width={(1, 2): 10}
)
fig = vis.create_figure(showscale=True, colorbar_title='Vertex ID')
fig.show()
```

## Visualizing 3D Graphs

Visualizing graphs in 3D is easy with Plotly. What we need is just to describe a position as a tuple of three numbers. The following code gives 3D positions for the same Petersen graph, using a realization on a tetrahedron.

```
# set 3D positions
r = math.sqrt(3)
def mid(a, b):
return tuple((a[i] + b[i]) / 2 for i in range(3))
pos3d = {
0: (0, 0, r),
3: (1, 1/r, 0),
6: (-1, 1/r, 0),
7: (0, -2/r, 0),
}
pos3d[1] = mid(pos3d[0], pos3d[6])
pos3d[2] = mid(pos3d[3], pos3d[7])
pos3d[4] = mid(pos3d[0], pos3d[3])
pos3d[5] = mid(pos3d[0], pos3d[7])
pos3d[8] = mid(pos3d[3], pos3d[6])
pos3d[9] = mid(pos3d[6], pos3d[7])
for v in G:
print('%d: (%6s, %6s, %6s)' % (v, '%.3f' % pos3d[v][0], '%.3f' % pos3d[v][1], '%.3f' % pos3d[v][2]))
```

Output:

```
0: ( 0.000, 0.000, 1.732)
1: (-0.500, 0.289, 0.866)
2: ( 0.500, -0.289, 0.000)
3: ( 1.000, 0.577, 0.000)
4: ( 0.500, 0.289, 0.866)
5: ( 0.000, -0.577, 0.866)
6: (-1.000, 0.577, 0.000)
7: ( 0.000, -1.155, 0.000)
8: ( 0.000, 0.577, 0.000)
9: (-0.500, -0.289, 0.000)
```

Now, visualize it!

```
vis = GraphVisualization(G, pos3d, node_color=int)
fig = vis.create_figure(showscale=True, colorscale='peach', colorbar_title='Vertex ID')
fig.show()
```

*Note*: Unfortunately, `node_border_width`

in 3D does not work as expected due to Plotly’s bug.

## Visualizing Larger Graphs

Lastly, let’s visualize some random larger graphs. It would be a good idea to make the nodes smaller for better visibility.

```
G = nx.watts_strogatz_graph(300, 12, 0.01, seed=12345)
pos = nx.spring_layout(G)
vis = GraphVisualization(G, pos, node_size=4, node_border_width=1, edge_width=0.5)
fig = vis.create_figure(height=800, width=800, showlabel=False)
fig.show()
```

When you hover over a vertex, its name (vertex label by default) will show up.

```
G = nx.erdos_renyi_graph(300, 0.008, seed=12345)
pos = nx.spring_layout(G, iterations=20)
vis = GraphVisualization(G, pos, node_size=4, node_border_width=1, edge_width=0.5)
fig = vis.create_figure(height=600, width=600, showlabel=False)
fig.show()
```