How to Visualize Multilayer Networks ===================================== **Goal:** Create publication-ready visualizations of multilayer networks. **Prerequisites:** A loaded network (see :doc:`load_and_build_networks`). Basic Visualization ------------------- Quick Plot ~~~~~~~~~~ .. code-block:: python from py3plex.core import multinet # Load network network = multinet.multi_layer_network() network.add_edges([ ['Alice', 'friends', 'Bob', 'friends', 1], ['Bob', 'friends', 'Carol', 'friends', 1], ['Alice', 'work', 'Carol', 'work', 1], ], input_type="list") # Visualize network.visualize_network(show=True) This opens an interactive window with the network visualization. Save to File ~~~~~~~~~~~~ .. code-block:: python network.visualize_network( output_file='network.png', show=False ) Customizing Visualizations --------------------------- Node Sizes and Colors ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from py3plex.visualization import visualize_network_custom # Compute node importance from py3plex.dsl import Q result = Q.nodes().compute("degree").execute(network) degrees = {node: data['degree'] for node, data in result.items()} # Visualize with custom node sizes visualize_network_custom( network, node_sizes=degrees, node_color_by_layer=True, output_file='network_sized.png' ) Edge Weights ~~~~~~~~~~~~ .. code-block:: python visualize_network_custom( network, edge_width_by_weight=True, show=True ) Layout Algorithms ----------------- Force-Directed Layout ~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from py3plex.visualization.multilayer import hairball_plot hairball_plot( network, layout_algorithm='force_directed', output_file='force_layout.png' ) Circular Layout ~~~~~~~~~~~~~~~ .. code-block:: python hairball_plot( network, layout_algorithm='circular', output_file='circular_layout.png' ) Layer-Specific Visualization ----------------------------- Visualize Single Layer ~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from py3plex.dsl import Q, L # Extract layer layer_network = Q.edges().from_layers(L["friends"]).execute(network) # Visualize layer_network.visualize_network( output_file='friends_layer.png', show=True ) Side-by-Side Layer Comparison ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python import matplotlib.pyplot as plt fig, axes = plt.subplots(1, 3, figsize=(18, 6)) for idx, layer in enumerate(['friends', 'work', 'family']): layer_net = Q.edges().from_layers(L[layer]).execute(network) # Visualize on specific axis visualize_on_axis(layer_net, axes[idx], title=f'Layer: {layer}') plt.tight_layout() plt.savefig('layers_comparison.png', dpi=300) DSL-Driven Visualization Workflows ----------------------------------- **Goal:** Use DSL queries to select and visualize specific network subsets. The DSL enables you to create targeted visualizations by filtering nodes and edges before rendering. This is essential for large multilayer networks where visualizing everything is impractical. Visualize High-Degree Subnetwork ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Focus visualization on hub nodes:** .. code-block:: python from py3plex.core import multinet from py3plex.dsl import Q import matplotlib.pyplot as plt # Load network network = multinet.multi_layer_network(directed=False) network.load_network( "py3plex/datasets/_data/synthetic_multilayer.edges", input_type="multiedgelist" ) # Query high-degree nodes hubs = ( Q.nodes() .compute("degree") .where(degree__gt=6) .execute(network) ) print(f"Hub nodes (degree > 6): {len(hubs)}") # Extract hub subgraph hub_subgraph = network.core_network.subgraph(hubs.keys()) # Create visualization import networkx as nx pos = nx.spring_layout(hub_subgraph, k=0.5, iterations=50) plt.figure(figsize=(12, 10)) # Color by layer layer_colors = {'layer1': 'red', 'layer2': 'blue', 'layer3': 'green'} node_colors = [layer_colors.get(node[1], 'gray') for node in hub_subgraph.nodes()] # Size by degree node_sizes = [hubs[node]['degree'] * 100 for node in hub_subgraph.nodes()] nx.draw_networkx( hub_subgraph, pos, node_color=node_colors, node_size=node_sizes, with_labels=True, font_size=8, alpha=0.7 ) plt.title('Hub Nodes Subnetwork (degree > 6)', fontsize=14) plt.axis('off') plt.tight_layout() plt.savefig('hub_subnetwork.png', dpi=300, bbox_inches='tight') print("Saved: hub_subnetwork.png") **Expected output:** .. code-block:: text Hub nodes (degree > 6): 18 Saved: hub_subnetwork.png Visualize Community Structure with DSL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Community-aware visualization:** .. code-block:: python from py3plex.algorithms.community_detection.community_wrapper import louvain_communities from py3plex.dsl import Q, execute_query import matplotlib.pyplot as plt import networkx as nx # Detect communities communities = louvain_communities(network) # Attach community labels for (node, layer), comm_id in communities.items(): network.core_network.nodes[(node, layer)]['community'] = comm_id # Visualize each community separately community_ids = set(communities.values()) n_communities = len(community_ids) fig, axes = plt.subplots(1, min(n_communities, 4), figsize=(20, 5)) if n_communities == 1: axes = [axes] for idx, comm_id in enumerate(sorted(community_ids)[:4]): # Query community members comm_nodes = execute_query( network, f'SELECT nodes WHERE community={comm_id}' ) # Extract community subgraph comm_subgraph = network.core_network.subgraph(comm_nodes) # Visualize pos = nx.spring_layout(comm_subgraph, k=0.3) # Color by layer layer_colors = {'layer1': '#FF6B6B', 'layer2': '#4ECDC4', 'layer3': '#45B7D1'} node_colors = [layer_colors.get(node[1], 'gray') for node in comm_subgraph.nodes()] nx.draw_networkx( comm_subgraph, pos, ax=axes[idx], node_color=node_colors, node_size=200, with_labels=False, alpha=0.8 ) axes[idx].set_title(f'Community {comm_id}\n({len(comm_nodes)} nodes)') axes[idx].axis('off') plt.tight_layout() plt.savefig('communities_separate.png', dpi=300, bbox_inches='tight') print(f"Saved: communities_separate.png with {min(n_communities, 4)} communities") Layer-Specific Visualizations with DSL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Compare layer structures visually:** .. code-block:: python from py3plex.dsl import Q, L import matplotlib.pyplot as plt import networkx as nx layers = network.get_layers() n_layers = len(layers) fig, axes = plt.subplots(1, n_layers, figsize=(6*n_layers, 5)) if n_layers == 1: axes = [axes] for idx, layer in enumerate(layers): # Query layer nodes and edges layer_nodes = Q.nodes().from_layers(L[layer]).execute(network) layer_edges = Q.edges().from_layers(L[layer]).execute(network) # Extract layer subgraph layer_subgraph = network.core_network.subgraph(layer_nodes.keys()) # Compute metrics for sizing layer_metrics = ( Q.nodes() .from_layers(L[layer]) .compute("degree") .execute(network) ) # Visualization pos = nx.spring_layout(layer_subgraph, k=0.5, iterations=50) # Size by degree node_sizes = [layer_metrics[node]['degree'] * 50 for node in layer_subgraph.nodes()] nx.draw_networkx( layer_subgraph, pos, ax=axes[idx], node_size=node_sizes, node_color='lightblue', edge_color='gray', with_labels=False, alpha=0.7 ) axes[idx].set_title(f'{layer}\n{len(layer_nodes)} nodes, {len(layer_edges)} edges') axes[idx].axis('off') plt.tight_layout() plt.savefig('layer_comparison.png', dpi=300, bbox_inches='tight') print("Saved: layer_comparison.png") **Expected output:** .. code-block:: text Saved: layer_comparison.png Visualize Central Nodes ~~~~~~~~~~~~~~~~~~~~~~~~ **Highlight nodes by centrality:** .. code-block:: python from py3plex.dsl import Q import matplotlib.pyplot as plt import networkx as nx import numpy as np # Compute centrality for all nodes centrality_result = ( Q.nodes() .compute("betweenness_centrality", "degree") .execute(network) ) # Extract betweenness values for coloring betweenness = { node: data['betweenness_centrality'] for node, data in centrality_result.items() } # Create visualization plt.figure(figsize=(14, 10)) pos = nx.spring_layout(network.core_network, k=0.5, iterations=50) # Node colors based on betweenness (using colormap) node_list = list(network.core_network.nodes()) betw_values = [betweenness.get(node, 0) for node in node_list] # Node sizes based on degree degrees = {node: data['degree'] for node, data in centrality_result.items()} node_sizes = [degrees.get(node, 1) * 100 for node in node_list] # Draw nodes = nx.draw_networkx_nodes( network.core_network, pos, node_size=node_sizes, node_color=betw_values, cmap=plt.cm.YlOrRd, alpha=0.8 ) nx.draw_networkx_edges( network.core_network, pos, alpha=0.2, edge_color='gray' ) # Add colorbar plt.colorbar(nodes, label='Betweenness Centrality') plt.title('Network Centrality Visualization\n(size=degree, color=betweenness)', fontsize=14) plt.axis('off') plt.tight_layout() plt.savefig('centrality_viz.png', dpi=300, bbox_inches='tight') print("Saved: centrality_viz.png") Dynamics State Visualization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Visualize epidemic simulation results:** .. code-block:: python from py3plex.dynamics import SIRDynamics from py3plex.dsl import Q import matplotlib.pyplot as plt import networkx as nx # Run SIR simulation sir = SIRDynamics(network, beta=0.3, gamma=0.1, initial_infected=0.05) sir.set_seed(42) results = sir.run(steps=100) # Attach final state final_state = results.trajectory[-1] for node, state in final_state.items(): network.core_network.nodes[node]['sir_state'] = state # Query infected and recovered nodes infected = Q.nodes().where(sir_state='I').execute(network) recovered = Q.nodes().where(sir_state='R').execute(network) susceptible = Q.nodes().where(sir_state='S').execute(network) print(f"Infected: {len(infected)}, Recovered: {len(recovered)}, Susceptible: {len(susceptible)}") # Visualization plt.figure(figsize=(14, 10)) pos = nx.spring_layout(network.core_network, k=0.5, iterations=50) # Color by SIR state state_colors = {'S': 'lightblue', 'I': 'red', 'R': 'green'} node_colors = [ state_colors.get(network.core_network.nodes[node].get('sir_state'), 'gray') for node in network.core_network.nodes() ] nx.draw_networkx( network.core_network, pos, node_color=node_colors, node_size=300, with_labels=False, alpha=0.8 ) # Legend from matplotlib.patches import Patch legend_elements = [ Patch(facecolor='lightblue', label=f'Susceptible ({len(susceptible)})'), Patch(facecolor='red', label=f'Infected ({len(infected)})'), Patch(facecolor='green', label=f'Recovered ({len(recovered)})') ] plt.legend(handles=legend_elements, loc='upper right') plt.title('SIR Epidemic Simulation (t=100)', fontsize=14) plt.axis('off') plt.tight_layout() plt.savefig('sir_visualization.png', dpi=300, bbox_inches='tight') print("Saved: sir_visualization.png") **Expected output:** .. code-block:: text Infected: 8, Recovered: 82, Susceptible: 30 Saved: sir_visualization.png Interactive Visualization with DSL Filtering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **Create interactive plots with Plotly:** .. code-block:: python from py3plex.dsl import Q import plotly.graph_objects as go import networkx as nx # Query nodes with metrics metrics = ( Q.nodes() .compute("degree", "betweenness_centrality") .execute(network) ) # Extract subgraph (optional: filter for clarity) high_degree = { node: data for node, data in metrics.items() if data['degree'] > 4 } subgraph = network.core_network.subgraph(high_degree.keys()) # Layout pos = nx.spring_layout(subgraph, k=0.5) # Create edge trace edge_x = [] edge_y = [] for edge in subgraph.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 = [] node_colors = [] for node in subgraph.nodes(): x, y = pos[node] node_x.append(x) node_y.append(y) degree = high_degree[node]['degree'] betw = high_degree[node]['betweenness_centrality'] node_text.append(f'{node}
Degree: {degree}
Betweenness: {betw:.4f}') node_sizes.append(degree * 5) node_colors.append(betw) 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=node_colors, colorbar=dict( title="Betweenness" ), line_width=2 ) ) # Create figure fig = go.Figure( data=[edge_trace, node_trace], layout=go.Layout( title='Interactive Network Visualization (degree > 4)', 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) ) ) # Save to HTML fig.write_html('interactive_network.html') print("Saved: interactive_network.html (open in browser)") **Why use DSL for visualization?** * **Targeted views:** Filter nodes/edges to reduce visual clutter * **Multi-scale:** Visualize different network scales (layer, community, subnetwork) * **Attribute-driven:** Color/size nodes based on computed metrics * **Reproducible:** Query-based selections are version-controllable * **Efficient:** Process only relevant subsets for large networks **Next steps with DSL visualization:** * **Full DSL tutorial:** :doc:`query_with_dsl` - Complete DSL reference * **Community detection:** :doc:`run_community_detection` - Visualize communities * **Dynamics:** :doc:`simulate_dynamics` - Visualize dynamics results Community Visualization ----------------------- Color by Communities ~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from py3plex.algorithms.community_detection import louvain_communities from py3plex.visualization import visualize_communities # Detect communities communities = louvain_communities(network) # Visualize with community colors visualize_communities( network, communities, output_file='communities.png', show=True ) Interactive Visualization ------------------------- Using Plotly ~~~~~~~~~~~~ .. code-block:: python from py3plex.visualization import plotly_visualization # Create interactive plot fig = plotly_visualization(network) # Show in browser fig.show() # Or save as HTML fig.write_html('network_interactive.html') Export for Gephi/Cytoscape --------------------------- Export to GraphML ~~~~~~~~~~~~~~~~~ .. code-block:: python import networkx as nx # Convert to NetworkX G = network.to_networkx() # Export nx.write_graphml(G, 'network.graphml') Then open `network.graphml` in Gephi or Cytoscape for advanced visualization. Next Steps ---------- * **Compute statistics to visualize:** :doc:`compute_statistics` * **Detect communities:** :doc:`run_community_detection` * **API reference:** :doc:`../reference/api_index`