pandas 拥有强大的时间序列处理能力,涵盖日期解析、重采样、滚动窗口、时区处理等。
日期时间类型
pd.timestamp— 单个时间点
import pandas as pd
import numpy as np
# 创建 timestamp
ts = pd.timestamp('2024-07-15')
ts = pd.timestamp('2024-07-15 14:30:00')
ts = pd.timestamp('2024-07-15 14:30:00+08:00') # 带时区
ts = pd.timestamp.now() # 当前时间
ts = pd.timestamp.now(tz='asia/shanghai')
ts = pd.timestamp(year=2024, month=7, day=15, hour=14, minute=30)
# timestamp 属性
print(ts.year) # 2024
print(ts.month) # 7
print(ts.day) # 15
print(ts.hour) # 14
print(ts.minute) # 30
print(ts.second) # 0
print(ts.dayofweek) # 0 (周一=0, 周日=6)
print(ts.dayofyear) # 197
print(ts.quarter) # 3
print(ts.week) # 29
print(ts.days_in_month) # 31
print(ts.is_leap_year) # true (2024 是闰年)
pd.datetimeindex— 时间索引
# 从字符串列表创建 dates = pd.datetimeindex(['2024-01-01', '2024-01-02', '2024-01-03']) # pd.date_range() — 生成日期范围 ⭐ dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='d') # 每天 dates = pd.date_range(start='2024-01-01', periods=12, freq='ms') # 每月初,12 期 dates = pd.date_range(start='2024-01-01', periods=8, freq='q') # 每季末,8 期 dates = pd.date_range(start='2024-01-01', periods=52, freq='w-mon') # 每周一 dates = pd.date_range(start='2024-01-01', periods=5, freq='b') # 工作日 dates = pd.date_range(start='2024-01-01 09:00', periods=8, freq='h') # 每小时 dates = pd.date_range(start='2024-01-01', periods=96, freq='15min') # 每 15 分钟 # 常用频率别名 # 'd' 日历日 'b' 工作日 'w' 周日结束的周 # 'm' 月末 'ms' 月初 'q' 季末 # 'qs' 季初 'y' 年末 'ys' 年初 # 'h' 小时 'min' 分钟 's' 秒 # 'w-mon' 周一结束 'bms' 月初工作日
pd.timedelta— 时间差
# 创建 timedelta
delta = pd.timedelta(days=1)
delta = pd.timedelta('1 day')
delta = pd.timedelta('2 days 3 hours 30 minutes')
delta = pd.timedelta('1w') # 1 周
delta = pd.timedelta('3d 5h') # 3 天 5 小时
# 时间运算
ts = pd.timestamp('2024-07-15')
print(ts + pd.timedelta(days=7)) # 2024-07-22
print(ts - pd.timedelta(weeks=2)) # 2024-07-01
print(ts + pd.timedelta(hours=48)) # 2024-07-17 00:00:00
解析与转换
pd.to_datetime()— 字符串转时间
# 基本转换 s = pd.series(['2024-01-01', '2024-01-02', 'invalid', '2024-01-04']) dates = pd.to_datetime(s, errors='coerce') # 无效值 -> nat # errors 选项 # 'raise': 遇到无效值报错(默认) # 'coerce': 无效值转为 nat # 'ignore': 无效值保持原样 # 指定格式(加速解析) dates = pd.to_datetime(s, format='%y-%m-%d') # 常用格式码 # %y 四位年份 %y 两位年份 %m 月份(01-12) # %d 日期(01-31) %h 时(00-23) %m 分(00-59) # %s 秒(00-59) %f 微秒 %z utc偏移 # %b 英文缩写月份 %a 星期全称 %a 星期缩写 # 推断格式 dates = pd.to_datetime(s, infer_datetime_format=true) # 自动推断
以时间为索引的 dataframe
# 创建时间索引
dates = pd.date_range('2024-01-01', periods=365, freq='d')
df = pd.dataframe({
'sales': np.random.randn(365).cumsum() + 100,
'visitors': np.random.randint(100, 500, 365) + np.arange(365)
}, index=dates)
# 按时间切片
df['2024-01'] # 2024 年 1 月所有数据
df['2024-01-15'] # 1 月 15 日
df['2024-01':'2024-03'] # 1 月到 3 月
df['2024-01-15':'2024-02-15'] # 跨时间段
# 时间属性访问
df['year'] = df.index.year
df['month'] = df.index.month
df['day'] = df.index.day
df['weekday'] = df.index.weekday # 周一=0
df['quarter'] = df.index.quarter
df['day_name'] = df.index.day_name() # 'monday', 'tuesday'...
df['is_month_start'] = df.index.is_month_start
df['is_quarter_end'] = df.index.is_quarter_end
between_time()— 按时间段筛选
# 创建带时间戳的数据
idx = pd.date_range('2024-01-01', periods=96, freq='15min')
ts = pd.series(range(96), index=idx)
# 筛选 09:00 - 17:00 的数据
ts.between_time('09:00', '17:00')
# 筛选非工作时间
ts.between_time('17:00', '09:00', include_start=false)
重采样resample()
将时间序列从一种频率转换到另一种频率。
dates = pd.date_range('2024-01-01', periods=365, freq='d')
df = pd.dataframe({
'value': np.random.randn(365).cumsum()
}, index=dates)
# 降采样: 日 -> 月
monthly = df.resample('m')['value'].mean() # 月均值
monthly = df.resample('m')['value'].sum() # 月总和
monthly = df.resample('m')['value'].last() # 月末值
monthly = df.resample('m')['value'].first() # 月初值
monthly = df.resample('m')['value'].ohlc() # open/high/low/close
# 升采样: 日 -> 小时(需要填充)
hourly = df.resample('h').ffill() # 前向填充
hourly = df.resample('h').bfill() # 后向填充
hourly = df.resample('h').interpolate() # 插值
hourly = df.resample('h').asfreq() # 不填充(nan)
# 多列聚合
result = df.resample('w').agg({
'sales': 'sum',
'visitors': 'mean'
})
# 自定义聚合
result = df.resample('m').apply(lambda x: x.max() - x.min())
resample常用频率
| 频率 | 含义 | 频率 | 含义 |
|---|---|---|---|
| 'd' | 日历日 | 'w' | 周(周日结束) |
| 'm' | 月末 | 'ms' | 月初 |
| 'q' | 季末 | 'qs' | 季初 |
| 'y' / 'a' | 年末 | 'ys' / 'as' | 年初 |
| 'h' / 'h' | 小时 | 'min' / 't' | 分钟 |
| 'w-mon' | 周一结束 | 'b' | 工作日 |
| 'sm' | 半月(15日+月末) | 'bm' | 工作月末 |
| 'q-dec' | 12月结束的季 | 'bq' | 工作季末 |
滚动窗口rolling()
df = pd.dataframe({
'price': np.random.randn(252).cumsum() + 100
}, index=pd.date_range('2024-01-01', periods=252, freq='b'))
# 简单移动平均
df['sma_7'] = df['price'].rolling(window=7).mean()
df['sma_30'] = df['price'].rolling(window=30).mean()
# 其他滚动统计
df['rolling_std'] = df['price'].rolling(7).std() # 滚动标准差
df['rolling_min'] = df['price'].rolling(7).min() # 滚动最小值
df['rolling_max'] = df['price'].rolling(7).max() # 滚动最大值
df['rolling_sum'] = df['price'].rolling(7).sum() # 滚动求和
df['rolling_median'] = df['price'].rolling(7).median() # 滚动中位数
df['rolling_quantile'] = df['price'].rolling(30).quantile(0.9) # 滚动分位数
df['rolling_skew'] = df['price'].rolling(30).skew() # 滚动偏度
df['rolling_corr'] = df['price'].rolling(30).corr(df['price'].shift()) # 滚动相关
# 自定义滚动函数
df['rolling_range'] = df['price'].rolling(7).apply(
lambda x: x.max() - x.min()
)
# rolling 参数
# window: 窗口大小(int 或 offset 如 '7d')
# min_periods: 最少非空观测数
# center: 标签是否居中(默认 false)
# win_type: 窗口类型 (如 'gaussian', 'triang')
# closed: 区间闭合方式 ('right'/'left'/'both'/'neither')
时间偏移窗口
# 按时间而非行数的滚动窗口(索引必须是 datetimeindex) df['rolling_7d'] = df['price'].rolling(window='7d').mean() # 支持的时间窗口 df['price'].rolling(window='3d').mean() df['price'].rolling(window='2h').mean() df['price'].rolling(window='30min').mean()
expanding()— 扩展窗口
从序列开始累积到当前,窗口越来越大。
# 累积统计 df['expanding_mean'] = df['price'].expanding().mean() df['expanding_std'] = df['price'].expanding().std() df['expanding_max'] = df['price'].expanding().max() # 最小观测数 df['expanding_mean_min10'] = df['price'].expanding(min_periods=10).mean()
ewm()— 指数加权
# 指数加权移动平均 df['ewm_alpha'] = df['price'].ewm(alpha=0.3).mean() # 平滑因子 α df['ewm_span'] = df['price'].ewm(span=7).mean() # 跨度(相当于 7 天 sma) df['ewm_halflife'] = df['price'].ewm(halflife=3).mean() # 半衰期 df['ewm_com'] = df['price'].ewm(com=3.5).mean() # 质心 # 其他 ewm 统计 df['ewm_std'] = df['price'].ewm(span=7).std() df['ewm_var'] = df['price'].ewm(span=7).var() df['ewm_corr'] = df['price'].ewm(span=7).corr(df['price'].shift())
shift()/diff()/pct_change()
# shift: 平移 df['price_lag1'] = df['price'].shift(1) # 前移 1 期 df['price_lead1'] = df['price'].shift(-1) # 后移 1 期 df['price_lag7'] = df['price'].shift(7) # 前移 7 期 # 按频率平移(索引必须是 datetimeindex) df['price_1m_ago'] = df['price'].shift(periods=1, freq='m') # diff: 差分 df['price_diff'] = df['price'].diff() # 一阶差分 df['price_diff2'] = df['price'].diff(2) # 二阶差分 # pct_change: 变化率 df['returns'] = df['price'].pct_change() # 日变化率 df['weekly_return'] = df['price'].pct_change(periods=5) # 周变化率
时区处理
# 本地化时区
ts = pd.timestamp('2024-07-15 14:30:00')
ts_beijing = ts.tz_localize('asia/shanghai')
# timestamp('2024-07-15 14:30:00+0800')
# 时区转换
ts_ny = ts_beijing.tz_convert('america/new_york')
# timestamp('2024-07-15 02:30:00-0400')
# dataframe 时区操作
idx = pd.date_range('2024-01-01', periods=10, freq='d')
df = pd.dataframe({'val': range(10)}, index=idx)
df = df.tz_localize('asia/shanghai') # 添加时区
df = df.tz_convert('utc') # 转时区
df = df.tz_localize(none) # 移除时区(不转换时间)
# 常用时区
# 'asia/shanghai' 中国标准时间 (utc+8)
# 'asia/tokyo' 日本标准时间 (utc+9)
# 'america/new_york' 美东 (utc-5/-4)
# 'europe/london' 英国 (utc+0/+1)
# 'utc' / 'etc/gmt' 世界协调时
时间偏移pd.dateoffset
from pandas.tseries.offsets import day, monthend, yearend, bday
ts = pd.timestamp('2024-07-15')
print(ts + day(1)) # 2024-07-16
print(ts + monthend()) # 2024-07-31
print(ts + bday(5)) # 5 个工作日后的日期
print(ts + pd.dateoffset(months=3)) # 3 个月后
print(ts + pd.dateoffset(years=1, days=-1)) # 一年差一天
# 生成偏移序列
from pandas.tseries.offsets import monthend
pd.date_range('2024-01-31', periods=12, freq=monthend())
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论