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

如何在Plotly中捕获用户操作图例后的可见图例项?

如何在Plotly中捕获用户操作图例后的可见图例项?

我完全懂你的需求——用户点击雷达图的图例切换显示状态后,你想实时拿到当前还在展示的Zone名称,用来后续过滤数据框做分析对吧?结合你用的Streamlit+Plotly场景,我们可以通过Streamlit会话状态 + Plotly的restyle事件监听来实现,我来一步步给你说怎么改代码:


完整修改后的可运行代码

import streamlit as st
import pandas as pd
from typing import Dict, List
import plotly.graph_objects as go

metrics = {
    "Baseline": [60, 70, 80, 65, 90],
    "Zone A": [75, 85, 70, 80, 95],
    "Zone B": [65, 60, 75, 70, 85],
    "Zone C": [55, 65, 85, 60, 80],
    "Zone D": [80, 90, 70, 85, 88]
}

categories = ["Safety", "Infrastructure", "Resources", "Response Time", "Efficiency"]

def create_radar_chart(metrics: Dict[str, List[float]], categories: List[str]) -> go.Figure:
    """Create radar chart."""
    fig = go.Figure()
    fig.add_trace(go.Scatterpolar(
        r=metrics['Baseline'],
        theta=categories,
        fill='toself',
        name='Baseline',
        line=dict(color='#808080'),
        fillcolor='rgba(128, 128, 128, 0.2)'
    ))

    colors = ['#00FFB8', '#4A9DFF', '#FF6B6B', '#FFD93D', '#6C63FF']
    for i, (zone, values) in enumerate(metrics.items()):
        if zone != 'Baseline':
            color = colors[i % len(colors)]
            fig.add_trace(go.Scatterpolar(
                r=values,
                theta=categories,
                fill='toself',
                name=zone,
                line=dict(color=color),
                fillcolor=f'rgba{tuple(list(int(color.lstrip("#")[i:i+2], 16) for i in (0, 2, 4)) + [0.4])}'
            ))

    fig.update_layout(
        width=500,
        height=500,
        polar=dict(
            radialaxis=dict(
                visible=True,
                range=[0, max([max(vals) for vals in metrics.values()]) * 1.1]
            ),
            bgcolor='rgba(0,0,0,0)'
        ),
        showlegend=True,
        paper_bgcolor='rgba(0,0,0,0)',
        plot_bgcolor='rgba(0,0,0,0)',
        font_color = '#7A7F8C',
        legend_font_color = '#7A7F8C',
    )

    return fig

# 初始化会话状态:持久化存储当前可见的Zone名称
if "visible_zones" not in st.session_state:
    st.session_state.visible_zones = list(metrics.keys())  # 初始状态下所有区域都可见

# 创建并显示雷达图,指定唯一key用于捕获交互事件
fig = create_radar_chart(metrics, categories)
st.plotly_chart(fig, key="radar_chart")

# 捕获图例点击触发的restyle事件
chart_interaction = st.session_state.get("radar_chart", {})
restyle_event = chart_interaction.get("restyleData")

if restyle_event:
    # 解析事件数据:格式为 [{'visible': 可见状态}, [受影响的trace索引列表]]
    visible_status = restyle_event[0].get("visible")
    affected_traces = restyle_event[1]

    for trace_idx in affected_traces:
        trace_name = fig.data[trace_idx].name
        # 处理可见状态:如果是批量修改则取对应索引的值,否则直接用当前状态
        is_visible = visible_status[trace_idx] if isinstance(visible_status, list) else visible_status

        # 更新会话状态中的可见Zone列表
        if is_visible and trace_name not in st.session_state.visible_zones:
            st.session_state.visible_zones.append(trace_name)
        elif not is_visible and trace_name in st.session_state.visible_zones:
            st.session_state.visible_zones.remove(trace_name)

# 展示当前可见的Zone,这里可以替换成你的数据分析逻辑
st.subheader("当前可见的区域")
st.markdown(f"可见项:{', '.join(st.session_state.visible_zones)}")

# 示例:用可见Zone过滤数据框(假设你有对应的业务数据)
# sample_df = pd.DataFrame({
#     "Zone": ["Zone A", "Zone B", "Zone C", "Zone D", "Baseline"],
#     "Overall Score": [85, 75, 70, 88, 75]
# })
# filtered_df = sample_df[sample_df["Zone"].isin(st.session_state.visible_zones)]
# st.subheader("过滤后的数据分析")
# st.dataframe(filtered_df)

核心逻辑说明

  1. 会话状态持久化:用st.session_state.visible_zones保存可见区域列表,避免Streamlit每次重运行后丢失状态。
  2. 交互事件捕获:给st.plotly_chart指定唯一key,Streamlit会自动把图表的所有交互事件(比如图例点击)存在对应的会话状态中。
  3. 事件解析与状态更新:图例点击会触发Plotly的restyle事件,我们解析事件里的trace索引和可见状态,实时更新visible_zones列表。
  4. 业务联动:你可以直接用st.session_state.visible_zones过滤数据框,实现图表和后续分析模块的联动。

备注:内容来源于stack exchange,提问作者Sree

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 15:47:56