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

Python如何制作带双Y轴的动画图表,解决不同尺度数据展示问题

问题描述

我想要制作带次Y轴的动画图表,当前正在使用plotly.express实现,也可接受其他开源方案。我已查阅过多组柱状图动画、次坐标轴添加的相关实现方法,但尚未找到带次Y轴的动画图表的落地方案。

现有代码

import pandas as pd
import plotly.express as px
df = pd.read_csv("plotly_animation_stackoverflow.csv")
px.bar(data_frame=df,x="date",y=["A","B","C"],animation_frame="lag",barmode="group")

运行后由于A、B列数值和C列数值尺度差异过大,C列的柱状图完全无法显示:
现有代码运行效果图

补充需求

我的数据集会随着lag值增加而扩展,这类需求在Tableau中可以很容易实现,但我希望使用开源方案落地。请问是否有方案支持向图表传入自定义函数,实现拖动滑块时自动应用对应lag值计算的效果?

数据集示例

date,A,B,C,lag
8/22/2016,54987,36488,0.3389,0
8/23/2016,91957,73793,0.3389,0
8/24/2016,91957,73793,0.3357,0
8/25/2016,91957,73793,0.3291,0
8/26/2016,91957,73793,0.3295,0
8/29/2016,91957,73793,0.3281,0
8/30/2016,107657,82877,0.3273,0
8/31/2016,107657,82877,0.3247,0
9/1/2016,107657,82877,0.322,0
9/2/2016,107657,82877,0.3266,0
8/22/2016,54987,36488,NA,1
8/23/2016,91957,73793,0.3389,1
8/24/2016,91957,73793,0.3389,1
8/25/2016,91957,73793,0.3357,1
8/26/2016,91957,73793,0.3291,1
8/29/2016,91957,73793,0.3295,1
8/30/2016,107657,82877,0.3281,1
8/31/2016,107657,82877,0.3273,1
9/1/2016,107657,82877,0.3247,1
9/2/2016,107657,82877,0.322,1
9/3/2016,,,0.3266,1
8/22/2016,54987,36488,,2
8/23/2016,91957,73793,,2
8/24/2016,91957,73793,0.3389,2
8/25/2016,91957,73793,0.3389,2
8/26/2016,91957,73793,0.3357,2
8/29/2016,91957,73793,0.3291,2
8/30/2016,107657,82877,0.3295,2
8/31/2016,107657,82877,0.3281,2
9/1/2016,107657,82877,0.3273,2
9/2/2016,107657,82877,0.3247,2
9/3/2016,,,0.322,2
9/4/2016,,,0.3266,2

解决方案

plotly express本身对次Y轴和动画的组合支持度较低,你可以用更灵活的plotly.graph_objects实现需求,若需要自定义滑块触发逻辑,也可以搭配Dash框架实现完全自定义的交互效果。

方案1:基于plotly.graph_objects实现带次Y轴的动画图表

这个方案直接生成静态HTML动画,无需额外服务,满足基础的次Y轴+动画需求:

import pandas as pd
import plotly.graph_objects as go

# 读取并预处理数据
df = pd.read_csv("plotly_animation_stackoverflow.csv", parse_dates=["date"])
lag_list = sorted(df["lag"].unique())

# 生成每一帧的内容
frames = []
for lag in lag_list:
    df_lag = df[df["lag"] == lag].dropna(subset=["A", "B", "C"], how="all")
    frames.append(
        go.Frame(
            data=[
                # 主Y轴的A、B柱状图
                go.Bar(x=df_lag["date"], y=df_lag["A"], name="A", yaxis="y", offsetgroup=0),
                go.Bar(x=df_lag["date"], y=df_lag["B"], name="B", yaxis="y", offsetgroup=1),
                # 次Y轴的C柱状图
                go.Bar(x=df_lag["date"], y=df_lag["C"], name="C", yaxis="y2", offsetgroup=2),
            ],
            name=str(lag)
        )
    )

# 初始化图表
fig = go.Figure(
    data=frames[0]["data"],
    layout=go.Layout(
        barmode="group",
        # 主Y轴配置
        yaxis=dict(title="A/B数值", side="left"),
        # 次Y轴配置
        yaxis2=dict(title="C数值", side="right", overlaying="y", showgrid=False),
        # 动画滑块配置
        sliders=[dict(
            steps=[dict(
                method="animate",
                args=[[str(lag)], dict(mode="immediate")],
                label=str(lag)
            ) for lag in lag_list]
        )]
    ),
    frames=frames
)

fig.show()

上述代码会将A、B列放在左侧主Y轴,C列放在右侧次Y轴,滑块切换时自动加载对应lag的数据集,自动适配数据范围。

方案2:基于Dash实现自定义滑块逻辑

如果需要滑块拖动时动态计算/扩展数据集,可以用Dash框架实现回调交互,完全满足自定义函数触发的需求:

from dash import Dash, dcc, html, Input, Output
import pandas as pd
import plotly.graph_objects as go

app = Dash(__name__)
# 这里可以替换成你的全量数据集,也可以在回调里实时读取/计算
df_full = pd.read_csv("plotly_animation_stackoverflow.csv", parse_dates=["date"])
max_lag = df_full["lag"].max()

app.layout = html.Div([
    dcc.Slider(min=0, max=max_lag, step=1, value=0, id="lag_slider"),
    dcc.Graph(id="anim_chart")
])

@app.callback(
    Output("anim_chart", "figure"),
    Input("lag_slider", "value")
)
def update_chart(selected_lag):
    # 这里可以写任意自定义逻辑,比如实时计算对应lag的数据集,无需提前预处理全量数据
    df_lag = df_full[df_full["lag"] == selected_lag].dropna(subset=["A", "B", "C"], how="all")
    # 生成图表
    fig = go.Figure()
    fig.add_trace(go.Bar(x=df_lag["date"], y=df_lag["A"], name="A"))
    fig.add_trace(go.Bar(x=df_lag["date"], y=df_lag["B"], name="B"))
    fig.add_trace(go.Bar(x=df_lag["date"], y=df_lag["C"], name="C", yaxis="y2"))
    fig.update_layout(
        barmode="group",
        yaxis=dict(title="A/B数值", side="left"),
        yaxis2=dict(title="C数值", side="right", overlaying="y", showgrid=False)
    )
    return fig

if __name__ == "__main__":
    app.run_server(debug=True)

启动服务后拖动滑块即可触发update_chart函数,你可以在函数内实现任意的lag对应逻辑,完全匹配Tableau的交互体验。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 23:45:04