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.

Jupyter Lab Image

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()

Jupyter Lab Image

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()

Jupyter Lab Image

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()

Jupyter Lab Image

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()

Jupyter Lab Image

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()

Jupyter Lab Image