Chapter 4: Network Visualization Tools

Effective Graph Visualization Using NetworkX, PyVis, igraph, and Gephi

📖 Reading Time: 20-25 minutes 📊 Difficulty: Beginner 💻 Code Examples: 0 📝 Exercises: 0

AI Terakoya HomeMachine LearningNetwork Analysis›Chapter 4

🌐 EN | 🇯🇵 JP | Last sync: 2025-11-16

1. NetworkX Visualization

1.1 Integration with Matplotlib

NetworkX is fully integrated with matplotlib, making it suitable for static graph visualization.

import networkx as nx
import matplotlib.pyplot as plt

# Create graph
G = nx.karate_club_graph()

# Basic visualization
plt.figure(figsize=(12, 8))
nx.draw(G, with_labels=True, node_color='lightblue',
        node_size=500, font_size=10, font_weight='bold')
plt.title('Karate Club Network')
plt.axis('off')
plt.tight_layout()
plt.savefig('karate_network.png', dpi=300, bbox_inches='tight')
plt.show()

1.2 Layout Algorithms

Selecting the appropriate layout is essential for understanding network structure.

import numpy as np

# Comparison of various layouts
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
layouts = {
    'Spring': nx.spring_layout(G, k=0.3, iterations=50),
    'Circular': nx.circular_layout(G),
    'Kamada-Kawai': nx.kamada_kawai_layout(G),
    'Spectral': nx.spectral_layout(G)
}

for ax, (name, pos) in zip(axes.flat, layouts.items()):
    nx.draw(G, pos, ax=ax, node_color='lightblue',
            node_size=300, with_labels=True, font_size=8)
    ax.set_title(f'{name} Layout', fontsize=14, fontweight='bold')
    ax.axis('off')

plt.tight_layout()
plt.savefig('layout_comparison.png', dpi=300, bbox_inches='tight')
plt.show()

# Layout algorithm selection criteria
# - Spring: General-purpose, force-directed balance (O(n²))
# - Circular: Visualizes symmetry (O(n))
# - Kamada-Kawai: More accurate distance representation (O(n³))
# - Spectral: Emphasizes community structure (O(n²))

1.3 Node and Edge Customization

Visually representing network attributes deepens data insights.

# Customization based on degree centrality
degree_centrality = nx.degree_centrality(G)
betweenness_centrality = nx.betweenness_centrality(G)

# Node size: degree centrality
node_sizes = [v * 3000 for v in degree_centrality.values()]

# Node color: betweenness centrality
node_colors = list(betweenness_centrality.values())

# Edge width: weight (in this example, product of degrees)
edge_weights = [G.degree(u) * G.degree(v) * 0.1
                for u, v in G.edges()]

plt.figure(figsize=(14, 10))
pos = nx.spring_layout(G, k=0.3, seed=42)

# Draw
nx.draw_networkx_nodes(G, pos, node_size=node_sizes,
                       node_color=node_colors, cmap='YlOrRd',
                       alpha=0.9, edgecolors='black', linewidths=1.5)

nx.draw_networkx_edges(G, pos, width=edge_weights,
                       alpha=0.5, edge_color='gray')

nx.draw_networkx_labels(G, pos, font_size=9, font_weight='bold')

plt.title('Network Visualization Using Centrality Metrics',
          fontsize=16, fontweight='bold')
plt.colorbar(plt.cm.ScalarMappable(cmap='YlOrRd'),
             label='Betweenness Centrality', ax=plt.gca())
plt.axis('off')
plt.tight_layout()
plt.savefig('customized_network.png', dpi=300, bbox_inches='tight')
plt.show()

# Visualization best practices
# 1. Node size: Represents importance (centrality)
# 2. Node color: Represents category or continuous value
# 3. Edge width: Represents relationship strength
# 4. Layout: Choose according to data characteristics

2. Advanced Visualization Libraries

2.1 Interactive Visualization with PyVis

PyVis generates interactive network visualizations in HTML format.

from pyvis.network import Network
import networkx as nx

# Create PyVis network
net = Network(height='750px', width='100%', bgcolor='#222222',
              font_color='white', notebook=True)

# Import from NetworkX graph
G = nx.karate_club_graph()

# Community detection
from networkx.algorithms import community
communities = community.greedy_modularity_communities(G)
community_map = {}
for i, comm in enumerate(communities):
    for node in comm:
        community_map[node] = i

# Set node colors and sizes
for node in G.nodes():
    # Change color by community
    color = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A'][community_map[node]]
    # Change size by degree
    size = G.degree(node) * 3
    net.add_node(node, label=str(node), color=color, size=size,
                 title=f'Node {node}  
Degree: {G.degree(node)}')

# Add edges
for edge in G.edges():
    net.add_edge(edge[0], edge[1])

# Physics simulation settings
net.set_options("""
var options = {
  "physics": {
    "forceAtlas2Based": {
      "gravitationalConstant": -50,
      "centralGravity": 0.01,
      "springLength": 100,
      "springConstant": 0.08
    },
    "maxVelocity": 50,
    "solver": "forceAtlas2Based",
    "timestep": 0.35,
    "stabilization": {"iterations": 150}
  }
}
""")

# HTML output
net.save_graph('interactive_network.html')
print("Interactive graph saved to interactive_network.html")

2.2 Interactive Graphs with Plotly

Plotly provides highly customizable interactive graphs.

import plotly.graph_objects as go

# Calculate layout
pos = nx.spring_layout(G, k=0.5, seed=42)

# Create edge trace
edge_x = []
edge_y = []
for edge in G.edges():
    x0, y0 = pos[edge[0]]
    x1, y1 = pos[edge[1]]
    edge_x.extend([x0, x1, None])
    edge_y.extend([y0, y1, None])

edge_trace = go.Scatter(
    x=edge_x, y=edge_y,
    line=dict(width=0.5, color='#888'),
    hoverinfo='none',
    mode='lines')

# Create node trace
node_x = []
node_y = []
node_text = []
node_sizes = []
for node in G.nodes():
    x, y = pos[node]
    node_x.append(x)
    node_y.append(y)
    node_text.append(f'Node {node}  
Degree: {G.degree(node)}')
    node_sizes.append(G.degree(node) * 5)

node_trace = go.Scatter(
    x=node_x, y=node_y,
    mode='markers',
    hoverinfo='text',
    text=node_text,
    marker=dict(
        showscale=True,
        colorscale='YlOrRd',
        size=node_sizes,
        color=[G.degree(node) for node in G.nodes()],
        colorbar=dict(
            thickness=15,
            title='Node Degree',
            xanchor='left',
            titleside='right'
        ),
        line=dict(width=2, color='white')))

# Create figure
fig = go.Figure(data=[edge_trace, node_trace],
                layout=go.Layout(
                    title='Plotly Interactive Network',
                    showlegend=False,
                    hovermode='closest',
                    margin=dict(b=0, l=0, r=0, t=40),
                    xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
                    yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
                    plot_bgcolor='rgba(240,240,240,0.9)'))

fig.write_html('plotly_network.html')
fig.show()

2.3 Large-Scale Network Visualization

Large graphs with over 10,000 nodes require special approaches.

# Efficient visualization of large graphs
def visualize_large_network(G, max_nodes=5000, sample_method='degree'):
    """
    Sample and visualize large networks

    Parameters:
    - G: NetworkX graph
    - max_nodes: Maximum number of nodes to display
    - sample_method: 'degree', 'random', 'pagerank'
    """
    if len(G.nodes()) > max_nodes:
        print(f"Sampling nodes from {len(G.nodes())} to {max_nodes}")

        if sample_method == 'degree':
            # Prioritize high-degree nodes
            top_nodes = sorted(G.degree(), key=lambda x: x[1],
                             reverse=True)[:max_nodes]
            nodes_to_keep = [n for n, d in top_nodes]
        elif sample_method == 'pagerank':
            # Select high PageRank nodes
            pr = nx.pagerank(G)
            top_nodes = sorted(pr.items(), key=lambda x: x[1],
                             reverse=True)[:max_nodes]
            nodes_to_keep = [n for n, p in top_nodes]
        else:  # random
            import random
            nodes_to_keep = random.sample(list(G.nodes()), max_nodes)

        G_sample = G.subgraph(nodes_to_keep).copy()
    else:
        G_sample = G

    # Visualize
    plt.figure(figsize=(16, 12))
    pos = nx.spring_layout(G_sample, k=1/np.sqrt(len(G_sample.nodes())),
                          iterations=20)

    degree_centrality = nx.degree_centrality(G_sample)
    node_sizes = [v * 1000 for v in degree_centrality.values()]

    nx.draw_networkx(G_sample, pos,
                     node_size=node_sizes,
                     node_color=list(degree_centrality.values()),
                     cmap='viridis',
                     with_labels=False,
                     alpha=0.7,
                     edge_color='gray',
                     width=0.5)

    plt.title(f'Sampled Network ({len(G_sample.nodes())} nodes)',
              fontsize=16, fontweight='bold')
    plt.axis('off')
    plt.tight_layout()
    plt.savefig('large_network_sampled.png', dpi=300, bbox_inches='tight')
    plt.show()

# Usage example
G_large = nx.barabasi_albert_graph(10000, 3, seed=42)
visualize_large_network(G_large, max_nodes=500, sample_method='pagerank')

3. Fast Analysis with igraph

3.1 igraph vs NetworkX

igraph is implemented in C and excels at high-speed processing of large graphs.

FeatureNetworkXigraph
Implementation LanguagePythonC (with Python bindings)
SpeedModerateFast (10-100x faster)
Memory EfficiencyStandardEfficient
Learning CurveGentleSomewhat steep
EcosystemRich (matplotlib, etc.)Custom visualization
ApplicabilitySmall to medium scale (~10K nodes)Large scale (100K+ nodes)

3.2 High-Speed Algorithm Implementation

import igraph as ig
import time

# Convert NetworkX graph to igraph
def nx_to_igraph(G_nx):
    """Convert NetworkX graph to igraph"""
    G_ig = ig.Graph()
    G_ig.add_vertices(list(G_nx.nodes()))
    G_ig.add_edges(list(G_nx.edges()))
    return G_ig

# Performance comparison
G_nx = nx.barabasi_albert_graph(5000, 3, seed=42)
G_ig = nx_to_igraph(G_nx)

# NetworkX: PageRank
start = time.time()
pr_nx = nx.pagerank(G_nx)
time_nx = time.time() - start

# igraph: PageRank
start = time.time()
pr_ig = G_ig.pagerank()
time_ig = time.time() - start

print(f"NetworkX PageRank: {time_nx:.4f} seconds")
print(f"igraph PageRank: {time_ig:.4f} seconds")
print(f"Speedup: {time_nx/time_ig:.2f}x")

# Community detection with igraph
start = time.time()
communities = G_ig.community_multilevel()
time_community = time.time() - start

print(f"\nigraph community detection: {time_community:.4f} seconds")
print(f"Communities detected: {len(communities)}")
print(f"Modularity: {communities.modularity:.4f}")

3.3 Large Graph Processing

# Efficient processing of large graphs using igraph
def analyze_large_graph_igraph(n_nodes=100000, m_edges=3):
    """Efficient analysis of large graphs"""
    print(f"Generating graph: {n_nodes} nodes...")
    G = ig.Graph.Barabasi(n_nodes, m_edges)

    print("Calculating centrality metrics...")
    start = time.time()

    # Calculate various centralities
    degree = G.degree()
    betweenness = G.betweenness()
    closeness = G.closeness()
    pagerank = G.pagerank()

    calc_time = time.time() - start
    print(f"Calculation time: {calc_time:.2f} seconds")

    # Community detection
    print("Detecting communities...")
    start = time.time()
    communities = G.community_multilevel()
    comm_time = time.time() - start
    print(f"Detection time: {comm_time:.2f} seconds")
    print(f"Number of communities: {len(communities)}")

    # Visualization (sampling)
    print("Sampling for visualization...")
    # Select top 500 nodes
    top_nodes = sorted(range(len(pagerank)),
                      key=lambda i: pagerank[i], reverse=True)[:500]
    G_sample = G.subgraph(top_nodes)

    # igraph visualization
    visual_style = {
        "vertex_size": [pagerank[i] * 1000 for i in top_nodes],
        "vertex_color": [communities.membership[i] for i in top_nodes],
        "vertex_label": None,
        "edge_width": 0.5,
        "edge_color": "#cccccc",
        "layout": G_sample.layout_fruchterman_reingold()
    }

    ig.plot(G_sample,
            "large_graph_igraph.png",
            bbox=(1200, 1200),
            **visual_style)

    print("Visualization complete: large_graph_igraph.png")

    return {
        'nodes': n_nodes,
        'edges': G.ecount(),
        'calc_time': calc_time,
        'comm_time': comm_time,
        'communities': len(communities),
        'modularity': communities.modularity
    }

# Execute
results = analyze_large_graph_igraph(n_nodes=100000, m_edges=3)
print(f"\nResults summary: {results}")

4. Introduction to Gephi

4.1 Gephi Features

Gephi is a powerful desktop application for interactive visualization and network exploration.

Main Advantages of Gephi:

4.2 Data Export/Import

# Export from NetworkX to Gephi format
import networkx as nx

# Create sample graph and add attributes
G = nx.karate_club_graph()

# Add node attributes
degree_centrality = nx.degree_centrality(G)
betweenness_centrality = nx.betweenness_centrality(G)
communities = nx.community.greedy_modularity_communities(G)

# Add community ID to nodes
community_map = {}
for i, comm in enumerate(communities):
    for node in comm:
        community_map[node] = i

for node in G.nodes():
    G.nodes[node]['degree_centrality'] = degree_centrality[node]
    G.nodes[node]['betweenness_centrality'] = betweenness_centrality[node]
    G.nodes[node]['community'] = community_map[node]
    G.nodes[node]['label'] = f'Node_{node}'

# Add edge attributes
for u, v in G.edges():
    G[u][v]['weight'] = G.degree(u) + G.degree(v)

# Export as GEXF format (Gephi recommended format)
nx.write_gexf(G, 'network_for_gephi.gexf')
print("GEXF file created: network_for_gephi.gexf")

# Can also export as GraphML format
nx.write_graphml(G, 'network_for_gephi.graphml')
print("GraphML file created: network_for_gephi.graphml")

# CSV edge list format (simple method)
import pandas as pd

edges_data = []
for u, v, data in G.edges(data=True):
    edges_data.append({
        'Source': u,
        'Target': v,
        'Weight': data.get('weight', 1)
    })

edges_df = pd.DataFrame(edges_data)
edges_df.to_csv('edges.csv', index=False)

# Node list CSV
nodes_data = []
for node, data in G.nodes(data=True):
    nodes_data.append({
        'Id': node,
        'Label': data.get('label', str(node)),
        'Community': data.get('community', 0),
        'Degree_Centrality': data.get('degree_centrality', 0),
        'Betweenness_Centrality': data.get('betweenness_centrality', 0)
    })

nodes_df = pd.DataFrame(nodes_data)
nodes_df.to_csv('nodes.csv', index=False)

print("CSV files created: edges.csv, nodes.csv")

4.3 Visualization Best Practices

Recommended Gephi Workflow Steps

  1. Data Import : File → Open → Select GEXF/GraphML file
  2. Calculate Statistics : Run the following in Statistics panel
    • Average Degree
    • Network Diameter
    • Modularity (community detection)
    • PageRank
  3. Apply Layout : Select ForceAtlas2 in Layout panel
    • Scaling: 2.0-10.0 (depending on graph size)
    • Gravity: 1.0
    • Prevent Overlap: Check
  4. Visual Adjustments : Configure in Appearance panel
    • Node size: Ranking → Degree/PageRank
    • Node color: Partition → Modularity Class
    • Labels: Size = proportional to node size
  5. Export : Preview → Export → PNG/PDF (300+ DPI recommended)

5. Practice: Large-Scale Network Visualization

5.1 Sampling Techniques

# Implementation of various sampling techniques
class NetworkSampler:
    """Network sampling class for large networks"""

    @staticmethod
    def random_node_sampling(G, sample_size):
        """Random node sampling"""
        import random
        nodes = random.sample(list(G.nodes()),
                            min(sample_size, len(G.nodes())))
        return G.subgraph(nodes).copy()

    @staticmethod
    def random_edge_sampling(G, sample_ratio=0.1):
        """Random edge sampling"""
        import random
        n_edges = int(len(G.edges()) * sample_ratio)
        edges = random.sample(list(G.edges()), n_edges)
        H = nx.Graph()
        H.add_edges_from(edges)
        return H

    @staticmethod
    def induced_subgraph_sampling(G, sample_size):
        """Induced subgraph sampling (prioritizing important nodes)"""
        # Select important nodes using PageRank
        pr = nx.pagerank(G)
        top_nodes = sorted(pr.items(), key=lambda x: x[1],
                          reverse=True)[:sample_size]
        nodes = [n for n, _ in top_nodes]
        return G.subgraph(nodes).copy()

    @staticmethod
    def snowball_sampling(G, seed_nodes, k=2):
        """Snowball sampling (k-hop neighborhood)"""
        sampled_nodes = set(seed_nodes)
        for _ in range(k):
            new_nodes = set()
            for node in sampled_nodes:
                new_nodes.update(G.neighbors(node))
            sampled_nodes.update(new_nodes)
        return G.subgraph(sampled_nodes).copy()

    @staticmethod
    def forest_fire_sampling(G, sample_size, p=0.4):
        """Forest Fire sampling"""
        import random
        sampled_nodes = set()
        queue = [random.choice(list(G.nodes()))]

        while len(sampled_nodes) < sample_size and queue:
            current = queue.pop(0)
            if current not in sampled_nodes:
                sampled_nodes.add(current)
                neighbors = list(G.neighbors(current))
                # Add neighboring nodes with probability p
                n_select = int(len(neighbors) * p)
                queue.extend(random.sample(neighbors,
                                         min(n_select, len(neighbors))))

        return G.subgraph(sampled_nodes).copy()

# Comparison of sampling techniques
G_large = nx.barabasi_albert_graph(10000, 3, seed=42)
sample_size = 500

samplers = {
    'Random Node': NetworkSampler.random_node_sampling(G_large, sample_size),
    'Induced (PageRank)': NetworkSampler.induced_subgraph_sampling(G_large, sample_size),
    'Snowball (k=2)': NetworkSampler.snowball_sampling(
        G_large, [0, 1, 2], k=2),
    'Forest Fire': NetworkSampler.forest_fire_sampling(
        G_large, sample_size, p=0.4)
}

# Compare characteristics of each sampling technique
fig, axes = plt.subplots(2, 2, figsize=(16, 14))

for ax, (name, G_sample) in zip(axes.flat, samplers.items()):
    pos = nx.spring_layout(G_sample, k=0.5, iterations=20)
    degree_centrality = nx.degree_centrality(G_sample)
    node_sizes = [v * 500 for v in degree_centrality.values()]

    nx.draw_networkx(G_sample, pos, ax=ax,
                     node_size=node_sizes,
                     node_color=list(degree_centrality.values()),
                     cmap='viridis',
                     with_labels=False,
                     alpha=0.7,
                     edge_color='gray',
                     width=0.5)

    # Statistical information
    density = nx.density(G_sample)
    avg_degree = sum(dict(G_sample.degree()).values()) / len(G_sample.nodes())

    ax.set_title(f'{name}\nNodes: {len(G_sample.nodes())}, '
                f'Density: {density:.4f}, Avg Degree: {avg_degree:.2f}',
                fontsize=12, fontweight='bold')
    ax.axis('off')

plt.tight_layout()
plt.savefig('sampling_comparison.png', dpi=300, bbox_inches='tight')
plt.show()

5.2 Hierarchical Visualization

# Hierarchical network visualization
def hierarchical_visualization(G, threshold_degree=10):
    """
    Hierarchical visualization: Display important nodes and their surroundings progressively
    """
    # Level 1: High-degree nodes (hubs)
    high_degree_nodes = [n for n, d in G.degree() if d >= threshold_degree]

    # Level 2: Direct neighbors of hubs
    level2_nodes = set()
    for hub in high_degree_nodes:
        level2_nodes.update(G.neighbors(hub))
    level2_nodes = list(level2_nodes - set(high_degree_nodes))

    # Level 3: Other nodes (sampled)
    remaining = set(G.nodes()) - set(high_degree_nodes) - set(level2_nodes)
    import random
    level3_nodes = random.sample(list(remaining),
                                min(100, len(remaining)))

    # Hierarchical layout
    fig = plt.figure(figsize=(18, 6))

    # Level 1 visualization
    ax1 = plt.subplot(131)
    G1 = G.subgraph(high_degree_nodes).copy()
    pos1 = nx.spring_layout(G1, k=1, seed=42)
    nx.draw_networkx(G1, pos1, ax=ax1,
                     node_color='red', node_size=500,
                     with_labels=True, font_size=8)
    ax1.set_title(f'Level 1: Hub Nodes ({len(high_degree_nodes)})',
                  fontsize=14, fontweight='bold')
    ax1.axis('off')

    # Level 2 visualization
    ax2 = plt.subplot(132)
    G2 = G.subgraph(high_degree_nodes + level2_nodes).copy()
    pos2 = nx.spring_layout(G2, k=0.5, seed=42)
    node_colors = ['red' if n in high_degree_nodes else 'lightblue'
                   for n in G2.nodes()]
    node_sizes = [500 if n in high_degree_nodes else 200
                  for n in G2.nodes()]
    nx.draw_networkx(G2, pos2, ax=ax2,
                     node_color=node_colors, node_size=node_sizes,
                     with_labels=False)
    ax2.set_title(f'Level 2: +Direct Neighbors ({len(G2.nodes())})',
                  fontsize=14, fontweight='bold')
    ax2.axis('off')

    # Level 3 visualization
    ax3 = plt.subplot(133)
    all_nodes = high_degree_nodes + level2_nodes + level3_nodes
    G3 = G.subgraph(all_nodes).copy()
    pos3 = nx.spring_layout(G3, k=0.3, seed=42)
    node_colors = ['red' if n in high_degree_nodes
                   else 'lightblue' if n in level2_nodes
                   else 'lightgreen' for n in G3.nodes()]
    node_sizes = [500 if n in high_degree_nodes
                  else 200 if n in level2_nodes
                  else 100 for n in G3.nodes()]
    nx.draw_networkx(G3, pos3, ax=ax3,
                     node_color=node_colors, node_size=node_sizes,
                     with_labels=False, alpha=0.8)
    ax3.set_title(f'Level 3: +Others ({len(G3.nodes())})',
                  fontsize=14, fontweight='bold')
    ax3.axis('off')

    plt.tight_layout()
    plt.savefig('hierarchical_viz.png', dpi=300, bbox_inches='tight')
    plt.show()

# Usage example
G = nx.barabasi_albert_graph(1000, 3, seed=42)
hierarchical_visualization(G, threshold_degree=20)

5.3 Creating Interactive Dashboards

# Interactive dashboard using Plotly Dash
from dash import Dash, dcc, html, Input, Output
import plotly.graph_objects as go
import networkx as nx

# Create graph
G = nx.karate_club_graph()

# Dashboard application
app = Dash(__name__)

# Layout
app.layout = html.Div([
    html.H1("Network Visualization Dashboard",
            style={'textAlign': 'center'}),

    html.Div([
        html.Label("Layout Algorithm:"),
        dcc.Dropdown(
            id='layout-dropdown',
            options=[
                {'label': 'Spring Layout', 'value': 'spring'},
                {'label': 'Circular Layout', 'value': 'circular'},
                {'label': 'Kamada-Kawai', 'value': 'kamada_kawai'},
                {'label': 'Spectral Layout', 'value': 'spectral'}
            ],
            value='spring'
        )
    ], style={'width': '300px', 'margin': '20px'}),

    html.Div([
        html.Label("Node Color Metric:"),
        dcc.Dropdown(
            id='metric-dropdown',
            options=[
                {'label': 'Degree Centrality', 'value': 'degree'},
                {'label': 'Betweenness Centrality', 'value': 'betweenness'},
                {'label': 'Eigenvector Centrality', 'value': 'eigenvector'},
                {'label': 'PageRank', 'value': 'pagerank'}
            ],
            value='degree'
        )
    ], style={'width': '300px', 'margin': '20px'}),

    dcc.Graph(id='network-graph', style={'height': '800px'})
])

# Callback
@app.callback(
    Output('network-graph', 'figure'),
    [Input('layout-dropdown', 'value'),
     Input('metric-dropdown', 'value')]
)
def update_graph(layout_type, metric_type):
    # Calculate layout
    if layout_type == 'spring':
        pos = nx.spring_layout(G, k=0.5, seed=42)
    elif layout_type == 'circular':
        pos = nx.circular_layout(G)
    elif layout_type == 'kamada_kawai':
        pos = nx.kamada_kawai_layout(G)
    else:  # spectral
        pos = nx.spectral_layout(G)

    # Calculate metrics
    if metric_type == 'degree':
        metric = nx.degree_centrality(G)
    elif metric_type == 'betweenness':
        metric = nx.betweenness_centrality(G)
    elif metric_type == 'eigenvector':
        metric = nx.eigenvector_centrality(G)
    else:  # pagerank
        metric = nx.pagerank(G)

    # Edge trace
    edge_x, edge_y = [], []
    for edge in G.edges():
        x0, y0 = pos[edge[0]]
        x1, y1 = pos[edge[1]]
        edge_x.extend([x0, x1, None])
        edge_y.extend([y0, y1, None])

    edge_trace = go.Scatter(
        x=edge_x, y=edge_y,
        line=dict(width=0.5, color='#888'),
        hoverinfo='none',
        mode='lines'
    )

    # Node trace
    node_x = [pos[node][0] for node in G.nodes()]
    node_y = [pos[node][1] for node in G.nodes()]
    node_text = [f'Node {node}  
{metric_type}: {metric[node]:.4f}'
                 for node in G.nodes()]

    node_trace = go.Scatter(
        x=node_x, y=node_y,
        mode='markers+text',
        hoverinfo='text',
        text=[str(n) for n in G.nodes()],
        textposition="top center",
        hovertext=node_text,
        marker=dict(
            showscale=True,
            colorscale='YlOrRd',
            size=[metric[node] * 50 for node in G.nodes()],
            color=[metric[node] for node in G.nodes()],
            colorbar=dict(
                thickness=15,
                title=metric_type,
                xanchor='left',
                titleside='right'
            ),
            line=dict(width=2, color='white')
        )
    )

    # Create figure
    fig = go.Figure(
        data=[edge_trace, node_trace],
        layout=go.Layout(
            title=f'{layout_type.title()} Layout - {metric_type.title()}',
            showlegend=False,
            hovermode='closest',
            margin=dict(b=0, l=0, r=0, t=40),
            xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
            yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
            plot_bgcolor='white'
        )
    )

    return fig

# Run application
# if __name__ == '__main__':
#     app.run_server(debug=True, port=8050)

print("Dashboard code ready")
print("To execute, uncomment the last 2 lines")

Visualization Tool Selection Guidelines:

Disclaimer