Plotly Graph Objects Bar图未正确渲染DataFrame数值,显示递增索引值的问题求助
Plotly Graph Objects Bar图未正确渲染DataFrame数值,显示递增索引值的问题求助
大家好!最近我在用Streamlit做一个健身日志的仪表盘,遇到了Plotly Graph Objects Bar图的渲染问题,折腾了好几个小时都没搞定,来这里求助各位大佬!
背景情况
我有JSON格式的训练日志数据,先把它转换成Pandas DataFrame,然后计算最近7天的训练总量(volume = 每组reps * weight的总和)。我用Matplotlib做的图完全符合预期,但换成Plotly Graph Objects之后就出问题了——因为要实现钻取交互效果必须用Plotly,但现在图表完全不按DataFrame里的数值渲染。
问题现象
- 条形图的高度/长度完全不对应实际的训练量数值(我实际的数值是
[0,0,1200,0,3286,0,0]),反而像是在显示0-6的递增索引值; - 不管训练量是不是0,图表上都会预留条形位置,而不是只在有训练的日期(非0值)显示条形。
我已经在仪表盘上显示了DataFrame验证过,数值是完全正确的,但Plotly就是不渲染正确的数值。
相关代码
1. 加载并处理训练数据
import json import pandas as pd import plotly.graph_objects as go import streamlit as st # Load and flatten workout data with open("workout_log.json") as f: raw_data = json.load(f) rows = [] notes_by_date = {} for entry in raw_data: date = pd.to_datetime(entry["date"]) # Use full Timestamp objects notes_by_date[date.normalize()] = entry.get("notes", "") # Normalize to midnight for matching for ex in entry["exercises"]: for s in ex["sets"]: rows.append({ "date": date.normalize(), # Store as normalized Timestamp "workout": entry.get("workout_name", ""), "exercise": ex["name"], "type": ex.get("type", ""), "reps": s["reps"], "weight": s["weight"] }) df = pd.DataFrame(rows)
2. 计算最近7天的训练总量
# Ensure last 7 days are always present, even if rest days today = pd.Timestamp.today().normalize() last_7_days = [today - pd.Timedelta(days=i) for i in range(6, -1, -1)] volume_by_day = ( df.groupby("date") .apply(lambda x: (x["reps"] * x["weight"]).sum()) .reindex(last_7_days, fill_value=0) ) df_volume = volume_by_day.reset_index() df_volume.columns = ["date", "volume"] df_volume = df_volume.set_index("date")
3. 现在有问题的Plotly代码
fig = go.Figure() fig.add_trace(go.Bar( y=df_volume.index, x=df_volume['volume'], marker_color='indigo' )) fig.update_layout( title="️ Training Volume — Last 7 Days", yaxis_title="Date", xaxis_title="Total Volume (kg)", xaxis_tickangle=-45, height=400 ) st.plotly_chart(fig)
我尝试过的方法
- 调换x和y轴的参数
- 重命名DataFrame的列和索引
- 直接使用
volume_by_day而不是转成df_volume - 手动设置轴的范围
但都没有解决问题,还是显示错误的条形长度/高度。
期望的效果
和Matplotlib版本一致:
- 只在训练量>0的日期显示条形
- 条形的高度/长度严格对应实际的训练量数值
有没有大佬能帮我看看问题出在哪?感谢!
内容来源于stack exchange
解决方案(针对上述问题)
你的问题主要出在三个地方:未过滤0值数据、日期轴的类型处理和横向条形图的参数缺失,调整后就能正常渲染了:
方案1:纵向条形图(x轴日期,y轴训练量)
如果你的Matplotlib参考图是纵向的,用这段代码:
# 过滤掉训练量为0的休息日,只保留有训练的日期 df_volume_filtered = df_volume[df_volume['volume'] > 0].reset_index() fig = go.Figure() fig.add_trace(go.Bar( # 把Timestamp转成易读的日期字符串,让Plotly识别为分类轴 x=df_volume_filtered['date'].dt.strftime('%Y-%m-%d'), y=df_volume_filtered['volume'], marker_color='indigo' )) fig.update_layout( title="️ Training Volume — Last 7 Days", xaxis_title="Date", yaxis_title="Total Volume (kg)", xaxis_tickangle=-45, height=400 ) st.plotly_chart(fig)
方案2:横向条形图(y轴日期,x轴训练量)
如果坚持用横向条形图,必须显式指定orientation='h',代码如下:
df_volume_filtered = df_volume[df_volume['volume'] > 0].reset_index() fig = go.Figure() fig.add_trace(go.Bar( y=df_volume_filtered['date'].dt.strftime('%Y-%m-%d'), x=df_volume_filtered['volume'], marker_color='indigo', orientation='h' # 必须明确指定横向方向 )) fig.update_layout( title="️ Training Volume — Last 7 Days", yaxis_title="Date", xaxis_title="Total Volume (kg)", height=400 ) st.plotly_chart(fig)
问题原因分析
- 未过滤0值数据:原始代码包含了所有7天的数据,包括训练量为0的休息日,Plotly会为这些日期绘制高度/长度为0的条形(几乎不可见但确实存在);
- 日期轴的类型问题:直接使用
Timestamp作为轴数值,Plotly会把它当成连续的时间轴而非分类轴,导致数值映射异常;转成字符串后,Plotly会将其识别为独立分类,渲染逻辑正常; - 横向条形图参数缺失:使用横向条形图时必须显式设置
orientation='h',否则Plotly会用默认的纵向逻辑处理,导致数值和轴的映射关系错误。
调整后应该就能完全匹配你Matplotlib版本的显示效果,同时支持Streamlit的钻取交互了!
相关产品推荐
相关产品推荐

