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

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里的数值渲染。

问题现象

  1. 条形图的高度/长度完全不对应实际的训练量数值(我实际的数值是[0,0,1200,0,3286,0,0]),反而像是在显示0-6的递增索引值;
  2. 不管训练量是不是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)

问题原因分析

  1. 未过滤0值数据:原始代码包含了所有7天的数据,包括训练量为0的休息日,Plotly会为这些日期绘制高度/长度为0的条形(几乎不可见但确实存在);
  2. 日期轴的类型问题:直接使用Timestamp作为轴数值,Plotly会把它当成连续的时间轴而非分类轴,导致数值映射异常;转成字符串后,Plotly会将其识别为独立分类,渲染逻辑正常;
  3. 横向条形图参数缺失:使用横向条形图时必须显式设置orientation='h',否则Plotly会用默认的纵向逻辑处理,导致数值和轴的映射关系错误。

调整后应该就能完全匹配你Matplotlib版本的显示效果,同时支持Streamlit的钻取交互了!

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:13:00