NetworkX#

The hvPlot NetworkX plotting API is meant as a drop-in replacement for the networkx.draw methods. In most cases the existing code will work as is or with minor modifications, returning a HoloViews object rendering an interactive bokeh plot, equivalent to the matplotlib plot the standard API constructs. First let us import the plotting interface and give it the canonical name hvnx:

import hvplot.networkx as hvnx

import networkx as nx
import holoviews as hv

In this user guide we will follow along with many of the examples in the NetworkX tutorial on drawing graphs.

The hxnx namespace provides all the same plotting functions as nx, this means in most cases one can simply be swapped for the other. This also includes most keywords used to customize the plots. The main difference is in the way multiple plots are composited, like all other hvPlot APIs the networkX functions returns HoloViews objects which can be composited using + and * operations:

G = nx.petersen_graph()

spring = hvnx.draw(G, with_labels=True)
shell = hvnx.draw_shell(G, nlist=[range(5, 10), range(5)], with_labels=True, font_weight='bold')

spring + shell
H = nx.triangular_lattice_graph(1, 20)
hvnx.draw_planar(H, node_color='green', edge_color='brown')

The most common layout functions have dedicated drawing methods such as the draw_shell function above, which automatically computes the node positions.

However layout algorithms are not necessarily deterministic, so if we want to plot and overlay subsets of either the nodes or edges using the nodelist and edgelist keywords the node positions should be computed ahead of time and passed in explicitly:

pos = nx.layout.spring_layout(G)

hvnx.draw(G, pos, nodelist=[0, 1, 2, 3, 4], node_color='blue') *\
hvnx.draw_networkx_nodes(G, pos, nodelist=[5, 6, 7, 8, 9], node_color='green')

The hvnx namespace also makes save and show utilities available to save the plot to HTML or PNG files or display it in a separate browser window when working in a standard Python interpreter.

G = nx.dodecahedral_graph()

shells = [[2, 3, 4, 5, 6], [8, 1, 0, 19, 18, 17, 16, 15, 14, 7], [9, 10, 11, 12, 13]]
shell = hvnx.draw_shell(G, nlist=shells)

pos = nx.nx_agraph.graphviz_layout(G)
graphviz = hvnx.draw(G, pos=pos)

layout = shell + graphviz

hvnx.save(layout, 'graph_layout.png')

Styling Graphs#

The full set of options which are inherited from networkx’s API are listed in the hxnx.draw() docstring. Using these the more common styling of nodes and edges can easily be altered through the common set of options that are inherited from networkx. In addition common HoloViews options to control the size of the plots, axes and styling are also supported. Finally, some layout functions also accept special keyword arguments such as the nlist argument for the shell layout which specifies the shells.

options = {
    'node_color': 'black',
    'node_size': 100,
    'edge_width': 3,
    'width': 300,
    'height': 300
}

random = hvnx.draw_random(G, **options)
circular = hvnx.draw_circular(G, **options)
spectral = hvnx.draw_spectral(G, **options)
shell = hvnx.draw_shell(G, nlist=[range(5,10), range(5)], **options)

(random + circular + spectral + shell).cols(2)

In addition to being able to set scalar style values hvPlot also supports the HoloViews concept of style mapping, which uses so called dim transforms to map attributes of the graph nodes and edges to vary the visual attributes of the plot. For example we might construct a graph with edge weights and node sizes as attributes. The plotting function will extract these attributes which means they can be used to scale visual properties of the plot such as the edge_width, edge_color or node_size:

G = nx.Graph()

G.add_edge('a', 'b', weight=0.6)
G.add_edge('a', 'c', weight=0.2)
G.add_edge('c', 'd', weight=0.1)
G.add_edge('c', 'e', weight=0.7)
G.add_edge('c', 'f', weight=0.9)
G.add_edge('a', 'd', weight=0.3)

G.add_node('a', size=20)
G.add_node('b', size=10)
G.add_node('c', size=12)
G.add_node('d', size=5)
G.add_node('e', size=8)
G.add_node('f', size=3)

pos = nx.spring_layout(G)  # positions for all nodes

hvnx.draw(G, pos, edge_color='weight', edge_cmap='viridis',
          edge_width=hv.dim('weight')*10, node_size=hv.dim('size')*20)

The full set of options that are supported can be accessed on the hvnx.draw function (note this does not include some bokeh specific option to control the styling of selection, nonselection and hover nodes and edges which may also be supplied and follow a pattern like hover_node_fill_color or selection_edge_line_alpha).

For reference here is the docstring listing the main supported option:

print(hvnx.draw.__doc__)
    Draw the graph G using hvPlot.

    Draw the graph with hvPlot with options for node positions,
    labeling, titles, and many other drawing features.

    Parameters
    ----------
    G : graph
       A networkx graph
    pos : dictionary, optional
       A dictionary with nodes as keys and positions as values.
       If not specified a spring layout positioning will be computed.
       See :py:mod:`networkx.drawing.layout` for functions that
       compute node positions.
    arrows : bool, optional (default=True)
       For directed graphs, if True draw arrowheads.
       Note: Arrows will be the same color as edges.
    arrowhead_length : float, optional (default=0.025)
       The length of the arrows as fraction of the overall extent of
       the graph
    with_labels :  bool, optional (default=True)
       Set to True to draw labels on the nodes.
    nodelist : list, optional (default G.nodes())
       Draw only specified nodes
    edgelist : list, optional (default=G.edges())
       Draw only specified edges
    node_size : scalar or array, optional (default=300)
       Size of nodes.  If an array is specified it must be the
       same length as nodelist.
    node_color : color string, node attribute, or array of floats, (default='r')
       Can be a single color, the name of an attribute on the nodes or
       sequence of colors with the same length as nodelist.  If the
       node_color references an attribute on the nodes or is a list of
       values they will be colormapped using the cmap and vmin, vmax
       parameters.
    node_shape :  string, optional (default='o')
       The shape of the node. Specification is as valid bokeh marker.
    alpha : float, optional (default=1.0)
       The node and edge transparency
    cmap : Colormap, optional (default=None)
       Colormap for mapping intensities of nodes
    vmin,vmax : float, optional (default=None)
       Minimum and maximum for node colormap scaling
    linewidths : [None | scalar | sequence]
       Line width of symbol border (default =1.0)
    edge_width : float, optional (default=1.0)
       Line width of edges
    edge_color : color string, or array of floats (default='r')
       Can be a single color, the name of an attribute on the edges or
       sequence of colors with the same length as the edges.  If the
       edge_color references an attribute on the edges or is a list of
       values they will be colormapped using the edge_cmap and
       edge_vmin, edge_vmax parameters.
    edge_cmap : Matplotlib colormap, optional (default=None)
       Colormap for mapping intensities of edges
    edge_vmin,edge_vmax : floats, optional (default=None)
       Minimum and maximum for edge colormap scaling
    style : string, optional (default='solid')
       Edge line style (solid|dashed|dotted,dashdot)
    labels : dictionary or string, optional (default=None)
       Node labels in a dictionary keyed by node of text labels or
       a string referencing a node attribute
    font_size : int, optional (default=12)
       Font size for text labels
    font_color : string, optional (default='black')
       Font color string
    font_family : string, optional (default='sans-serif')
       Font family
    label : string, optional
       Label for graph legend
    selection_policy : string, optional (default='nodes')
       Whether to select 'nodes', 'edges' or None on tap and selection
       events.
    inspection_policy : string, optional (default='nodes')
       Whether to select 'nodes', 'edges' or None on tap and selection
       events.
    geo : boolean, optional (default=False)
       Whether to return a GeoViews graph
    crs : cartopy.crs.CRS
       A cartopy coordinate reference system (enables a geographic plot)
    height : int, optional (default=400)
       The height of the plot in pixels
    width : int, optional (default=400)
       The width of the plot in pixels
    

The main difference to the networkx.draw API are a few options which are not supported (such as font_weight and arrowsize) and the renaming of width (which controls the edge line width) to edge_width since width and height are reserved for defining the screen dimensions of the plot.

Examples#

To demonstrate that the API works almost identically this section reproduces various examples from the NetworkX documentation.

Plot properties#

Compute some network properties for the lollipop graph.

URL: https://networkx.github.io/documentation/stable/auto_examples/basic/plot_properties.html

#    Copyright (C) 2004-2018 by
#    Aric Hagberg <hagberg@lanl.gov>
#    Dan Schult <dschult@colgate.edu>
#    Pieter Swart <swart@lanl.gov>
#    All rights reserved.
#    BSD license.

#    Adapted by Philipp Rudiger <prudiger@anaconda.com>

G = nx.lollipop_graph(4, 6)

pathlengths = []

print("source vertex {target:length, }")
for v in G.nodes():
    spl = dict(nx.single_source_shortest_path_length(G, v))
    print('{} {} '.format(v, spl))
    for p in spl:
        pathlengths.append(spl[p])

print('')
print("average shortest path length %s" % (sum(pathlengths) / len(pathlengths)))

# histogram of path lengths
dist = {}
for p in pathlengths:
    if p in dist:
        dist[p] += 1
    else:
        dist[p] = 1

print('')
print("length #paths")
verts = dist.keys()
for d in sorted(verts):
    print('%s %d' % (d, dist[d]))

print("radius: %d" % nx.radius(G))
print("diameter: %d" % nx.diameter(G))
print("eccentricity: %s" % nx.eccentricity(G))
print("center: %s" % nx.center(G))
print("periphery: %s" % nx.periphery(G))
print("density: %s" % nx.density(G))

hvnx.draw(G, with_labels=True)
source vertex {target:length, }
0 {0: 0, 1: 1, 2: 1, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7} 
1 {1: 0, 0: 1, 2: 1, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7} 
2 {2: 0, 0: 1, 1: 1, 3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 8: 6, 9: 7} 
3 {3: 0, 0: 1, 1: 1, 2: 1, 4: 1, 5: 2, 6: 3, 7: 4, 8: 5, 9: 6} 
4 {4: 0, 5: 1, 3: 1, 6: 2, 0: 2, 1: 2, 2: 2, 7: 3, 8: 4, 9: 5} 
5 {5: 0, 4: 1, 6: 1, 3: 2, 7: 2, 0: 3, 1: 3, 2: 3, 8: 3, 9: 4} 
6 {6: 0, 5: 1, 7: 1, 4: 2, 8: 2, 3: 3, 9: 3, 0: 4, 1: 4, 2: 4} 
7 {7: 0, 6: 1, 8: 1, 5: 2, 9: 2, 4: 3, 3: 4, 0: 5, 1: 5, 2: 5} 
8 {8: 0, 7: 1, 9: 1, 6: 2, 5: 3, 4: 4, 3: 5, 0: 6, 1: 6, 2: 6} 
9 {9: 0, 8: 1, 7: 2, 6: 3, 5: 4, 4: 5, 3: 6, 0: 7, 1: 7, 2: 7} 

average shortest path length 2.86

length #paths
0 10
1 24
2 16
3 14
4 12
5 10
6 8
7 6
radius: 4
diameter: 7
eccentricity: {0: 7, 1: 7, 2: 7, 3: 6, 4: 5, 5: 4, 6: 4, 7: 5, 8: 6, 9: 7}
center: [5, 6]
periphery: [0, 1, 2, 9]
density: 0.26666666666666666

Simple Path#

Draw a graph with hvPlot.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_simple_path.html

G = nx.path_graph(8)
hvnx.draw(G)

Node colormap#

Draw a graph with hvPlot, color by degree.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_node_colormap.html

# Author: Aric Hagberg (hagberg@lanl.gov)
# Adapted by Philipp Rudiger <prudiger@anaconda.com>

G = nx.cycle_graph(24)
pos = nx.spring_layout(G, iterations=200)

# Preferred API
# hvnx.draw(G, pos, node_color='index', node_size=500, cmap='Blues')

# Original code
hvnx.draw(G, pos, node_color=range(24), node_size=500, cmap='Blues')

Edge Colormap#

Draw a graph with hvPlot, color edges.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_edge_colormap.html

# Author: Aric Hagberg (hagberg@lanl.gov)
# Adapted by Philipp Rudiger <prudiger@anaconda.com>

G = nx.star_graph(20)
pos = nx.spring_layout(G)
colors = range(20)
hvnx.draw(G, pos, node_color='#A0CBE2', edge_color=colors,
          edge_width=4, edge_cmap='Blues', with_labels=False)

House With Colors#

Draw a graph with hvPlot.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_house_with_colors.html

# Author: Aric Hagberg (hagberg@lanl.gov)
# Adapted by Philipp Rudiger <prudiger@anaconda.com>

G = nx.house_graph()
# explicitly set positions
pos = {0: (0, 0),
       1: (1, 0),
       2: (0, 1),
       3: (1, 1),
       4: (0.5, 2.0)}

hvnx.draw_networkx_nodes(G, pos, node_size=2000, nodelist=[4], padding=0.2) *\
hvnx.draw_networkx_nodes(G, pos, node_size=3000, nodelist=[0, 1, 2, 3], node_color='black') *\
hvnx.draw_networkx_edges(G, pos, alpha=0.5, width=6, xaxis=None, yaxis=None)

Circular Tree#

URL: https://networkx.org/documentation/stable/auto_examples/graphviz_layout/plot_circular_tree.html

try:
    import pygraphviz  # noqa
    from networkx.drawing.nx_agraph import graphviz_layout
except ImportError:
    try:
        import pydot  # noqa
        from networkx.drawing.nx_pydot import graphviz_layout
    except ImportError:
        raise ImportError("This example needs Graphviz and either "
                          "PyGraphviz or pydot")

G = nx.balanced_tree(3, 5)
pos = graphviz_layout(G, prog='twopi', args='')
hvnx.draw(G, pos, node_size=20, alpha=0.5, node_color="blue", with_labels=False, width=600, height=600)

Spectral Embedding#

The spectral layout positions the nodes of the graph based on the eigenvectors of the graph Laplacian L=D−A, where A is the adjacency matrix and D is the degree matrix of the graph. By default, the spectral layout will embed the graph in two dimensions (you can embed your graph in other dimensions using the dim argument to either draw_spectral() or spectral_layout()).

When the edges of the graph represent similarity between the incident nodes, the spectral embedding will place highly similar nodes closer to one another than nodes which are less similar.

This is particularly striking when you spectrally embed a grid graph. In the full grid graph, the nodes in the center of the graph are pulled apart more than nodes on the periphery. As you remove internal nodes, this effect increases.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_spectral_grid.html

options = {
    'node_size': 100,
    'width': 250, 'height': 250
}

G = nx.grid_2d_graph(6, 6)
spectral1 = hvnx.draw_spectral(G, **options)

G.remove_edge((2, 2), (2, 3))
spectral2 = hvnx.draw_spectral(G, **options)

G.remove_edge((3, 2), (3, 3))
spectral3 = hvnx.draw_spectral(G, **options)

G.remove_edge((2, 2), (3, 2))
spectral4 = hvnx.draw_spectral(G, **options)

G.remove_edge((2, 3), (3, 3))
spectral5 = hvnx.draw_spectral(G, **options)

G.remove_edge((1, 2), (1, 3))
spectral6 = hvnx.draw_spectral(G, **options)

G.remove_edge((4, 2), (4, 3))
spectral7 = hvnx.draw_spectral(G, **options)

(hv.Empty() + spectral1 + hv.Empty() +
 spectral2 + spectral3 + spectral4 +
 spectral5 + spectral6 + spectral7).cols(3)

Plot four grids#

Draw a graph with hvPlot.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_four_grids.html

# Author: Aric Hagberg (hagberg@lanl.gov)
# Adapted by Philipp Rudiger <prudiger@anaconda.com>

#    Copyright (C) 2004-2018
#    Aric Hagberg <hagberg@lanl.gov>
#    Dan Schult <dschult@colgate.edu>
#    Pieter Swart <swart@lanl.gov>
#    All rights reserved.
#    BSD license.

G = nx.grid_2d_graph(4, 4)  # 4x4 grid

pos = nx.spring_layout(G, iterations=100)

g1 = hvnx.draw(G, pos, font_size=8)

g2 = hvnx.draw(G, pos, node_color='black', node_size=0, with_labels=False)

g3 = hvnx.draw(G, pos, node_color='green', node_size=250, with_labels=False, edge_width=6)

H = G.to_directed()
g4 = hvnx.draw(H, pos, node_color='blue', node_size=20, with_labels=False)

(g1 + g2 + g3 + g4).cols(2)

Ego Graph#

Example using the NetworkX ego_graph() function to return the main egonet of the largest hub in a Barabási-Albert network.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_ego_graph.html

# Author:  Drew Conway (drew.conway@nyu.edu)
# Adapted by Philipp Rudiger <prudiger@anaconda.com>

from operator import itemgetter

# Create a BA model graph
n = 1000
m = 2
G = nx.generators.barabasi_albert_graph(n, m)
# find node with largest degree
node_and_degree = G.degree()
(largest_hub, degree) = sorted(node_and_degree, key=itemgetter(1))[-1]
# Create ego graph of main hub
hub_ego = nx.ego_graph(G, largest_hub)
# Draw graph
pos = nx.spring_layout(hub_ego)
g = hvnx.draw(hub_ego, pos, node_color='blue', node_size=50, with_labels=False)
# Draw ego as large and red
gnodes = hvnx.draw_networkx_nodes(hub_ego, pos, nodelist=[largest_hub], node_size=300, node_color='red')

g * gnodes

Random Geometric Graph#

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_random_geometric_graph.html

G = nx.random_geometric_graph(200, 0.125)
# position is stored as node attribute data for random_geometric_graph
pos = nx.get_node_attributes(G, 'pos')

# find node near center (0.5,0.5)
dmin = 1
ncenter = 0
for n in pos:
    x, y = pos[n]
    d = (x - 0.5)**2 + (y - 0.5)**2
    if d < dmin:
        ncenter = n
        dmin = d

# color by path length from node near center
p = nx.single_source_shortest_path_length(G, ncenter)

hvnx.draw_networkx_edges(G, pos, nodelist=[ncenter], alpha=0.4, width=600, height=600) *\
hvnx.draw_networkx_nodes(G, pos, nodelist=list(p.keys()),
                         node_size=80,
                         node_color=list(p.values()),
                         cmap='Reds_r')

Weighted Graph#

An example using Graph as a weighted network.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_weighted_graph.html

# Author: Aric Hagberg (hagberg@lanl.gov)
# Adapted by Philipp Rudiger <prudiger@anaconda.com>

import networkx as nx

G = nx.Graph()

G.add_edge('a', 'b', weight=0.6)
G.add_edge('a', 'c', weight=0.2)
G.add_edge('c', 'd', weight=0.1)
G.add_edge('c', 'e', weight=0.7)
G.add_edge('c', 'f', weight=0.9)
G.add_edge('a', 'd', weight=0.3)

elarge = [(u, v) for (u, v, attr) in G.edges(data=True) if attr['weight'] > 0.5]
esmall = [(u, v) for (u, v, attr) in G.edges(data=True) if attr['weight'] <= 0.5]

pos = nx.spring_layout(G)  # positions for all nodes

# nodes
nodes = hvnx.draw_networkx_nodes(G, pos, node_size=700)

# edges
edges1 = hvnx.draw_networkx_edges(
    G, pos, edgelist=elarge, edge_width=6)
edges2 = hvnx.draw_networkx_edges(
    G, pos, edgelist=esmall, edge_width=6, alpha=0.5, edge_color='blue', style='dashed')
labels = hvnx.draw_networkx_labels(G, pos, font_size=20, font_family='sans-serif')

edges1 * edges2 * nodes * labels

Directed Graph#

Draw a graph with directed edges using a colormap and different node sizes.

Edges have different colors and alphas (opacity). Drawn using matplotlib.

URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_directed.html

# Author: Rodrigo Dorantes-Gilardi (rodgdor@gmail.com)
# Adapted by Philipp Rudiger <prudiger@anaconda.com>

G = nx.generators.directed.random_k_out_graph(10, 3, 0.5)
pos = nx.layout.spring_layout(G)

node_sizes = [3 + 10 * i for i in range(len(G))]
M = G.number_of_edges()
edge_colors = range(2, M + 2)
edge_alphas = [(5 + i) / (M + 4) for i in range(M)]

nodes = hvnx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color='blue')
edges = hvnx.draw_networkx_edges(G, pos, node_size=node_sizes, arrowstyle='->',
                               arrowsize=10, edge_color=edge_colors,
                               edge_cmap='Blues', edge_width=2, colorbar=True)

nodes * edges

Giant Component#

This example illustrates the sudden appearance of a giant connected component in a binomial random graph.

https://networkx.org/documentation/stable/auto_examples/graphviz_layout/plot_giant_component.html

#    Copyright (C) 2006-2018
#    Aric Hagberg <hagberg@lanl.gov>
#    Dan Schult <dschult@colgate.edu>
#    Pieter Swart <swart@lanl.gov>
#    All rights reserved.
#    BSD license.

# Adapted by Philipp Rudiger <prudiger@anaconda.com>

import math

try:
    import pygraphviz  # noqa
    from networkx.drawing.nx_agraph import graphviz_layout
    layout = graphviz_layout
except ImportError:
    try:
        import pydot  # noqa
        from networkx.drawing.nx_pydot import graphviz_layout
        layout = graphviz_layout
    except ImportError:
        print("PyGraphviz and pydot not found;\n"
              "drawing with spring layout;\n"
              "will be slow.")
        layout = nx.spring_layout

n = 150  # 150 nodes
# p value at which giant component (of size log(n) nodes) is expected
p_giant = 1.0 / (n - 1)
# p value at which graph is expected to become completely connected
p_conn = math.log(n) / float(n)

# the following range of p values should be close to the threshold
pvals = [0.003, 0.006, 0.008, 0.015]

region = 220  # for pylab 2x2 subplot layout
plots = []
for p in pvals:
    G = nx.binomial_graph(n, p)
    pos = layout(G)
    region += 1
    g = hvnx.draw(G, pos, with_labels=False, node_size=15)
    # identify largest connected component
    Gcc = sorted([G.subgraph(c) for c in nx.connected_components(G)], key=len, reverse=True)
    G0 = Gcc[0]
    edges = hvnx.draw_networkx_edges(
        G0, pos, with_labels=False, edge_color='red', edge_width=6.0)
    
    # show other connected components
    other_edges = []
    for Gi in Gcc[1:]:
        if len(Gi) > 1:
            edge = hvnx.draw_networkx_edges(Gi, pos,
                                   with_labels=False,
                                   edge_color='red',
                                   alpha=0.3,
                                   edge_width=5.0
                                  )
            other_edges.append(edge)
    plots.append((g*edges*hv.Overlay(other_edges)).relabel("p = %6.3f" % (p)))

hv.Layout(plots).cols(2)
This web page was generated from a Jupyter notebook and not all interactivity will work on this website. Right click to download and run locally for full Python-backed interactivity.

Right click to download this notebook from GitHub.