High performance graph data structures and algorithms for Python
—
Efficient sequence classes for accessing and manipulating collections of vertices and edges with filtering and attribute operations. These classes provide powerful query and manipulation capabilities for graph elements.
Collections of vertices with advanced filtering, selection, and attribute manipulation methods.
class VertexSeq:
def __init__(self, graph, vertices=None):
"""
Create vertex sequence.
Parameters:
- graph: Graph object
- vertices: list/range, vertex indices (None for all vertices)
"""
def __len__(self):
"""Get number of vertices in sequence."""
def __getitem__(self, key):
"""
Access vertices by index or slice.
Parameters:
- key: int/slice/str, vertex index, slice, or attribute name
Returns:
Vertex or VertexSeq or attribute values
"""
def select(self, *args, **kwargs):
"""
Filter vertices by criteria.
Parameters:
- *args: vertex indices or boolean conditions
- **kwargs: attribute-based filters
Returns:
VertexSeq, filtered vertices
"""
def find(self, *args, **kwargs):
"""
Find first vertex matching criteria.
Parameters:
- *args: search criteria
- **kwargs: attribute filters
Returns:
Vertex, first match
"""
def attributes(self):
"""
Get list of vertex attribute names.
Returns:
list of str, attribute names
"""
@property
def indices(self):
"""
Get vertex indices.
Returns:
list of int, vertex indices in sequence
"""Usage Examples:
import igraph as ig
# Create sample graph with attributes
g = ig.Graph([(0,1), (1,2), (2,0), (1,3), (3,4)], directed=False)
g.vs["name"] = ["Alice", "Bob", "Carol", "Dave", "Eve"]
g.vs["age"] = [25, 30, 28, 35, 22]
g.vs["city"] = ["NYC", "LA", "NYC", "LA", "Chicago"]
# Access all vertices
all_vertices = g.vs
print(f"Total vertices: {len(all_vertices)}")
# Access by index
vertex_0 = g.vs[0]
print(f"Vertex 0: {vertex_0}")
# Access by slice
first_three = g.vs[0:3]
print(f"First three vertices: {first_three.indices}")
# Access attributes
names = g.vs["name"]
ages = g.vs["age"]
print(f"Names: {names}")
print(f"Ages: {ages}")
# Get available attributes
print(f"Available attributes: {g.vs.attributes()}")Advanced filtering capabilities using attribute values, graph properties, and custom criteria.
class VertexSeq:
def select(self, *args, **kwargs):
"""
Advanced vertex filtering.
Selection methods:
- By index: vs.select(0, 2, 4)
- By range: vs.select(range(5, 10))
- By attribute value: vs.select(name="Alice")
- By attribute condition: vs.select(age_gt=30)
- By degree: vs.select(_degree_gt=5)
- By boolean list: vs.select([True, False, True, ...])
- By lambda: vs.select(lambda v: v.degree() > 2)
Attribute conditions:
- attr_eq, attr_ne: equal, not equal
- attr_lt, attr_le: less than, less or equal
- attr_gt, attr_ge: greater than, greater or equal
- attr_in, attr_notin: in list, not in list
Graph property conditions:
- _degree, _indegree, _outdegree: vertex degrees
- _between: vertices between given vertices
Returns:
VertexSeq, filtered vertices
"""Usage Examples:
# Select by attribute values
nyc_vertices = g.vs.select(city="NYC")
print(f"NYC vertices: {nyc_vertices.indices}")
# Select by attribute conditions
older_vertices = g.vs.select(age_gt=27)
young_vertices = g.vs.select(age_le=25)
print(f"Older than 27: {older_vertices['name']}")
print(f"25 or younger: {young_vertices['name']}")
# Select by multiple conditions
nyc_older = g.vs.select(city="NYC", age_gt=27)
print(f"NYC and older than 27: {nyc_older['name']}")
# Select by degree
high_degree = g.vs.select(_degree_gt=1)
print(f"High degree vertices: {high_degree['name']}")
# Select by index list
specific_vertices = g.vs.select([0, 2, 4])
print(f"Specific vertices: {specific_vertices['name']}")
# Select with lambda function
lambda_selection = g.vs.select(lambda v: v["age"] < 30 and v.degree() >= 2)
print(f"Lambda selection: {lambda_selection['name']}")
# Select by attribute list membership
la_chicago = g.vs.select(city_in=["LA", "Chicago"])
print(f"LA or Chicago: {la_chicago['name']}")
# Exclude vertices
not_bob = g.vs.select(name_ne="Bob")
print(f"Not Bob: {not_bob['name']}")Collections of edges with specialized filtering for network topology analysis.
class EdgeSeq:
def __init__(self, graph, edges=None):
"""
Create edge sequence.
Parameters:
- graph: Graph object
- edges: list/range, edge indices (None for all edges)
"""
def select(self, *args, **kwargs):
"""
Filter edges by criteria.
Special edge filters:
- _source, _target: source/target vertex conditions
- _between: edges between vertex sets
- _within: edges within vertex set
- _incident: edges incident to vertices
Returns:
EdgeSeq, filtered edges
"""
def find(self, *args, **kwargs):
"""
Find first edge matching criteria.
Returns:
Edge, first match
"""
def attributes(self):
"""
Get edge attribute names.
Returns:
list of str, attribute names
"""
@property
def indices(self):
"""
Get edge indices.
Returns:
list of int, edge indices
"""Usage Examples:
# Add edge attributes
g.es["weight"] = [1.0, 2.5, 1.8, 0.9, 3.2]
g.es["type"] = ["friend", "work", "family", "friend", "work"]
# Access all edges
all_edges = g.es
print(f"Total edges: {len(all_edges)}")
# Access edge attributes
weights = g.es["weight"]
types = g.es["type"]
print(f"Edge weights: {weights}")
print(f"Edge types: {types}")
# Select by attribute
friend_edges = g.es.select(type="friend")
heavy_edges = g.es.select(weight_gt=2.0)
print(f"Friend edges: {friend_edges.indices}")
print(f"Heavy edges (>2.0): {heavy_edges.indices}")
# Select by source/target vertices
from_alice = g.es.select(_source=0) # Alice is vertex 0
to_bob = g.es.select(_target=1) # Bob is vertex 1
print(f"Edges from Alice: {from_alice.indices}")
print(f"Edges to Bob: {to_bob.indices}")
# Select edges between specific vertices
alice_bob = g.es.select(_between=([0], [1]))
print(f"Edges between Alice and Bob: {alice_bob.indices}")
# Select edges within a set (internal edges)
core_vertices = [0, 1, 2] # Alice, Bob, Carol
internal_edges = g.es.select(_within=core_vertices)
print(f"Internal edges in core: {internal_edges.indices}")
# Select edges incident to vertices
incident_to_bob = g.es.select(_incident=1)
print(f"Edges incident to Bob: {incident_to_bob.indices}")Complex edge filtering patterns for network analysis.
Usage Examples:
# Multiple source/target conditions
multi_source = g.es.select(_source_in=[0, 1]) # From Alice or Bob
multi_target = g.es.select(_target_in=[2, 3]) # To Carol or Dave
print(f"From Alice/Bob: {multi_source.indices}")
print(f"To Carol/Dave: {multi_target.indices}")
# Edges between two sets of vertices
group1 = [0, 1] # Alice, Bob
group2 = [2, 3, 4] # Carol, Dave, Eve
between_groups = g.es.select(_between=(group1, group2))
print(f"Between groups: {between_groups.indices}")
# Combine topological and attribute filters
heavy_friend_edges = g.es.select(type="friend", weight_gt=1.5)
print(f"Heavy friendship edges: {heavy_friend_edges.indices}")
# Edges involving high-degree vertices
high_deg_vertices = g.vs.select(_degree_gt=1).indices
edges_with_hubs = g.es.select(_incident_in=high_deg_vertices)
print(f"Edges involving hubs: {edges_with_hubs.indices}")
# Complex lambda selection
complex_edges = g.es.select(lambda e: e["weight"] > 1.0 and
g.vs[e.source]["age"] + g.vs[e.target]["age"] > 55)
print(f"Complex selection: {complex_edges.indices}")Efficient batch operations on vertex and edge attributes.
class VertexSeq:
def __setitem__(self, attr, values):
"""
Set attribute values for vertices in sequence.
Parameters:
- attr: str, attribute name
- values: list/value, attribute values
"""
def __delitem__(self, attr):
"""
Delete attribute from vertices.
Parameters:
- attr: str, attribute name to delete
"""
class EdgeSeq:
def __setitem__(self, attr, values):
"""Set attribute values for edges."""
def __delitem__(self, attr):
"""Delete attribute from edges."""Usage Examples:
# Set new attributes
g.vs["degree"] = g.degree()
g.vs["normalized_age"] = [age/max(g.vs["age"]) for age in g.vs["age"]]
# Set attributes for filtered vertices
nyc_vertices = g.vs.select(city="NYC")
nyc_vertices["region"] = "East Coast"
high_degree = g.vs.select(_degree_gt=1)
high_degree["is_hub"] = True
# Set edge attributes
g.es["normalized_weight"] = [w/max(g.es["weight"]) for w in g.es["weight"]]
# Conditional attribute setting
for v in g.vs:
if v["age"] >= 30:
v["generation"] = "Millennial"
else:
v["generation"] = "Gen Z"
# Batch attribute operations
young_vertices = g.vs.select(age_lt=30)
young_vertices["category"] = "Young Adult"
# Delete attributes
del g.vs["normalized_age"] # Remove from all vertices
del nyc_vertices["region"] # Remove from filtered set
# Get attribute statistics
ages = g.vs["age"]
print(f"Age stats - Min: {min(ages)}, Max: {max(ages)}, Avg: {sum(ages)/len(ages):.1f}")
weights = g.es["weight"]
print(f"Weight stats - Min: {min(weights)}, Max: {max(weights)}, Avg: {sum(weights)/len(weights):.1f}")Mathematical and set operations on vertex and edge sequences.
class VertexSeq:
def union(self, other):
"""Union with another vertex sequence."""
def intersection(self, other):
"""Intersection with another vertex sequence."""
def difference(self, other):
"""Difference from another vertex sequence."""
# Similar operations available for EdgeSeqUsage Examples:
# Set operations on vertex sequences
nyc_vertices = g.vs.select(city="NYC")
young_vertices = g.vs.select(age_lt=30)
high_degree = g.vs.select(_degree_gt=1)
# Union (vertices in either set)
nyc_or_young = nyc_vertices.union(young_vertices)
print(f"NYC or young: {nyc_or_young['name']}")
# Intersection (vertices in both sets)
nyc_and_young = nyc_vertices.intersection(young_vertices)
print(f"NYC and young: {nyc_and_young['name']}")
# Difference (vertices in first set but not second)
nyc_not_young = nyc_vertices.difference(young_vertices)
print(f"NYC but not young: {nyc_not_young['name']}")
# Chain operations
complex_selection = nyc_vertices.union(high_degree).intersection(young_vertices)
print(f"Complex selection: {complex_selection['name']}")
# Edge sequence operations
friend_edges = g.es.select(type="friend")
heavy_edges = g.es.select(weight_gt=2.0)
friend_or_heavy = friend_edges.union(heavy_edges)
friend_and_heavy = friend_edges.intersection(heavy_edges)
print(f"Friend or heavy edges: {friend_or_heavy.indices}")
print(f"Heavy friend edges: {friend_and_heavy.indices}")Efficient iteration patterns and list comprehensions with sequences.
Usage Examples:
# Iterate over vertices
for vertex in g.vs:
print(f"Vertex {vertex.index}: {vertex['name']}, degree: {vertex.degree()}")
# Iterate over filtered vertices
for vertex in g.vs.select(age_gt=28):
print(f"Older vertex: {vertex['name']} (age {vertex['age']})")
# List comprehensions with sequences
vertex_info = [(v['name'], v['age'], v.degree()) for v in g.vs]
print("Vertex info:", vertex_info)
# Edge iteration
for edge in g.es:
source_name = g.vs[edge.source]['name']
target_name = g.vs[edge.target]['name']
print(f"Edge: {source_name} -- {target_name} ({edge['type']}, {edge['weight']})")
# Filtered edge iteration
for edge in g.es.select(weight_gt=1.5):
print(f"Heavy edge: {edge.source} -- {edge.target}")
# Create derived attributes using comprehensions
g.vs["name_length"] = [len(name) for name in g.vs["name"]]
g.es["weight_category"] = ["light" if w < 2.0 else "heavy" for w in g.es["weight"]]
# Complex comprehensions
g.vs["connection_strength"] = [
sum(g.es.select(_incident=v.index)["weight"])
for v in g.vs
]
# Neighborhood analysis
for vertex in g.vs.select(_degree_gt=2):
neighbors = [g.vs[n]['name'] for n in g.neighbors(vertex.index)]
print(f"{vertex['name']} is connected to: {neighbors}")Efficient patterns for working with large graphs and sequences.
Usage Examples:
# Efficient attribute access patterns
def analyze_large_graph(graph):
"""Efficient analysis of large graphs using sequences."""
# Pre-compute commonly used attributes
degrees = graph.degree()
betweenness = graph.betweenness()
# Batch attribute setting (more efficient than individual)
graph.vs["degree"] = degrees
graph.vs["betweenness"] = betweenness
graph.vs["normalized_betw"] = [b/max(betweenness) for b in betweenness]
# Use indices for fast filtering
high_degree_indices = [i for i, d in enumerate(degrees) if d > 10]
high_degree_vs = graph.vs[high_degree_indices]
# Efficient edge filtering
important_edges = graph.es.select(_source_in=high_degree_indices,
_target_in=high_degree_indices)
return high_degree_vs, important_edges
# Memory-efficient iteration for very large graphs
def process_vertices_in_chunks(graph, chunk_size=1000):
"""Process vertices in chunks to manage memory."""
n_vertices = graph.vcount()
for start in range(0, n_vertices, chunk_size):
end = min(start + chunk_size, n_vertices)
chunk = graph.vs[start:end]
# Process chunk
for vertex in chunk:
# Do processing here
pass
print(f"Processed vertices {start} to {end-1}")
# Efficient neighbor analysis
def analyze_neighborhoods(graph):
"""Efficient neighborhood analysis."""
# Pre-compute all neighborhoods
all_neighbors = [set(graph.neighbors(i)) for i in range(graph.vcount())]
# Analyze without repeated graph queries
for i, vertex in enumerate(graph.vs):
neighbor_ages = [graph.vs[n]["age"] for n in all_neighbors[i]]
vertex["avg_neighbor_age"] = sum(neighbor_ages) / len(neighbor_ages) if neighbor_ages else 0
# Usage for large graphs
if g.vcount() > 1000:
high_deg, important_edges = analyze_large_graph(g)
process_vertices_in_chunks(g, chunk_size=500)
else:
# Use simpler patterns for small graphs
analyze_neighborhoods(g)Install with Tessl CLI
npx tessl i tessl/pypi-igraph