Plotly Sankey图节点悬停时高亮流入(蓝)流出(红)连接的实现方法
问题解答
Plotly支持该交互效果,需通过自定义回调实现
Plotly静态Sankey图本身不支持节点悬停时差异化高亮流入/流出连接的功能,但可以通过Dash框架或Plotly FigureWidget结合回调逻辑实现。
方法1:使用Dash框架构建网页端交互
Dash是Plotly官方的交互式应用框架,适合部署网页可视化。核心逻辑是监听节点悬停事件,动态更新对应连接的颜色:
import dash from dash import dcc, html, Input, Output, State import plotly.graph_objects as go # 基础Sankey数据 nodes = [{"label": "A"}, {"label": "B"}, {"label": "C"}, {"label": "D"}] links = [ {"source": 0, "target": 1, "value": 2}, {"source": 0, "target": 2, "value": 3}, {"source": 1, "target": 3, "value": 1}, {"source": 2, "target": 3, "value": 4} ] default_link_color = "rgba(150,150,150,0.5)" # 初始化Sankey图 fig = go.Figure(go.Sankey( node=dict(label=[n["label"] for n in nodes]), link=dict( source=[l["source"] for l in links], target=[l["target"] for l in links], value=[l["value"] for l in links], color=[default_link_color]*len(links) ) )) app = dash.Dash(__name__) app.layout = html.Div([ dcc.Graph(id="sankey-graph", figure=fig), dcc.Store(id="links-data", data=links) ]) @app.callback( Output("sankey-graph", "figure"), Input("sankey-graph", "hoverData"), State("sankey-graph", "figure"), State("links-data", "data") ) def update_sankey_hover(hover_data, current_fig, links): # 重置所有连接为默认颜色 updated_colors = [default_link_color]*len(links) if hover_data: hover_node_idx = hover_data["points"][0]["nodeIndex"] # 标记流入/流出连接的颜色 for i, link in enumerate(links): if link["target"] == hover_node_idx: updated_colors[i] = "rgba(0,0,255,0.7)" # 流入蓝色 elif link["source"] == hover_node_idx: updated_colors[i] = "rgba(255,0,0,0.7)" # 流出红色 current_fig["data"][0]["link"]["color"] = updated_colors return current_fig if __name__ == "__main__": app.run_server(debug=True)
方法2:使用Plotly FigureWidget(Jupyter环境)
在Jupyter Notebook/Lab中,可直接用FigureWidget结合Python回调实现:
import plotly.graph_objects as go # 基础数据 nodes = [{"label": "A"}, {"label": "B"}, {"label": "C"}, {"label": "D"}] links = [ {"source": 0, "target": 1, "value": 2}, {"source": 0, "target": 2, "value": 3}, {"source": 1, "target": 3, "value": 1}, {"source": 2, "target": 3, "value": 4} ] default_color = "rgba(150,150,150,0.5)" # 创建可交互的FigureWidget fig_widget = go.FigureWidget(go.Sankey( node=dict(label=[n["label"] for n in nodes]), link=dict( source=[l["source"] for l in links], target=[l["target"] for l in links], value=[l["value"] for l in links], color=[default_color]*len(links) ) )) # 定义悬停回调逻辑 def hover_fn(trace, points, state): with fig_widget.batch_update(): # 重置颜色 trace.link.color = [default_color]*len(links) if points.node_indices: hover_idx = points.node_indices[0] # 更新流入/流出连接颜色 for i, link in enumerate(links): if link["target"] == hover_idx: trace.link.color[i] = "rgba(0,0,255,0.7)" elif link["source"] == hover_idx: trace.link.color[i] = "rgba(255,0,0,0.7)" # 绑定悬停事件 fig_widget.data[0].on_hover(hover_fn) # 显示组件 fig_widget
替代方案:使用Bokeh实现无后端交互
如果不想依赖Plotly生态,Bokeh支持通过前端JS回调实现该功能,无需后端服务器:
from bokeh.plotting import figure, show from bokeh.models import Sankey, ColumnDataSource, CustomJS, HoverTool # 构建数据源 source = ColumnDataSource(data=dict( start=["A", "A", "B", "C"], end=["B", "C", "D", "D"], value=[2, 3, 1, 4], color=["#96969680"]*4 )) sankey = Sankey( source=source, start="start", end="end", value="value", line_color="color", line_alpha=0.8 ) p = figure(width=600, height=400, tools="hover") p.add_layout(sankey) # 自定义JS回调处理悬停高亮 hover_callback = CustomJS(args=dict(source=source, sankey=sankey), code=""" const data = source.data; if (!cb_data.index.indices.length) return; const hovered_idx = cb_data.index.indices[0]; const hovered_label = sankey.nodes[hovered_idx].name; // 重置所有连接颜色 data.color.fill("#96969680"); // 标记流入/流出连接 for (let i=0; i<data.start.length; i++) { if (data.end[i] === hovered_label) { data.color[i] = "#0000ffb3"; } else if (data.start[i] === hovered_label) { data.color[i] = "#ff0000b3"; } } source.change.emit(); """) # 绑定悬停工具回调 hover_tool = p.select_one(HoverTool) hover_tool.callback = hover_callback show(p)
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

