0
# Layout
1
2
Graph layout and positioning algorithms for visualization and spatial analysis. igraph provides over 30 layout algorithms ranging from force-directed methods to specialized algorithms for trees, hierarchies, and large networks.
3
4
## Capabilities
5
6
### Force-Directed Layouts
7
8
Physics-based layouts that simulate forces between vertices to achieve aesthetically pleasing arrangements.
9
10
```python { .api }
11
class Graph:
12
def layout_fruchterman_reingold(self, weights=None, maxiter=500, area=None, coolexp=1.5, repulserad=None, dim=2, **kwargs):
13
"""
14
Fruchterman-Reingold force-directed layout.
15
16
Parameters:
17
- weights: str/list, edge weights (shorter for higher weights)
18
- maxiter: int, maximum iterations
19
- area: float, area of layout (None for automatic)
20
- coolexp: float, cooling exponent
21
- repulserad: float, repulsion radius (None for automatic)
22
- dim: int, number of dimensions (2 or 3)
23
24
Returns:
25
Layout object with vertex coordinates
26
"""
27
28
def layout_kamada_kawai(self, weights=None, maxiter=None, epsilon=0, kkconst=None, dim=2, **kwargs):
29
"""
30
Kamada-Kawai spring-based layout.
31
32
Parameters:
33
- weights: edge weights
34
- maxiter: int, maximum iterations (None for automatic)
35
- epsilon: float, stopping criterion
36
- kkconst: float, spring constant (None for automatic)
37
- dim: int, dimensions
38
39
Returns:
40
Layout object
41
"""
42
43
def layout_spring(self, weights=None, maxiter=500, area=None, coolexp=1.5, repulserad=None, dim=2, **kwargs):
44
"""
45
Alias for layout_fruchterman_reingold().
46
"""
47
48
def layout_davidson_harel(self, weights=None, maxiter=10, fineiter=None, cool_fact=0.75, weight_node_dist=1.0, weight_border=0.0, weight_edge_lengths=None, weight_edge_crossings=1.0, weight_node_edge_dist=0.2, **kwargs):
49
"""
50
Davidson-Harel layout with multiple aesthetic criteria.
51
52
Parameters:
53
- weights: edge weights
54
- maxiter: int, coarse iterations
55
- fineiter: int, fine-tuning iterations
56
- cool_fact: float, cooling factor
57
- weight_*: float, weights for different aesthetic criteria
58
59
Returns:
60
Layout object
61
"""
62
```
63
64
**Usage Examples:**
65
66
```python
67
import igraph as ig
68
69
# Create sample graph
70
g = ig.Graph.Barabasi(50, m=2)
71
72
# Fruchterman-Reingold (most common)
73
fr_layout = g.layout_fruchterman_reingold()
74
print(f"FR layout shape: {len(fr_layout)} x {len(fr_layout[0])}")
75
76
# With edge weights (closer vertices for higher weights)
77
g.es["weight"] = [1.0 + i*0.1 for i in range(g.ecount())]
78
weighted_fr = g.layout_fruchterman_reingold(weights="weight")
79
80
# Kamada-Kawai (good for smaller graphs)
81
kk_layout = g.layout_kamada_kawai(maxiter=1000)
82
83
# 3D layout
84
fr_3d = g.layout_fruchterman_reingold(dim=3)
85
print(f"3D layout shape: {len(fr_3d)} x {len(fr_3d[0])}") # N x 3
86
87
# Davidson-Harel with custom weights
88
dh_layout = g.layout_davidson_harel(
89
maxiter=20,
90
weight_node_dist=2.0,
91
weight_edge_crossings=0.5
92
)
93
```
94
95
### Tree and Hierarchical Layouts
96
97
Specialized layouts for trees, DAGs, and hierarchical structures.
98
99
```python { .api }
100
class Graph:
101
def layout_reingold_tilford(self, mode=ALL, root=None):
102
"""
103
Reingold-Tilford tree layout.
104
105
Parameters:
106
- mode: tree direction (ALL, OUT, IN)
107
- root: int/list, root vertices (None for automatic)
108
109
Returns:
110
Layout object with hierarchical positioning
111
"""
112
113
def layout_reingold_tilford_circular(self, mode=ALL, root=None):
114
"""
115
Circular Reingold-Tilford layout.
116
117
Parameters:
118
- mode: tree direction
119
- root: root vertices
120
121
Returns:
122
Layout object in circular arrangement
123
"""
124
125
def layout_sugiyama(self, layers=None, weights=None, hgap=1, vgap=1, maxiter=100, **kwargs):
126
"""
127
Sugiyama layered layout for directed acyclic graphs.
128
129
Parameters:
130
- layers: list, predefined layer assignment
131
- weights: edge weights
132
- hgap: float, horizontal gap between vertices
133
- vgap: float, vertical gap between layers
134
- maxiter: int, maximum iterations for crossing reduction
135
136
Returns:
137
Layout object with layered structure
138
"""
139
```
140
141
**Usage Examples:**
142
143
```python
144
# Tree layouts
145
tree = ig.Graph.Tree(31, children=2) # Binary tree
146
147
# Standard tree layout
148
tree_layout = tree.layout_reingold_tilford(root=[0])
149
150
# Circular tree layout
151
circular_tree = tree.layout_reingold_tilford_circular(root=[0])
152
153
# For directed acyclic graphs
154
dag = ig.Graph([(0,1), (0,2), (1,3), (1,4), (2,4), (2,5), (3,6), (4,6), (5,6)], directed=True)
155
156
# Sugiyama layered layout
157
sugiyama_layout = dag.layout_sugiyama()
158
159
# Manual layer assignment
160
layers = [0, 1, 1, 2, 2, 2, 3] # Layer for each vertex
161
manual_sugiyama = dag.layout_sugiyama(layers=layers, hgap=2, vgap=1.5)
162
```
163
164
### Large Graph Layouts
165
166
Efficient algorithms designed for large networks with thousands or millions of vertices.
167
168
```python { .api }
169
class Graph:
170
def layout_drl(self, weights=None, dim=2, fixednode=None, **kwargs):
171
"""
172
DrL (Distributed Recursive Layout) for large graphs.
173
174
Parameters:
175
- weights: edge weights
176
- dim: int, dimensions (2 or 3)
177
- fixednode: list, vertices with fixed positions
178
179
Returns:
180
Layout object
181
"""
182
183
def layout_graphopt(self, weights=None, niter=500, node_charge=0.001, node_mass=30, spring_length=0, spring_constant=1, max_sa_movement=5, **kwargs):
184
"""
185
GraphOpt layout algorithm.
186
187
Parameters:
188
- weights: edge weights
189
- niter: int, number of iterations
190
- node_charge: float, vertex charge (repulsion)
191
- node_mass: float, vertex mass
192
- spring_length: float, natural spring length
193
- spring_constant: float, spring force constant
194
- max_sa_movement: float, maximum movement per iteration
195
196
Returns:
197
Layout object
198
"""
199
200
def layout_lgl(self, weights=None, maxiter=150, maxdelta=None, area=None, coolexp=1.5, repulserad=None, cellsize=None, **kwargs):
201
"""
202
Large Graph Layout (LGL) algorithm.
203
204
Parameters:
205
- weights: edge weights
206
- maxiter: int, maximum iterations
207
- maxdelta: float, maximum movement (None for automatic)
208
- area: float, layout area
209
- coolexp: float, cooling exponent
210
- repulserad: float, repulsion radius
211
- cellsize: float, cell size for spatial indexing
212
213
Returns:
214
Layout object
215
"""
216
```
217
218
**Usage Examples:**
219
220
```python
221
# Large network
222
large_g = ig.Graph.Barabasi(1000, m=3)
223
224
# DrL layout (good for large networks)
225
drl_layout = large_g.layout_drl()
226
227
# GraphOpt layout
228
graphopt_layout = large_g.layout_graphopt(niter=1000, node_charge=0.002)
229
230
# LGL layout
231
lgl_layout = large_g.layout_lgl(maxiter=200)
232
233
# For very large graphs, use fewer iterations
234
if large_g.vcount() > 5000:
235
quick_layout = large_g.layout_drl(dim=2) # Default parameters are efficient
236
```
237
238
### Geometric and Grid Layouts
239
240
Regular and geometric layout patterns for specific visualization needs.
241
242
```python { .api }
243
class Graph:
244
def layout_circle(self, order=None):
245
"""
246
Circular layout with vertices on a circle.
247
248
Parameters:
249
- order: list, vertex ordering (None for current order)
250
251
Returns:
252
Layout object with circular positioning
253
"""
254
255
def layout_grid(self, width=0, dim=2):
256
"""
257
Grid layout in regular pattern.
258
259
Parameters:
260
- width: int, grid width (0 for automatic square)
261
- dim: int, dimensions
262
263
Returns:
264
Layout object with grid positioning
265
"""
266
267
def layout_sphere(self):
268
"""
269
Spherical layout in 3D space.
270
271
Returns:
272
Layout object with 3D spherical coordinates
273
"""
274
275
def layout_random(self, dim=2):
276
"""
277
Random vertex positions.
278
279
Parameters:
280
- dim: int, dimensions
281
282
Returns:
283
Layout object with random coordinates
284
"""
285
286
def layout_fruchterman_reingold_3d(self, **kwargs):
287
"""
288
3D Fruchterman-Reingold layout (alias for layout_fruchterman_reingold(dim=3)).
289
290
Returns:
291
Layout object with 3D coordinates
292
"""
293
294
def layout_kamada_kawai_3d(self, **kwargs):
295
"""
296
3D Kamada-Kawai layout (alias for layout_kamada_kawai(dim=3)).
297
298
Returns:
299
Layout object with 3D coordinates
300
"""
301
302
def layout_random_3d(self, **kwargs):
303
"""
304
3D random layout (alias for layout_random(dim=3)).
305
306
Returns:
307
Layout object with 3D random coordinates
308
"""
309
310
def layout_grid_3d(self, **kwargs):
311
"""
312
3D grid layout (alias for layout_grid(dim=3)).
313
314
Returns:
315
Layout object with 3D grid coordinates
316
"""
317
```
318
319
**Usage Examples:**
320
321
```python
322
# Geometric layouts
323
g = ig.Graph.Ring(12)
324
325
# Circular layout
326
circle_layout = g.layout_circle()
327
328
# Custom vertex ordering for circle
329
vertex_order = list(range(0, 12, 2)) + list(range(1, 12, 2)) # Even then odd
330
ordered_circle = g.layout_circle(order=vertex_order)
331
332
# Grid layout
333
grid_g = ig.Graph.Lattice([4, 3]) # 4x3 grid graph
334
grid_layout = grid_g.layout_grid(width=4)
335
336
# 3D sphere
337
sphere_layout = g.layout_sphere()
338
339
# Random layout (useful as starting point)
340
random_layout = g.layout_random(dim=2)
341
```
342
343
### Multidimensional Scaling
344
345
Layouts based on preserving distances and similarities between vertices.
346
347
```python { .api }
348
class Graph:
349
def layout_mds(self, weights=None, dim=2, **kwargs):
350
"""
351
Multidimensional scaling layout.
352
353
Parameters:
354
- weights: edge weights for distance calculation
355
- dim: int, target dimensions
356
357
Returns:
358
Layout object preserving graph distances
359
"""
360
361
def layout_gem(self, weights=None, maxiter=40*40, temp_max=None, temp_min=None, temp_init=None):
362
"""
363
GEM (Graph EMbedder) layout algorithm.
364
365
Parameters:
366
- weights: edge weights
367
- maxiter: int, maximum iterations
368
- temp_max: float, maximum temperature
369
- temp_min: float, minimum temperature
370
- temp_init: float, initial temperature
371
372
Returns:
373
Layout object
374
"""
375
```
376
377
**Usage Examples:**
378
379
```python
380
# MDS layout (preserves shortest path distances)
381
mds_layout = g.layout_mds()
382
383
# With edge weights
384
g.es["weight"] = [abs(i-j) + 1 for i, j in g.get_edgelist()]
385
weighted_mds = g.layout_mds(weights="weight")
386
387
# GEM layout
388
gem_layout = g.layout_gem(maxiter=2000)
389
```
390
391
### Layout Manipulation
392
393
Methods for transforming, scaling, and adjusting existing layouts.
394
395
```python { .api }
396
class Layout:
397
def scale(self, *args):
398
"""
399
Scale layout coordinates.
400
401
Parameters:
402
- args: scaling factors (single value or per-dimension)
403
404
Returns:
405
None (modifies in place)
406
"""
407
408
def translate(self, *args):
409
"""
410
Translate layout coordinates.
411
412
Parameters:
413
- args: translation offsets
414
415
Returns:
416
None (modifies in place)
417
"""
418
419
def rotate(self, angle, dim1=0, dim2=1):
420
"""
421
Rotate layout in specified plane.
422
423
Parameters:
424
- angle: float, rotation angle in radians
425
- dim1, dim2: int, dimensions defining rotation plane
426
427
Returns:
428
None (modifies in place)
429
"""
430
431
def mirror(self, dim):
432
"""
433
Mirror layout along specified dimension.
434
435
Parameters:
436
- dim: int, dimension to mirror
437
438
Returns:
439
None (modifies in place)
440
"""
441
442
def fit_into(self, bbox, keep_aspect_ratio=True):
443
"""
444
Fit layout into bounding box.
445
446
Parameters:
447
- bbox: BoundingBox or (left, top, right, bottom)
448
- keep_aspect_ratio: bool, preserve proportions
449
450
Returns:
451
None (modifies in place)
452
"""
453
454
def copy(self):
455
"""
456
Create copy of layout.
457
458
Returns:
459
Layout, independent copy
460
"""
461
```
462
463
**Usage Examples:**
464
465
```python
466
from igraph.drawing import BoundingBox
467
import math
468
469
# Create and manipulate layout
470
layout = g.layout_fruchterman_reingold()
471
472
# Make a copy for manipulation
473
layout_copy = layout.copy()
474
475
# Scale by factor of 2
476
layout_copy.scale(2.0)
477
478
# Translate to center at origin
479
center_x = sum(pos[0] for pos in layout_copy) / len(layout_copy)
480
center_y = sum(pos[1] for pos in layout_copy) / len(layout_copy)
481
layout_copy.translate(-center_x, -center_y)
482
483
# Rotate 45 degrees
484
layout_copy.rotate(math.pi/4)
485
486
# Mirror horizontally
487
layout_copy.mirror(0)
488
489
# Fit into specific bounding box
490
bbox = BoundingBox(0, 0, 800, 600)
491
layout_copy.fit_into(bbox, keep_aspect_ratio=True)
492
493
# Different scaling per dimension
494
layout2 = layout.copy()
495
layout2.scale(1.5, 0.8) # Stretch horizontally, compress vertically
496
```
497
498
### Layout Comparison and Selection
499
500
Techniques for choosing appropriate layouts and comparing their quality.
501
502
**Usage Examples:**
503
504
```python
505
import time
506
507
# Compare layout algorithms for a specific graph
508
g = ig.Graph.Barabasi(100, m=3)
509
510
layouts = {}
511
times = {}
512
513
# Test different algorithms
514
algorithms = [
515
("fruchterman_reingold", g.layout_fruchterman_reingold),
516
("kamada_kawai", g.layout_kamada_kawai),
517
("drl", g.layout_drl),
518
("graphopt", g.layout_graphopt)
519
]
520
521
for name, method in algorithms:
522
start_time = time.time()
523
try:
524
layout = method()
525
layouts[name] = layout
526
times[name] = time.time() - start_time
527
print(f"{name}: {times[name]:.3f}s")
528
except Exception as e:
529
print(f"{name}: failed ({e})")
530
531
# Layout quality metrics (custom functions)
532
def layout_stress(graph, layout, weights=None):
533
"""Calculate layout stress (difference between graph and Euclidean distances)."""
534
import math
535
stress = 0
536
dist_matrix = graph.shortest_paths(weights=weights)
537
538
for i in range(len(layout)):
539
for j in range(i+1, len(layout)):
540
# Euclidean distance in layout
541
euclidean = math.sqrt(sum((layout[i][k] - layout[j][k])**2 for k in range(2)))
542
# Graph distance
543
graph_dist = dist_matrix[i][j]
544
if graph_dist != float('inf'):
545
stress += (euclidean - graph_dist) ** 2
546
return stress
547
548
# Evaluate layouts
549
for name, layout in layouts.items():
550
if layout:
551
stress = layout_stress(g, layout)
552
print(f"{name} stress: {stress:.2f}")
553
554
# Adaptive layout selection based on graph size
555
def choose_layout(graph):
556
"""Choose appropriate layout algorithm based on graph properties."""
557
n = graph.vcount()
558
m = graph.ecount()
559
560
if n <= 100:
561
return graph.layout_kamada_kawai()
562
elif n <= 1000:
563
return graph.layout_fruchterman_reingold()
564
else:
565
return graph.layout_drl()
566
567
optimal_layout = choose_layout(g)
568
```