← 返回数据分析目录
← 返回学习笔记首页
Pandas时间序列基础
数据分析专题 · 日期时间数据处理
专题: Python数据分析系统学习
关键词: Pandas, 时间序列, DatetimeIndex, date_range, 时区, 频率, dateutil, 偏移
一、时间序列概述
时间序列(Time Series)是数据分析中最常见的数据类型之一。无论是金融市场的股价波动、电商平台的日销售额、气象站的温度记录,还是服务器的CPU使用率监控,几乎所有的数据采集场景都离不开时间维度。Pandas作为Python数据分析的核心库,提供了强大而灵活的时间序列处理能力,涵盖日期解析、范围生成、索引操作、频率变换、时区处理和日期偏移计算等方方面面。
Pandas的时间序列功能构建在以下三个核心数据类型之上:Timestamp (时间戳,即单个时间点)、Timedelta (时间差,表示两个时间点之间的间隔)和Period (时间区间,如某个月或某个季度)。其中DatetimeIndex是最常用的时间索引类型,它本质上是由多个Timestamp对象组成的索引,支持丰富的日期属性访问和高效的切片操作。
本笔记将系统梳理Pandas时间序列处理的核心API和方法,从基础的日期解析到高级的时区处理和偏移量计算,涵盖实际数据分析和特征工程中最常用的技术点。
前置知识: 本文假设读者已掌握Pandas基础操作(DataFrame、Series的创建与访问)。建议先安装pandas库(pip install pandas),并确保版本不低于1.0以获得完整的时间序列功能支持。
二、日期解析
现实世界中的数据通常以字符串形式存储日期和时间,例如"2024-01-15"或"2024/01/15 14:30:00"。将这些字符串转换为Pandas可识别的日期时间对象是时间序列分析的第一步。
2.1 使用 pd.to_datetime()
to_datetime()是Pandas中最核心的日期解析函数,可以处理单个字符串、字符串列表、Series或DataFrame列。它支持多种日期格式的自动推断。
# 解析单个字符串
pd.to_datetime ('2024-01-15' )
# 输出: Timestamp('2024-01-15 00:00:00')
# 解析字符串列表
pd.to_datetime (['2024-01-15' , '2024-02-20' , '2024-03-10' ])
# 输出: DatetimeIndex(['2024-01-15', '2024-02-20', '2024-03-10'], dtype='datetime64[ns]')
# 解析不同格式的日期字符串
pd.to_datetime ('2024/01/15' ) # 斜杠分隔
pd.to_datetime ('15-01-2024' ) # 日-月-年
pd.to_datetime ('20240115' ) # 紧凑格式
pd.to_datetime ('2024-01-15 14:30:00' ) # 包含时间
2.2 在读取文件时使用 parse_dates
当使用pd.read_csv()等函数读取外部数据文件时,通过parse_dates参数可以自动解析日期列,这是最高效的方式。
# 自动解析指定列为日期类型
df = pd.read_csv ('sales.csv' , parse_dates =[ 'date' , 'order_time' ] )
# 将多列组合解析为日期(年、月、日分开存储时很有用)
df = pd.read_csv ('sales.csv' , parse_dates ={ 'date' : [ 'year' , 'month' , 'day' ] } )
# 将日期列作为索引(最常用的方式)
df = pd.read_csv ('stock.csv' , index_col ='date' , parse_dates =True )
parse_dates=True会让Pandas自动检测所有可以解析为日期的列,但这种方式不够精确,建议明确指定需要解析的列名。
2.3 使用 date_parser 参数自定义解析
当日期格式特殊且Pandas的自动解析无法正确处理时,可以通过date_parser参数传入自定义的解析函数。该函数接收一个字符串列表并返回解析后的日期对象列表。
# 自定义日期解析器——处理非标准格式
from datetime import datetime
custom_parser = lambda x: datetime.strptime (x, '%Y/%m/%d-%H:%M:%S' )
df = pd.read_csv ('data.csv' , parse_dates =[ 'timestamp' ] ,
date_parser =custom_parser)
# 使用dateutil库处理更灵活的格式
from dateutil.parser import parse as dateutil_parse
df = pd.read_csv ('data.csv' , parse_dates =[ 'datetime' ] ,
date_parser =dateutil_parse)
2.4 errors 参数处理异常情况
实际数据中经常混入非法日期值,如"2024-02-30"(2月没有30日)或完全不可解析的文本。errors参数提供了三种处理策略。
# errors='raise':遇到错误时抛出异常(默认行为)
pd.to_datetime ('2024-02-30' , errors ='raise' )
# 抛出: OutOfBoundsDatetime: Out of bounds nanosecond timestamp: ...
# errors='coerce':将无法解析的值设为NaT(Not a Time)
pd.to_datetime ('2024-02-30' , errors ='coerce' )
# 输出: NaT
# errors='ignore':保持原样返回(通常是字符串或数值)
pd.to_datetime ('2024-02-30' , errors ='ignore' )
# 输出: '2024-02-30'(字符串原样返回)
# 实用技巧:批量处理时用coerce保护管道
dates = pd.Series (['2024-01-15' , 'invalid_date' , '2024-03-10' ])
pd.to_datetime (dates, errors ='coerce' )
# 输出:
# 0 2024-01-15
# 1 NaT
# 2 2024-03-10
# dtype: datetime64[ns]
注意事项: Pandas的时间精度为纳秒(nanosecond),因此支持的时间范围约为1677年至2262年。超出此范围的时间戳会导致OutOfBoundsDatetime错误,这时可以考虑使用errors='coerce'进行处理,或转换为其他表示方式。
三、日期范围生成
在时间序列分析中,经常需要生成等间隔的日期序列,用于重采样、填充缺失日期或创建模拟数据集。Pandas提供了三个核心函数来生成不同类型的日期范围。
3.1 pd.date_range() 生成日期序列
date_range生成等间隔的时间戳序列,返回DatetimeIndex。可以通过start/end指定起止时间,或通过start+periods指定起始时间和长度,二者至少提供一组。
# 指定起止日期(默认频率为天)
pd.date_range (start ='2024-01-01' , end ='2024-01-10' )
# 输出: DatetimeIndex(['2024-01-01', '2024-01-02', ..., '2024-01-10'])
# 指定起始日期和长度
pd.date_range (start ='2024-01-01' , periods =5 )
# 输出: DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05'])
# 指定结束日期和长度
pd.date_range (end ='2024-01-10' , periods =5 )
# 输出: DatetimeIndex(['2024-01-06', '2024-01-07', ..., '2024-01-10'])
# 包含时间信息的日期范围
pd.date_range ('2024-01-01 08:00' , '2024-01-01 12:00' , freq ='h' )
# 输出: DatetimeIndex(['2024-01-01 08:00:00', '2024-01-01 09:00:00', ..., '2024-01-01 12:00:00'])
3.2 pd.period_range() 生成时间区间
与date_range返回时间戳不同,period_range返回PeriodIndex,表示固定的时间区间,如"2024年1月"或"2024年第1季度"。这在财务分析和报表统计中非常有用。
# 按月生成区间
pd.period_range (start ='2024-01' , periods =6 , freq ='M' )
# 输出: PeriodIndex(['2024-01', '2024-02', ..., '2024-06'], dtype='period[M]')
# 按季度生成
pd.period_range ('2024Q1' , '2025Q4' , freq ='Q' )
# 输出: PeriodIndex(['2024Q1', '2024Q2', ..., '2025Q4'], dtype='period[Q-DEC]')
# 按年生成
pd.period_range ('2020' , '2025' , freq ='Y' )
# 输出: PeriodIndex(['2020', '2021', '2022', '2023', '2024', '2025'], dtype='period[A-DEC]')
3.3 pd.bdate_range() 生成工作日序列
bdate_range专门生成工作日(周一至周五)序列,自动跳过周末。适合股票市场、财务结算等只考虑工作日的场景。
# 生成2024年1月的工作日
pd.bdate_range ('2024-01-01' , '2024-01-15' )
# 输出: DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03',
# '2024-01-04', '2024-01-05', '2024-01-08',
# '2024-01-09', '2024-01-10', '2024-01-11',
# '2024-01-12', '2024-01-15'])
# 注意:1月6日(周六)、7日(周日)、13日(周六)、14日(周日)被自动跳过
# 指定自定义节假日(使用holidays参数)
pd.bdate_range ('2024-01-01' , '2024-01-10' ,
holidays =[ '2024-01-01' ] ) # 元旦
# 输出: 跳过1月1日后的剩余工作日
三者对比: date_range适合精准到秒级的时间点序列;period_range适合聚合分析,如"某个月的销售总额";bdate_range专门用于金融等行业的工作日日历。根据分析场景选择合适类型可以大幅简化后续代码。
3.4 freq 参数详解
freq参数决定了日期序列的间隔和步长,是date_range系列函数的核心参数。下表列出最常用的频率别名。
别名 说明 示例
D 日历日 '2024-01-01', '2024-01-02', ...
B 工作日 跳过周六周日
W 每周(默认周日结束) W-MON表示周一结束的周
M 月末 '2024-01-31', '2024-02-29'
MS 月初 '2024-01-01', '2024-02-01'
Q 季末(默认12月结束) '2024-03-31', '2024-06-30'
Y 年末 '2024-12-31', '2025-12-31'
h 每小时 '2024-01-01 00:00', '01:00', ...
BH 工作小时 9:00-17:00范围内的工作时间
T/min 每分钟 '2024-01-01 00:00', '00:01', ...
S 每秒 '2024-01-01 00:00:00', '00:00:01'
# freq组合使用——每3天
pd.date_range ('2024-01-01' , '2024-01-15' , freq ='3D' )
# 输出: DatetimeIndex(['2024-01-01', '2024-01-04', '2024-01-07', '2024-01-10', '2024-01-13'])
# 每2个月
pd.date_range ('2024-01-01' , '2025-01-01' , freq ='2MS' )
# 输出: DatetimeIndex(['2024-01-01', '2024-03-01', '2024-05-01', ..., '2025-01-01'])
# 每30分钟
pd.date_range ('2024-01-01 09:00' , '2024-01-01 17:00' , freq ='30T' )
# 输出: 9:00, 9:30, 10:00, ..., 17:00 共17个时间点
四、DatetimeIndex 索引
将DatetimeIndex设置为DataFrame的行索引后,可以充分利用Pandas的时间索引特性进行快速访问和切片。
4.1 年月日时分秒属性访问
DatetimeIndex支持通过.dt访问器或直接在Index上访问多个时间属性,对于特征提取非常方便。
# 创建带有时间索引的DataFrame
dates = pd.date_range ('2024-01-01' , periods =5 , freq ='h' )
df = pd.DataFrame ({'value' : range (5 )}, index =dates)
# 提取各种时间属性
df['year' ] = df.index.year
df['month' ] = df.index.month
df['day' ] = df.index.day
df['hour' ] = df.index.hour
df['minute' ] = df.index.minute
df['second' ] = df.index.second
df['weekday' ] = df.index.weekday # 0=周一, 6=周日
df['weekofyear' ] = df.index.isocalendar().week # 年内第几周
df['dayofyear' ] = df.index.dayofyear # 年内第几天
df['quarter' ] = df.index.quarter # 第几季度
df['is_month_start' ] = df.index.is_month_start
df['is_month_end' ] = df.index.is_month_end
df['is_weekend' ] = df.index.weekday >= 5 # 是否为周末
# 对于Series,需要通过.dt访问器
s = pd.Series (pd.date_range ('2024-01-01' , periods =3 ))
s.dt.year # => [2024, 2024, 2024]
s.dt.month # => [1, 1, 1]
s.dt.day # => [1, 2, 3]
实用技巧: 在特征工程中,weekday和is_weekend属性常用于判断"星期效应";hour属性可用于分析"时间效应"(如商场客流高峰时段);quarter属性则广泛用于财务数据的季节性分析。
4.2 时间戳索引与切片
DatetimeIndex支持直接使用日期字符串进行索引和范围切片,这种语法非常直观。
# 创建示例数据:2024年1月的每日数据
dates = pd.date_range ('2024-01-01' , '2024-01-31' , freq ='D' )
df = pd.DataFrame ({'value' : range (31 )}, index =dates)
# 精确索引
df.loc ['2024-01-15' ] # 获取1月15日的数据
# 范围切片(半开区间,包含左边界不包含右边界)
df.loc ['2024-01-10' :'2024-01-20' ] # 1月10日至20日(含)
# 按年月切片——自动匹配范围内所有日期
df.loc ['2024-01' ] # 获取整个1月的数据
# 按年月日时分秒切片
df.loc ['2024-01-15 00:00:00' :'2024-01-20 23:59:59' ]
# 使用 truncate 方法
df.truncate (before ='2024-01-10' , after ='2024-01-20' )
字符串切片之所以能按年月精准匹配,是因为Pandas内部对DatetimeIndex的字符串索引进行了智能推断——"2024-01"会被解释为"2024-01-01"到"2024-01-31"的完整范围。
五、时间频率与偏移
频率(Frequency)是时间序列的核心概念之一,决定了时间点之间的间隔规律。Pandas使用频率字符串(如'D'、'W'、'M')和偏移量对象(DateOffset)来描述时间频率。
5.1 基础频率与组合频率
频率字符串可以添加数字前缀表示倍数,如'3D'表示每3天。还可以通过加法组合不同频率。
# 基础频率
pd.date_range ('2024-01-01' , periods =4 , freq ='W' ) # 每周(周日)
pd.date_range ('2024-01-01' , periods =4 , freq ='W-MON' ) # 每周一
# 组合频率——每2天 + 每3小时 = 每2天零3小时
pd.date_range ('2024-01-01' , periods =4 , freq ='2D3h' )
# 输出: ['2024-01-01', '2024-01-03 03:00', '2024-01-05 06:00', '2024-01-07 09:00']
5.2 工作日偏移与自定义
BH(Business Hour)是工作小时偏移,默认指9:00-17:00的工作时段。结合CustomBusinessHour可以处理非标准的工作时间安排。
# 工作小时偏移(默认9:00-17:00)
pd.date_range ('2024-01-01 09:00' , periods =4 , freq ='BH' )
# 输出: ['2024-01-01 09:00', '2024-01-01 10:00', '2024-01-01 11:00', '2024-01-01 12:00']
# 自定义工作小时(如8:00-12:00, 13:00-17:00)
from pandas.tseries.offsets import CustomBusinessHour
cbh = CustomBusinessHour (start ='08:00' , end ='17:00' )
pd.date_range ('2024-01-01 08:00' , periods =6 , freq =cbh)
5.3 自定义偏移量
当内置频率无法满足需求时,可以使用DateOffset创建任意偏移规则。DateOffset支持按年、月、周、日、小时、分钟等单位的灵活组合。
from pandas.tseries.offsets import DateOffset
# 自定义偏移:每个月最后一个工作日
pd.date_range ('2024-01-31' , periods =4 , freq =DateOffset (months =1 ))
# 输出: ['2024-01-31', '2024-02-29', '2024-03-31', '2024-04-30']
# 注意:自动处理了2月只有29天的问题
# 同时偏移多个单位
offset = DateOffset (days =10 , hours =2 , minutes =30 )
pd.Timestamp ('2024-01-15 08:00' ) + offset
# 输出: Timestamp('2024-01-25 10:30:00')
# 负偏移——向前推算
pd.Timestamp ('2024-02-01' ) - DateOffset (days =5 )
# 输出: Timestamp('2024-01-27 00:00:00')
六、时区处理
在全球化数据分析场景中,时区处理是不可避免的难题。Pandas提供了完整的时区支持,包括本地化(将无时区时间赋予时区信息)和转换(在不同时区之间切换)。
6.1 tz_localize:本地化
tz_localize用于将没有时区信息的时间戳(naive时间)赋予特定的时区。这是一个"贴标签"的过程,不会改变时间点本身。
# 创建一个无时区的时间序列
dates = pd.date_range ('2024-01-01 08:00' , periods =3 , freq ='D' )
print (dates.tz) # None —— 无时区信息
# 本地化为中国时区(UTC+8)
dates_utc8 = dates.tz_localize ('Asia/Shanghai' )
print (dates_utc8)
# 输出: DatetimeIndex(['2024-01-01 08:00:00+08:00', ...], dtype='datetime64[ns, Asia/Shanghai]')
6.2 tz_convert:时区转换
tz_convert用于在已经有时区信息的时间戳之间进行转换,会真正改变显示的时间。
# 将上海时间转换为纽约时间
dates_ny = dates_utc8.tz_convert ('America/New_York' )
print (dates_ny)
# 输出: DatetimeIndex(['2023-12-31 19:00:00-05:00', ...])
# 说明:上海1月1日08:00 = 纽约12月31日19:00(冬令时,UTC-5)
# UTC中转:先转UTC再转目标时区
dates_utc = dates_utc8.tz_convert ('UTC' )
print (dates_utc)
# 输出: DatetimeIndex(['2024-01-01 00:00:00+00:00', ...])
6.3 在 date_range 中直接指定时区
从创建时就指定时区是最为便捷的方式。date_range的tz参数可以在生成日期范围的同时赋予时区信息。
# 直接生成带时区的时间序列
dates_tz = pd.date_range ('2024-01-01' , periods =5 , freq ='D' , tz ='Asia/Shanghai' )
print (dates_tz)
# 输出: DatetimeIndex(['2024-01-01 00:00:00+08:00', '2024-01-02 00:00:00+08:00', ...])
# 读取CSV时指定时区
df = pd.read_csv ('data.csv' , parse_dates =[ 'timestamp' ] )
df['timestamp'] = df['timestamp' ].dt.tz_localize ('UTC' ).dt.tz_convert ('Asia/Shanghai' )
常见错误: 对已有时区信息的时间序列再次调用tz_localize会抛出"Already tz-aware"异常。必须先通过tz_convert转换,或使用tz=None移除时区信息后再本地化。另外,夏令时转换时可能出现重复或缺失的时间点(AmbiguousTimeError/NonExistentTimeError),需通过ambiguous和nonexistent参数处理。
6.4 常用时区名称
时区名称 UTC偏移 (标准时) 适用地区
Asia/Shanghai UTC+8 中国(北京时间)
Asia/Tokyo UTC+9 日本、韩国
America/New_York UTC-5 美国东部
America/Los_Angeles UTC-8 美国西部
Europe/London UTC+0 英国(格林威治)
UTC UTC+0 协调世界时
可以在Python中通过pytz.all_timezones查看所有支持的时区名称列表。
七、时间序列常用操作
Pandas为时间序列数据提供了专用的一组移位和差分操作,在金融分析和信号处理中极为常用。
7.1 shift():数据移位
shift()将数据沿时间轴向前或向后移动。正数表示将数据向后推,相当于引用过去的值;负数表示将数据向前拉,相当于引用未来的值。
# 创建时间序列
dates = pd.date_range ('2024-01-01' , periods =5 , freq ='D' )
s = pd.Series ([100 , 102 , 105 , 103 , 108 ], index =dates)
# 滞后1期:相当于昨天
s_shifted = s.shift (1 )
# 输出:
# 2024-01-01 NaN
# 2024-01-02 100.0
# 2024-01-03 102.0
# 2024-01-04 105.0
# 2024-01-05 103.0
# 前移1期:相当于明天
s.shift (-1 )
# 计算"昨日收益率"的实用场景
returns = (s - s.shift (1 )) / s.shift (1 )
7.2 diff():差分运算
diff()计算当前值与前n个时间点的差值,等价于s - s.shift(n)。diff(1)是逐期差分,可消除线性趋势。
# 一阶差分:当前值 - 上一个值
s.diff (1 )
# 输出:
# 2024-01-01 NaN
# 2024-01-02 2.0 (102-100)
# 2024-01-03 3.0 (105-102)
# 2024-01-04 -2.0 (103-105)
# 2024-01-05 5.0 (108-103)
# 二阶差分:在一阶差分基础上再差分
s.diff (2 )
# 季节差分:对周数据的周同比(lag=7)
weekly_sales.diff (7 ) # 与上周同日对比
7.3 pct_change():百分比变化
pct_change()计算当前值相比于前n个值的百分比变化率,是金融分析中的核心函数。
# 日收益率计算
s.pct_change (1 )
# 输出:
# 2024-01-01 NaN
# 2024-01-02 0.020000 (102-100)/100 = +2%
# 2024-01-03 0.029412 (105-102)/102 ≈ +2.94%
# 2024-01-04 -0.019048 (103-105)/105 ≈ -1.90%
# 2024-01-05 0.048544 (108-103)/103 ≈ +4.85%
# 周同比变化率(与7天前比较)
daily_data.pct_change (7 )
# 累计收益率:从第一天开始的累计变化
(s / s.iloc [0 ] - 1 ).mul (100 ).round (2 )
对比总结: shift是位移操作,保留量纲;diff是做减法,适合检查平稳性(ARIMA模型前置步骤);pct_change是归一化比率,适合不同价格区间资产的横向对比。三者组合使用可以构建复杂的特征工程流程。
八、日期偏移计算
实际业务中经常需要计算多个日期之间的偏移量,或者找到相对于某个日期"下个月末"、"下一个工作日"之类的目标日期。Pandas提供了两种主要方案。
8.1 DateOffset 的应用
DateOffset是Pandas原生的偏移量类,支持与Timestamp和DatetimeIndex的算术运算。
from pandas.tseries.offsets import DateOffset, MonthEnd, MonthBegin, Week, BDay
# 下个月末
pd.Timestamp ('2024-01-15' ) + MonthEnd (0 ) # 当前月最后一天
# 输出: Timestamp('2024-01-31 00:00:00')
pd.Timestamp ('2024-01-15' ) + MonthEnd (1 ) # 下个月最后一天
# 输出: Timestamp('2024-02-29 00:00:00')
# 下个周一
pd.Timestamp ('2024-01-05' ) + Week (weekday =0 ) # 0=周一
# 往后数5个工作日
pd.Timestamp ('2024-01-05' ) + BDay (5 )
# 输出: Timestamp('2024-01-12 00:00:00')(跳过周末)
# 组合偏移:下个月第一个工作日
pd.Timestamp ('2024-01-15' ) + MonthBegin (1 ) + BDay (0 )
8.2 relativedelta 灵活计算
dateutil库的relativedelta提供了比DateOffset更灵活的参数,尤其适合计算"3个月后的同一天"、"下个月的第4个周五"等复杂场景。
from dateutil.relativedelta import relativedelta
# 3个月后的同一天
pd.Timestamp ('2024-01-31' ) + relativedelta (months =3 )
# 输出: Timestamp('2024-04-30 00:00:00')
# 注意:4月只有30天,自动处理了边界情况
# 计算两个日期之间的差
d1 = pd.Timestamp ('2024-06-15' )
d2 = pd.Timestamp ('2024-01-10' )
rd = relativedelta (d1, d2)
print (f"{rd.years}年{rd.months}月{rd.days}天" )
# 输出: 0年5月5天
# 结合Pandas使用:批量计算日期偏移
dates = pd.date_range ('2024-01-01' , periods =5 , freq ='MS' )
dates + relativedelta (months =1 , days =15 )
# 在每个月初基础上加1个月15天
DateOffset vs relativedelta: DateOffset与Pandas的集成更紧密,支持工作日日历、节假日等高级功能;relativedelta的参数命名更灵活(如years/months/days直接用关键字参数),适合快速计算。二者可以混合使用,推荐在Pandas生态中优先使用DateOffset。
九、综合实战案例
下面通过一个真实的金融数据分析场景,整合以上所有知识点。假设我们有某只股票2024年1月的分钟级交易数据,需要完成一系列时间序列分析任务。
import pandas as pd
import numpy as np
from pandas.tseries.offsets import BDay, MonthEnd
from dateutil.relativedelta import relativedelta
# 步骤1:生成2024年1月的工作日交易数据(9:30-16:00,每30分钟)
trading_hours = pd.date_range (
'2024-01-01 09:30' , '2024-01-31 16:00' ,
freq ='30T'
)
# 过滤出工作日
trading_hours = trading_hours[trading_hours.weekday < 5 ]
# 步骤2:模拟股价数据(随机游走)
np.random.seed(42 )
returns = np.random.randn(len(trading_hours)) * 0.001
price = 100 * np.cumprod(1 + returns)
df = pd.DataFrame ({'close' : price}, index =trading_hours)
# 步骤3:提取时间特征
df['hour' ] = df.index.hour
df['minute' ] = df.index.minute
df['weekday' ] = df.index.weekday
df['is_morning' ] = df.index.hour < 12
# 步骤4:计算技术指标
df['return_1d' ] = df['close' ].pct_change () # 30分钟收益率
df['return_shift1' ] = df['close' ].shift (1 ) # 上一个收盘价
df['diff_close' ] = df['close' ].diff () # 价格差分
# 步骤5:按日重采样计算统计数据
daily_stats = df.resample('D' ).agg({
'close' : ['first' , 'last' , 'max' , 'min' , 'mean' ],
'return_1d' : 'std'
})
daily_stats.columns = ['open' , 'close' , 'high' , 'low' , 'avg' , 'volatility' ]
# 步骤6:计算月收益率
month_end = df.resample('M' )['close' ].last ()
monthly_return = month_end.pct_change ()
print (f"2024年1月收益率: {monthly_return.iloc[0]:.2%}" )
# 步骤7:查找每月最后一个交易日
last_trading_days = df.resample('M' ).apply (lambda x: x.index[-1 ])
print ("月末交易日:" , last_trading_days)
# 步骤8:时区处理——将数据转换到纽约时间
df.index = df.index.tz_localize ('America/New_York' )
df_asia = df.index.tz_convert ('Asia/Shanghai' )
print ("纽约时间:" , df.index[:3 ])
print ("上海时间:" , df_asia[:3 ])
上述案例展示了从数据生成、特征工程、指标计算、重采样到时区处理的全流程,涵盖了本文讨论的大部分核心知识点。
十、核心要点总结
时间序列处理速查表:
日期解析: pd.to_datetime()是最核心的函数;read_csv中使用parse_dates自动解析;errors='coerce'可防止异常值中断流程
日期范围生成: date_range(时间戳)、period_range(时间区间)、bdate_range(工作日序列),配合freq参数灵活定义间隔
DatetimeIndex属性: 通过.year/.month/.day/.hour/.weekday等提取时间特征;字符串切片(df.loc['2024-01'])智能匹配全月
频率与偏移: 频率别名可叠加使用(如'2D3h');DateOffset支持自定义偏移规则;BDay处理工作日日历
时区处理: tz_localize贴标签(naive→aware),tz_convert转换显示时间;创建时直接用tz参数更便捷
时间序列操作: shift()做滞后/前导、diff()做差分、pct_change()计算变化率——三者是特征工程的基石
日期偏移: MonthEnd/MonthBegin可以精确找到月末月初;relativedelta适合灵活的单位组合计算
重采样(Resample): df.resample('D')按日聚合,支持agg自定义统计函数,是高维时间序列分析的必备工具
学习路径建议: 掌握以上基础知识后,可以进一步学习Pandas时间序列的高级主题:(1) 滑动窗口计算(rolling().mean()等);(2) 时间序列重采样(resample的各种聚合策略);(3) 缺失时间点的插值与填充(asfreq、reindex、interpolate);(4) 与statsmodels库结合进行时间序列建模(ARIMA、季节性分解等)。