1

I have workouts logged in JSON like this:

[
  {
    "date": "2025-07-14",
    "workout_name": "Lower",
    "exercises": [
      {
        "name": "Barbell Back Squat",
        "type": "",
        "sets": [
          {
            "reps": 3,
            "weight": 85.0
          },
          {
            "reps": 3,
            "weight": 85.0
          }
        ]
      },
      {
        "name": "Barbell Deadlift",
        "type": "",
        "sets": [
          {
            "reps": 3,
            "weight": 115.0
          },
          {
            "reps": 3,
            "weight": 115.0
          }
        ]
      }
    ],
    "notes": "submax but still left me sore"
  },

I'm visualising them in a dashboard with streamlit, and I have a plotly.graph_objects.Bar chart showing the last 7 days, below is another version I made with matplotlib.pyplot that illustrates how it should look.

Working pyplot bar chart

But since I want a drill-through effect using streamlit, I'm now trying to recreate this using graph objects, and it will not adjust the bar height to the values in my dataframe, nor will it place bars only where non-0 values are present, it appears to be visualising an incremental index, even though I definitely have the y axis values pointing to my totals.

That is, it appears to be populating them with [0,1,2,3,4,5,6] rather than the actual values of [0,0,1200,0,3286,0,0].

Here's the Python I have to place my JSON in a pandas DataFrame:

# 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)

And here's the Python that calculates this new "volume per day" metric and places that in a new DataFrame attached to the last 7 days:

# 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")

And this code generates the chart:

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
)

I've changed the x and y axis around, I've renamed the index, I've renamed the values column, I've made the x and y axis point directly at this df:

volume_by_day = (
df.groupby("date")
.apply(lambda x: (x["reps"] * x["weight"]).sum())
.reindex(last_7_days, fill_value=0)

)

rather than making a new one. I've dictated the axis heights. Whatever I do, the height of the bars renders as [0,1,2,3,4,5,6].

For clarity, here is the result of the graph_objects.Bar in my dash, and I displayed the dataframe above it to illustrate the actual values:

incorrect graph_objects.Bar chart

1 Answer 1

0
fig.add_trace(go.Bar(
    y=df_volume.index,
    x=df_volume['volume'],
    marker_color='indigo'
))

You have set date as index and put it on y axis on your chart.

You should change it to:

fig.add_trace(go.Bar(
    x=df_volume.index,
    y=df_volume['volume'],
    marker_color='indigo'
))

Most of the time, it is proper to put date axis to xaxis (horizontal), so that it would be clear to understand the change by time.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, but this gives me a horizontal chart with the same [0,1,2,3,4,5,6] values, any combination of x and y pointing at those columns with 'orientation='h'' or 'orienatation='v'' also results in those values
Hi, I've fixed this - for anyone curious, the issue was something to do with how graph_objects parses a DataFrame, I bypassed this by using a reference to the pd.dataframe.groupby calculation reformatted as a list with the following code """ fig = go.Figure() fig.add_trace(go.Bar( x=volume_by_day.index.strftime("%Y-%m-%d").tolist(), y=volume_by_day.values.tolist(), marker_color='indigo', orientation='v' )) """

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.