别再只会用groupby了!Pandas resample() 处理时间序列数据的5个实战场景(附代码)

当你面对时间序列数据时,是否还在机械地使用groupby进行月度汇总?实际上,Pandas的resample()方法才是时间维度聚合的"专业选手"。本文将带你突破groupby的思维定式,掌握resample()在真实业务场景中的高阶应用技巧。

1. 为什么resample()比groupby更适合时间序列?

groupby是数据分析师的瑞士军刀,但处理时间序列时却显得笨拙。假设我们需要计算2023年各月的销售总额:

# 使用groupby的常见写法
df['month'] = df['date'].dt.to_period('M')
monthly_sales = df.groupby('month')['sales'].sum()

# 使用resample的优雅写法
monthly_sales = df.set_index('date').resample('M')['sales'].sum()

两种方法结果相同,但resample()具有三大优势:

  1. 代码简洁性:无需创建辅助列,直接基于时间索引操作
  2. 时间精度保障:自动处理时区转换、闰秒等边界情况
  3. 灵活的频率转换:支持从纳秒到年的任意时间粒度转换

关键区别在于groupby是通用分组工具,而resample()是专为时间序列设计的频率转换器。当处理金融交易、传感器数据等时间敏感场景时,这种专业性差异会显著影响结果准确性。

2. 财务数据分析:如何正确计算月度收益率?

在量化金融中,计算资产收益率是常见需求。假设我们有某股票的日级交易数据:

import pandas as pd
import numpy as np

# 生成示例数据
dates = pd.date_range('2023-01-01', '2023-12-31', freq='B')
prices = np.exp(np.cumsum(np.random.normal(0.001, 0.02, len(dates))))
df = pd.DataFrame({'close': prices}, index=dates)

使用resample()计算月度收益率的正确姿势:

monthly_return = df['close'].resample('M').last().pct_change()

这里有两个关键点:

  • 使用.last()获取每月最后一个交易日的收盘价
  • pct_change()计算相邻月份的收益率变化

对比groupby实现可能存在的陷阱:

# 错误示范:直接按月groupby求均值
df.groupby(pd.Grouper(freq='M'))['close'].mean().pct_change()

这种写法会导致:

  1. 错误使用均价而非期末价计算收益
  2. 可能包含非交易日的无效数据
  3. 无法正确处理月末非交易日的情况

3. 物联网数据处理:传感器数据的动态聚合

物联网设备常产生高频但不规则的监测数据。假设某温度传感器每5-15分钟采集一次数据:

# 生成不规则时间戳数据
np.random.seed(42)
timestamps = pd.to_datetime('2023-06-01') + pd.to_timedelta(
    np.cumsum(np.random.randint(5, 15, 1000)), unit='min')
temps = 25 + np.cumsum(np.random.normal(0, 0.1, 1000))
df = pd.DataFrame({'temp': temps}, index=timestamps)

我们需要计算每小时的平均温度,但常规方法会遇到问题:

# 简单按小时groupby会丢失时间连续性
df.groupby(df.index.hour)['temp'].mean()

正确的resample()解决方案:

hourly_avg = df['temp'].resample('H').mean().ffill()

进阶技巧:使用closedlabel参数控制时间区间归属

# 确保每个小时包含前59分钟的数据
production_env = df['temp'].resample('H', closed='right', label='right').mean()

参数组合对比表:

参数组合 区间包含 标签显示 适用场景
closed='right', label='right' [09:00, 10:00) 10:00 生产环境监控
closed='left', label='left' (09:00, 10:00] 09:00 财务周期报表
closed='right', label='left' [09:00, 10:00) 09:00 科研数据分析

4. 业务报表自动化:从原始数据到周报生成

假设我们需要从订单数据生成周度经营报表:

orders = pd.DataFrame({
    'order_date': pd.date_range('2023-01-01', '2023-03-31', freq='4H'),
    'amount': np.random.lognormal(3, 1, 546)
}).set_index('order_date')

使用resample()一站式生成多维度报表:

weekly_report = orders.resample('W-MON').agg({
    'amount': ['sum', 'count', lambda x: x[x > 100].count()]
}).rename(columns={
    'sum': '周营收',
    'count': '总订单量',
    '<lambda>': '大额订单数'
})

实用技巧:结合pd.Grouper实现混合频率分析

# 同时分析周趋势和月对比
orders.groupby([
    pd.Grouper(freq='W-MON'), 
    pd.Grouper(freq='M')
])['amount'].sum().unstack()

5. 高频交易数据:tick数据的秒级聚合

处理股票tick数据时,常需要将原始委托记录转换为规则时间序列:

ticks = pd.DataFrame({
    'time': pd.to_datetime(['09:30:02.123', '09:30:02.456', '09:30:03.789']),
    'price': [101.2, 101.3, 101.25],
    'volume': [200, 150, 300]
}).set_index('time')

使用L指定毫秒级精度:

# 每500毫秒聚合一次
ohlc = ticks['price'].resample('500L').ohlc()
vol = ticks['volume'].resample('500L').sum()
pd.concat([ohlc, vol], axis=1)

性能优化:对于超高频数据,使用asof避免重复计算

# 获取每秒钟最后一个有效报价
ticks['price'].resample('S').asof()

6. 高级技巧:处理不完整时间序列的实战方案

实际业务中常遇到数据缺失问题。假设某门店营业时间不固定:

sales = pd.Series(
    [1200, 1500, np.nan, 1800, np.nan, 2000],
    index=pd.to_datetime(['2023-01-02', '2023-01-03', '2023-01-04',
                         '2023-01-05', '2023-01-06', '2023-01-07'])
)

常规resample()会保留缺失日期:

daily_sales = sales.resample('D').asfreq()

智能填充方案组合:

# 步骤1:前向填充营业日数据
filled = sales.resample('D').ffill(limit=1)

# 步骤2:标记非营业日为特殊值
result = filled.where(filled.notna(), -1)

# 步骤3:计算有效营业日的周均值
weekly_avg = filled.resample('W').mean()

处理节假日等特殊日期的推荐方案:

from pandas.tseries.holiday import USFederalHolidayCalendar
cal = USFederalHolidayCalendar()
holidays = cal.holidays(start='2023-01-01', end='2023-12-31')

# 排除节假日后的工作日均值
biz_days = sales.resample('B').mean().drop(holidays, errors='ignore')
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐