1. 时间序列重采样基础概念

时间序列数据在金融、物联网、气象等领域无处不在。想象你手里有一份每分钟记录的股票价格数据,但老板需要的是每小时趋势报告;或者传感器每秒钟采集的温度数据,而你需要按日分析变化规律。这时候就需要用到**重采样(Resampling)**技术了。

重采样本质上是改变时间序列频率的过程,主要分为两类:

  • 下采样(Downsampling) :从高频到低频的转换(如分钟→小时)
  • 上采样(Upsampling) :从低频到高频的转换(如日→小时)

Pandas中的 resample() 就像个智能的时间漏斗,能自动帮我们完成这些转换。我处理过的一个物联网项目,原始数据是每10秒采集的设备状态,通过 resample('5T').mean() 就轻松转换成了5分钟粒度的分析数据。

2. 基础下采样操作实战

2.1 简单聚合操作

先看个股票数据的例子。假设我们有2023年某支股票的分钟级交易数据:

import pandas as pd
import numpy as np

# 生成示例数据:2023-01-01 9:30到11:30的分钟数据
date_range = pd.date_range('2023-01-01 09:30', '2023-01-01 11:30', freq='1T')
stock_data = pd.DataFrame({
    'price': np.random.uniform(100, 200, len(date_range)),
    'volume': np.random.randint(1000, 5000, len(date_range))
}, index=date_range)

# 转换为5分钟K线
five_min_kline = stock_data.resample('5T').agg({
    'price': 'ohlc',
    'volume': 'sum'
})

这样我们就得到了包含开盘价(open)、最高价(high)、最低价(low)、收盘价(close)和总成交量(sum)的标准K线数据。实际项目中,我常用这种方式快速生成不同时间维度的技术分析数据。

2.2 多维度聚合

对于复杂分析,我们可能需要同时计算多个指标。比如在电商销售分析中:

# 模拟小时级销售数据
sales_index = pd.date_range('2023-06-01', '2023-06-30', freq='H')
sales_data = pd.DataFrame({
    'amount': np.random.randint(100, 5000, len(sales_index)),
    'orders': np.random.randint(1, 50, len(sales_index))
}, index=sales_index)

# 按天统计:销售额总和、订单数平均、最大单笔金额
daily_stats = sales_data.resample('D').agg({
    'amount': ['sum', 'max'],
    'orders': ['mean', 'count']
})

这种多维聚合特别适合制作每日/每周经营报表。记得去年双十一大促时,我就是用这种方法实时监控销售指标的。

3. 高级参数深度解析

3.1 closed与label参数

这两个参数控制着"时间桶"的开关方式和标签位置,用个物流行业的例子说明:

# 快递分拣数据(每分钟记录)
package_data = pd.Series(
    np.random.randint(10, 100, 1440),
    index=pd.date_range('2023-03-15', periods=1440, freq='1T')
)

# 默认左闭右开
hourly_default = package_data.resample('H').sum()

# 右闭左开
hourly_right_closed = package_data.resample('H', closed='right').sum()

# 标签使用右边界
hourly_right_label = package_data.resample('H', label='right').sum()

在物流系统中, closed='right' 可能更合理 - 比如10:00-11:00的时段,11:00整的分拣数据应该计入当前时段而非下一时段。

3.2 origin参数

这个参数控制时间窗口的起点。在分析全球业务数据时特别有用:

# 全球服务器日志(UTC时间)
logs_index = pd.date_range('2023-05-01', '2023-05-02', freq='15T')
logs = pd.Series(np.random.randint(0, 100, len(logs_index)), index=logs_index)

# 按美东时间每天开始
ny_daily = logs.resample('24H', origin='2023-05-01 04:00:00').sum()

这样就能按照特定时区的自然日进行统计,避免了UTC转换的麻烦。

4. 金融场景实战案例

4.1 股票波动率分析

波动率是金融分析的重要指标,通常需要计算历史波动率:

# 获取股票分钟收益率
minute_returns = stock_data['price'].pct_change()

# 计算30分钟滚动波动率
volatility_30min = minute_returns.resample('30T').std() * np.sqrt(252*6.5*60/30)

这里用到了年化波动率的转换公式。实际交易系统中,不同时间尺度的波动率分析能帮助识别市场异常。

4.2 期货合约滚动

处理连续期货合约时,resample能优雅地解决合约切换问题:

# 假设有主力合约每日收盘价
contracts = {
    '2023-03': pd.Series(np.random.normal(4000, 50, 22), 
                         index=pd.date_range('2023-03-01', '2023-03-31', freq='B')[:22]),
    '2023-04': pd.Series(np.random.normal(4050, 50, 21), 
                         index=pd.date_range('2023-04-01', '2023-04-30', freq='B'))
}

# 构建连续合约
continuous = pd.concat(contracts.values()).resample('B').last().ffill()

这种方法避免了传统方法中的向前跳跃(forward jumping)问题。

5. 物联网数据处理技巧

5.1 设备状态分析

处理传感器数据时,经常遇到不规律的时间戳:

# 模拟温度传感器数据(不规则时间戳)
np.random.seed(42)
timestamps = pd.to_datetime(['2023-07-15 08:23:15',
                            '2023-07-15 08:25:47',
                            '2023-07-15 08:26:12',
                            '2023-07-15 08:30:00',
                            '2023-07-15 08:35:21'])
temps = pd.Series([25.3, 25.5, 25.6, 26.1, 26.3], index=timestamps)

# 5分钟均值填充
regular_temps = temps.resample('5T').mean().ffill()

在工业设备监控中,这种规整化处理能让后续分析更准确。

5.2 异常值检测

结合resample和rolling可以检测设备异常:

# 生成带异常值的设备振动数据
vibration = pd.Series(np.random.normal(10, 1, 1000),
                     index=pd.date_range('2023-08-01', periods=1000, freq='10S'))
vibration.iloc[[100, 300, 700]] = 25  # 注入异常值

# 计算5分钟移动Z-Score
z_scores = (vibration.resample('5T').mean() - 
            vibration.resample('5T').mean().rolling(24).mean()
           ) / vibration.resample('5T').mean().rolling(24).std()

这种方法在我参与的智能制造项目中成功识别了多起设备早期故障。

6. 性能优化与陷阱规避

6.1 大数据量处理

处理海量时间序列时(比如千万级GPS轨迹),可以用这些技巧:

# 使用loffset参数分散计算压力
gps_data.resample('5T', loffset='2.5T').mean()

# 对分类数据使用特殊聚合
def most_common(series):
    return series.mode()[0]

user_actions.resample('1H').agg({'action_type': most_common})

在最近的车联网项目中,通过合理设置这些参数,处理速度提升了3倍。

6.2 常见坑点

  • 时区问题:始终明确时区信息
df.tz_localize('UTC').resample('H').sum()
  • 空值处理:根据业务选择ffill/bfill/interpolate
  • 边缘效应:使用 closed label 精确控制边界

记得有次分析全球交易数据,就因为没注意时区转换,导致日切点错位,产生了完全错误的分析结论。

Logo

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

更多推荐