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
相关产品推荐
相关产品推荐

