Spaces:
Runtime error
Runtime error
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``: | |
```python | |
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](https://networkx.github.io/documentation/stable/tutorial.html#drawing-graphs) 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: | |
```python | |
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 | |
``` | |
```python | |
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: | |
```python | |
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. | |
```python | |
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. | |
```python | |
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](https://holoviews.org/user_guide/Style_Mapping.html#styling-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``: | |
```python | |
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: | |
```python | |
print(hvnx.draw.__doc__) | |
``` | |
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 | |
```python | |
# 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) | |
``` | |
### Simple Path | |
Draw a graph with hvPlot. | |
URL: https://networkx.github.io/documentation/stable/auto_examples/drawing/plot_simple_path.html | |
```python | |
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 | |
```python | |
# 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 | |
```python | |
# 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 | |
```python | |
# 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 | |
```python | |
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 | |
```python | |
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 | |
```python | |
# 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 | |
```python | |
# 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 | |
```python | |
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 | |
```python | |
# 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 | |
```python | |
# 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 | |
```python | |
# 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) | |
``` | |