0
# Visualization
1
2
Integration with matplotlib and graphviz for creating publication-quality graph visualizations. rustworkx provides comprehensive visualization capabilities with extensive customization options for nodes, edges, labels, and overall graph appearance.
3
4
## Capabilities
5
6
### Matplotlib Integration
7
8
High-quality graph visualization using matplotlib with extensive customization options.
9
10
```python { .api }
11
def mpl_draw(graph, pos = None, with_labels: bool = False, labels = None, node_list = None, node_size: int = 300, node_color = 'red', node_shape: str = 'o', alpha: float = 1.0, cmap = None, vmin = None, vmax = None, ax = None, linewidths = None, edgecolors = None, label = None, style: str = 'solid', width = None, edge_color = 'black', arrows: bool = True, arrowstyle: str = '-|>', arrowsize: int = 10, edge_cmap = None, edge_vmin = None, edge_vmax = None, connectionstyle: str = 'arc3', min_source_margin: int = 0, min_target_margin: int = 0, font_size: int = 12, font_color = 'black', font_weight: str = 'normal', font_family: str = 'sans-serif', bbox = None, horizontalalignment: str = 'center', verticalalignment: str = 'center', transform = None, clip_on: bool = True, node_attrs = None, edge_attrs = None):
12
"""
13
Draw graph using matplotlib with extensive customization options.
14
15
Provides comprehensive control over all visual elements including
16
nodes, edges, labels, colors, and styling.
17
18
Parameters:
19
- graph: Input graph (PyGraph or PyDiGraph)
20
- pos (dict, optional): Node positions {node_id: (x, y)}, auto-generated if None
21
- with_labels (bool): Display node labels
22
- labels (dict, optional): Custom node labels {node_id: label}
23
- node_list (list, optional): Specific nodes to draw
24
- node_size (int or list): Node sizes in points
25
- node_color (color or list): Node colors
26
- node_shape (str): Node shape ('o', 's', '^', etc.)
27
- alpha (float): Node transparency (0-1)
28
- cmap (colormap): Colormap for node colors
29
- vmin, vmax (float): Color scale limits
30
- ax (matplotlib.axes): Axes to draw on
31
- linewidths (float or list): Node border widths
32
- edgecolors (color or list): Node border colors
33
- label (str): Graph label for legend
34
- style (str): Edge line style ('solid', 'dashed', 'dotted')
35
- width (float or list): Edge line widths
36
- edge_color (color or list): Edge colors
37
- arrows (bool): Draw arrows for directed graphs
38
- arrowstyle (str): Arrow style ('-|>', '->', '<->', etc.)
39
- arrowsize (int): Arrow size
40
- edge_cmap (colormap): Colormap for edge colors
41
- edge_vmin, edge_vmax (float): Edge color scale limits
42
- connectionstyle (str): Edge curve style ('arc3')
43
- min_source_margin, min_target_margin (int): Arrow margins
44
- font_size (int): Label font size
45
- font_color (color): Label text color
46
- font_weight (str): Label font weight ('normal', 'bold')
47
- font_family (str): Label font family
48
- bbox (dict): Label bounding box properties
49
- horizontalalignment (str): Label horizontal alignment
50
- verticalalignment (str): Label vertical alignment
51
- transform (matplotlib.transforms): Coordinate transformation
52
- clip_on (bool): Clip drawing to axes bounds
53
- node_attrs (callable): Function to extract node attributes
54
- edge_attrs (callable): Function to extract edge attributes
55
56
Returns:
57
None (displays graph using matplotlib)
58
"""
59
```
60
61
### Graphviz Integration
62
63
Professional graph visualization using Graphviz rendering engines with DOT language support.
64
65
```python { .api }
66
def graphviz_draw(graph, node_attr_fn = None, edge_attr_fn = None, graph_attr = None, filename = None, image_type = None, method: str = 'dot'):
67
"""
68
Draw graph using Graphviz with DOT language attributes.
69
70
Leverages Graphviz's powerful layout engines and styling
71
capabilities for professional-quality graph visualization.
72
73
Parameters:
74
- graph: Input graph (PyGraph or PyDiGraph)
75
- node_attr_fn (callable, optional): Function returning node attributes dict
76
- edge_attr_fn (callable, optional): Function returning edge attributes dict
77
- graph_attr (dict, optional): Global graph attributes
78
- filename (str, optional): Output file path
79
- image_type (str, optional): Output format ('png', 'svg', 'pdf', etc.)
80
- method (str): Graphviz layout engine ('dot', 'neato', 'fdp', 'sfdp', 'circo', 'twopi')
81
82
Returns:
83
Image or None: PIL Image object if no filename specified, None if saved to file
84
"""
85
```
86
87
## Usage Examples
88
89
### Basic Matplotlib Visualization
90
91
```python
92
import rustworkx as rx
93
import matplotlib.pyplot as plt
94
95
# Create sample graph
96
graph = rx.generators.karate_club_graph()
97
98
# Basic visualization with default settings
99
plt.figure(figsize=(10, 8))
100
rx.visualization.mpl_draw(graph, with_labels=True)
101
plt.title("Karate Club Graph")
102
plt.axis('off')
103
plt.show()
104
```
105
106
### Customized Node and Edge Styling
107
108
```python
109
# Create weighted graph
110
weighted_graph = rx.PyGraph()
111
nodes = weighted_graph.add_nodes_from(['Hub', 'A', 'B', 'C', 'D'])
112
edges = [
113
(0, 1, 0.8), # Hub to A: strong
114
(0, 2, 0.3), # Hub to B: weak
115
(0, 3, 0.9), # Hub to C: very strong
116
(0, 4, 0.1), # Hub to D: very weak
117
(1, 2, 0.5), # A to B: medium
118
(2, 3, 0.7), # B to C: strong
119
]
120
weighted_graph.add_edges_from(edges)
121
122
# Custom layout
123
pos = rx.spring_layout(weighted_graph, seed=42)
124
125
# Customize visualization based on data
126
node_sizes = [1000 if i == 0 else 300 for i in range(5)] # Hub is larger
127
node_colors = ['red' if i == 0 else 'lightblue' for i in range(5)]
128
129
# Edge widths based on weights
130
edge_weights = [weighted_graph.get_edge_data(s, t) for s, t in weighted_graph.edge_list()]
131
edge_widths = [w * 5 for w in edge_weights] # Scale for visibility
132
133
plt.figure(figsize=(10, 8))
134
rx.visualization.mpl_draw(
135
weighted_graph,
136
pos=pos,
137
node_size=node_sizes,
138
node_color=node_colors,
139
width=edge_widths,
140
with_labels=True,
141
labels={0: 'HUB', 1: 'A', 2: 'B', 3: 'C', 4: 'D'},
142
font_size=16,
143
font_weight='bold'
144
)
145
plt.title("Weighted Network with Custom Styling")
146
plt.axis('off')
147
plt.show()
148
```
149
150
### Directed Graph Visualization
151
152
```python
153
# Create directed graph
154
digraph = rx.PyDiGraph()
155
process_nodes = digraph.add_nodes_from(['Start', 'Process1', 'Process2', 'Decision', 'End'])
156
process_edges = [
157
(0, 1, 'begin'),
158
(1, 2, 'data'),
159
(2, 3, 'evaluate'),
160
(3, 4, 'success'),
161
(3, 1, 'retry') # Feedback loop
162
]
163
digraph.add_edges_from(process_edges)
164
165
# Hierarchical layout
166
pos = rx.shell_layout(
167
digraph,
168
nlist=[[0], [1, 2], [3], [4]], # Arrange in levels
169
scale=2.0
170
)
171
172
# Directed graph styling
173
plt.figure(figsize=(12, 8))
174
rx.visualization.mpl_draw(
175
digraph,
176
pos=pos,
177
with_labels=True,
178
labels={i: label for i, label in enumerate(['Start', 'Process1', 'Process2', 'Decision', 'End'])},
179
node_color=['lightgreen', 'lightblue', 'lightblue', 'orange', 'lightcoral'],
180
node_size=1500,
181
arrows=True,
182
arrowsize=20,
183
arrowstyle='->',
184
edge_color='gray',
185
font_size=10,
186
font_weight='bold'
187
)
188
plt.title("Process Flow Diagram")
189
plt.axis('off')
190
plt.show()
191
```
192
193
### Colormap-Based Visualization
194
195
```python
196
# Graph with numeric node data for colormap
197
network = rx.generators.erdos_renyi_gnp_random_graph(20, 0.3, seed=42)
198
199
# Calculate centrality for coloring
200
centrality = rx.betweenness_centrality(network)
201
centrality_values = [centrality[node] for node in network.node_indices()]
202
203
# Use colormap for nodes
204
plt.figure(figsize=(10, 8))
205
rx.visualization.mpl_draw(
206
network,
207
pos=rx.spring_layout(network, seed=42),
208
node_color=centrality_values,
209
cmap='viridis',
210
node_size=500,
211
with_labels=True,
212
font_size=8,
213
font_color='white'
214
)
215
216
# Add colorbar
217
plt.colorbar(plt.cm.ScalarMappable(cmap='viridis'), label='Betweenness Centrality')
218
plt.title("Network Colored by Betweenness Centrality")
219
plt.axis('off')
220
plt.show()
221
```
222
223
### Multi-Graph Subplot Visualization
224
225
```python
226
# Compare different graph structures
227
graphs = {
228
'Complete': rx.generators.complete_graph(6),
229
'Cycle': rx.generators.cycle_graph(6),
230
'Star': rx.generators.star_graph(6),
231
'Path': rx.generators.path_graph(6)
232
}
233
234
fig, axes = plt.subplots(2, 2, figsize=(12, 12))
235
axes = axes.flatten()
236
237
for i, (name, graph) in enumerate(graphs.items()):
238
pos = rx.circular_layout(graph) if name != 'Star' else rx.spring_layout(graph)
239
240
rx.visualization.mpl_draw(
241
graph,
242
pos=pos,
243
ax=axes[i],
244
node_color='lightblue',
245
node_size=300,
246
with_labels=True
247
)
248
axes[i].set_title(f"{name} Graph ({graph.num_nodes()} nodes, {graph.num_edges()} edges)")
249
axes[i].axis('off')
250
251
plt.tight_layout()
252
plt.show()
253
```
254
255
### Graphviz Visualization
256
257
```python
258
# Professional visualization with Graphviz
259
hierarchical_graph = rx.PyDiGraph()
260
org_nodes = hierarchical_graph.add_nodes_from([
261
'CEO', 'CTO', 'CFO', 'Dev1', 'Dev2', 'Acc1', 'Acc2'
262
])
263
org_edges = [
264
(0, 1, 'reports_to'), # CEO -> CTO
265
(0, 2, 'reports_to'), # CEO -> CFO
266
(1, 3, 'manages'), # CTO -> Dev1
267
(1, 4, 'manages'), # CTO -> Dev2
268
(2, 5, 'manages'), # CFO -> Acc1
269
(2, 6, 'manages'), # CFO -> Acc2
270
]
271
hierarchical_graph.add_edges_from(org_edges)
272
273
# Node styling function
274
def node_attrs(node_data):
275
role = ['CEO', 'CTO', 'CFO', 'Dev1', 'Dev2', 'Acc1', 'Acc2'][node_data]
276
if role == 'CEO':
277
return {'shape': 'box', 'style': 'filled', 'fillcolor': 'gold', 'fontweight': 'bold'}
278
elif role in ['CTO', 'CFO']:
279
return {'shape': 'box', 'style': 'filled', 'fillcolor': 'lightblue'}
280
else:
281
return {'shape': 'ellipse', 'style': 'filled', 'fillcolor': 'lightgreen'}
282
283
# Edge styling function
284
def edge_attrs(edge_data):
285
if edge_data == 'reports_to':
286
return {'color': 'red', 'style': 'bold'}
287
else:
288
return {'color': 'blue'}
289
290
# Generate DOT source
291
dot_source = rx.visualization.graphviz_draw(
292
hierarchical_graph,
293
node_attr_fn=lambda node: node_attrs(node),
294
edge_attr_fn=lambda edge: edge_attrs(edge),
295
graph_attr={'rankdir': 'TB', 'splines': 'ortho'}, # Top-to-bottom, orthogonal edges
296
method='dot' # Hierarchical layout
297
)
298
299
print("Generated DOT source:")
300
print(dot_source)
301
302
# Save to file (requires graphviz installation)
303
# rx.visualization.graphviz_draw(
304
# hierarchical_graph,
305
# node_attr_fn=node_attrs,
306
# edge_attr_fn=edge_attrs,
307
# filename='org_chart.png',
308
# image_type='png',
309
# method='dot'
310
# )
311
```
312
313
### Interactive Matplotlib Features
314
315
```python
316
# Create interactive plot with hover information
317
network = rx.generators.barabasi_albert_graph(30, 2, seed=42)
318
pos = rx.spring_layout(network, seed=42)
319
320
# Calculate node properties for display
321
degrees = {node: network.degree(node) for node in network.node_indices()}
322
max_degree = max(degrees.values())
323
324
# Size nodes by degree
325
node_sizes = [300 + (degrees[node] / max_degree) * 700 for node in network.node_indices()]
326
327
plt.figure(figsize=(12, 10))
328
scatter = plt.scatter(
329
[pos[node][0] for node in network.node_indices()],
330
[pos[node][1] for node in network.node_indices()],
331
s=node_sizes,
332
c=[degrees[node] for node in network.node_indices()],
333
cmap='plasma',
334
alpha=0.7
335
)
336
337
# Draw edges
338
for source, target in network.edge_list():
339
x1, y1 = pos[source]
340
x2, y2 = pos[target]
341
plt.plot([x1, x2], [y1, y2], 'k-', alpha=0.3, linewidth=0.5)
342
343
# Add colorbar and labels
344
plt.colorbar(scatter, label='Node Degree')
345
plt.title("Scale-Free Network (Node size and color = degree)")
346
plt.axis('off')
347
348
# Add node labels for high-degree nodes
349
for node in network.node_indices():
350
if degrees[node] > max_degree * 0.7: # Label high-degree nodes
351
x, y = pos[node]
352
plt.annotate(f'{node}\n(deg={degrees[node]})',
353
(x, y), xytext=(5, 5), textcoords='offset points',
354
fontsize=8, bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8))
355
356
plt.show()
357
```
358
359
### Animated Graph Visualization
360
361
```python
362
import matplotlib.animation as animation
363
364
# Create sequence of graphs showing growth
365
def create_growth_sequence(final_size=20, steps=10):
366
"""Create sequence showing preferential attachment growth."""
367
sequences = []
368
369
for i in range(1, steps + 1):
370
size = max(3, int(final_size * i / steps))
371
graph = rx.generators.barabasi_albert_graph(size, 2, seed=42)
372
sequences.append(graph)
373
374
return sequences
375
376
# Generate growth sequence
377
growth_graphs = create_growth_sequence(20, 10)
378
379
# Create animation
380
fig, ax = plt.subplots(figsize=(10, 8))
381
382
def animate(frame):
383
ax.clear()
384
graph = growth_graphs[frame]
385
pos = rx.spring_layout(graph, seed=42)
386
387
rx.visualization.mpl_draw(
388
graph,
389
pos=pos,
390
ax=ax,
391
node_color='lightblue',
392
node_size=300,
393
with_labels=True,
394
font_size=8
395
)
396
397
ax.set_title(f"Preferential Attachment Growth: {graph.num_nodes()} nodes, {graph.num_edges()} edges")
398
ax.axis('off')
399
400
# Create animation
401
anim = animation.FuncAnimation(fig, animate, frames=len(growth_graphs),
402
interval=800, repeat=True)
403
404
plt.show()
405
406
# Save animation (optional)
407
# anim.save('graph_growth.gif', writer='pillow', fps=1)
408
```
409
410
### Advanced Styling with Custom Functions
411
412
```python
413
def create_styled_visualization(graph, title="Styled Graph"):
414
"""Create a professionally styled graph visualization."""
415
416
# Calculate layout
417
pos = rx.spring_layout(graph, seed=42)
418
419
# Calculate node properties
420
degrees = {node: graph.degree(node) for node in graph.node_indices()}
421
centrality = rx.betweenness_centrality(graph)
422
423
# Style configuration
424
node_sizes = [200 + degrees[node] * 100 for node in graph.node_indices()]
425
node_colors = [centrality[node] for node in graph.node_indices()]
426
427
# Create visualization
428
plt.figure(figsize=(12, 10))
429
430
# Draw edges first (behind nodes)
431
for source, target in graph.edge_list():
432
x1, y1 = pos[source]
433
x2, y2 = pos[target]
434
plt.plot([x1, x2], [y1, y2], 'gray', alpha=0.4, linewidth=1)
435
436
# Draw nodes with custom styling
437
scatter = plt.scatter(
438
[pos[node][0] for node in graph.node_indices()],
439
[pos[node][1] for node in graph.node_indices()],
440
s=node_sizes,
441
c=node_colors,
442
cmap='coolwarm',
443
alpha=0.8,
444
edgecolors='black',
445
linewidths=1
446
)
447
448
# Add labels for important nodes
449
important_nodes = [node for node in graph.node_indices()
450
if centrality[node] > 0.1]
451
452
for node in important_nodes:
453
x, y = pos[node]
454
plt.annotate(f'{node}', (x, y), ha='center', va='center',
455
fontweight='bold', fontsize=10,
456
bbox=dict(boxstyle='circle,pad=0.3', facecolor='white', alpha=0.8))
457
458
# Styling
459
plt.colorbar(scatter, label='Betweenness Centrality', shrink=0.8)
460
plt.title(title, fontsize=16, fontweight='bold', pad=20)
461
plt.axis('off')
462
463
# Add legend
464
legend_elements = [
465
plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='gray',
466
markersize=8, label='Small degree'),
467
plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='gray',
468
markersize=12, label='Large degree')
469
]
470
plt.legend(handles=legend_elements, loc='upper right')
471
472
plt.tight_layout()
473
return plt.gcf()
474
475
# Apply to different graph types
476
test_graphs = [
477
(rx.generators.karate_club_graph(), "Karate Club Network"),
478
(rx.generators.barabasi_albert_graph(30, 2), "Scale-Free Network"),
479
(rx.generators.erdos_renyi_gnp_random_graph(25, 0.15), "Random Network")
480
]
481
482
for graph, title in test_graphs:
483
fig = create_styled_visualization(graph, title)
484
plt.show()
485
```