High performance graph data structures and algorithms for Python
—
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.
Physics-based layouts that simulate forces between vertices to achieve aesthetically pleasing arrangements.
class Graph:
def layout_fruchterman_reingold(self, weights=None, maxiter=500, area=None, coolexp=1.5, repulserad=None, dim=2, **kwargs):
"""
Fruchterman-Reingold force-directed layout.
Parameters:
- weights: str/list, edge weights (shorter for higher weights)
- maxiter: int, maximum iterations
- area: float, area of layout (None for automatic)
- coolexp: float, cooling exponent
- repulserad: float, repulsion radius (None for automatic)
- dim: int, number of dimensions (2 or 3)
Returns:
Layout object with vertex coordinates
"""
def layout_kamada_kawai(self, weights=None, maxiter=None, epsilon=0, kkconst=None, dim=2, **kwargs):
"""
Kamada-Kawai spring-based layout.
Parameters:
- weights: edge weights
- maxiter: int, maximum iterations (None for automatic)
- epsilon: float, stopping criterion
- kkconst: float, spring constant (None for automatic)
- dim: int, dimensions
Returns:
Layout object
"""
def layout_spring(self, weights=None, maxiter=500, area=None, coolexp=1.5, repulserad=None, dim=2, **kwargs):
"""
Alias for layout_fruchterman_reingold().
"""
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):
"""
Davidson-Harel layout with multiple aesthetic criteria.
Parameters:
- weights: edge weights
- maxiter: int, coarse iterations
- fineiter: int, fine-tuning iterations
- cool_fact: float, cooling factor
- weight_*: float, weights for different aesthetic criteria
Returns:
Layout object
"""Usage Examples:
import igraph as ig
# Create sample graph
g = ig.Graph.Barabasi(50, m=2)
# Fruchterman-Reingold (most common)
fr_layout = g.layout_fruchterman_reingold()
print(f"FR layout shape: {len(fr_layout)} x {len(fr_layout[0])}")
# With edge weights (closer vertices for higher weights)
g.es["weight"] = [1.0 + i*0.1 for i in range(g.ecount())]
weighted_fr = g.layout_fruchterman_reingold(weights="weight")
# Kamada-Kawai (good for smaller graphs)
kk_layout = g.layout_kamada_kawai(maxiter=1000)
# 3D layout
fr_3d = g.layout_fruchterman_reingold(dim=3)
print(f"3D layout shape: {len(fr_3d)} x {len(fr_3d[0])}") # N x 3
# Davidson-Harel with custom weights
dh_layout = g.layout_davidson_harel(
maxiter=20,
weight_node_dist=2.0,
weight_edge_crossings=0.5
)Specialized layouts for trees, DAGs, and hierarchical structures.
class Graph:
def layout_reingold_tilford(self, mode=ALL, root=None):
"""
Reingold-Tilford tree layout.
Parameters:
- mode: tree direction (ALL, OUT, IN)
- root: int/list, root vertices (None for automatic)
Returns:
Layout object with hierarchical positioning
"""
def layout_reingold_tilford_circular(self, mode=ALL, root=None):
"""
Circular Reingold-Tilford layout.
Parameters:
- mode: tree direction
- root: root vertices
Returns:
Layout object in circular arrangement
"""
def layout_sugiyama(self, layers=None, weights=None, hgap=1, vgap=1, maxiter=100, **kwargs):
"""
Sugiyama layered layout for directed acyclic graphs.
Parameters:
- layers: list, predefined layer assignment
- weights: edge weights
- hgap: float, horizontal gap between vertices
- vgap: float, vertical gap between layers
- maxiter: int, maximum iterations for crossing reduction
Returns:
Layout object with layered structure
"""Usage Examples:
# Tree layouts
tree = ig.Graph.Tree(31, children=2) # Binary tree
# Standard tree layout
tree_layout = tree.layout_reingold_tilford(root=[0])
# Circular tree layout
circular_tree = tree.layout_reingold_tilford_circular(root=[0])
# For directed acyclic graphs
dag = ig.Graph([(0,1), (0,2), (1,3), (1,4), (2,4), (2,5), (3,6), (4,6), (5,6)], directed=True)
# Sugiyama layered layout
sugiyama_layout = dag.layout_sugiyama()
# Manual layer assignment
layers = [0, 1, 1, 2, 2, 2, 3] # Layer for each vertex
manual_sugiyama = dag.layout_sugiyama(layers=layers, hgap=2, vgap=1.5)Efficient algorithms designed for large networks with thousands or millions of vertices.
class Graph:
def layout_drl(self, weights=None, dim=2, fixednode=None, **kwargs):
"""
DrL (Distributed Recursive Layout) for large graphs.
Parameters:
- weights: edge weights
- dim: int, dimensions (2 or 3)
- fixednode: list, vertices with fixed positions
Returns:
Layout object
"""
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):
"""
GraphOpt layout algorithm.
Parameters:
- weights: edge weights
- niter: int, number of iterations
- node_charge: float, vertex charge (repulsion)
- node_mass: float, vertex mass
- spring_length: float, natural spring length
- spring_constant: float, spring force constant
- max_sa_movement: float, maximum movement per iteration
Returns:
Layout object
"""
def layout_lgl(self, weights=None, maxiter=150, maxdelta=None, area=None, coolexp=1.5, repulserad=None, cellsize=None, **kwargs):
"""
Large Graph Layout (LGL) algorithm.
Parameters:
- weights: edge weights
- maxiter: int, maximum iterations
- maxdelta: float, maximum movement (None for automatic)
- area: float, layout area
- coolexp: float, cooling exponent
- repulserad: float, repulsion radius
- cellsize: float, cell size for spatial indexing
Returns:
Layout object
"""Usage Examples:
# Large network
large_g = ig.Graph.Barabasi(1000, m=3)
# DrL layout (good for large networks)
drl_layout = large_g.layout_drl()
# GraphOpt layout
graphopt_layout = large_g.layout_graphopt(niter=1000, node_charge=0.002)
# LGL layout
lgl_layout = large_g.layout_lgl(maxiter=200)
# For very large graphs, use fewer iterations
if large_g.vcount() > 5000:
quick_layout = large_g.layout_drl(dim=2) # Default parameters are efficientRegular and geometric layout patterns for specific visualization needs.
class Graph:
def layout_circle(self, order=None):
"""
Circular layout with vertices on a circle.
Parameters:
- order: list, vertex ordering (None for current order)
Returns:
Layout object with circular positioning
"""
def layout_grid(self, width=0, dim=2):
"""
Grid layout in regular pattern.
Parameters:
- width: int, grid width (0 for automatic square)
- dim: int, dimensions
Returns:
Layout object with grid positioning
"""
def layout_sphere(self):
"""
Spherical layout in 3D space.
Returns:
Layout object with 3D spherical coordinates
"""
def layout_random(self, dim=2):
"""
Random vertex positions.
Parameters:
- dim: int, dimensions
Returns:
Layout object with random coordinates
"""
def layout_fruchterman_reingold_3d(self, **kwargs):
"""
3D Fruchterman-Reingold layout (alias for layout_fruchterman_reingold(dim=3)).
Returns:
Layout object with 3D coordinates
"""
def layout_kamada_kawai_3d(self, **kwargs):
"""
3D Kamada-Kawai layout (alias for layout_kamada_kawai(dim=3)).
Returns:
Layout object with 3D coordinates
"""
def layout_random_3d(self, **kwargs):
"""
3D random layout (alias for layout_random(dim=3)).
Returns:
Layout object with 3D random coordinates
"""
def layout_grid_3d(self, **kwargs):
"""
3D grid layout (alias for layout_grid(dim=3)).
Returns:
Layout object with 3D grid coordinates
"""Usage Examples:
# Geometric layouts
g = ig.Graph.Ring(12)
# Circular layout
circle_layout = g.layout_circle()
# Custom vertex ordering for circle
vertex_order = list(range(0, 12, 2)) + list(range(1, 12, 2)) # Even then odd
ordered_circle = g.layout_circle(order=vertex_order)
# Grid layout
grid_g = ig.Graph.Lattice([4, 3]) # 4x3 grid graph
grid_layout = grid_g.layout_grid(width=4)
# 3D sphere
sphere_layout = g.layout_sphere()
# Random layout (useful as starting point)
random_layout = g.layout_random(dim=2)Layouts based on preserving distances and similarities between vertices.
class Graph:
def layout_mds(self, weights=None, dim=2, **kwargs):
"""
Multidimensional scaling layout.
Parameters:
- weights: edge weights for distance calculation
- dim: int, target dimensions
Returns:
Layout object preserving graph distances
"""
def layout_gem(self, weights=None, maxiter=40*40, temp_max=None, temp_min=None, temp_init=None):
"""
GEM (Graph EMbedder) layout algorithm.
Parameters:
- weights: edge weights
- maxiter: int, maximum iterations
- temp_max: float, maximum temperature
- temp_min: float, minimum temperature
- temp_init: float, initial temperature
Returns:
Layout object
"""Usage Examples:
# MDS layout (preserves shortest path distances)
mds_layout = g.layout_mds()
# With edge weights
g.es["weight"] = [abs(i-j) + 1 for i, j in g.get_edgelist()]
weighted_mds = g.layout_mds(weights="weight")
# GEM layout
gem_layout = g.layout_gem(maxiter=2000)Methods for transforming, scaling, and adjusting existing layouts.
class Layout:
def scale(self, *args):
"""
Scale layout coordinates.
Parameters:
- args: scaling factors (single value or per-dimension)
Returns:
None (modifies in place)
"""
def translate(self, *args):
"""
Translate layout coordinates.
Parameters:
- args: translation offsets
Returns:
None (modifies in place)
"""
def rotate(self, angle, dim1=0, dim2=1):
"""
Rotate layout in specified plane.
Parameters:
- angle: float, rotation angle in radians
- dim1, dim2: int, dimensions defining rotation plane
Returns:
None (modifies in place)
"""
def mirror(self, dim):
"""
Mirror layout along specified dimension.
Parameters:
- dim: int, dimension to mirror
Returns:
None (modifies in place)
"""
def fit_into(self, bbox, keep_aspect_ratio=True):
"""
Fit layout into bounding box.
Parameters:
- bbox: BoundingBox or (left, top, right, bottom)
- keep_aspect_ratio: bool, preserve proportions
Returns:
None (modifies in place)
"""
def copy(self):
"""
Create copy of layout.
Returns:
Layout, independent copy
"""Usage Examples:
from igraph.drawing import BoundingBox
import math
# Create and manipulate layout
layout = g.layout_fruchterman_reingold()
# Make a copy for manipulation
layout_copy = layout.copy()
# Scale by factor of 2
layout_copy.scale(2.0)
# Translate to center at origin
center_x = sum(pos[0] for pos in layout_copy) / len(layout_copy)
center_y = sum(pos[1] for pos in layout_copy) / len(layout_copy)
layout_copy.translate(-center_x, -center_y)
# Rotate 45 degrees
layout_copy.rotate(math.pi/4)
# Mirror horizontally
layout_copy.mirror(0)
# Fit into specific bounding box
bbox = BoundingBox(0, 0, 800, 600)
layout_copy.fit_into(bbox, keep_aspect_ratio=True)
# Different scaling per dimension
layout2 = layout.copy()
layout2.scale(1.5, 0.8) # Stretch horizontally, compress verticallyTechniques for choosing appropriate layouts and comparing their quality.
Usage Examples:
import time
# Compare layout algorithms for a specific graph
g = ig.Graph.Barabasi(100, m=3)
layouts = {}
times = {}
# Test different algorithms
algorithms = [
("fruchterman_reingold", g.layout_fruchterman_reingold),
("kamada_kawai", g.layout_kamada_kawai),
("drl", g.layout_drl),
("graphopt", g.layout_graphopt)
]
for name, method in algorithms:
start_time = time.time()
try:
layout = method()
layouts[name] = layout
times[name] = time.time() - start_time
print(f"{name}: {times[name]:.3f}s")
except Exception as e:
print(f"{name}: failed ({e})")
# Layout quality metrics (custom functions)
def layout_stress(graph, layout, weights=None):
"""Calculate layout stress (difference between graph and Euclidean distances)."""
import math
stress = 0
dist_matrix = graph.shortest_paths(weights=weights)
for i in range(len(layout)):
for j in range(i+1, len(layout)):
# Euclidean distance in layout
euclidean = math.sqrt(sum((layout[i][k] - layout[j][k])**2 for k in range(2)))
# Graph distance
graph_dist = dist_matrix[i][j]
if graph_dist != float('inf'):
stress += (euclidean - graph_dist) ** 2
return stress
# Evaluate layouts
for name, layout in layouts.items():
if layout:
stress = layout_stress(g, layout)
print(f"{name} stress: {stress:.2f}")
# Adaptive layout selection based on graph size
def choose_layout(graph):
"""Choose appropriate layout algorithm based on graph properties."""
n = graph.vcount()
m = graph.ecount()
if n <= 100:
return graph.layout_kamada_kawai()
elif n <= 1000:
return graph.layout_fruchterman_reingold()
else:
return graph.layout_drl()
optimal_layout = choose_layout(g)Install with Tessl CLI
npx tessl i tessl/pypi-igraph