Python中关于日期和时间、日历数据的模块为datatime、time、calendar
**datatime模块中的数据类型如下:即from datatime import *****
data | 用公历日历存储日历日期(年、月、日) |
---|---|
time | 将时间存储为小时、分钟、秒、微秒 |
datatime | 存储日期和时间 |
timedelta | 表示两个datatime值之间的的差(如日、秒、微秒) |
tzinfo | 用于存储时区信息的基本类型 |
datatime格式化详细说明(兼容ISO C89)
%Y | 四位的年份 |
---|---|
%y | 两位的年份 |
%m | 两位的月份[01, 12] |
%d | 两位的日期号[01, 31] |
%H | 小时,24小时制[00, 23] |
%I | 小时,12小时制[01, 12] |
%M | 两位的分钟[00, 59] |
%S | 秒[00, 61](60,61是闰秒) |
%w | 星期日期[0(星期天), 6] |
%U | 一年中的星期数[00, 53]。以星期天为每周的第一天,一年中第一个星期天前的日期作为‘第0周’ |
%W | 一年中的星期数[00, 53]。以星期一为每周的第一天,一年中第一个星期一前的日期作为‘第0周’ |
%z | 格式为+HHMM或-HHMM的UTC时区偏移;如果没有时区则为空 |
%F | %Y-%m-%d的简写(如,2021-6-24) |
%D | %m/%d/%y的简写(如,06/24/21) |
datatime对象特定地区日期格式化选项
%a | 缩写的工作日名称 |
---|---|
%A | 全写的工作日名称 |
%b | 简写的月份名称 |
%B | 全写的月份名称 |
%c | 完整的日期和时间 (如,‘Tue 01 May 2012 04:20:57 PM’) |
%p | AM或PM的地区等效 |
%x | 适合地区的格式化日期 (如在美国 '05/01/2012’即为May 1) |
%X | 适合地区的时间 (如,’ 04:24:12 PM ') |
import numpy as np import pandas as pd np.random.seed(12345) import matplotlib.pyplot as plt plt.rc('figure', figsize=(10, 6)) PREVIOUS_MAX_ROWS = pd.options.display.max_rows pd.options.display.max_rows = 20 np.set_printoptions(precision=4, suppress=True)
from datetime import datetime now = datetime.now() now '''datetime.datetime(2021, 6, 24, 16, 47, 28, 879956)''' now.year, now.month, now.day '''(2021, 6, 24)''' # timedelta表示两个datetime对象的时间差 delta = datetime(2011, 1, 7) - datetime(2008, 6, 24, 8, 15) delta '''datetime.timedelta(days=926, seconds=56700)''' delta.days '''926''' delta.seconds '''56700''' # 为datetime对象加/减一个timedelta产生新的datatime对象 from datetime import timedelta start = datetime(2011, 1, 7) start + timedelta(12) '''datetime.datetime(2011, 1, 19, 0, 0)''' start - 2 * timedelta(12) '''datetime.datetime(2010, 12, 14, 0, 0)'''
# 用str方法或传递一个指定格式给strftime方法对datetime对象和pandas的Timestamp对象进行格式化 stamp = datetime(2011, 1, 3) str(stamp) ''''2011-01-03 00:00:00'''' stamp.strftime('%Y-%m-%d') ''''2011-01-03'''' # 用datetime.strptime()和格式码将字符串转换为日期 # datetime.strptime()是在已知格式的情况下转换日期的好方法 value = '2011-01-03' datetime.strptime(value, '%Y-%m-%d') '''datetime.datetime(2011, 1, 3, 0, 0)''' datestrs = ['7/6/2011', '8/6/2011'] [datetime.strptime(x, '%m/%d/%Y') for x in datestrs] '''[datetime.datetime(2011, 7, 6, 0, 0), datetime.datetime(2011, 8, 6, 0, 0)]''' # 对于通用的日期格式可以使用dateutil包的parser.parse方法 # dateutil可以解析大部分人类可理解的日期表示 from dateutil.parser import parse parse('2011-01-03') '''datetime.datetime(2011, 1, 3, 0, 0)''' parse('Jan 31, 1997 10:45 PM') '''datetime.datetime(1997, 1, 31, 22, 45)''' # 传递dayfirst=True,来解析日期在月份之前的数据 parse('6/12/2011', dayfirst=True) '''datetime.datetime(2011, 12, 6, 0, 0)''' # pd.to_datetime()可以转换很多不同的日期格式 datestrs = ['2011-07-06 12:00:00', '2011-08-06 00:00:00'] pd.to_datetime(datestrs) '''DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00:00'], dtype='datetime64[ns]', freq=None)''' # pd.to_datetime()还可处理那些被认为是缺失值的值(None,空字符串等) idx = pd.to_datetime(datestrs + [None]) idx '''DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00:00', 'NaT'], dtype='datetime64[ns]', freq=None)''' idx[2] # NaT(Not a time)是pandas中时间戳数据值是null的值 '''NaT''' pd.isnull(idx) '''array([False, False, True])'''
pandas中的基础时间序列种类是由时间戳索引的Series;
pandas外部则通常表示为python字符串或datatime对象
from datetime import datetime dates = [datetime(2011, 1, 2), datetime(2011, 1, 5), datetime(2011, 1, 7), datetime(2011, 1, 8), datetime(2011, 1, 10), datetime(2011, 1, 12)] # pandas中基础的时间序列:由时间戳索引的Series ts = pd.Series(np.random.randn(6), index=dates) ts ''' 2011-01-02 -0.204708 2011-01-05 0.478943 2011-01-07 -0.519439 2011-01-08 -0.555730 2011-01-10 1.965781 2011-01-12 1.393406 dtype: float64 ''' # 这些datetime对象实际上是被放在一个DatetimeIndex中 ts.index ''' DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07', '2011-01-08', '2011-01-10', '2011-01-12'], dtype='datetime64[ns]', freq=None) '''
# 跟其他Series一样,不同索引的时间序列之间的算术运算会自动按日期对齐 ts + ts[::2] ''' 2011-01-02 -0.409415 2011-01-05 NaN 2011-01-07 -1.038877 2011-01-08 NaN 2011-01-10 3.931561 2011-01-12 NaN dtype: float64 ''' # pandas用NumPy的datetime64数据类型以纳秒ns形式存储时间戳 ts.index.dtype '''dtype('<M8[ns]')''' # DatetimeIndex中的各个标量值是pandas的Timestamp对象 stamp = ts.index[0] stamp '''Timestamp('2011-01-02 00:00:00')'''
# 当基于标签进行索引和选择时,时间序列和其他pandas.Series类似 stamp = ts.index[2] ts[stamp] '''-0.5194387150567381''' # 传递能解释为日期的字符串,'20110110'也可以 ts['1/10/2011'] '''1.9657805725027142'''
# 对于长的时间序列,可以传递一个可解释为年份或年份月份的字符串选择数据 longer_ts = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000)) longer_ts ''' 2000-01-01 0.092908 2000-01-02 0.281746 2000-01-03 0.769023 2000-01-04 1.246435 2000-01-05 1.007189 ... 2002-09-22 0.930944 2002-09-23 -0.811676 2002-09-24 -1.830156 2002-09-25 -0.138730 2002-09-26 0.334088 Freq: D, Length: 1000, dtype: float64 ''' longer_ts['2001'] ''' 2001-01-01 1.599534 2001-01-02 0.474071 2001-01-03 0.151326 2001-01-04 -0.542173 2001-01-05 -0.475496 ... 2001-12-27 0.057874 2001-12-28 -0.433739 2001-12-29 0.092698 2001-12-30 -1.397820 2001-12-31 1.457823 Freq: D, Length: 365, dtype: float64 ''' longer_ts['2001-05'] ''' 2001-05-01 -0.622547 2001-05-02 0.936289 2001-05-03 0.750018 2001-05-04 -0.056715 2001-05-05 2.300675 ... 2001-05-27 0.235477 2001-05-28 0.111835 2001-05-29 -1.251504 2001-05-30 -2.949343 2001-05-31 0.634634 Freq: D, Length: 31, dtype: float64 '''
ts ''' 2011-01-02 -0.204708 2011-01-05 0.478943 2011-01-07 -0.519439 2011-01-08 -0.555730 2011-01-10 1.965781 2011-01-12 1.393406 dtype: float64 ''' # 传递datatime对象进行切片 ts[datetime(2011, 1, 7):] ''' 2011-01-07 -0.519439 2011-01-08 -0.555730 2011-01-10 1.965781 2011-01-12 1.393406 dtype: float64 ''' # 传递不包含在时间序列中的时间戳进行切片,以执行范围查询 ts['1/6/2011':'1/11/2011'] ''' 2011-01-07 -0.519439 2011-01-08 -0.555730 2011-01-10 1.965781 dtype: float64 '''
传递可别解释为日期的字符串、datatime对象或时间戳进行切片都是产生了原时间序列的视图;即没有数据被复制,且在切片上的修改会反映到原始数据上
# 等价的实例方法truncate,可以在两个日期间对Series进行切片 ts.truncate(before='1/5/2011', after='1/10/2011') ''' 2011-01-05 0.478943 2011-01-07 -0.519439 2011-01-08 -0.555730 2011-01-10 1.965781 dtype: float64 '''
# 上面的这些操作也适用于DataFrame dates = pd.date_range('1/1/2000', periods=100, freq='W-WED') long_df = pd.DataFrame(np.random.randn(100, 4), index=dates, columns=['Colorado', 'Texas', 'New York', 'Ohio']) long_df.loc['5-2001']
# 在某个时间戳上有多个数据观察值,即时间序列含有重复索引 dates = pd.DatetimeIndex(['1/1/2000', '1/2/2000', '1/2/2000', '1/2/2000', '1/3/2000']) dup_ts = pd.Series(np.arange(5), index=dates) dup_ts ''' 2000-01-01 0 2000-01-02 1 2000-01-02 2 2000-01-02 3 2000-01-03 4 dtype: int32 ''' # 通过index.is_unique属性,查看索引是否唯一 dup_ts.index.is_unique '''False'''
# 对含有重复索引的时间序列进行索引,结果是标量还是Series切片取决于时间戳是否重复 dup_ts['1/3/2000'] # not duplicated '''4''' dup_ts['1/2/2000'] # duplicated ''' 2000-01-02 1 2000-01-02 2 2000-01-02 3 dtype: int32 '''
# 聚合含有重复索引的数据,传递level=0给groupby即可 grouped = dup_ts.groupby(level=0) grouped.mean() ''' 2000-01-01 0 2000-01-02 2 2000-01-03 4 dtype: int32 ''' grouped.count() ''' 2000-01-01 1 2000-01-02 3 2000-01-03 1 dtype: int64 '''
# pd.date_range():根据特定频率生成指定长度的DatatimeIndex # 默认生成每日的时间戳 index = pd.date_range('2012-04-01', '2012-06-01') index ''' DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04', '2012-04-05', '2012-04-06', '2012-04-07', '2012-04-08', '2012-04-09', '2012-04-10', '2012-04-11', '2012-04-12', '2012-04-13', '2012-04-14', '2012-04-15', '2012-04-16', '2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20', '2012-04-21', '2012-04-22', '2012-04-23', '2012-04-24', '2012-04-25', '2012-04-26', '2012-04-27', '2012-04-28', '2012-04-29', '2012-04-30', '2012-05-01', '2012-05-02', '2012-05-03', '2012-05-04', '2012-05-05', '2012-05-06', '2012-05-07', '2012-05-08', '2012-05-09', '2012-05-10', '2012-05-11', '2012-05-12', '2012-05-13', '2012-05-14', '2012-05-15', '2012-05-16', '2012-05-17', '2012-05-18', '2012-05-19', '2012-05-20', '2012-05-21', '2012-05-22', '2012-05-23', '2012-05-24', '2012-05-25', '2012-05-26', '2012-05-27', '2012-05-28', '2012-05-29', '2012-05-30', '2012-05-31', '2012-06-01'], dtype='datetime64[ns]', freq='D') '''
# 若只传递一个起始或结尾日期,则必须传递一个用于生成范围的数字给periods pd.date_range(start='2012-04-01', periods=20) ''' DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04', '2012-04-05', '2012-04-06', '2012-04-07', '2012-04-08', '2012-04-09', '2012-04-10', '2012-04-11', '2012-04-12', '2012-04-13', '2012-04-14', '2012-04-15', '2012-04-16', '2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20'], dtype='datetime64[ns]', freq='D') ''' pd.date_range(end='2012-06-01', periods=20) ''' DatetimeIndex(['2012-05-13', '2012-05-14', '2012-05-15', '2012-05-16', '2012-05-17', '2012-05-18', '2012-05-19', '2012-05-20', '2012-05-21', '2012-05-22', '2012-05-23', '2012-05-24', '2012-05-25', '2012-05-26', '2012-05-27', '2012-05-28', '2012-05-29', '2012-05-30', '2012-05-31', '2012-06-01'], dtype='datetime64[ns]', freq='D') '''
# 传递时间序列频率给freq,会生成落在指定日期范围的DatatimeIndex pd.date_range('2000-01-01', '2000-12-01', freq='BM') ''' DatetimeIndex(['2000-01-31', '2000-02-29', '2000-03-31', '2000-04-28', '2000-05-31', '2000-06-30', '2000-07-31', '2000-08-31', '2000-09-29', '2000-10-31', '2000-11-30'], dtype='datetime64[ns]', freq='BM') '''
# pd.date_range()默认保留开始或结束时间戳的时间 # 传递normalize=True会标准化为零点的时间戳,即去掉了时间戳中的时间 pd.date_range('2012-05-02 12:56:31', periods=5) ''' DatetimeIndex(['2012-05-02 12:56:31', '2012-05-03 12:56:31', '2012-05-04 12:56:31', '2012-05-05 12:56:31', '2012-05-06 12:56:31'], dtype='datetime64[ns]', freq='D') ''' pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True) ''' DatetimeIndex(['2012-05-02', '2012-05-03', '2012-05-04', '2012-05-05', '2012-05-06'], dtype='datetime64[ns]', freq='D') '''
基础时间序列频率 | ||
---|---|---|
别名 | 偏置类型 | 描述 |
D | Day | 日历日的每天 |
M | BusinessDay | 工作日的每天 |
H | Hour | 每小时 |
T或min | Minute | 每分钟 |
S | Second | 每秒 |
L或ms | Milli | 每毫秒(1/1000秒) |
U | Micro | 每微秒(1/1000000秒) |
M | MonthEnd | 日历的月内最后一天 |
BM | BusinessMonthEnd | 工作日的月内最后一天 |
MS | MonthBegin | 日历的月内第一天 |
BMS | BusinessMonthBegin | 工作日的月内第一天 |
W-MON,W-TUE,…… | Week | 按给定星期日期按每周取日期(MON,TUE,WED,THU,FRI,SAT或SUN) |
WOM-1MON,WOM-2MON,…… | WeekOfMonth | 在本月的一/二/三或四周创建按周分隔的日期(如WOM-3FRI:每月第3个星期五) |
Q-JAN,Q-FEB,…… | QuarterEnd | 每月最后一个日历日的季度日期,以表示月份结束的年份 (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV或DEC) |
BQ-JAN,BQ-FEB,…… | BusinessQuarterEnd | 每月最后一个工作日对应的季度日期,以表示月份结束的年份 |
QS-JAN,QS-FEB,…… | QuarterBegin | 每月第一个日历日对应的季度日期,以表示月份结束的年份 |
BQS-JAN,BQS-FEB,…… | BusinessQuarterBegin | 每月第一个工作日对应的季度日期,以表示月份结束的年份 |
A-JAN,A-FEB,…… | YearEnd | 给定月份所在月的最后一个日历日的年度日期 (JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV或DEC) |
BA-JAN,BA-FEB,…… | BusinessYearEnd | 给定月份所在月的最后一个工作日的年度日期 |
AS-JAN,AS-FEB,…… | YearBegin | 给定月份所在月的第一个日历日的年度日期 |
BAS-JAN,BAS-FEB,…… | BusinessYearBegin | 给定月份所在月的第一个工作日的年度日期 |
# 对于每个基础频率,都有一个对象可别用于定义日期偏置 # 如,每小时的频率可用Hour类来表示 from pandas.tseries.offsets import Hour, Minute hour = Hour() hour '''<Hour>''' # 传递整数定义偏置量的倍数 four_hours = Hour(4) four_hours '''<4 * Hours>''' pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4h') ''' DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 04:00:00', '2000-01-01 08:00:00', '2000-01-01 12:00:00', '2000-01-01 16:00:00', '2000-01-01 20:00:00', '2000-01-02 00:00:00', '2000-01-02 04:00:00', '2000-01-02 08:00:00', '2000-01-02 12:00:00', '2000-01-02 16:00:00', '2000-01-02 20:00:00', '2000-01-03 00:00:00', '2000-01-03 04:00:00', '2000-01-03 08:00:00', '2000-01-03 12:00:00', '2000-01-03 16:00:00', '2000-01-03 20:00:00'], dtype='datetime64[ns]', freq='4H') ''' # 多个偏置可通过加法联合 Hour(2) + Minute(30) '''<150 * Minutes>''' pd.date_range('2000-01-01', periods=10, freq='1h30min') ''' DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 01:30:00', '2000-01-01 03:00:00', '2000-01-01 04:30:00', '2000-01-01 06:00:00', '2000-01-01 07:30:00', '2000-01-01 09:00:00', '2000-01-01 10:30:00', '2000-01-01 12:00:00', '2000-01-01 13:30:00'], dtype='datetime64[ns]', freq='90T') ''' # 传递freq='WOM-3FRI',获取指定日期范围的月中某星期的第三个星期五的日期 rng = pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI') list(rng) ''' [Timestamp('2012-01-20 00:00:00', freq='WOM-3FRI'), Timestamp('2012-02-17 00:00:00', freq='WOM-3FRI'), Timestamp('2012-03-16 00:00:00', freq='WOM-3FRI'), Timestamp('2012-04-20 00:00:00', freq='WOM-3FRI'), Timestamp('2012-05-18 00:00:00', freq='WOM-3FRI'), Timestamp('2012-06-15 00:00:00', freq='WOM-3FRI'), Timestamp('2012-07-20 00:00:00', freq='WOM-3FRI'), Timestamp('2012-08-17 00:00:00', freq='WOM-3FRI')] '''
# 移位指将时间序列中数据按时间向前或向后移动 # 通过Series或者DataFrame的shift()方法实现 ts = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2000', periods=4, freq='M')) ts ''' 2000-01-31 -0.066748 2000-02-29 0.838639 2000-03-31 -0.117388 2000-04-30 -0.517795 Freq: M, dtype: float64 ''' # 向前移位数据,会在起始位引入缺失值 ts.shift(2) ''' 2000-01-31 NaN 2000-02-29 NaN 2000-03-31 -0.066748 2000-04-30 0.838639 Freq: M, dtype: float64 ''' # 向后移位数据,会在结束为引入缺失值 ts.shift(-2) ''' 2000-01-31 -0.117388 2000-02-29 -0.517795 2000-03-31 NaN 2000-04-30 NaN Freq: M, dtype: float64 '''
shift()常用于计算时间序列或DataFrame多列时间序列的百分比变化,即ts/ts.shift(1)-1
# 传递频率freq='M'给shift将会移位时间戳,而不是数据 # 这里表示:时间戳向前移位两个月,即+2个月 # 注意数据的向前移位与时间戳的向前移位的差异 ts.shift(2, freq='M') ''' 2000-03-31 -0.066748 2000-04-30 0.838639 2000-05-31 -0.117388 2000-06-30 -0.517795 Freq: M, dtype: float64 ''' # 传递freq='D',表示每个时间戳按天向前移位3天,即+3天 ts.shift(3, freq='D') ''' 2000-02-03 -0.066748 2000-03-03 0.838639 2000-04-03 -0.117388 2000-05-03 -0.517795 dtype: float64 ''' # # 传递freq='90T',表示每个时间戳向前移位90分钟 ts.shift(1, freq='90T') ''' 2000-01-31 01:30:00 -0.066748 2000-02-29 01:30:00 0.838639 2000-03-31 01:30:00 -0.117388 2000-04-30 01:30:00 -0.517795 dtype: float64 '''
# pandas日期偏置也可使用datatime或Timestamp对象完成 from pandas.tseries.offsets import Day, MonthEnd now = datetime(2011, 11, 17) now + 3 * Day() '''Timestamp('2011-11-20 00:00:00')''' # 锚定偏置量,如MonthEnd,BusinessMonthEnd会将日期前滚到下一个日期 now + MonthEnd() '''Timestamp('2011-11-30 00:00:00')''' now + MonthEnd(2) '''Timestamp('2011-12-31 00:00:00')''' # 锚定偏置可用rollforward,rollback显示的将日期向前或向后滚动 offset = MonthEnd() offset.rollforward(now) '''Timestamp('2011-11-30 00:00:00')''' offset.rollback(now) '''Timestamp('2011-12-31 00:00:00')'''
# 将移位方法与groupby一起使用 ts = pd.Series(np.random.randn(20), index=pd.date_range('1/15/2000', periods=20, freq='4d')) ts ''' 2000-01-15 -0.116696 2000-01-19 2.389645 2000-01-23 -0.932454 2000-01-27 -0.229331 2000-01-31 -1.140330 2000-02-04 0.439920 2000-02-08 -0.823758 2000-02-12 -0.520930 2000-02-16 0.350282 2000-02-20 0.204395 2000-02-24 0.133445 2000-02-28 0.327905 2000-03-03 0.072153 2000-03-07 0.131678 2000-03-11 -1.297459 2000-03-15 0.997747 2000-03-19 0.870955 2000-03-23 -0.991253 2000-03-27 0.151699 2000-03-31 1.266151 Freq: 4D, dtype: float64 ''' # offset.rollforward会默认对ts的时间戳索引进行操作 ts.groupby(offset.rollforward).mean() ''' 2000-01-31 -0.005833 2000-02-29 0.015894 2000-03-31 0.150209 dtype: float64 ''' # resample()方法可达到同的效果 ts.resample('M').mean() ''' 2000-01-31 -0.005833 2000-02-29 0.015894 2000-03-31 0.150209 Freq: M, dtype: float64 '''
# 时区通常被表示为UTC的偏置,时区信息来源于第三方库pytz import pytz pytz.common_timezones[-5:] '''['US/Eastern', 'US/Hawaii', 'US/Mountain', 'US/Pacific', 'UTC']''' # pytz.timezone()获得pytz的时区对象 tz = pytz.timezone('America/New_York') tz '''<DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>'''
rng = pd.date_range('3/9/2012 9:30', periods=6, freq='D') ts = pd.Series(np.random.randn(len(rng)), index=rng) ts ''' 2012-03-09 09:30:00 -0.202469 2012-03-10 09:30:00 0.050718 2012-03-11 09:30:00 0.639869 2012-03-12 09:30:00 0.597594 2012-03-13 09:30:00 -0.797246 2012-03-14 09:30:00 0.472879 Freq: D, dtype: float64 ''' # 用于返回时区 print(ts.index.tz) '''None''' # 日期范围可通过时区集合生成 pd.date_range('3/9/2012 9:30', periods=10, freq='D', tz='UTC') ''' DatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00', '2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00', '2012-03-15 09:30:00+00:00', '2012-03-16 09:30:00+00:00', '2012-03-17 09:30:00+00:00', '2012-03-18 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq='D') '''
ts ''' 2012-03-09 09:30:00 -0.202469 2012-03-10 09:30:00 0.050718 2012-03-11 09:30:00 0.639869 2012-03-12 09:30:00 0.597594 2012-03-13 09:30:00 -0.797246 2012-03-14 09:30:00 0.472879 Freq: D, dtype: float64 ''' # 用tz_localize()可将简单时区转化为本地化时区 ts_utc = ts.tz_localize('UTC') ts_utc ''' 2012-03-09 09:30:00+00:00 -0.202469 2012-03-10 09:30:00+00:00 0.050718 2012-03-11 09:30:00+00:00 0.639869 2012-03-12 09:30:00+00:00 0.597594 2012-03-13 09:30:00+00:00 -0.797246 2012-03-14 09:30:00+00:00 0.472879 Freq: D, dtype: float64 ''' ts_utc.index ''' DatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00', '2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq='D') ''' # 本地化时区可通过tz_convert()转换为另一个时区 ts_utc.tz_convert('America/New_York') ''' 2012-03-09 04:30:00-05:00 -0.202469 2012-03-10 04:30:00-05:00 0.050718 2012-03-11 05:30:00-04:00 0.639869 2012-03-12 05:30:00-04:00 0.597594 2012-03-13 05:30:00-04:00 -0.797246 2012-03-14 05:30:00-04:00 0.472879 Freq: D, dtype: float64 ''' # 用tz_localize()本地化时区为美国纽约时区 ts_eastern = ts.tz_localize('America/New_York') ts_eastern.tz_convert('UTC') ''' 2012-03-09 14:30:00+00:00 -0.202469 2012-03-10 14:30:00+00:00 0.050718 2012-03-11 13:30:00+00:00 0.639869 2012-03-12 13:30:00+00:00 0.597594 2012-03-13 13:30:00+00:00 -0.797246 2012-03-14 13:30:00+00:00 0.472879 dtype: float64 ''' ts_eastern.tz_convert('Europe/Berlin') ''' 2012-03-09 15:30:00+01:00 -0.202469 2012-03-10 15:30:00+01:00 0.050718 2012-03-11 14:30:00+01:00 0.639869 2012-03-12 14:30:00+01:00 0.597594 2012-03-13 14:30:00+01:00 -0.797246 2012-03-14 14:30:00+01:00 0.472879 dtype: float64 ''' # tz_localize()、tz_convert()也是DatetimeIndex的实例方法 ts.index.tz_localize('Asia/Shanghai') ''' DatetimeIndex(['2012-03-09 09:30:00+08:00', '2012-03-10 09:30:00+08:00', '2012-03-11 09:30:00+08:00', '2012-03-12 09:30:00+08:00', '2012-03-13 09:30:00+08:00', '2012-03-14 09:30:00+08:00'], dtype='datetime64[ns, Asia/Shanghai]', freq=None) '''
# 单独的Timestamp对象也可本地化为时区感知时间戳,并进行时区转化 stamp = pd.Timestamp('2011-03-12 04:00') stamp_utc = stamp.tz_localize('utc') stamp_utc.tz_convert('America/New_York') '''Timestamp('2011-03-11 23:00:00-0500', tz='America/New_York')''' # 创建Timestamp对象时传递时区 stamp_moscow = pd.Timestamp('2011-03-12 04:00', tz='Europe/Moscow') stamp_moscow '''Timestamp('2011-03-12 04:00:00+0300', tz='Europe/Moscow')''' # 时区感知的Timestamp对象内存储了一个Unix纪元(1970-1-1)至今的纳秒数量的UTC时间戳数值 stamp_utc.value '''1299902400000000000''' # 纳秒数量UTC时间戳数值在时区转化中不变 stamp_utc.tz_convert('America/New_York').value '''1299902400000000000'''
from pandas.tseries.offsets import Hour stamp = pd.Timestamp('2012-03-12 01:30', tz='US/Eastern') stamp '''Timestamp('2012-03-12 01:30:00-0400', tz='US/Eastern')''' # 加的Hour()是UTC的1小时 stamp + Hour() '''Timestamp('2012-03-12 02:30:00-0400', tz='US/Eastern')''' stamp = pd.Timestamp('2012-11-04 00:30', tz='US/Eastern') stamp '''Timestamp('2012-11-04 00:30:00-0400', tz='US/Eastern')''' stamp + 2 * Hour() '''Timestamp('2012-11-04 01:30:00-0500', tz='US/Eastern')'''
# 不同时区间的操作会先自动转为UTC时间,结果也是UTC时间 rng = pd.date_range('3/7/2012 9:30', periods=10, freq='B') ts = pd.Series(np.random.randn(len(rng)), index=rng) ts ''' 2012-03-07 09:30:00 0.522356 2012-03-08 09:30:00 -0.546348 2012-03-09 09:30:00 -0.733537 2012-03-12 09:30:00 1.302736 2012-03-13 09:30:00 0.022199 2012-03-14 09:30:00 0.364287 2012-03-15 09:30:00 -0.922839 2012-03-16 09:30:00 0.312656 2012-03-19 09:30:00 -1.128497 2012-03-20 09:30:00 -0.333488 Freq: B, dtype: float64 ''' ts1 = ts[:7].tz_localize('Europe/London') ts2 = ts1[2:].tz_convert('Europe/Moscow') result = ts1 + ts2 result.index ''' DatetimeIndex(['2012-03-07 09:30:00+00:00', '2012-03-08 09:30:00+00:00', '2012-03-09 09:30:00+00:00', '2012-03-12 09:30:00+00:00', '2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00', '2012-03-15 09:30:00+00:00'], dtype='datetime64[ns, UTC]', freq=None) '''
Period类表示的是时间区间,即时间范围。如,一些天、一些月、一些季度、一些年
# 以下Period对象表示从2007年1月1日到2007年12月31日 p = pd.Period(2007, freq='A-DEC') p '''Period('2007', 'A-DEC')''' # 做加减法时直接按频率freq='A-DEC'移位 p + 5 '''Period('2012', 'A-DEC')''' p - 2 '''Period('2005', 'A-DEC')''' # 具有相同频率的两个区间的差是频率的倍数 pd.Period('2014', freq='A-DEC') - p '''<7 * YearEnds: month=12>'''
# 用pd.period_range()可构造规则区间序列 rng = pd.period_range('2000-01-01', '2000-06-30', freq='M') rng '''PeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '2000-06'], dtype='period[M]', freq='M')''' # PeriodIndex类存储的是区间序列,可作为pandas数据结构的轴索引 pd.Series(np.random.randn(6), index=rng) ''' 2000-01 -0.514551 2000-02 -0.559782 2000-03 -0.783408 2000-04 -1.797685 2000-05 -0.172670 2000-06 0.680215 Freq: M, dtype: float64 '''
# pd.PeriodIndex()可生成PeriodIndex类 values = ['2001Q3', '2002Q2', '2003Q1'] index = pd.PeriodIndex(values, freq='Q-DEC') index '''PeriodIndex(['2001Q3', '2002Q2', '2003Q1'], dtype='period[Q-DEC]', freq='Q-DEC')'''
# 创建一个Period区间,以12月作为年度结束月份 p = pd.Period('2007', freq='A-DEC') p '''Period('2007', 'A-DEC')''' # asfreq()可以将区间和PeriodIndex对象转换为其他的频率 p.asfreq('M', how='start') '''Period('2007-01', 'M')''' p.asfreq('M', how='end') '''Period('2007-12', 'M')'''
# 创建一个Period区间,以6月作为年度结束月份 # 这种情况下,2006年7月至2007年6月作为2007年度 p = pd.Period('2007', freq='A-JUN') p '''Period('2007', 'A-JUN')''' # 指定how为'start',会返回2007年度的第一个月 # 由于是以6月作为年度的结束月份,故第一个月份为2006-07 p.asfreq('M', 'start') '''Period('2006-07', 'M')''' p.asfreq('M', 'end') '''Period('2007-06', 'M')'''
# 以上是从低频率转换为高频率,也可从高频率转换为低频率,即类似月转为年 p = pd.Period('Aug-2007', 'M') p.asfreq('A-JUN') # 传入'A-JUN',表名年度结束月份为6月,故2007-8属于2008年度 '''Period('2008', 'A-JUN')''' p = pd.Period('Aug-2007', 'M') p.asfreq('A-SEP') # 传入'A-SEP',表名年度结束月份为6月,故2007-8属于2007年度 '''Period('2007', 'A-SEP')'''
# 完整的PeriodIndex对象或时间序列均可做以上类似的转换 rng = pd.period_range('2006', '2009', freq='A-DEC') ts = pd.Series(np.random.randn(len(rng)), index=rng) ts ''' 2006 1.607578 2007 0.200381 2008 -0.834068 2009 -0.302988 Freq: A-DEC, dtype: float64 ''' ts.asfreq('M', how='start') ''' 2006-01 1.607578 2007-01 0.200381 2008-01 -0.834068 2009-01 -0.302988 Freq: M, dtype: float64 ''' ts.asfreq('B', how='end') ''' 2006-12-29 1.607578 2007-12-31 0.200381 2008-12-31 -0.834068 2009-12-31 -0.302988 Freq: B, dtype: float64 '''
# 季度数据一般是财年结尾,故类似2012Q4一般有不同的含义 # 设置freq='Q-JAN',表示季度结尾的月份是1月 # 故2012Q4表示的是2011年11月到2012年1月这4个月 p = pd.Period('2012Q4', freq='Q-JAN') p '''Period('2012Q4', 'Q-JAN')''' p.asfreq('D', 'start') '''Period('2011-11-01', 'D')''' p.asfreq('D', 'end') '''Period('2012-01-31', 'D')''' # 获取季度倒数第2个工作日下午4点的时间戳 # p.asfreq('B', 'e') - 1获取季度倒数第2个工作 # (p.asfreq('B', 'e') - 1).asfreq('T', 's')将获取的日期转换为分钟,且是当天零点 # 再'+ 16 * 60'是加下午4点的分钟数 p4pm = (p.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60 p4pm '''Period('2012-01-30 16:00', 'T')''' # 用to_timestamp()转换为时间戳 p4pm.to_timestamp() '''Timestamp('2012-01-30 16:00:00')'''
# pd.period_range()用于生成季度序列 rng = pd.period_range('2011Q3', '2012Q4', freq='Q-JAN') ts = pd.Series(np.arange(len(rng)), index=rng) ts ''' 2011Q3 0 2011Q4 1 2012Q1 2 2012Q2 3 2012Q3 4 2012Q4 5 Freq: Q-JAN, dtype: int32 ''' # 获取季度倒数第2个工作日下午4点的时间戳 new_rng = (rng.asfreq('B', 'e') - 1).asfreq('T', 's') + 16 * 60 ts.index = new_rng.to_timestamp() ts ''' 2010-10-28 16:00:00 0 2011-01-28 16:00:00 1 2011-04-28 16:00:00 2 2011-07-28 16:00:00 3 2011-10-28 16:00:00 4 2012-01-30 16:00:00 5 dtype: int32 '''
rng = pd.date_range('2000-01-01', periods=3, freq='M') ts = pd.Series(np.random.randn(3), index=rng) ts ''' 2000-01-31 1.663261 2000-02-29 -0.996206 2000-03-31 1.521760 Freq: M, dtype: float64 ''' # 通过时间戳索引的Series和DataFrame可用to_period()方法转换为区间 pts = ts.to_period() pts ''' 2000-01 1.663261 2000-02 -0.996206 2000-03 1.521760 Freq: M, dtype: float64 '''
rng = pd.date_range('1/29/2000', periods=6, freq='D') ts2 = pd.Series(np.random.randn(6), index=rng) ts2 ''' 2000-01-29 0.244175 2000-01-30 0.423331 2000-01-31 -0.654040 2000-02-01 2.089154 2000-02-02 -0.060220 2000-02-03 -0.167933 Freq: D, dtype: float64 ''' # 区间转换后索引包含重复区间是被允许的 ts2.to_period('M') ''' 2000-01 0.244175 2000-01 0.423331 2000-01 -0.654040 2000-02 2.089154 2000-02 -0.060220 2000-02 -0.167933 Freq: M, dtype: float64 '''
# 创建以JAN-1月作为每年最后一个季度末的月份 rng = pd.period_range('2011Q3', '2012Q4', freq='Q-JAN') ts3 = pd.Series(np.arange(len(rng)), index=rng) ts3 ''' 2011Q3 0 2011Q4 1 2012Q1 2 2012Q2 3 2012Q3 4 2012Q4 5 Freq: Q-JAN, dtype: int32 ''' # to_timestamp()可将区间再转换为时间戳 # how默认为start,取季度第一个月零点作为索引;也可设置how='end' ts3.to_timestamp() ''' 2010-08-01 0 2010-11-01 1 2011-02-01 2 2011-05-01 3 2011-08-01 4 2011-11-01 5 Freq: QS-NOV, dtype: int32 '''
data = pd.read_csv('examples/macrodata.csv') data.head(5)
data.year.head() ''' 0 1959.0 1 1959.0 2 1959.0 3 1959.0 4 1960.0 Name: year, dtype: float64 ''' data.quarter.head() ''' 0 1.0 1 2.0 2 3.0 3 4.0 4 1.0 Name: quarter, dtype: float64 ''' index = pd.PeriodIndex(year=data.year, quarter=data.quarter, freq='Q-DEC') index ''' PeriodIndex(['1959Q1', '1959Q2', '1959Q3', '1959Q4', '1960Q1', '1960Q2', '1960Q3', '1960Q4', '1961Q1', '1961Q2', ... '2007Q2', '2007Q3', '2007Q4', '2008Q1', '2008Q2', '2008Q3', '2008Q4', '2009Q1', '2009Q2', '2009Q3'], dtype='period[Q-DEC]', length=203, freq='Q-DEC') ''' data.index = index data.head()
重新采样 :将时间序列从一个频率转换为另一个频率的过程
向下采样:将更高频率聚合到低频率
向上采样:将低频率转换到高频率
但并非所有的重新采样都是上面两类,如将W-WED转换到W-REI
pandas对象的resample()方法会进行频率转换,其有类似groupby的功能
rng = pd.date_range('2000-01-01', periods=100, freq='D') ts = pd.Series(np.random.randn(len(rng)), index=rng) ts ''' 2000-01-01 0.631634 2000-01-02 -1.594313 2000-01-03 -1.519937 2000-01-04 1.108752 2000-01-05 1.255853 ... 2000-04-05 -0.423776 2000-04-06 0.789740 2000-04-07 0.937568 2000-04-08 -2.253294 2000-04-09 -1.772919 Freq: D, Length: 100, dtype: float64 ''' ts.resample('M').mean() ''' 2000-01-31 -0.165893 2000-02-29 0.078606 2000-03-31 0.223811 2000-04-30 -0.063643 Freq: M, dtype: float64 ''' # kind='period'指定结果中索引的类型为period ts.resample('M', kind='period').mean() ''' 2000-01 -0.165893 2000-02 0.078606 2000-03 0.223811 2000-04 -0.063643 Freq: M, dtype: float64 '''
resample()方法的参数 | |
---|---|
freq | 采样频率,为字符串或DataOffset对象(如,‘5min’,‘M’,或Second(1)) |
axis | 沿着哪个轴采样,默认axis=0 |
fill_method | 向上采样时的差值方式,默认不插值,可选’ffill’、‘bfill’ |
closed | 向下采样中,每段间隔的哪一段是封闭的,可选’right’、‘left’ |
label | 向下采样中,用’right’/'left’的箱标签标记聚合结果(如,9:30到9:35的5分钟间隔可被标记为9:30/9:35) |
loffset | 对箱标签进行时间调校(如,’-1s’/Second(-1)可将聚合标签向前移动1秒) |
limit | 在前向或后向填充时,填充区间最大值 |
kind | 指定结果中索引为区间(‘period’)或时间戳(‘timestamp’),默认为时间序列索引 |
convention | 对区间重新采样时,用于将低频周期转换为高频的约定,默认为’start’,可选’end’ |
使用resample()向下采样需要考虑的事情:
rng = pd.date_range('2000-01-01', periods=12, freq='T') ts = pd.Series(np.arange(12), index=rng) ts ''' 2000-01-01 00:00:00 0 2000-01-01 00:01:00 1 2000-01-01 00:02:00 2 2000-01-01 00:03:00 3 2000-01-01 00:04:00 4 2000-01-01 00:05:00 5 2000-01-01 00:06:00 6 2000-01-01 00:07:00 7 2000-01-01 00:08:00 8 2000-01-01 00:09:00 9 2000-01-01 00:10:00 10 2000-01-01 00:11:00 11 Freq: T, dtype: int32 ''' # 默认closed='left',将索引间隔分为[00:00,00:05)、[00:05,00:10)..., # 之后再在这样的间隔上将数据聚合 # 默认label='left',在结果中取索引间隔的左侧作为行标签 ts.resample('5min', closed='left').sum() ''' 2000-01-01 00:00:00 10 2000-01-01 00:05:00 35 2000-01-01 00:10:00 21 Freq: 5T, dtype: int32 ''' ts.resample('5min', closed='right').sum() ''' 1999-12-31 23:55:00 0 2000-01-01 00:00:00 15 2000-01-01 00:05:00 40 2000-01-01 00:10:00 11 Freq: 5T, dtype: int32 ''' ts.resample('5min', closed='right', label='right').sum() ''' 2000-01-01 00:00:00 0 2000-01-01 00:05:00 15 2000-01-01 00:10:00 40 2000-01-01 00:15:00 11 Freq: 5T, dtype: int32 ''' # 设置loffset='1s',会将结果中的索引向右移动1秒,即加1秒 ts.resample('5min', closed='right', label='right', loffset='1s').sum() ''' 2000-01-01 00:00:01 0 2000-01-01 00:05:01 15 2000-01-01 00:10:01 40 2000-01-01 00:15:01 11 Freq: 5T, dtype: int32 '''
# ohlc()会获得索引间隔内的第一个值、最后一个值、最大值、最小值 ts.resample('5min').ohlc()
# upsampling时并不需要聚合,用asfreq()进行频率转换 frame = pd.DataFrame(np.random.randn(2, 4), index=pd.date_range('1/1/2000', periods=2, freq='W-WED'), columns=['Colorado', 'Texas', 'New York', 'Ohio']) frame
# 用asfreq()进行频率转换,从低频向高频转换时会引入缺失值 df_daily = frame.resample('D').asfreq() df_daily
# 若不想引入缺失值,则可用ffill()设置填充值 frame.resample('D').ffill()
# 传递limit=2给ffill()则仅向前填充两行数据 frame.resample('D').ffill(limit=2)
frame.resample('W-THU').ffill()
frame = pd.DataFrame(np.random.randn(24, 4), index=pd.period_range('1-2000', '12-2001', freq='M'), columns=['Colorado', 'Texas', 'New York', 'Ohio']) frame[:5]
# 向下采样,频率为'A-DEC',年度,12月作为年底最后一月 annual_frame = frame.resample('A-DEC').mean() annual_frame
# 向上采样,并设置填充方式ffill(),默认convention='start' # Q-DEC: 季度, 12月作为最后一季度的最后一个月 annual_frame.resample('Q-DEC').ffill()
# 向上采样,设置填充方式ffill() # 传递convention='end',设置结果中索引为原来第一个索引年份的季度末开始,到原来最后一个索引年份的季度末终止 annual_frame.resample('Q-DEC', convention='end').ffill()
向下采样中,原序列中频率必须是结果中频率的子区间;月----年
向上采样总,原序列中频率必须是结果中频率的父区间;年----月
仔细揣摩以下的几种操作,通过画图理解
annual_frame.resample('Q-MAR').ffill()
annual_frame.resample('Q-MAR', convention='end').ffill()
annual_frame.resample('Q-APR').ffill()
annual_frame.resample('Q-APR', convention='end').ffill()
移动窗口函数与其他的统计函数一样,都会自动排除缺失数据
close_px_all = pd.read_csv('examples/stock_px_2.csv', parse_dates=True, index_col=0) close_px = close_px_all[['AAPL', 'MSFT', 'XOM']] close_px
# 按照工作日频率进行重采样 close_px = close_px.resample('B').ffill() close_px
%matplotlib inline close_px.AAPL.plot() # rolling(),可以在Series和DataFrame上通过一个window(即下方传入的数字250)进行调用 # 这里是根据250个滑动窗口分组 close_px.AAPL.rolling(250).mean().plot()
# 传递min_periods=10,会最小先计算前10闭的标准差,再计算前11笔的标准差,以此类推 # 直到计算到前250组数据后,再采用移动窗口求标准差 appl_std250 = close_px.AAPL.rolling(250, min_periods=10).std() appl_std250[5:12] ''' 2003-01-09 NaN 2003-01-10 NaN 2003-01-13 NaN 2003-01-14 NaN 2003-01-15 0.077496 2003-01-16 0.074760 2003-01-17 0.112368 Freq: B, Name: AAPL, dtype: float64 ''' appl_std250.plot()
# expanding()会使视窗逐渐扩大,左边不动,右边向右扩展;求扩展窗口的均值 # 如,这里2003-01-16为原01-16和01-15的均值 # 如,这里2003-01-17为原01-15至01-17的均值 expanding_mean = appl_std250.expanding().mean() expanding_mean[5:12] ''' 2003-01-09 NaN 2003-01-10 NaN 2003-01-13 NaN 2003-01-14 NaN 2003-01-15 0.077496 2003-01-16 0.076128 2003-01-17 0.088208 Freq: B, Name: AAPL, dtype: float64 ''' # 传递logy=True,对y取对数 close_px.rolling(60).mean().plot(logy=True)
# 传递'20D',固定取日历日的20天进行移动;前面传递250是取索引列的250天作为一个窗口 close_px.rolling('20D').mean().head()
# 传递'3D',固定取日历日的3天进行移动平均 # 故结果中2003-01-06的均值不变,因为4和5号没有数据 close_px.rolling('3D').mean().head()
# 通过span=30指定衰减因子,即权重分别为2/31、2/31*29/31..... aapl_px = close_px.AAPL['2006':'2007'] # 简单移动平均 ma60 = aapl_px.rolling(30, min_periods=20).mean() # 指数加权平均 ewma60 = aapl_px.ewm(span=30).mean() ma60.plot(style='k--', label='Simple MA') ewma60.plot(style='k-', label='EW MA') plt.legend()
同时操作两个时间序列,使用相同的移动窗口大小,如corr()或协方差
spx_px = close_px_all['SPX'] spx_rets = spx_px.pct_change() spx_rets ''' 2003-01-02 NaN 2003-01-03 -0.000484 2003-01-06 0.022474 2003-01-07 -0.006545 2003-01-08 -0.014086 ... 2011-10-10 0.034125 2011-10-11 0.000544 2011-10-12 0.009795 2011-10-13 -0.002974 2011-10-14 0.017380 Name: SPX, Length: 2214, dtype: float64 ''' returns = close_px.pct_change() returns
# 注意这里,returns.AAPL有2292行数据,而spx_rets有2214行数据 # 故他们的行索引在计算移动视窗时并不是完全一样的 # 正常情况下,是不会出现这样的情况,可能是作者弄错了 # corr()计算滚动相关系数 corr = returns.AAPL.rolling(125, min_periods=100).corr(spx_rets) corr.plot()
corr = returns.rolling(125, min_periods=100).corr(spx_rets) corr.plot()
# 在rolling()及相关方法上使用apply()可以在移动窗口中使用自定义的数组函数 from scipy.stats import percentileofscore # 有多少百分比的样本x小于0.02 score_at_2percent = lambda x: percentileofscore(x, 0.02) result = returns.AAPL.rolling(250).apply(score_at_2percent) result.plot()