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

使用pandas DataFrame.plot()绘制双Y轴体重差柱状图与卡路里折线图

问题分析与解决

核心问题排查

  • 变量名拼写错误:代码中定义的卡路里列表变量是iest_cals,但后续调用add_nans和赋值给df['calories']时误用了未定义的est_cals,导致calories列全为NaN,自然无法绘制折线图。
  • X轴对齐问题:使用日期ordinal值作为X轴时,柱状图的X轴被识别为离散分类,折线图的数值型X轴容易出现对齐偏差,导致折线无法正常显示。

修正后的代码

import pandas as pd
import numpy as np
from datetime import date
import matplotlib.pyplot as plt
from mplcursors import cursor
from statistics import mean

def add_nans(cals: list, dates: pd.Series) -> list:
    # 避免修改原列表,创建新列表补充NaN
    new_cals = cals.copy()
    for _ in range(len(dates) - len(cals)):
        new_cals.append(np.nan)
    return new_cals

# Weight tracking
real_weight = [
    223.8, 222, 221.2, 220.8, 221.4, 219.4, 219.8, 219.2, 219.6, 218.2, 221.6, 220.2, 219.4, 218.8, 218, 217.4,
    218.6, 216.8, 217.6, 215.8, 216, 216, 219.6, 216.8, 216.2, 215.6, 215.8, 217.2, 214.6, 219.6, 217.4, 216.0,
    215.2, 214.2, 214.4, 216.4, 215.4, 215, 214.2, 214.4, 216, 214.2
] 
# 修正变量名拼写
est_cals = [
    2129, 2323, 2298, 1984, 2020, 2102, 1980, 2386, 2146, 2377, 2500, 2265, 2150, 1840, 2073, 2070, 2050, 2108,
    2130, 2770, 2170, 2270, 1955, 2030, 2020, 2500, 1910, 2150, 2695, 1980, 2110, 2190, 2940, 1970, 2750, 1870,
    2470, 2200, 2400, 4000, 2200, 1916
]
lw = len(real_weight)
dates = pd.date_range(start='01/11/2024', end='07/11/2024') 
# 传递正确的变量名给add_nans
real_weight = add_nans(cals=real_weight, dates=dates)
est_cals = add_nans(cals=est_cals, dates=dates)
difference = [0] + [round(j-i,4) for i, j in zip(real_weight[:-1], real_weight[1:])]
goalw, startw = 185, real_weight[0] 
lin_weight = np.linspace(startw, goalw, len(dates))
lpw = round(7 * (lin_weight[0] - lin_weight[1]), 2)
df = pd.DataFrame(data={'date': dates, 'lin_weight': lin_weight, 'real_weight': real_weight})
# 赋值正确的卡路里数据
df['calories'] = est_cals
df['day'] = df.date
df['difference'] = difference
df['ordinal'] = pd.to_datetime(df['date']).apply(lambda date: date.toordinal())
df['idx'] = df.index
df = df.set_index('date')
startdt, enddt = df.index[0].date(), df.index[-1].date()
avg_dif = df.difference.abs().mean()

# 清洗数据:仅删除体重和卡路里均为空的行
df_clean = df.dropna(subset=['real_weight', 'calories'], how='all')
day = [f'day {i+1}' for i in range(len(df_clean.index))]
df_clean['day_1'] = day
max_day = df_clean.loc[df_clean['difference'] == df_clean.difference.max(), 'day_1'].item()
min_day = df_clean.loc[df_clean['difference'] == df_clean.difference.min(), 'day_1'].item()

# 绘图逻辑:用日期索引保证双Y轴对齐
fig, ax1 = plt.subplots(figsize=(12,6))

# 绘制体重差值柱状图
ax1.bar(df_clean.index, df_clean['difference'], color='magenta', label='Daily Weight Difference (lbs)')
ax1.axhline(y=0, linewidth=1, c='black', linestyle='-')
ax1.set_xlabel('Days into Diet')
ax1.set_ylabel('Difference in Daily Weight (lbs)')
ax1.set_title(f'Daily Weight Difference vs Calorie Intake ({startdt} to {df_clean.tail(1).index.item().date()})\n')
# 设置自定义X轴刻度标签
ax1.set_xticks(df_clean.index)
ax1.set_xticklabels(day, rotation=45)

# 绘制卡路里折线图(双Y轴)
ax2 = ax1.twinx()
ax2.plot(df_clean.index, df_clean['calories'], color='darkorange', linestyle='-', marker='o', label='Daily Calorie Intake (kcal)')
ax2.set_ylabel('Daily Calorie Intake (kcal)')

# 合并双Y轴图例
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1 + lines2, labels1 + labels2, loc='upper right')

cursor(hover=True)
plt.tight_layout()
plt.show()

关键修正说明

  1. 变量名统一:将拼写错误的iest_cals改为est_cals,确保卡路里数据能正确写入DataFrame。
  2. NaN处理优化:修改add_nans函数避免破坏原数据,同时调整dropna逻辑,仅删除两列数据均为空的行,保留有效记录。
  3. X轴对齐修复:直接使用DataFrame的日期索引作为X轴,彻底解决柱状图与折线图的X轴错位问题。
  4. 图例优化:手动合并双Y轴的图例,让图表信息展示更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:44:55