如何基于Plotly实现NetworkX节点与时间序列子图的联动交互
Got it, let's get this interactive linking working between your NetworkX graph and time series plot in Plotly. I’ve put together a practical, modifiable solution that fits your existing two-column subgraph setup:
Core Approach
We’ll use Plotly’s custom click event callbacks to connect the right-side NetworkX nodes to the left-side time series. Here’s the breakdown:
- Attach unique node IDs to each graph node using Plotly’s
customdataattribute—this lets us identify exactly which node was clicked. - Name each time series trace after its corresponding node ID, so we can match clicks to the correct curve.
- Write a JavaScript callback that listens for node clicks, then toggles visibility of time series traces to only show the clicked node’s data.
Full Code Example
Replace your existing subgraph code with this (swap out the sample data for your actual time series and NetworkX graph):
import plotly.graph_objects as go import networkx as nx import pandas as pd # ---------------------- Replace with your actual data ---------------------- # Time series data: key = node ID, value = {x: timestamps, y: values} ts_data = { "NodeA": {"x": pd.date_range(start="2023-01-01", periods=10), "y": [1,3,2,5,4,6,8,7,9,10]}, "NodeB": {"x": pd.date_range(start="2023-01-01", periods=10), "y": [2,4,1,3,5,7,6,8,10,9]}, "NodeC": {"x": pd.date_range(start="2023-01-01", periods=10), "y": [5,3,6,2,7,4,8,1,9,0]} } # Your existing NetworkX graph G = nx.Graph() G.add_nodes_from(["NodeA", "NodeB", "NodeC"]) G.add_edges_from([("NodeA", "NodeB"), ("NodeB", "NodeC")]) # --------------------------------------------------------------------------- # Build the two-column figure fig = go.Figure() # 1. Left subplot: Time series traces for node_id, data in ts_data.items(): fig.add_trace(go.Scatter( x=data["x"], y=data["y"], name=node_id, # Critical: Match this to node IDs in your graph visible=True, xaxis="x1", yaxis="y1" )) # 2. Right subplot: NetworkX graph converted to Plotly pos = nx.spring_layout(G) # Use your preferred layout here # Add nodes with customdata for ID tracking node_x = [pos[node][0] for node in G.nodes()] node_y = [pos[node][1] for node in G.nodes()] fig.add_trace(go.Scatter( x=node_x, y=node_y, mode="markers+text", text=[node for node in G.nodes()], textposition="top center", marker=dict(size=15, color="#1f77b4"), customdata=[node for node in G.nodes()], # Stores node ID for click events name="Nodes", # Used to identify node traces in the callback xaxis="x2", yaxis="y2" )) # Add edges for edge in G.edges(): x0, y0 = pos[edge[0]] x1, y1 = pos[edge[1]] fig.add_trace(go.Scatter( x=[x0, x1], y=[y0, y1], mode="lines", line=dict(width=2, color="#999999"), showlegend=False, xaxis="x2", yaxis="y2" )) # Configure layout fig.update_layout( grid=dict(rows=1, columns=2, width_ratios=[2, 1]), xaxis1=dict(domain=[0, 0.65], title="Time"), yaxis1=dict(title="Value"), xaxis2=dict(domain=[0.7, 1], showticklabels=False), yaxis2=dict(showticklabels=False), title="Interactive Time Series + Network Graph", height=600 ) # Add click event callback to link nodes to time series fig.add_js_callback(""" // Track selected node (use an array here if you want multi-select) let selectedNode = null; // Listen for plot click events document.addEventListener('plotly_click', function(event) { const clickedTrace = event.points[0].trace; // Only react to clicks on the node trace if (clickedTrace.name === 'Nodes') { const nodeId = event.points[0].customdata; selectedNode = nodeId; // Get all time series traces (adjust the filter if your traces are ordered differently) const tsTraces = document.getElementById('graph').data.filter(trace => trace.xaxis === 'x1'); // Create visibility array: only show the clicked node's time series const visibility = tsTraces.map(trace => trace.name === nodeId ? true : false); // Update the time series subplot Plotly.restyle('graph', 'visible', visibility); } }); """) # Show the figure fig.show(config={'displayModeBar': False})
Key Details to Adapt to Your Setup
- Trace Matching: Make sure the
nameof each time series trace exactly matches the node ID in your NetworkX graph—this is how we link clicks to curves. - Trace Filtering: In the callback,
tsTraces = document.getElementById('graph').data.filter(trace => trace.xaxis === 'x1')filters for left-subplot traces. If your trace ordering is different, adjust this filter to target only your time series traces. - Multi-Select: To let users click multiple nodes to show multiple curves, replace
selectedNodewith an array. Modify the callback to toggle node IDs in the array, then set visibility based on whether the trace name is in the selected array. - Graph ID: The code uses
'graph'as the Plotly figure's ID (default for inline plots). If you’re embedding the plot in a web page with a custom ID, updatedocument.getElementById('graph')to match.
内容的提问来源于stack exchange,提问作者Natasha
相关产品推荐
相关产品推荐

