Pandas时间序列数据解析、重采样、特征工程与性能优化实战教程
在Python数据分析里,Pandas的时间序列能力并不是简单把日期列读进来就算完成。Pandas内部使用numpy的datetime64保存时间戳,可获得纳秒级精度;如果时间列仍是object类型,后续的rolling、resample、shift、diff等操作很容易失效。实际数据来源通常包括数据库导出的ISO格式字符串、Unix时间戳、带时区的RFC 3339格式。读CSV时若没有显式处理时间列,Pandas会把它识别为object,这是很多时间序列分析出错的起点。一、时间列解析与格式控制
pd.to_datetime是处理杂乱时间数据的常用入口。对标准程度较高的字符串,它可以自动识别并统一为Timestamp对象:
import pandas as pd
dates = ['2023/01/01', 'Jan 5 2023', '15-06-2023']
parsed_dates = pd.to_datetime(dates)
遇到非标准格式时,应显式传入format,避免自动推断带来的错误和性能损耗:
custom_dates = ['01-2023-15', '02-2023-28']
pd.to_datetime(custom_dates, format='%d-%Y-%m')
这里%d、%Y、%m分别对应日、年、月。原文强调,CSV读取阶段可以用parse_dates指定时间列,也可以用pd.to_datetime后续转换;但不要省略转换,否则时间列会停留在object类型。
二、时区本地化与转换
时区问题常见于全球化业务。Pandas区分naive time和aware time,前者不带时区,后者带时区信息。基本流程是先tz_localize本地化,再tz_convert转换到目标时区:
naive_time = pd.Timestamp('2023-07-15 14:30')
aware_time = naive_time.tz_localize('Asia/Shanghai')
ny_time = aware_time.tz_convert('America/New_York')
如果数据里混合了不同时区,直接参与计算容易产生异常。建议在清洗阶段统一时区,例如先转为UTC再处理:
df = df.tz_convert('UTC')
这一步的核心目的不是“显示时间”,而是让时间索引在比较、对齐和聚合时使用同一基准。
三、DatetimeIndex与重采样
时间序列的高级操作通常依赖DatetimeIndex。重采样相当于按时间桶做聚合,类似SQL按时间分组。下面把秒级数据聚合成5分钟均值:
import numpy as np
rng = pd.date_range('2023-01-01', periods=3600, freq='s')
ts = pd.Series(np.random.randn(len(rng)), index=rng)
ts.resample('5T').mean()
金融场景常按日计算OHLC和成交量,可用agg一次完成多个统计:
df.resample('1D').agg({
'price': ['first', 'max', 'min', 'last'],
'volume': 'sum'
})
滑动窗口则用于平滑和趋势分析,Pandas提供rolling、ewm、expanding等窗口:
ts.rolling(window=30).mean()
ts.ewm(span=30).mean()
ts.expanding().std()
如果窗口内需要自定义权重,可以结合apply:
def custom_roll(x):
return x[-1] * 0.6 + x[-2] * 0.3 + x[-3] * 0.1
ts.rolling(window=3).apply(custom_roll)
四、时间特征工程
把时间戳拆成多维特征,能提升模型对周期性的感知。原文给出的做法包括小时、星期、是否周末,以及小时的正弦余弦编码:
df['hour'] = df.index.hour
df['day_of_week'] = df.index.dayofweek
df['is_weekend'] = df.index.dayofweek > 4
df['time_sin'] = np.sin(2*np.pi*df.index.hour/24)
df['time_cos'] = np.cos(2*np.pi*df.index.hour/24)
预测任务还常构造滞后特征和差分特征:
for lag in :
df = df['value'].shift(lag)
df['diff_1'] = df['value'].diff(1)
shift用于把历史值移到当前行,diff用于消除趋势或获得变化量。它们都要求索引或数据顺序正确,否则滞后关系会错位。
五、非均匀时间序列与多序列对齐
物联网设备常产生不均匀时间间隔。原始记录可能像下面这样:
raw_data = {
'timestamp': ['2023-01-01 08:00', '2023-01-01 08:07', '2023-01-01 08:13'],
'temperature':
}
df = pd.DataFrame(raw_data)
df['timestamp'] = pd.to_datetime(df['timestamp'])
要转换为均匀频率,可以先构造完整时间范围,再重索引并插值:
full_range = pd.date_range(
start=df['timestamp'].min(),
end=df['timestamp'].max(),
freq='5T'
)
df = df.set_index('timestamp').reindex(full_range)
df['temperature'] = df['temperature'].interpolate()
多时间序列对齐也很常见。例如每日销售数据与每小时天气数据频率不同,可以按销售时间点对天气做向前填充:
sales = pd.Series(
,
index=pd.date_range('2023-01-01', periods=3, freq='D')
)
weather = pd.Series(
,
index=pd.date_range('2023-01-01 08:00', periods=6, freq='4H')
)
aligned_weather = weather.reindex(sales.index, method='ffill')
这里的ffill表示用前一个有效观测值填充对齐后的空隙,适合“低频指标对上高频时间戳”的场景。
六、性能优化与排查要点
大规模时间序列处理中,性能差异主要来自类型转换和格式推断。原文列出以下几项优化:
1. 使用pd.to_datetime(..., format=...)比自动推断快3-5倍。
2. 对固定频率数据,先创建DatetimeIndex再构建DataFrame。
3. 将resample与asfreq结合使用,例如高效下采样:
ts.resample('1H').asfreq()
4. 用pd.read_csv(..., parse_dates=['time_col'])替代读入后再转换。
5. 对历史数据可关闭时区检查,原文示例为:
pd.to_datetime(..., utc=True, infer_datetime_format=True)
原文提到,在处理某电商平台一年期用户行为日志约2.7亿条记录时,通过预定义时间列格式和批量处理,将解析时间从45分钟缩短到3分钟。关键点不是某个单行技巧,而是避免对每个时间戳单独做格式推断。
整体来看,Pandas时间序列处理的排查顺序可以归纳为:先确认时间列类型,再用to_datetime和format解析;有跨时区数据时先localize/convert并统一到UTC;需要聚合时确保DatetimeIndex,再使用resample、rolling、shift、diff;面对非均匀或多频率数据,先reindex对齐,必要时interpolate或ffill;数据量上来后,优先减少自动推断和重复转换。这样处理,才能把原文中的解析、重采样、特征工程和优化技巧落到可复用的代码流程中。
Re: Pandas时间序列数据解析、重采样、特征工程与性能优化实战教程
感谢楼主整理,内容很系统。时间列从 object 转成 datetime 这点太重要了,我之前就是 CSV 读进来没管,后面 resample 和 shift 结果全不对。楼主把 to_datetime 的 format、时区 localize/convert 和统一 UTC 串起来讲,很清楚,尤其是 aware 和 naive 的区分,做跨时区业务时确实容易忽略。重采样按时间桶聚合、OHLC 用 agg 一次算多个指标,以及 rolling、ewm、expanding 的对比,都很实用。特征工程里小时 sin/cos 编码、lag 和 diff 的构造,对预测任务帮助很大,顺序或索引错位这个坑也提醒得到位。非均匀序列先构造完整时间范围再 reindex、interpolate,也是处理传感器数据的常见做法。想请教下,如果数据量到千万行级别,resample 和 rolling 有没有比较推荐的优化思路?我一般先保证 DatetimeIndex 有序,再考虑分块处理。感谢分享。Re: Pandas时间序列数据解析、重采样、特征工程与性能优化实战教程
感谢分享,内容很扎实。时间列如果是 object 类型,后面 rolling、resample、shift、diff 失效这个点特别真实,读 CSV 时老老实实 parse_dates 或 to_datetime 能省很多事。时区那部分先 localize 再 convert、清洗时统一 UTC 也很关键,不然多来源数据对齐很容易错位。重采样做 OHLC、rolling 自定义权重、以及小时的正弦余弦编码都很实用。非均匀时间序列先建完整时间范围再 reindex、interpolate 的思路也清楚,不过如果设备长时间断线,插值前可能还要考虑一下最大间隔限制。标题提到性能优化,想问问后续会不会讲大数据量下 resample、rolling 和 shift 的提速和内存控制,期待继续更新。Re: Pandas时间序列数据解析、重采样、特征工程与性能优化实战教程
楼主这个总结挺扎实的,尤其是把“时间列没转 datetime64 就是后面一堆坑的源头”点出来,这点很多人栽过。我补几个自己踩过的细节。 一是 to_datetime 的 errors 参数最好显式写。默认 raise 在脏数据里一报错整列就挂了,但直接上 coerce 又会悄悄产生 NaT,所以我会先 coerce 跑一遍,统计 NaT 占比,再回头看是格式问题还是真空值,别稀里糊涂把缺数据当成了格式错误。 二是时区那块,tz_localize 和 tz_convert 真的容易混。本地化是给没有时区的时间“贴标签”,转换是已经有标签了改到另一个时区。对已经 aware 的索引再调 tz_localize 会直接报错,对 naive 的调 tz_convert 也会报,所以清洗脚本里我一般先判断索引的 tz 属性再决定走哪条路。 三是重采样那个例子,resample('5T') 后面最好跟一个 closed 和 label 的考虑。默认左闭右开、标签取左边界,做指标日报的时候如果没对齐业务口径,很容易出现跨天归属争议。这种默认值不写清楚,上线后对不上数就是玄学。 四是 rolling().apply() 那个自定义权重,性能其实挺差的,尤其是窗口大、数据长的时候,apply 是 Python 层逐窗口调用。如果权重固定,我会干脆用 shift 加权求和手动算,既快又好读,比如 0
页:
[1]