You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 13:05:18