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

如何用Python/Matplotlib/Seaborn按天分组绘制小时平均volume图表?

实现类似Excel透视表的按天按小时Volume平均值图表(Python/Seaborn/Matplotlib)

问题背景

现有mean_by_day_hour DataFrame结构如下:

day  hour  volume
0    Mon     0  301.25
1    Mon     0  260.50
2    Mon     0  329.00
3    Mon     1  149.00
4    Mon     1  161.00
..   ...   ...     ...
499  Sun    22  722.00
500  Sun    22  857.00
501  Sun    23  600.25
502  Sun    23  457.00
503  Sun    23  510.00

需要绘制按天、按小时统计的volume平均值图表,效果和Excel透视表一致。曾尝试添加time_interval列并手动调整xticks,但存在两个问题:

  • 无法实现Excel式的双X轴(上层为day,下层为hour)效果
  • 手动调整xticks不够自动化,DataFrame结构变化时需重新修改代码

倾向使用Seaborn的hue参数,也接受Matplotlib实现,当前代码如下:

#Loc is to order by day of week, not relevant here
mean_by_day_hour = pd.pivot_table(df, values=["volume"], index=["day", "hour", "period"], aggfunc=np.mean).loc[get_day_names(df, "day_number")]
print(mean_by_day_hour)
mean_by_day_hour.reset_index(inplace=True)
#Workaround by creating time_interval column
mean_by_day_hour["time_interval"] = mean_by_day_hour["day"].astype(str) + "-" + mean_by_day_hour["hour"].astype(str) + "h"
plt.figure(figsize=(10, 5))
sns.lineplot(x="time_interval", y='volume', hue="period", data=mean_by_day_hour, errorbar=None)
#df_list has the different dataframes: one per period
plt.xticks(mean_by_day_hour["time_interval"].iloc[0:int(len(mean_by_day_hour)/len(df_list)):12].index, mean_by_day_hour["time_interval"].iloc[0:len(mean_by_day_hour):12*len(df_list)].values)
plt.show()

解决方案

步骤1:预处理数据(规范顺序+聚合平均值)

首先确保星期按正确顺序排序,同时完成day-hour-period组合的平均值计算:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

# 定义固定的星期顺序,避免字符串排序混乱
day_order = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

# 聚合计算各组合的平均值(如果原数据未做聚合)
agg_df = mean_by_day_hour.groupby(['day', 'hour', 'period'])['volume'].mean().reset_index()
# 将day转为分类类型,强制按指定顺序排序
agg_df['day'] = pd.Categorical(agg_df['day'], categories=day_order, ordered=True)
agg_df = agg_df.sort_values(['day', 'hour'])

步骤2:绘制双X轴图表(完全模拟Excel透视表效果)

通过Matplotlib双轴功能结合Seaborn,实现上层显示星期、下层显示小时的效果,全程自动适配数据:

plt.figure(figsize=(16, 6))

# 用数据索引作为x轴基准,方便后续定位刻度
sns.lineplot(x=agg_df.index, y='volume', hue='period', data=agg_df, marker='o', errorbar=None)

# 处理下层X轴(小时)
# 提取每个hour对应的第一个数据索引位置
hour_positions = agg_df.groupby(['day', 'hour']).first().index.get_level_values(0).index
hour_labels = agg_df.groupby(['day', 'hour']).first()['hour'].values
plt.xticks(ticks=hour_positions, labels=hour_labels, rotation=0)

# 添加上层X轴(星期)
ax1 = plt.gca()
ax2 = ax1.twiny()
# 计算每个星期对应的中间索引位置作为刻度点
day_positions = agg_df.groupby('day').apply(lambda x: x.index.mean()).values
day_labels = agg_df['day'].unique()
ax2.set_xticks(ticks=day_positions)
ax2.set_xticklabels(day_labels, fontweight='bold')
# 调整上层轴的位置,使其显示在下层轴上方
ax2.spines['top'].set_position(('outward', 30))

# 图表美化
ax1.set_xlabel('Hour', fontsize=12)
ax2.set_xlabel('Day', fontsize=12)
plt.ylabel('Average Volume', fontsize=12)
plt.title('Average Volume by Day and Hour', fontsize=14)
plt.tight_layout()
plt.show()

备选方案:按天分组的子图展示

如果不需要严格的双轴,也可以用Seaborn的catplot生成按天分组的子图,每个子图展示对应天数的小时趋势:

g = sns.catplot(x='hour', y='volume', hue='period', col='day', col_order=day_order,
                data=agg_df, kind='line', errorbar=None, height=4, aspect=0.8)
g.set_titles('{col_name}')
plt.tight_layout()
plt.show()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 05:12:07