Tutorial 4: Visualizing Evolving Communities

This tutorial demonstrates how to create effective visualizations for understanding and analyzing evolving communities in temporal networks.

Prerequisites

# Install the package if you haven't already
# pip install 'dyn-benchmark[all]'

# Import required modules
from dyn.benchmark.generator.groundtruth_generator import GroundtruthGenerator
from dyn.core.communities import Membership
from dyn.drawing.sankey_drawing import plot_sankey

1. Visualizing Community Flow with Sankey Diagrams

First, let’s create a benchmark to visualize:

# Create a generator with parameters to create interesting community evolution
generator = GroundtruthGenerator(seed=42)
groundtruth = generator.generate()

# Extract membership information
membership = Membership.from_tcommlist(groundtruth.tcommlist)

Sankey diagrams are ideal for visualizing how communities evolve over time:

# Create a Sankey diagram of community evolution
plot_sankey(membership.community_graph)

Let’s understand the key components of this visualization:

# Extract and print information about the community flow
for t in sorted(community_flow_graph.snapshots):
    communities_at_t = list(community_flow_graph.snapshot_nodes(t))
    print(f"Snapshot {t}: {len(communities_at_t)} communities")

    for community in communities_at_t:
        # Get predecessors (sources of incoming members)
        predecessors = list(community_flow_graph.predecessors(community))

        # Get successors (destinations of outgoing members)
        successors = list(community_flow_graph.successors(community))

        # Get size of the community
        size = community_flow_graph.node_size(community)

        print(f"  Community {community}: Size={size}, Predecessors={len(predecessors)}, Successors={len(successors)}")

Complete Example

# Install the package if you haven't already
# pip install 'dyn-benchmark[all]'

# Import required modules
from dyn.benchmark.generator.groundtruth_generator import GroundtruthGenerator
from dyn.core.communities import Membership
from dyn.drawing.sankey_drawing import plot_sankey

# Create a generator with parameters to create interesting community evolution
generator = GroundtruthGenerator(seed=42)
groundtruth = generator.generate()

# Extract membership information
membership = Membership.from_tcommlist(groundtruth.tcommlist)

# Create a Sankey diagram of community evolution
plot_sankey(membership.community_graph)

# Extract and print information about the community flow
for t in sorted(community_flow_graph.snapshots):
    communities_at_t = list(community_flow_graph.snapshot_nodes(t))
    print(f"Snapshot {t}: {len(communities_at_t)} communities")

    for community in communities_at_t:
        # Get predecessors (sources of incoming members)
        predecessors = list(community_flow_graph.predecessors(community))

        # Get successors (destinations of outgoing members)
        successors = list(community_flow_graph.successors(community))

        # Get size of the community
        size = community_flow_graph.node_size(community)

        print(f"  Community {community}: Size={size}, Predecessors={len(predecessors)}, Successors={len(successors)}")

2. Visualizing Network Snapshots with Communities

Let’s visualize the network structure at each snapshot, with nodes colored by community:

# Import required modules
from dyn.benchmark.generator.groundtruth_generator import GroundtruthGenerator
from dyn.core.communities import Membership
from dyn.drawing.sankey_drawing import plot_sankey

import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import os


def plot_network_with_communities(graph, snapshot, membership, figsize=(10, 8)):
    """Plot a network with nodes colored by community"""
    plt.figure(figsize=figsize)

    # Get community assignments for this snapshot
    communities = {}
    for row in membership.tcommlist:
        if row.snapshot == snapshot:
            communities[row.node_id] = row.evolving_community_id

    # Create a color map for communities
    unique_communities = set(communities.values())
    colors = plt.cm.tab20(np.linspace(0, 1, len(unique_communities)))
    color_map = {com: colors[i % len(colors)] for i, com in enumerate(unique_communities)}

    # Assign colors to nodes
    node_colors = [color_map.get(communities.get(node, -1), 'gray') for node in graph.nodes]

    # Compute layout (with seed for reproducibility)
    pos = nx.spring_layout(graph, seed=42)

    # Draw the network
    nx.draw_networkx(
        graph,
        pos=pos,
        node_color=node_colors,
        node_size=80,
        with_labels=False,
        width=0.5,
        alpha=0.8
    )

    # Add a legend for communities
    handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=color,
                         markersize=10, label=f'Community {com}')
             for com, color in color_map.items()]

    plt.legend(handles=handles, loc='upper right', bbox_to_anchor=(1.15, 1))
    plt.title(f"Network Communities at Snapshot {snapshot}", fontsize=14)
    plt.axis('off')
    plt.tight_layout()

    return plt.gcf()

# Plot each snapshot
snapshots = sorted(groundtruth.graphs.keys())

for snapshot in snapshots:
    graph = groundtruth.graphs[snapshot]
    fig = plot_network_with_communities(graph, snapshot, membership)
    plt.savefig(f"network_snapshot_{snapshot}.png", dpi=300, bbox_inches='tight')
    plt.show()