0
# Graph Drawing and Visualization
1
2
Graph layout algorithms and drawing functions with matplotlib integration for visualizing networks. NetworkX provides comprehensive visualization capabilities for exploring graph structure and properties.
3
4
## Capabilities
5
6
### Core Drawing Functions
7
8
Basic drawing functions using matplotlib for graph visualization.
9
10
```python { .api }
11
def draw(G, pos=None, ax=None, **kwds):
12
"""
13
Draw graph with matplotlib.
14
15
Parameters:
16
- G: NetworkX graph
17
- pos: Dictionary of node positions (computed if None)
18
- ax: Matplotlib axis object
19
- **kwds: Keyword arguments for customization (node_color, edge_color, etc.)
20
21
Returns:
22
None (draws to current matplotlib axis)
23
"""
24
25
def draw_networkx(G, pos=None, arrows=None, with_labels=True, **kwds):
26
"""Draw graph with node and edge labels."""
27
28
def draw_networkx_nodes(G, pos, nodelist=None, node_size=300, node_color='#1f78b4', node_shape='o', alpha=None, cmap=None, vmin=None, vmax=None, ax=None, linewidths=None, edgecolors=None, label=None, margins=None):
29
"""Draw graph nodes."""
30
31
def draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=None, arrowstyle=None, arrowsize=10, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=None, label=None, node_size=300, nodelist=None, node_shape='o', connectionstyle='arc3', min_source_margin=0, min_target_margin=0):
32
"""Draw graph edges."""
33
34
def draw_networkx_labels(G, pos, labels=None, font_size=12, font_color='k', font_family='sans-serif', font_weight='normal', alpha=None, bbox=None, horizontalalignment='center', verticalalignment='center', ax=None, clip_on=True):
35
"""Draw node labels."""
36
37
def draw_networkx_edge_labels(G, pos, edge_labels=None, label_pos=0.5, font_size=10, font_color='k', font_family='sans-serif', font_weight='normal', alpha=None, bbox=None, horizontalalignment='center', verticalalignment='center', ax=None, rotate=True, clip_on=True):
38
"""Draw edge labels."""
39
```
40
41
### Layout Algorithms
42
43
Algorithms for computing node positions for graph visualization.
44
45
```python { .api }
46
def spring_layout(G, k=None, pos=None, fixed=None, iterations=50, threshold=1e-4, weight='weight', scale=1, center=None, dim=2, seed=None):
47
"""
48
Position nodes using Fruchterman-Reingold force-directed algorithm.
49
50
Parameters:
51
- G: NetworkX graph
52
- k: Optimal distance parameter
53
- pos: Initial positions dictionary
54
- fixed: Nodes to keep fixed at initial positions
55
- iterations: Maximum number of iterations
56
- threshold: Convergence threshold
57
- weight: Edge data key for forces
58
- scale: Scale factor for positions
59
- center: Coordinate pair for center of layout
60
- dim: Dimension of layout (2 or 3)
61
- seed: Random seed
62
63
Returns:
64
Dictionary of node positions
65
"""
66
67
def kamada_kawai_layout(G, dist=None, pos=None, weight='weight', scale=1, center=None, dim=2):
68
"""Position nodes using Kamada-Kawai path-length cost-function."""
69
70
def circular_layout(G, scale=1, center=None, dim=2):
71
"""Position nodes on a circle."""
72
73
def random_layout(G, center=None, dim=2, seed=None):
74
"""Position nodes uniformly at random."""
75
76
def shell_layout(G, nlist=None, rotate=None, scale=1, center=None, dim=2):
77
"""Position nodes in concentric circles."""
78
79
def spectral_layout(G, weight='weight', scale=1, center=None, dim=2):
80
"""Position nodes using eigenvectors of graph Laplacian."""
81
82
def planar_layout(G, scale=1, center=None, dim=2):
83
"""Position nodes for planar graphs using Schnyder's algorithm."""
84
85
def spiral_layout(G, scale=1, center=None, dim=2, resolution=0.35, equidistant=False):
86
"""Position nodes in a spiral layout."""
87
88
def multipartite_layout(G, subset_key='subset', align='vertical', scale=1, center=None):
89
"""Position nodes in layers for multipartite graphs."""
90
91
def bipartite_layout(G, nodes, align='horizontal', scale=1, center=None, aspect_ratio=4/3):
92
"""Position nodes for bipartite graphs in two layers."""
93
```
94
95
### Specialized Drawing Functions
96
97
Drawing functions for specific layout algorithms.
98
99
```python { .api }
100
def draw_circular(G, **kwds):
101
"""Draw graph with circular layout."""
102
103
def draw_kamada_kawai(G, **kwds):
104
"""Draw graph with Kamada-Kawai layout."""
105
106
def draw_random(G, **kwds):
107
"""Draw graph with random layout."""
108
109
def draw_spectral(G, **kwds):
110
"""Draw graph with spectral layout."""
111
112
def draw_spring(G, **kwds):
113
"""Draw graph with spring layout."""
114
115
def draw_planar(G, **kwds):
116
"""Draw graph with planar layout."""
117
118
def draw_shell(G, **kwds):
119
"""Draw graph with shell layout."""
120
```
121
122
### Layout Utilities
123
124
Helper functions for working with graph layouts.
125
126
```python { .api }
127
def rescale_layout(pos, scale=1):
128
"""
129
Rescale layout coordinates.
130
131
Parameters:
132
- pos: Dictionary of node positions
133
- scale: Scale factor
134
135
Returns:
136
Dictionary of rescaled positions
137
"""
138
139
def rescale_layout_dict(pos, scale=1):
140
"""Rescale layout with proper handling of dict format."""
141
```
142
143
### PyGraphviz Integration
144
145
Interface with Graphviz layout engines (requires pygraphviz).
146
147
```python { .api }
148
def graphviz_layout(G, prog='neato', root=None, args=''):
149
"""
150
Create layout using Graphviz layout programs.
151
152
Parameters:
153
- G: NetworkX graph
154
- prog: Graphviz layout program ('dot', 'neato', 'fdp', 'sfdp', 'twopi', 'circo')
155
- root: Root node for directed layouts
156
- args: Additional arguments to layout program
157
158
Returns:
159
Dictionary of node positions
160
"""
161
162
def pygraphviz_layout(G, prog='neato', root=None, args=''):
163
"""Alias for graphviz_layout."""
164
165
def to_agraph(N):
166
"""Convert NetworkX graph to PyGraphviz AGraph."""
167
168
def from_agraph(A, create_using=None):
169
"""Convert PyGraphviz AGraph to NetworkX graph."""
170
171
def write_dot(G, path):
172
"""Write graph in DOT format using PyGraphviz."""
173
174
def read_dot(path):
175
"""Read graph from DOT file using PyGraphviz."""
176
177
def view_pygraphviz(G, edgelabel=None, prog='dot', args='', suffix='', path=None):
178
"""View graph using PyGraphviz and system viewer."""
179
```
180
181
### Pydot Integration
182
183
Alternative interface with Graphviz using pydot library.
184
185
```python { .api }
186
def pydot_layout(G, prog='neato', root=None, **kwds):
187
"""Create layout using pydot and Graphviz."""
188
189
def to_pydot(N):
190
"""Convert NetworkX graph to pydot graph."""
191
192
def from_pydot(P):
193
"""Convert pydot graph to NetworkX graph."""
194
```
195
196
## Usage Examples
197
198
### Basic Graph Drawing
199
200
```python
201
import networkx as nx
202
import matplotlib.pyplot as plt
203
204
# Create sample graphs
205
G1 = nx.complete_graph(6)
206
G2 = nx.cycle_graph(8)
207
G3 = nx.star_graph(7)
208
G4 = nx.wheel_graph(8)
209
210
# Create subplot for multiple graphs
211
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
212
213
# Draw graphs with different layouts
214
nx.draw(G1, ax=axes[0,0], with_labels=True, node_color='lightblue',
215
node_size=500, font_size=12, font_weight='bold')
216
axes[0,0].set_title("Complete Graph K6")
217
218
nx.draw_circular(G2, ax=axes[0,1], with_labels=True, node_color='lightgreen',
219
node_size=500, font_size=12, font_weight='bold')
220
axes[0,1].set_title("Cycle Graph C8")
221
222
nx.draw(G3, ax=axes[1,0], with_labels=True, node_color='lightcoral',
223
node_size=500, font_size=12, font_weight='bold')
224
axes[1,0].set_title("Star Graph S7")
225
226
nx.draw_circular(G4, ax=axes[1,1], with_labels=True, node_color='lightyellow',
227
node_size=500, font_size=12, font_weight='bold')
228
axes[1,1].set_title("Wheel Graph W8")
229
230
plt.tight_layout()
231
plt.show()
232
```
233
234
### Customized Graph Visualization
235
236
```python
237
import networkx as nx
238
import matplotlib.pyplot as plt
239
import numpy as np
240
241
# Create graph with attributes
242
G = nx.karate_club_graph()
243
244
# Add attributes for visualization
245
for node in G.nodes():
246
G.nodes[node]['group'] = 'A' if G.degree(node) > 5 else 'B'
247
248
# Compute different layouts
249
layouts = {
250
'Spring': nx.spring_layout(G, seed=42),
251
'Kamada-Kawai': nx.kamada_kawai_layout(G),
252
'Circular': nx.circular_layout(G),
253
'Spectral': nx.spectral_layout(G)
254
}
255
256
# Create visualization
257
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
258
axes = axes.flatten()
259
260
for i, (name, pos) in enumerate(layouts.items()):
261
# Node colors based on group
262
node_colors = ['red' if G.nodes[node]['group'] == 'A' else 'blue'
263
for node in G.nodes()]
264
265
# Node sizes based on degree
266
node_sizes = [G.degree(node) * 50 for node in G.nodes()]
267
268
# Draw graph
269
nx.draw(G, pos=pos, ax=axes[i],
270
node_color=node_colors,
271
node_size=node_sizes,
272
with_labels=True,
273
font_size=8,
274
font_weight='bold',
275
edge_color='gray',
276
alpha=0.7)
277
278
axes[i].set_title(f"{name} Layout")
279
280
plt.tight_layout()
281
plt.show()
282
```
283
284
### Edge and Node Customization
285
286
```python
287
import networkx as nx
288
import matplotlib.pyplot as plt
289
import numpy as np
290
291
# Create weighted graph
292
G = nx.Graph()
293
edges = [(1, 2, 0.8), (2, 3, 0.6), (3, 4, 0.9), (4, 1, 0.7),
294
(1, 3, 0.3), (2, 4, 0.4)]
295
G.add_weighted_edges_from(edges)
296
297
# Position nodes
298
pos = nx.spring_layout(G, seed=42)
299
300
# Get edge weights for visualization
301
edge_weights = [G[u][v]['weight'] for u, v in G.edges()]
302
303
# Create figure
304
plt.figure(figsize=(12, 5))
305
306
# Basic visualization
307
plt.subplot(131)
308
nx.draw(G, pos, with_labels=True, node_color='lightblue',
309
node_size=500, font_size=12, font_weight='bold')
310
plt.title("Basic Graph")
311
312
# Edge width based on weight
313
plt.subplot(132)
314
nx.draw(G, pos, with_labels=True,
315
node_color='lightgreen',
316
node_size=500,
317
edge_color=edge_weights,
318
width=[w*5 for w in edge_weights],
319
edge_cmap=plt.cm.Blues,
320
font_size=12, font_weight='bold')
321
plt.title("Edge Width by Weight")
322
323
# With edge labels
324
plt.subplot(133)
325
nx.draw(G, pos, with_labels=True,
326
node_color='lightcoral',
327
node_size=500,
328
font_size=12, font_weight='bold')
329
330
# Add edge labels
331
edge_labels = {(u, v): f"{d['weight']:.1f}" for u, v, d in G.edges(data=True)}
332
nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=10)
333
plt.title("With Edge Labels")
334
335
plt.tight_layout()
336
plt.show()
337
```
338
339
### Hierarchical and Bipartite Layouts
340
341
```python
342
import networkx as nx
343
import matplotlib.pyplot as plt
344
345
# Create bipartite graph
346
B = nx.Graph()
347
# Add nodes with bipartite attribute
348
B.add_nodes_from([1, 2, 3, 4], bipartite=0) # Top nodes
349
B.add_nodes_from(['a', 'b', 'c'], bipartite=1) # Bottom nodes
350
# Add edges
351
B.add_edges_from([(1, 'a'), (2, 'a'), (2, 'b'), (3, 'b'), (3, 'c'), (4, 'c')])
352
353
# Create tree
354
T = nx.balanced_tree(2, 3)
355
356
# Create multipartite graph
357
M = nx.Graph()
358
M.add_nodes_from([1, 2, 3], subset=0)
359
M.add_nodes_from([4, 5, 6, 7], subset=1)
360
M.add_nodes_from([8, 9], subset=2)
361
M.add_edges_from([(1, 4), (2, 5), (3, 6), (4, 8), (5, 8), (6, 9), (7, 9)])
362
363
# Create layouts
364
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
365
366
# Bipartite layout
367
top_nodes = {n for n, d in B.nodes(data=True) if d['bipartite'] == 0}
368
pos_bipartite = nx.bipartite_layout(B, top_nodes)
369
nx.draw(B, pos=pos_bipartite, ax=axes[0], with_labels=True,
370
node_color=['lightblue' if n in top_nodes else 'lightcoral' for n in B.nodes()],
371
node_size=500, font_size=12, font_weight='bold')
372
axes[0].set_title("Bipartite Layout")
373
374
# Tree layout (hierarchical)
375
pos_tree = nx.nx_agraph.graphviz_layout(T, prog='dot') if hasattr(nx, 'nx_agraph') else nx.spring_layout(T)
376
nx.draw(T, pos=pos_tree, ax=axes[1], with_labels=True,
377
node_color='lightgreen', node_size=500, font_size=12, font_weight='bold')
378
axes[1].set_title("Hierarchical Tree")
379
380
# Multipartite layout
381
pos_multi = nx.multipartite_layout(M, subset_key='subset')
382
colors = ['red' if d['subset'] == 0 else 'blue' if d['subset'] == 1 else 'green'
383
for n, d in M.nodes(data=True)]
384
nx.draw(M, pos=pos_multi, ax=axes[2], with_labels=True,
385
node_color=colors, node_size=500, font_size=12, font_weight='bold')
386
axes[2].set_title("Multipartite Layout")
387
388
plt.tight_layout()
389
plt.show()
390
```
391
392
### Interactive and Advanced Visualizations
393
394
```python
395
import networkx as nx
396
import matplotlib.pyplot as plt
397
from matplotlib.widgets import Slider
398
import numpy as np
399
400
# Create graph that changes over time
401
def create_temporal_graph(t):
402
"""Create graph that evolves with parameter t."""
403
G = nx.Graph()
404
n = 10
405
406
# Add nodes
407
G.add_nodes_from(range(n))
408
409
# Add edges based on time parameter
410
for i in range(n):
411
for j in range(i+1, n):
412
# Probability of edge depends on time and distance
413
prob = 0.3 * (1 + np.sin(t + i*0.5 + j*0.3))
414
if np.random.random() < prob:
415
G.add_edge(i, j)
416
417
return G
418
419
# Set up the figure and axis
420
fig, ax = plt.subplots(figsize=(10, 8))
421
plt.subplots_adjust(bottom=0.15)
422
423
# Initial graph
424
t_init = 0
425
G = create_temporal_graph(t_init)
426
pos = nx.circular_layout(G) # Keep positions fixed
427
428
# Initial drawing
429
nx.draw(G, pos=pos, ax=ax, with_labels=True,
430
node_color='lightblue', node_size=500,
431
font_size=12, font_weight='bold')
432
ax.set_title(f"Temporal Graph (t = {t_init:.2f})")
433
434
# Add slider
435
ax_slider = plt.axes([0.2, 0.05, 0.6, 0.03])
436
slider = Slider(ax_slider, 'Time', 0, 2*np.pi, valinit=t_init)
437
438
def update_graph(val):
439
"""Update graph based on slider value."""
440
ax.clear()
441
t = slider.val
442
443
# Create new graph
444
np.random.seed(42) # For reproducibility
445
G = create_temporal_graph(t)
446
447
# Draw updated graph
448
nx.draw(G, pos=pos, ax=ax, with_labels=True,
449
node_color='lightblue', node_size=500,
450
font_size=12, font_weight='bold')
451
ax.set_title(f"Temporal Graph (t = {t:.2f})")
452
fig.canvas.draw()
453
454
slider.on_changed(update_graph)
455
plt.show()
456
```
457
458
### Graph Comparison Visualization
459
460
```python
461
import networkx as nx
462
import matplotlib.pyplot as plt
463
464
def compare_graphs(graphs, titles, layout_func=nx.spring_layout):
465
"""Compare multiple graphs side by side."""
466
n_graphs = len(graphs)
467
fig, axes = plt.subplots(1, n_graphs, figsize=(5*n_graphs, 5))
468
469
if n_graphs == 1:
470
axes = [axes]
471
472
for i, (G, title) in enumerate(zip(graphs, titles)):
473
pos = layout_func(G, seed=42)
474
475
# Compute node colors based on centrality
476
centrality = nx.degree_centrality(G)
477
node_colors = [centrality[node] for node in G.nodes()]
478
479
nx.draw(G, pos=pos, ax=axes[i],
480
node_color=node_colors,
481
cmap=plt.cm.viridis,
482
node_size=500,
483
with_labels=True,
484
font_size=10,
485
font_weight='bold',
486
edge_color='gray',
487
alpha=0.8)
488
489
axes[i].set_title(f"{title}\n{G.number_of_nodes()} nodes, {G.number_of_edges()} edges")
490
491
plt.tight_layout()
492
return fig
493
494
# Create different graph types for comparison
495
graphs = [
496
nx.erdos_renyi_graph(20, 0.15, seed=42),
497
nx.barabasi_albert_graph(20, 2, seed=42),
498
nx.watts_strogatz_graph(20, 4, 0.3, seed=42),
499
nx.complete_graph(8)
500
]
501
502
titles = ['Erdős-Rényi', 'Barabási-Albert', 'Watts-Strogatz', 'Complete']
503
504
fig = compare_graphs(graphs, titles)
505
plt.show()
506
```