Python案例解析Pandas数据分组重采基准
📖 目录导览
为什么需要数据分组重采样?
在实际数据分析中,我们经常遇到这样的场景:原始数据记录频率不一致(比如每秒采集一次传感器数据),但我们需要按小时或天汇总;或者面对多支股票、多个门店的逐笔交易数据,需要分别按周统计每个主体的平均值。数据分组重采样正是解决这类“不同组别+不同时间粒度”需求的利器。

典型场景举例:
- 电商:按商品品类统计每日销售额
- 金融:按股票代码计算每周收益率
- IoT:按设备ID聚合每小时传感器均值
Pandas重采样核心函数速览
Pandas的resample()函数是时间序列重采样的基础,而groupby()负责分组,两者结合时,需要特别注意调用顺序。
基础语法:
df.groupby('分组列').resample('时间频率').聚合方法()
常用时间频率参数(offset aliases): | 参数 | 含义 | |--------|----------| | 'D' | 日 | | 'W' | 周 | | 'M' | 月末 | | 'MS' | 月初 | | 'Q' | 季度末 | | 'Y' | 年末 |
常用聚合方法:
mean()平均值sum()求和ohlc()开盘-最高-最低-收盘ffill()/bfill()向前/向后填充
真实案例:股票交易数据分组重采样
数据准备
假设我们有三个股票(AAPL、GOOG、MSFT)的分钟级交易数据,存储在一个CSV文件中:
import pandas as pd
import numpy as np
# 生成模拟数据(实际使用请从文件读取)
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=5000, freq='T')
stocks = ['AAPL', 'GOOG', 'MSFT']
df = pd.DataFrame({
'stock': np.random.choice(stocks, 5000),
'price': np.random.uniform(100, 500, 5000),
'volume': np.random.randint(100, 10000, 5000)
}, index=dates)
df.index.name = 'timestamp'
按股票+日级别重采样
需求1:计算每日每只股票的平均收盘价和总成交量
result = df.groupby('stock').resample('D').agg({
'price': 'mean',
'volume': 'sum'
})
result.head(10)
输出结果展示:
price volume
stock timestamp
AAPL 2024-01-01 313.215 102345
2024-01-02 298.456 98765
2024-01-03 321.789 110234
...
需求2:计算每周每只股票的OHLC(开盘、最高、最低、收盘)
weekly_ohlc = df.groupby('stock').resample('W').agg({
'price': 'ohlc'
})
weekly_ohlc.head()
分组+重采样组合技巧
技巧1:多层索引的处理
分组重采样后,DataFrame会生成(股票, 时间)的双层索引,如果要转为扁平结构:
result = result.reset_index(level=0) # 保留stock列 # 或者完全重置索引 result = result.reset_index()
技巧2:自定义聚合函数
需要计算过去7天成交量均值(不包含当天):
def custom_mean(x):
return x.shift(1).rolling(7).mean()
result = df.groupby('stock').resample('D')['volume'].apply(custom_mean)
技巧3:向前向后填充
当某些时间段没有数据时,避免出现NaN:
# 向前填充(使用上一周期数据)
df.groupby('stock').resample('D').first().ffill()
# 向后填充(使用下一周期数据)
df.groupby('stock').resample('D').first().bfill()
性能优化与常见坑
性能优化建议:
- 先排序再分组:确保时间索引排序,避免
resample报错df = df.sort_index()
- 使用
as_index=False:减少内存占用df.groupby('stock', as_index=False).resample('D') - 大数据集分块处理:使用
pd.read_csv(chunksize=10000)
常见问题(坑):
| 问题描述 | 解决方案 |
|---|---|
| 时间索引未设置 | 执行df.set_index('timestamp', inplace=True) |
| 非字符串日期 | 转换为datetime类型:pd.to_datetime() |
| 分组列与时间索引混淆 | 确认groupby列是普通列,resample对象是DatetimeIndex |
| 重采样后数据量暴增 | 检查是否误用了resample的how参数(已弃用) |
问答环节
Q1: resample和period有什么区别?
A: resample用于将时间序列从高频降为低频(如分钟→小时),而period主要处理时间戳的周期转换,分组重采样场景resample更灵活。
Q2: 重采样时能否同时按多个字段分组?
A: 可以,例如按[stock, exchange]两字段分组:
df.groupby(['stock', 'exchange']).resample('D').mean()
Q3: 如何重置多层索引为普通列?
A: 使用reset_index()可以一次性将所有索引变为列,如果需要保留某个索引层级,用reset_index(level=0)。
Q4: 我的数据时间戳跨年,resample能否正确处理?
A: Pandas自动处理时间边界,如果使用'AS'(年初)或'YS'(年起始),会按自然年分割。
掌握Pandas分组重采样,相当于获得了时间序列分析的瑞士军刀,本文通过股票数据案例,演示了从基础聚合到自定义函数的灵活应用,实际工作中,这个技巧能大幅提升处理交易数据、传感器数据、日志数据的效率。
推荐学习路径:
- 先理解
resample原生功能 - 结合
groupby做多实体分析 - 进阶学习
apply+自定义函数
参考了Pandas官方文档及Stack Overflow经典案例,经过二次整理形成,如需转载,请保留出处。*