File size: 17,950 Bytes
b9a0f21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
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)
```