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

Plotly下拉交互时散点图水平中位数线未正确更新问题

问题解决:Plotly下拉切换Y轴后水平中位数线不更新

问题原因

你的下拉菜单仅更新了散点图的trace数据(x/y值、标题等),但水平中位数线是布局(layout)中的shape元素,而非trace数据,原代码没有在下拉切换时同步更新这部分内容,导致水平中线始终停留在初始变量的中位数。垂直中线正常是因为rows列的中位数固定不变。

修复代码

import pandas as pd
import plotly.graph_objects as go

def plot_dq_scatter_dropdown(df):
    fig = go.Figure()
    
    # 初始化默认视图
    default_y = df.columns[1]
    median_x = df["rows"].median()
    median_y_default = df[default_y].median()
    
    # 添加初始散点
    fig.add_trace(go.Scatter(
        x=df["rows"],
        y=df[default_y],
        mode='markers',
        marker=dict(color=df['outlier_prob'], colorscale='viridis', showscale=True, colorbar=dict(title='Outlier Probability')),
        hoverinfo='text',
        text=df.index,
        name=f'{default_y} vs rows',
        showlegend=False
    ))
    
    # 添加初始中位数线
    fig.add_vline(x=median_x, line=dict(color="orange", dash="dash", width=2), 
                  annotation_text="Median rows", annotation_position="top left")
    fig.add_hline(y=median_y_default, line=dict(color="orange", dash="dash", width=2), 
                  annotation_text=f"Median {default_y}, {median_y_default}", annotation_position="top left")
    
    # 更新布局和下拉菜单
    fig.update_layout(
        title=f"Scatter Plot: rows vs {default_y}",
        xaxis_title="rows",
        yaxis_title=default_y,
        autosize=True,
        updatemenus=[dict(
            type="dropdown",
            x=0.17,
            y=1.15,
            showactive=True,
            buttons=[
                dict(
                    label=f"{y}",
                    method="update",
                    args=[
                        # 更新散点trace数据
                        {
                            'y': [df[y]],
                            'x': [df["rows"]],
                            'marker': dict(color=df['outlier_prob'], colorscale='viridis', showscale=True, colorbar=dict(title='Outlier Probability')),
                            'name': f'{y} vs rows'
                        },
                        # 更新布局:标题、Y轴标签、中位数线
                        {
                            'title': f"Scatter Plot: rows vs {y}",
                            'yaxis.title': y,
                            # 重新生成中位数线的shape数组
                            'shapes': [
                                # 垂直中线(固定)
                                dict(
                                    type='line',
                                    x0=median_x, x1=median_x,
                                    y0=0, y1=1,
                                    yref='paper',
                                    line=dict(color="orange", dash="dash", width=2)
                                ),
                                # 水平中线(随Y变量变化)
                                dict(
                                    type='line',
                                    y0=df[y].median(), y1=df[y].median(),
                                    x0=0, x1=1,
                                    xref='paper',
                                    line=dict(color="orange", dash="dash", width=2)
                                )
                            ],
                            # 更新中位数线的注释
                            'annotations': [
                                dict(
                                    x=median_x, y=1,
                                    xref='x', yref='paper',
                                    text="Median rows",
                                    showarrow=False,
                                    xanchor='left', yanchor='bottom'
                                ),
                                dict(
                                    x=0, y=df[y].median(),
                                    xref='paper', yref='y',
                                    text=f"Median {y}, {df[y].median():.2f}",
                                    showarrow=False,
                                    xanchor='left', yanchor='bottom'
                                )
                            ]
                        }
                    ]
                ) for y in df.columns if y not in ["rows", "outlier_prob"]
            ]
        )]
    )
    
    fig.show()

# 测试数据
df_input = pd.DataFrame({
    'rows': range(1, 101),
    'column_a': [i + (i % 10) for i in range(1, 101)],
    'column_b': [i * 2 for i in range(1, 101)],
    'column_c': [i ** 0.5 for i in range(1, 101)],
    'outlier_prob': [0.01 * (i % 10) for i in range(1, 101)]
})

# 调用函数
plot_dq_scatter_dropdown(df_input)

关键修改点

  1. 手动定义中位数线的shape:add_hline/add_vline本质是在fig.layout.shapes中添加元素,手动定义shape可以更灵活地在下拉切换时替换整个shapes数组。
  2. 下拉菜单同步更新布局的shapes和annotations:在每个按钮的args第二个参数(布局更新)中,重新计算当前Y变量的中位数,生成新的水平中线shape和对应的注释文本。
  3. 固定垂直中线:因为rows列的中位数固定,所以垂直中线的shape可以直接复用,无需重新计算。

内容的提问来源于stack exchange,提问作者PeCaDe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:45:55