Basic Usage

This guide covers the fundamental operations in Graphizy, from creating your first graph to performing basic analysis. By the end, you’ll understand all the core features and be ready for advanced applications.

The Graphing Class

The Graphing class is your main interface to Graphizy. It creates graphs, performs analysis, and handles visualization.

Basic Initialization:

from graphizy import Graphing, GraphizyConfig

# Simple initialization
grapher = Graphing(dimension=(800, 600))

# With custom configuration
config = GraphizyConfig(dimension=(800, 600))
grapher = Graphing(config=config)

Configuration Options:

from graphizy import GraphizyConfig

# Create detailed configuration
config = GraphizyConfig(
    dimension=(800, 600),
    drawing={
        "point_color": (255, 100, 100),  # Red points
        "line_color": (100, 100, 255),   # Blue lines
        "point_radius": 6,
        "line_thickness": 2
    }
)

grapher = Graphing(config=config)

Creating Graphs

Graphizy uses a unified make_graph() interface for all graph types:

Unified Interface:

from graphizy import generate_and_format_positions

# Generate sample data
data = generate_and_format_positions(800, 600, 100)

# Create different graph types
proximity_graph = grapher.make_graph("proximity", data, proximity_thresh=50.0)
delaunay_graph = grapher.make_graph("delaunay", data)
knn_graph = grapher.make_graph("knn", data, k=4)
mst_graph = grapher.make_graph("mst", data)
gabriel_graph = grapher.make_graph("gabriel", data)

Graph Type Details:

Proximity Graph

Connects points within a specified distance:

# Connect points within 60 units
graph = grapher.make_graph("proximity", data, proximity_thresh=60.0)

# Different distance metrics
graph = grapher.make_graph("proximity", data,
                          proximity_thresh=60.0,
                          metric="euclidean")  # default
graph = grapher.make_graph("proximity", data,
                          proximity_thresh=60.0,
                          metric="manhattan")
Delaunay Triangulation

Creates optimal triangular mesh:

# No additional parameters needed
delaunay_graph = grapher.make_graph("delaunay", data)
K-Nearest Neighbors

Connects each point to its k closest neighbors:

# Each point connects to 4 nearest neighbors
knn_graph = grapher.make_graph("knn", data, k=4)

# Different k values change connectivity
sparse_graph = grapher.make_graph("knn", data, k=2)    # Sparse
dense_graph = grapher.make_graph("knn", data, k=8)     # Dense
Minimum Spanning Tree

Creates minimal connected graph:

# Minimal edges to connect all points
mst_graph = grapher.make_graph("mst", data)
Gabriel Graph

Geometric proximity graph (subset of Delaunay):

# Geometric connectivity rules
gabriel_graph = grapher.make_graph("gabriel", data)

Data Formats

Graphizy expects data in a simple 3-column format:

Standard Format:

import numpy as np

# Manual data creation
data = np.array([
    [0, 100, 150],  # Point ID=0 at (100, 150)
    [1, 200, 200],  # Point ID=1 at (200, 200)
    [2, 150, 100],  # Point ID=2 at (150, 100)
    [3, 250, 180],  # Point ID=3 at (250, 180)
])

Data Generation Helpers:

from graphizy import generate_and_format_positions, generate_positions

# Generate random points (recommended)
data = generate_and_format_positions(
    size_x=800,        # Canvas width
    size_y=600,        # Canvas height
    num_particles=50   # Number of points
)

# Just positions (you add IDs)
positions = generate_positions(800, 600, 50)
data = np.column_stack((np.arange(50), positions))

Data Validation:

from graphizy import validate_graphizy_input

# Check your data format
is_valid = validate_graphizy_input(data, verbose=True)

if not is_valid:
    print("Data format issues detected!")

Graph Analysis

The new GraphAnalysisResult provides comprehensive analysis:

Basic Analysis:

# Get analysis result
result = grapher.get_graph_info(graph)

# Basic properties (computed lazily)
print(f"Vertices: {result.vertex_count}")
print(f"Edges: {result.edge_count}")
print(f"Density: {result.density:.3f}")
print(f"Connected: {result.is_connected}")
print(f"Components: {result.num_components}")

# Advanced properties (when available)
if result.transitivity is not None:
    print(f"Clustering: {result.transitivity:.3f}")

if result.diameter is not None:
    print(f"Diameter: {result.diameter}")

Summary Report:

# Get formatted summary
print(result.summary())

# Output:
# Graph Analysis Summary:
#   - Vertices: 100
#   - Edges: 245
#   - Density: 0.0495
#   - Connected: True
#   - Clustering (Transitivity): 0.3421

Top Nodes Analysis:

# Find most central nodes
top_degree = result.get_top_n_by('degree', n=5)
top_betweenness = result.get_top_n_by('betweenness', n=5)

print("Top 5 most connected nodes:")
for node_id, degree in top_degree:
    print(f"  Node {node_id}: {degree} connections")

print("Top 5 bridge nodes:")
for node_id, betweenness in top_betweenness:
    print(f"  Node {node_id}: {betweenness:.3f}")

Statistical Analysis:

# Get statistics for any metric
degree_stats = result.get_metric_stats('degree')
betweenness_stats = result.get_metric_stats('betweenness')

print("Degree Distribution:")
print(f"  Mean: {degree_stats['mean']:.2f}")
print(f"  Std: {degree_stats['std']:.2f}")
print(f"  Range: {degree_stats['min']:.0f} - {degree_stats['max']:.0f}")

Advanced Analysis Tools

Graphizy now includes research-grade analysis tools:

Percolation Analysis:

# Analyze critical thresholds
ranges = [20, 30, 40, 50, 60, 70, 80]

percolation_result = result.percolation_analyzer.analyze_percolation_threshold(
    data, ranges
)

print(f"Critical threshold: {percolation_result.critical_range}")
print(f"Max cluster size: {max(percolation_result.largest_cluster_sizes)}")

# Detect phase transition
transition = result.percolation_analyzer.detect_phase_transition(percolation_result)

if transition['has_transition']:
    print(f"Phase transition at: {transition['transition_range']}")
    print(f"Transition sharpness: {transition['transition_sharpness']:.3f}")

Social Network Analysis:

# Identify social roles
social_roles = result.social_analyzer.identify_social_roles(graph)

# Find different role types
bridges = [node_id for node_id, role in social_roles.items() if role.is_bridge()]
hubs = [node_id for node_id, role in social_roles.items() if role.is_hub()]
peripheral = [node_id for node_id, role in social_roles.items() if role.is_peripheral()]

print(f"Bridge nodes (connectors): {bridges}")
print(f"Hub nodes (popular): {hubs}")
print(f"Peripheral nodes: {peripheral}")

# Analyze specific roles
for node_id, role in list(social_roles.items())[:5]:
    print(f"Node {node_id}: {role.roles}")
    print(f"  Betweenness: {role.stats['betweenness']:.3f}")
    print(f"  Degree: {role.stats['degree']}")

Accessibility Analysis:

# For spatial applications (urban planning, etc.)
population_data = data  # Your population points
service_data = service_locations  # Your service points

accessibility = result.accessibility_analyzer.analyze_service_accessibility(
    population_data,
    service_data,
    service_type="hospital",
    service_distance=500.0  # 500m walking distance
)

print(f"Coverage: {accessibility.get_coverage_percentage():.1f}%")
print(f"Equity score: {accessibility.get_equity_score():.3f}")
print(f"Underserved areas: {len(accessibility.underserved_areas)}")

# Identify service gaps
gaps = result.accessibility_analyzer.identify_service_gaps(accessibility)
print(f"Service gaps: {len(gaps)}")

Visualization

Create beautiful visualizations of your networks:

Basic Visualization:

# Draw the graph
image = grapher.draw_graph(graph)

# Display on screen
grapher.show_graph(image, "My Network")

# Save to file
grapher.save_graph(image, "network.png")

Custom Styling:

# Update drawing configuration
grapher.update_config(drawing={
    "point_color": (255, 100, 100),  # Red points (BGR format)
    "line_color": (100, 255, 100),   # Green lines
    "point_radius": 8,               # Larger points
    "line_thickness": 2              # Thicker lines
})

# Draw with new style
styled_image = grapher.draw_graph(graph)
grapher.save_graph(styled_image, "styled_network.png")

Multiple Visualizations:

# Compare different graph types visually
graph_types = {
    'Proximity': grapher.make_graph("proximity", data, proximity_thresh=50.0),
    'Delaunay': grapher.make_graph("delaunay", data),
    'MST': grapher.make_graph("mst", data)
}

for name, graph in graph_types.items():
    image = grapher.draw_graph(graph)
    grapher.save_graph(image, f"{name.lower()}_graph.png")
    print(f"Saved {name} visualization")

Memory System (Temporal Analysis)

Track how networks evolve over time:

Basic Memory Setup:

# Initialize memory system
grapher.init_memory_manager(
    max_memory_size=50,      # Remember last 50 connections
    track_edge_ages=True     # Track connection ages
)

Temporal Evolution:

# Simulate network evolution
original_data = data.copy()

for timestep in range(30):
    # Simulate movement (your own function)
    data = simulate_movement(data, timestep)

    # Create current network
    current_graph = grapher.make_graph("proximity", data, proximity_thresh=60.0)

    # Update memory with current connections
    grapher.update_memory_with_graph(current_graph)

    # Analyze memory periodically
    if timestep % 10 == 0:
        memory_stats = grapher.get_memory_stats()
        print(f"Timestep {timestep}: {memory_stats['total_connections']} total connections")

Memory Visualization:

# Create memory-enhanced graph
memory_graph = grapher.make_memory_graph(data)

# Visualize with age-based coloring
memory_image = grapher.draw_memory_graph(
    memory_graph,
    use_age_colors=True,      # Color by connection age
    alpha_range=(0.3, 1.0)   # Fade old connections
)

grapher.save_graph(memory_image, "memory_network.png")

Memory Analysis:

# Get detailed memory statistics
memory_stats = grapher.get_memory_stats()

print("Memory Statistics:")
print(f"  Total connections tracked: {memory_stats['total_connections']}")
print(f"  Active connections: {memory_stats['active_connections']}")

# Get edge age information
if hasattr(grapher.memory_manager, 'get_edge_ages'):
    edge_ages = grapher.memory_manager.get_edge_ages()

    # Find most persistent connections
    persistent = []
    for edge, age_info in edge_ages.items():
        duration = age_info['last_seen'] - age_info['first_seen']
        persistent.append((edge, duration))

    # Sort by persistence
    persistent.sort(key=lambda x: x[1], reverse=True)

    print("Most persistent connections:")
    for (node1, node2), duration in persistent[:5]:
        print(f"  {node1} <-> {node2}: {duration} timesteps")

Performance Tips

For Large Datasets:

# Use efficient graph types for large data
large_data = generate_and_format_positions(1000, 1000, 500)

# MST is efficient for large datasets
mst_graph = grapher.make_graph("mst", large_data)

# Use reasonable proximity thresholds
prox_graph = grapher.make_graph("proximity", large_data, proximity_thresh=30.0)

# Smaller k for KNN
knn_graph = grapher.make_graph("knn", large_data, k=4)

Memory Optimization:

# For large temporal datasets
grapher.init_memory_manager(
    max_memory_size=20,       # Smaller memory
    track_edge_ages=False     # Disable for performance
)

Batch Processing:

# Process multiple datasets efficiently
datasets = [generate_and_format_positions(400, 300, n) for n in [20, 50, 100]]

results = []
for i, data in enumerate(datasets):
    graph = grapher.make_graph("proximity", data, proximity_thresh=50.0)
    result = grapher.get_graph_info(graph)

    results.append({
        'dataset': i,
        'vertices': result.vertex_count,
        'edges': result.edge_count,
        'density': result.density
    })

Common Workflows

Research Analysis Pipeline:

def analyze_network(data, graph_type="proximity", **kwargs):
    """Complete analysis pipeline"""

    # 1. Create graph
    graph = grapher.make_graph(graph_type, data, **kwargs)

    # 2. Basic analysis
    result = grapher.get_graph_info(graph)

    # 3. Advanced analysis
    percolation = None
    social_roles = None

    if graph_type == "proximity":
        # Percolation analysis for proximity graphs
        ranges = [kwargs.get('proximity_thresh', 50) * factor
                 for factor in [0.5, 0.75, 1.0, 1.25, 1.5]]
        percolation = result.percolation_analyzer.analyze_percolation_threshold(data, ranges)

    # Social analysis for all graph types
    social_roles = result.social_analyzer.identify_social_roles(graph)

    # 4. Visualization
    image = grapher.draw_graph(graph)

    return {
        'graph': graph,
        'analysis': result,
        'percolation': percolation,
        'social_roles': social_roles,
        'image': image
    }

# Use the pipeline
analysis = analyze_network(data, "proximity", proximity_thresh=60.0)

print(f"Network has {analysis['analysis'].vertex_count} nodes")
print(f"Found {len(analysis['social_roles'])} nodes with roles")

Comparative Analysis:

def compare_graph_types(data):
    """Compare different graph types on same data"""

    graph_configs = [
        ("proximity", {"proximity_thresh": 50.0}),
        ("delaunay", {}),
        ("mst", {}),
        ("knn", {"k": 4})
    ]

    results = {}

    for graph_type, params in graph_configs:
        try:
            graph = grapher.make_graph(graph_type, data, **params)
            analysis = grapher.get_graph_info(graph)

            results[graph_type] = {
                'vertices': analysis.vertex_count,
                'edges': analysis.edge_count,
                'density': analysis.density,
                'connected': analysis.is_connected,
                'clustering': analysis.transitivity
            }

            # Save visualization
            image = grapher.draw_graph(graph)
            grapher.save_graph(image, f"comparison_{graph_type}.png")

        except Exception as e:
            print(f"Failed to create {graph_type}: {e}")
            results[graph_type] = None

    return results

# Compare graph types
comparison = compare_graph_types(data)

# Print comparison table
print(f"{'Type':<12} {'Edges':<8} {'Density':<10} {'Connected':<10} {'Clustering':<10}")
print("-" * 60)

for graph_type, stats in comparison.items():
    if stats:
        clustering = stats['clustering']
        clustering_str = f"{clustering:.3f}" if clustering is not None else "N/A"
        print(f"{graph_type:<12} {stats['edges']:<8} {stats['density']:<10.3f} "
              f"{str(stats['connected']):<10} {clustering_str:<10}")

Error Handling

Handle common issues gracefully:

from graphizy import GraphCreationError, InvalidDataShapeError

try:
    # Validate data first
    is_valid = validate_graphizy_input(data, verbose=True)

    if not is_valid:
        print("Data validation failed")
        return

    # Create graph
    graph = grapher.make_graph("proximity", data, proximity_thresh=50.0)

    # Analyze graph
    result = grapher.get_graph_info(graph)

except InvalidDataShapeError as e:
    print(f"Data format error: {e}")
except GraphCreationError as e:
    print(f"Graph creation failed: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")

Handling Empty Graphs:

# Check for empty graphs
if graph.vcount() == 0:
    print("Warning: Graph has no vertices")
    return

if graph.ecount() == 0:
    print("Warning: Graph has no edges (isolated points)")

# Safe analysis
result = grapher.get_graph_info(graph)

if result.vertex_count > 0:
    print(f"Graph analysis successful: {result.vertex_count} vertices")
else:
    print("Cannot analyze empty graph")

Integration with Other Libraries

NetworkX Integration:

# Export to NetworkX for advanced analysis
try:
    import networkx as nx

    # Convert Graphizy graph to NetworkX
    nx_graph = grapher.to_networkx(graph)

    # Use NetworkX algorithms
    communities = nx.community.greedy_modularity_communities(nx_graph)
    centrality = nx.betweenness_centrality(nx_graph)

    print(f"Found {len(communities)} communities")
    print(f"Average betweenness: {sum(centrality.values()) / len(centrality):.3f}")

except ImportError:
    print("NetworkX not available")

Pandas Integration:

# Export analysis results to DataFrame
try:
    import pandas as pd

    # Convert graph analysis to DataFrame
    analysis_data = []

    for node_id in range(result.vertex_count):
        node_data = {
            'node_id': node_id,
            'degree': graph.degree(node_id),
            'betweenness': graph.betweenness(vertices=[node_id])[0]
        }
        analysis_data.append(node_data)

    df = pd.DataFrame(analysis_data)
    print(df.describe())

    # Save to CSV
    df.to_csv('network_analysis.csv', index=False)

except ImportError:
    print("Pandas not available")

Next Steps

Now that you understand the basics, explore these advanced topics:

  1. Advanced Analysis: Learn the research-grade analysis tools

  2. Memory Systems: Master temporal network analysis

  3. Weight Computation: Add sophisticated edge weights

  4. Plugin System: Create custom graph types

  5. Research Applications: Use domain-specific tutorials

  6. Performance Optimization: Handle large datasets efficiently

Recommended Learning Path:

  1. Master basic graph creation and analysis

  2. Experiment with different graph types

  3. Learn the advanced analysis tools

  4. Explore memory systems for temporal data

  5. Try the research tutorials for your domain

  6. Develop custom analysis pipelines

You now have a solid foundation in Graphizy’s core functionality and are ready to tackle advanced spatial network analysis!