时间序列的核心操作包括 重采样(resample)、滚动窗口(rolling)、扩展窗口(expanding)、指数加权移动(ewm) 以及 时区处理。


1. 重采样 resample()

将时间序列从一种频率转换为另一种频率,支持降采样和升采样。

1.1 降采样(高频 → 低频)

ts = pd.Series(
    range(10),
    index=pd.date_range('2024-01-01', periods=10, freq='D')
)
 
# 按周聚合(默认周日结束)
ts.resample('W').sum()
 
# 按月聚合
ts.resample('ME').mean()
 
# 按季度取最大值
ts.resample('QE').max()

1.2 自定义聚合

ts.resample('ME').agg(['sum', 'mean', 'max'])
 
# 多列不同聚合
df.resample('ME').agg({'A': 'sum', 'B': 'mean'})

1.3 升采样(低频 → 高频)

ts = pd.Series([100, 200, 300],
               index=pd.to_datetime(['2024-01-01', '2024-02-01', '2024-03-01']))
 
# 按日升采样,默认产生 NaN
ts.resample('D').asfreq()
 
# 前向填充
ts.resample('D').ffill()
 
# 后向填充
ts.resample('D').bfill()
 
# 插值填充
ts.resample('D').interpolate()

1.4 resample() 参数

参数说明
rule目标频率(字符串或 DateOffset)
label聚合标签:'right'(默认,区间右端点)或 'left'
closed区间闭合:'right'(默认)或 'left'
origin对齐起点
on若索引非时间,指定时间列
axis重采样轴(默认 0)
# 左端点标签 + 左闭合
ts.resample('W', label='left', closed='left').sum()

1.5 与 groupby 结合

# 按组 + 时间重采样
df.groupby('地区').resample('ME')['销售额'].sum()

2. 滚动窗口 rolling()

计算固定窗口内的滚动统计量,常用于移动平均、波动率等。

2.1 基本用法

ts = pd.Series(range(100), index=pd.date_range('2024-01-01', periods=100, freq='D'))
 
ts.rolling(window=7).mean()    # 7 日移动平均
ts.rolling(7).sum()
ts.rolling(7).std()
ts.rolling(7).max()

2.2 rolling() 参数

参数说明
window窗口大小(整数或时间偏移)
min_periods最小非空观测数(默认等于窗口)
center窗口是否居中(默认 False)
win_type加权窗口类型(如 'gaussian')
on指定列作为时间
closed区间闭合方向
# 按天数窗口
ts.rolling('7D').mean()
 
# 最小非空观测数
ts.rolling(7, min_periods=3).mean()
 
# 居中窗口
ts.rolling(7, center=True).mean()

2.3 自定义聚合函数

ts.rolling(7).apply(lambda x: x.max() - x.min())
ts.rolling(7).agg(['mean', 'std', 'skew', 'kurt'])

2.4 多列滚动

df.rolling(7).mean()

3. 扩展窗口 expanding()

从数据起点到当前点的累积窗口,用于计算累积统计量。

ts = pd.Series([1, 2, 3, 4, 5])
 
ts.expanding().sum()    # 累积和(等价于 cumsum)
ts.expanding().mean()   # 累积均值
ts.expanding().max()    # 累积最大值
ts.expanding().std()    # 累积标准差

expanding() 参数

参数说明
min_periods最小非空观测数(默认 1)
axis计算轴
ts.expanding(min_periods=3).sum()   # 前两个值为 NaN

4. 指数加权移动 ewm()

对历史数据赋予指数衰减权重,近期数据权重更高。

4.1 基本用法

ts = pd.Series([1, 2, 3, 4, 5])
 
ts.ewm(span=3).mean()   # 指数加权均值
ts.ewm(span=3).std()    # 指数加权标准差

4.2 ewm() 参数

参数说明
span衰减因子(span = 2/alpha - 1)
alpha平滑系数(0~1,直接指定)
com质心(com = (1 - alpha)/alpha)
halflife半衰期
min_periods最小非空观测数
adjust是否调整权重(默认 True)
ignore_na是否忽略 NaN(默认 False)
ts.ewm(alpha=0.3).mean()
ts.ewm(halflife=3).mean()

5. 时区处理

5.1 本地化(添加时区信息)

ts = pd.Timestamp('2024-01-01 12:00:00')
ts_local = ts.tz_localize('UTC')
# 2024-01-01 12:00:00+00:00

5.2 转换时区

ts_local.tz_convert('Asia/Shanghai')
# 2024-01-01 20:00:00+08:00

5.3 Series / DataFrame 时区操作

s = pd.Series(
    [1, 2, 3],
    index=pd.date_range('2024-01-01', periods=3, freq='D', tz='UTC')
)
 
s.tz_convert('Asia/Shanghai')
s.index.tz          # <UTC>
s.tz_localize(None)  # 移除时区信息

5.4 处理不明确/不存在的时间

# 夏令时切换时,本地化可能出现歧义
ts_ambiguous = pd.date_range('2024-03-31 01:30', periods=2, freq='h')
ts_ambiguous.tz_localize('Europe/Berlin', ambiguous='infer')
 
# 不存在的本地时间(如春令时跳过的时段)
ts_nonexistent = pd.date_range('2024-03-31 02:30', periods=1, freq='h')
ts_nonexistent.tz_localize('Europe/Berlin', nonexistent='shift_forward')
参数说明
ambiguous'raise'、'infer'、'NaT'、Bool 数组
nonexistent'raise'、'shift_forward'、'shift_backward'、'NaT'

6. 其他时间序列操作

差分与变化率

ts.diff()           # 一阶差分(当前-前值)
ts.pct_change()     # 百分比变化

滞后与超前

ts.shift(1)         # 前一期
ts.shift(-1)        # 后一期

时间序列重排

# 转成 DataFrame
ts.to_frame()
 
# 转成周期索引
ts.to_period('M')

缺失时间点处理

# 构建完整索引
full_idx = pd.date_range(ts.index.min(), ts.index.max(), freq='D')
ts = ts.reindex(full_idx).ffill()

时间序列填充与插值

ts.asfreq('D')              # 创建 NaN
ts.ffill()                   # 前向填充
ts.bfill()                   # 后向填充
ts.interpolate(method='time') # 时间插值

本章小结

  • 时间戳:Timestamp 单点时间,属性与运算丰富
  • 时间差:Timedelta 持续时间,支持复合运算
  • 周期:Period 时间段,适合月度/季度统计
  • 时间索引:DatetimeIndex、TimedeltaIndex、PeriodIndex 支持灵活切片
  • 重采样:resample 实现频率转换与聚合
  • 窗口计算:rolling、expanding、ewm 完成动态统计
  • 时区处理:tz_localize / tz_convert 管理时区,处理缺失/歧义时间