High performance graph data structures and algorithms for Python
—
Core operations for adding, removing, and modifying vertices and edges, plus basic graph properties and transformations. These fundamental operations enable dynamic graph construction and manipulation.
Add, remove, and manipulate vertices in the graph with support for attributes and batch operations.
class Graph:
def add_vertex(self, name=None, **attrs):
"""
Add single vertex to the graph.
Parameters:
- name: str, vertex name (stored as 'name' attribute)
- **attrs: keyword arguments for vertex attributes
Returns:
int, index of newly added vertex
"""
def add_vertices(self, n, attrs=None):
"""
Add multiple vertices to the graph.
Parameters:
- n: int or list, number of vertices or list of names
- attrs: dict, attribute dictionaries for new vertices
Returns:
None
"""
def delete_vertices(self, vertices):
"""
Remove vertices from the graph.
Parameters:
- vertices: int, str, list, or VertexSeq, vertices to remove
Returns:
None
Note: Adjacent edges are automatically removed
"""
def vcount(self):
"""
Get number of vertices in graph.
Returns:
int, vertex count
"""Usage Examples:
import igraph as ig
# Create empty graph and add vertices
g = ig.Graph()
# Add single vertex
v1 = g.add_vertex(name="Alice", age=25, city="NYC")
v2 = g.add_vertex(name="Bob", age=30, city="LA")
# Add multiple vertices
g.add_vertices(3, {"name": ["Carol", "Dave", "Eve"]})
# Add with different attributes per vertex
attrs = [
{"name": "Frank", "age": 35},
{"name": "Grace", "age": 28}
]
g.add_vertices(2)
for i, attr_dict in enumerate(attrs):
for key, val in attr_dict.items():
g.vs[g.vcount() - 2 + i][key] = val
print(f"Vertices: {g.vcount()}") # 7
# Delete vertices
g.delete_vertices([0, 2]) # Remove Alice and Carol
g.delete_vertices("Dave") # Remove by name (if unique)Add, remove, and manipulate edges between vertices with support for weights and other attributes.
class Graph:
def add_edge(self, source, target, **attrs):
"""
Add single edge to the graph.
Parameters:
- source: int or str, source vertex ID or name
- target: int or str, target vertex ID or name
- **attrs: keyword arguments for edge attributes
Returns:
int, index of newly added edge
"""
def add_edges(self, edges, attrs=None):
"""
Add multiple edges to the graph.
Parameters:
- edges: list of tuples, edge list [(source, target), ...]
- attrs: dict or list, edge attributes
Returns:
None
"""
def delete_edges(self, edges):
"""
Remove edges from the graph.
Parameters:
- edges: int, tuple, list, or EdgeSeq, edges to remove
Returns:
None
"""
def ecount(self):
"""
Get number of edges in graph.
Returns:
int, edge count
"""
def get_edgelist(self):
"""
Get list of all edges as (source, target) tuples.
Returns:
list of tuples, edge list
"""Usage Examples:
# Start with vertices
g = ig.Graph()
g.add_vertices(4, {"name": ["A", "B", "C", "D"]})
# Add single edge with attributes
g.add_edge("A", "B", weight=1.5, type="friendship")
g.add_edge(0, 2, weight=2.0) # By index
# Add multiple edges
edges = [(1, 2), (2, 3), (3, 0)]
g.add_edges(edges)
# Add edges with attributes
edge_attrs = {"weight": [1, 2, 3], "type": ["work", "family", "friend"]}
g.add_edges([(0, 1), (1, 3), (2, 0)], edge_attrs)
print(f"Edges: {g.ecount()}") # 6
print("Edge list:", g.get_edgelist())
# Delete edges
g.delete_edges([(0, 1)]) # Remove specific edge
g.delete_edges([0, 2]) # Remove by edge indexQuery fundamental graph properties and characteristics.
class Graph:
def is_directed(self):
"""
Check if graph is directed.
Returns:
bool, True if directed
"""
def is_simple(self):
"""
Check if graph is simple (no loops or multiple edges).
Returns:
bool, True if simple
"""
def is_connected(self, mode=WEAK):
"""
Check if graph is connected.
Parameters:
- mode: connectivity type (WEAK, STRONG for directed graphs)
Returns:
bool, True if connected
"""
def has_multiple(self):
"""
Check if graph has multiple edges.
Returns:
bool, True if multiple edges exist
"""
def is_loop(self, edges=None):
"""
Check which edges are self-loops.
Parameters:
- edges: list/EdgeSeq, edges to check (None for all)
Returns:
list of bool, True for each loop edge
"""
def density(self, loops=False):
"""
Calculate graph density.
Parameters:
- loops: bool, whether to consider self-loops
Returns:
float, density (0-1)
"""Usage Examples:
# Create sample graph
g = ig.Graph(edges=[(0,1), (1,2), (2,0)], directed=False)
# Check properties
print(f"Directed: {g.is_directed()}") # False
print(f"Simple: {g.is_simple()}") # True
print(f"Connected: {g.is_connected()}") # True
print(f"Density: {g.density():.3f}") # 1.0 (complete triangle)
# Add self-loop and multiple edge
g.add_edge(0, 0) # Self-loop
g.add_edge(0, 1) # Multiple edge
print(f"Simple: {g.is_simple()}") # False
print(f"Has multiple: {g.has_multiple()}") # True
print("Loop edges:", g.is_loop()) # [False, False, False, True, False]Convert between directed and undirected graphs and apply structural transformations.
class Graph:
def as_directed(self, mutual=True):
"""
Convert to directed graph.
Parameters:
- mutual: bool, create mutual edges for undirected edges
Returns:
Graph, directed version
"""
def as_undirected(self, mode="collapse", combine_edges=None):
"""
Convert to undirected graph.
Parameters:
- mode: str, how to handle directed edges
("collapse", "each", "mutual")
- combine_edges: str/dict, how to combine edge attributes
("ignore", "sum", "prod", "mean", "min", "max", "first", "last",
"random", or dict mapping attributes to functions)
Returns:
Graph, undirected version
"""
def simplify(self, multiple=True, loops=True, combine_edges=None):
"""
Remove multiple edges and/or loops.
Parameters:
- multiple: bool, remove multiple edges
- loops: bool, remove self-loops
- combine_edges: str/dict, how to combine attributes of multiple edges
Returns:
Graph, simplified version
"""
def copy(self):
"""
Create deep copy of the graph.
Returns:
Graph, independent copy
"""Usage Examples:
# Undirected graph with multiple edges
g = ig.Graph([(0,1), (1,2), (2,0), (0,1), (1,1)], directed=False)
# Convert to directed
g_dir = g.as_directed(mutual=True)
print(f"Directed edges: {g_dir.ecount()}") # 8 (each undirected becomes 2 directed)
# Back to undirected with edge combining
g_undir = g_dir.as_undirected(mode="collapse",
combine_edges={"weight": "sum"})
# Simplify graph
g_simple = g.simplify(multiple=True, loops=True)
print(f"Simplified edges: {g_simple.ecount()}") # 3 (removed duplicates and loop)
# Copy for safe manipulation
g_copy = g.copy()
g_copy.delete_vertices([0]) # Original g unchangedExtract portions of graphs based on vertex or edge selections.
class Graph:
def subgraph(self, vertices, implementation="auto"):
"""
Create induced subgraph from vertex subset.
Parameters:
- vertices: list/VertexSeq, vertices to include in subgraph
- implementation: str, algorithm choice ("auto", "copy_and_delete", "create_from_scratch")
Returns:
Graph, induced subgraph
"""
def subgraph_edges(self, edges, delete_vertices=True):
"""
Create subgraph from edge subset.
Parameters:
- edges: list/EdgeSeq, edges to include
- delete_vertices: bool, remove isolated vertices
Returns:
Graph, edge-induced subgraph
"""
def induced_subgraph(self, vertices, implementation="auto"):
"""
Alias for subgraph() method.
Parameters:
- vertices: vertex selection
- implementation: algorithm choice
Returns:
Graph, induced subgraph
"""Usage Examples:
# Create larger graph
g = ig.Graph.Ring(10) # 10-vertex cycle
g.vs["name"] = [f"v{i}" for i in range(10)]
# Induced subgraph from vertices
subverts = [0, 1, 2, 5, 6]
sg1 = g.subgraph(subverts)
print(f"Subgraph vertices: {sg1.vcount()}") # 5
print(f"Subgraph edges: {sg1.ecount()}") # 2 (only edges within subset)
# Subgraph from edges
subedges = [0, 1, 4, 5, 8] # Select specific edges
sg2 = g.subgraph_edges(subedges)
# Use vertex selection with names
sg3 = g.subgraph(["v0", "v1", "v9"]) # vertices 0, 1, 9Query and analyze vertex degrees and degree-related properties.
class Graph:
def degree(self, vertices=None, mode=ALL, loops=True):
"""
Get vertex degrees.
Parameters:
- vertices: list/VertexSeq, vertices to query (None for all)
- mode: degree type (ALL, IN, OUT for directed graphs)
- loops: bool, count self-loops
Returns:
list of int, vertex degrees
"""
def strength(self, vertices=None, mode=ALL, loops=True, weights=None):
"""
Get vertex strengths (weighted degrees).
Parameters:
- vertices: vertex selection (None for all)
- mode: degree type (ALL, IN, OUT)
- loops: bool, count self-loops
- weights: str/list, edge weights or attribute name
Returns:
list of float, vertex strengths
"""
def maxdegree(self, vertices=None, mode=ALL, loops=True):
"""
Get maximum degree in graph.
Parameters:
- vertices: vertex selection for max calculation
- mode: degree type (ALL, IN, OUT)
- loops: bool, count self-loops
Returns:
int, maximum degree
"""
def degree_sequence(self, mode=ALL):
"""
Get sorted degree sequence.
Parameters:
- mode: degree type (ALL, IN, OUT)
Returns:
list of int, degrees in descending order
"""Usage Examples:
# Create weighted graph
g = ig.Graph([(0,1), (1,2), (2,0), (1,3)], directed=False)
g.es["weight"] = [1, 2, 3, 4]
# Get degrees
degrees = g.degree()
print("Degrees:", degrees) # [2, 3, 2, 1]
# Get strengths (weighted degrees)
strengths = g.strength(weights="weight")
print("Strengths:", strengths) # [4, 7, 5, 4]
# Maximum degree
max_deg = g.maxdegree()
print("Max degree:", max_deg) # 3
# Degree sequence
deg_seq = g.degree_sequence()
print("Degree sequence:", deg_seq) # [3, 2, 2, 1]
# For directed graphs
g_dir = g.as_directed()
in_degrees = g_dir.degree(mode=ig.IN)
out_degrees = g_dir.degree(mode=ig.OUT)Install with Tessl CLI
npx tessl i tessl/pypi-igraph