您的位置:首页 > 编程语言 > Python开发

[Python for Data Analysis] Chapter10 时间序列

2016-05-19 00:14 896 查看

1 日期和时间数据类型及工具

datetime 里面有 datetime, time 以及 calendar 模块,其中 datetime 用得最多

datetime 模块中的数据类型:

date:以公历形式存储日历日期(年、月、日)

time:将时间存储为时、分、秒、毫秒

datetime:存储日期和时间,相当于 date 与 time 的结合

timedelta:两个 datetime 值的差(天数+毫秒数)

1.1 字符串和 datetime 的相互转换

1.1.1 datetime –>字符串: datetime.strftime

In [17]: from datetime import datetime
In [18]: now = datetime.now()
In [19]: str(now)
Out[19]: '2016-05-18 22:52:07.185000'
In [20]: stamp = datetime(2011,1,3)
In [21]: str(stamp)
Out[21]: '2011-01-03 00:00:00'
In [22]: stamp.strftime('%Y-%m-%d')
Out[22]: '2011-01-03'


datetime.strftime:把标准格式的日期转化为字符串

1.1.2 字符串–> datetime: datetime.strptime, dateutil.parser.parse, pandas.to_datetime

In [23]: value = '2011-01-03'
In [24]: datetime.strptime(value, '%Y-%m-%d')
Out[24]: datetime.datetime(2011, 1, 3, 0, 0)


datetime.strptime:把字符串转化为标准格式的日期,但每次都需要指定格式,为了省去麻烦,可以使用 dateutil 包的 parser.parse 方法。

In [25]: from dateutil.parser import parse
In [26]: parse('2011-01-03')
Out[26]: datetime.datetime(2011, 1, 3, 0, 0)
In [27]: parse('Jan 31, 1997 10:45 PM')
Out[27]: datetime.datetime(1997, 1, 31, 22, 45)
In [28]: parse('6/12/2011', dayfirst=True) #通过 dayfirst 来指定日是否位于月前面
Out[28]: datetime.datetime(2011, 12, 6, 0, 0)


dateutil.parser.parse:几乎可以解析人类能理解的所有日期格式(除了中文)

In [30]: import pandas as pd
In [31]: datestrs = ['7/6/2011', '8/6/2011']
In [32]: pd.to_datetime(datestrs)
Out[32]: DatetimeIndex(['2011-07-06', '2011-08-06'], dtype='datetime64[ns]', freq=None, tz=None)
In [34]: idx = pd.to_datetime(datestrs+[None]) #可以处理缺失值(None、空字符串等)
In [35]: idx
Out[35]: DatetimeIndex(['2011-07-06', '2011-08-06', 'NaT'], dtype='datetime64[ns]', freq=None, tz=None) #NaT表示Not a Time,是pandas中时间戳数据的 NA 值
In [36]: pd.isnull(idx)
Out[36]: array([False, False,  True], dtype=bool)


pandas.to_datetime:也不需要指定格式

1.1.3 datetime 完整的格式化编码如下:

%Y:4位数的年

%y:2位数的年

%m:2位数的月[01-12]

%d:2位数的日[01-31]

%H:24小时制的时[00-23]

%I:12小时制的时[01-12]

%M:2位数的分[00-59]

%S:秒[00-61](秒 60 和 61 用于闰秒)

%w:整数表示的星期几[0(星期天)-6]

%U:每年的第几周[00-53](星期天被认为是每周的第一天,每年的第一个星期天之前的那几天被认为是第0周

%W:每年的第几周[00-53](星期一被认为是每周的第一天,每年的第一个星期一之间的那几天被认为是第0周

%z:以+HHMM或-HHMM表示的UTC时区偏移量,如果时区为naive,则返回空字符串

%F:%Y-%m-%d简写形式,如2012-04-18

%D:%m/%d/%y简写形式,如04/18/12

2 时间序列基础

pandas 里 TimeSeries 是索引为 DatetimeIndex 的 DataFrame,索引各标量值是 Timestamp 对象,只要有需要,Timestamp 可以随时自动转换为 datetime 对象。

2.1 索引、选取、子集构造

索引、切片可以传入的参数有字符串日期、datetime和Timestamp,切片所产生的是源时间序列的视图,以下对 Series 和 DataFrame 都可行。

In [11]: dates = [datetime(2011,1,2), datetime(2011,1,5), datetime(2011,1,7), datetime(2011,1,8), datetime(2011,1,10)]
In [12]: ts = Series(np.random.randn(5), index=dates)
In [13]: ts
Out[13]:
2011-01-02   -0.229374
2011-01-05    0.019728
2011-01-07   -0.970264
2011-01-08    0.510564
2011-01-10   -0.362988
dtype: float64
In [14]: ts['1/10/2011']
Out[14]: -0.36298849884900225
In [15]: ts['20110110']
Out[15]: -0.36298849884900225


索引为日期的字符串

In [17]: longer_ts = Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))
In [18]: longer_ts['2001'] #按年来切片
Out[18]:
2001-01-01   -0.708689
2001-01-02    0.413011
2001-01-03    0.691649
2001-01-04   -1.618438
...
2001-12-28    1.797995
2001-12-29    0.523404
2001-12-30   -1.009356
2001-12-31   -1.727535
Freq: D, dtype: float64

In [19]: longer_ts['2002-05'] #按年月来切片
Out[19]:
2002-05-01   -0.268770
2002-05-02    0.258283
2002-05-03    0.585388
2002-05-04    0.288742
...
2002-05-28   -0.763912
2002-05-29    0.025560
2002-05-30    0.610600
2002-05-31    0.134653
Freq: D, dtype: float64


索引为年或年月来切片

In [20]: ts[datetime(2011,1,7):]
Out[20]:
2011-01-07   -0.970264
2011-01-08    0.510564
2011-01-10   -0.362988
dtype: float64

In [21]: ts[datetime(2011,1,7):datetime(2011,1,10)]
Out[21]:
2011-01-07   -0.970264
2011-01-08    0.510564
2011-01-10   -0.362988
dtype: float64

In [22]: ts['1/6/2011':'1/11/2011'] #ts里没有'1/6/2011''1/11/2011'这2个时间戳,但仍然可以使用其作索引,表示范围!
Out[22]:
2011-01-07   -0.970264
2011-01-08    0.510564
2011-01-10   -0.362988
dtype: float64


索引为日期范围:范围查询,可以使用不存在的时间戳,以表示范围

In [23]: ts.truncate(after='1/9/2011')
Out[23]:
2011-01-02   -0.229374
2011-01-05    0.019728
2011-01-07   -0.970264
2011-01-08    0.510564
dtype: float64


特定函数和参数来切片

2.2 带有重复索引的时间序列

In [28]: dup_ts = Series(np.arange(4), index=pd.DatetimeIndex(['1/1/2000','1/2/2000','1/2/2000','1/3/2000']))
In [29]: dup_ts
Out[29]:
2000-01-01    0
2000-01-02    1
2000-01-02    2
2000-01-03    3
dtype: int64

In [30]: dup_ts['1/3/2000']
Out[30]: 3
In [31]: dup_ts['1/2/2000'] #产生切片的效果!
Out[31]:
2000-01-02    1
2000-01-02    2
dtype: int64

In [32]: grouped = dup_ts.groupby(level=0) #level=0表示可使把相同的索引聚合在一起
In [33]: grouped.count()
Out[33]:
2000-01-01    1
2000-01-02    2
2000-01-03    1
dtype: int64


3 日期的范围、频率以及移动

pandas 中的时间序列一般是不规则的,但它常常需要以某种相对固定的频率进行分析,可以使用 resample().mean 等函数。

In [36]: ts.resample('D').mean()
Out[36]:
2011-01-02   -0.229374
2011-01-03         NaN
2011-01-04         NaN
2011-01-05    0.019728
2011-01-06         NaN
2011-01-07   -0.970264
2011-01-08    0.510564
2011-01-09         NaN
2011-01-10   -0.362988
2011-01-11         NaN
2011-01-12   -1.215119
Freq: D, dtype: float64


3.1 生成日期范围

使用 pd.date_range 函数

In [38]: pd.date_range('4/1/2012', '6/1/2012', freq='5D') #每5天生成一个日期
Out[38]:
DatetimeIndex(['2012-04-01', '2012-04-06', '2012-04-11', '2012-04-16',
'2012-04-21', '2012-04-26', '2012-05-01', '2012-05-06',
'2012-05-11', '2012-05-16', '2012-05-21', '2012-05-26',
'2012-05-31'],
dtype='datetime64[ns]', freq='5D')

In [39]: pd.date_range(start='4/1/2012', periods=20) #默认每1天生成一个日期
Out[39]:
DatetimeIndex(['2012-04-01', '2012-04-02', '2012-04-03', '2012-04-04',
...
'2012-04-17', '2012-04-18', '2012-04-19', '2012-04-20'],
dtype='datetime64[ns]', freq='D')

In [40]: pd.date_range(end='6/1/2012', periods=20)
Out[40]:
DatetimeIndex(['2012-05-13', '2012-05-14', '2012-05-15', '2012-05-16',
...
'2012-05-29', '2012-05-30', '2012-05-31', '2012-06-01'],
dtype='datetime64[ns]', freq='D')


start:开始日期

end:结束日期

periods:日期范围内日期的个数,periods*freq 等于日期范围的长度

freq:每多少天或其他明确时间频率的方式,默认为D,即1天

normalize:把日期规范化到午夜时间戳,即把时间规范化为 00:00:00

3.2 频率和日期偏移量

pd.date_range 函数里的参数 freq,可以是 4h、2h30min、3d6h 这样的字符串!

In [44]: pd.date_range('1/1/2000', periods=10, freq='1d6h20min')
Out[44]:
DatetimeIndex(['2000-01-01 00:00:00', '2000-01-02 06:20:00',
'2000-01-03 12:40:00', '2000-01-04 19:00:00',
'2000-01-06 01:20:00', '2000-01-07 07:40:00',
'2000-01-08 14:00:00', '2000-01-09 20:20:00',
'2000-01-11 02:40:00', '2000-01-12 09:00:00'],
dtype='datetime64[ns]', freq='1820T')


日期锚点偏移量代码部分如下,详见书本

D:每日历日

B:每工作日

H:每小时

T或min:每分钟

S:每秒

M:每月最后一个日历日

BM:每月最后一个工作日

MS:每月第一个日历日

BMS:每月第一个工作日

W-MON、W-TUE……:从指定的星期几开始算起,每周

WOM-3FRI:每月第3个星期五

BAS-JAN、BAS-FEB……:每年指定月份的第一个工作日

3.3 移动(超前和滞后)数据

TimeSeries.shift 函数,可以实现对数据或者时间戳的位移

In [46]: ts = Series(np.random.randn(4), index=pd.date_range('1/1/2000',periods=4,freq='M'))
In [47]: ts
Out[47]:
2000-01-31    0.769623
2000-02-29    1.007727
2000-03-31   -0.408647
2000-04-30    1.852691
Freq: M, dtype: float64

In [48]: ts.shift(2) #此时只位移数据,时间戳不变,会产生NaN
Out[48]:
2000-01-31         NaN
2000-02-29         NaN
2000-03-31    0.769623
2000-04-30    1.007727
Freq: M, dtype: float64

In [49]: ts.shift(-2)
Out[49]:
2000-01-31   -0.408647
2000-02-29    1.852691
2000-03-31         NaN
2000-04-30         NaN
Freq: M, dtype: float64

In [52]: ts.shift(periods=3, freq='D') #此时只位移时间戳,数据不变,所以不会产生NaN
Out[52]:
2000-02-03    0.769623
2000-03-03    1.007727
2000-04-03   -0.408647
2000-05-03    1.852691
dtype: float64

In [53]: ts.shift(periods=1, freq='3D')
Out[53]:
2000-02-03    0.769623
2000-03-03    1.007727
2000-04-03   -0.408647
2000-05-03    1.852691
dtype: float64


指定 freq 时,是对时间戳进行位移,其本质上是对每个时间戳加上 periods*freq 偏移量,只要 periods*freq 相同,结果就一样。

3.3.1 通过偏移量对日期进行位移

pandas 的日期偏移量也可以用在 datetime 或 Timestamp 对象上。

In [55]: from pandas.tseries.offsets import Day, MonthEnd
In [56]: now = datetime(2011, 11, 17)
In [57]: now + 3*Day() #向前偏移3天
Out[57]: Timestamp('2011-11-20 00:00:00')

In [58]: now + MonthEnd() #向前偏移到当月月末
Out[58]: Timestamp('2011-11-30 00:00:00')
In [59]: now + MonthEnd(2) #向前偏移到下月月末
Out[59]: Timestamp('2011-12-31 00:00:00')

In [60]: offset = MonthEnd()
In [61]: offset.rollforward(now) #向前偏移到当月月末
Out[61]: Timestamp('2011-11-30 00:00:00')
In [62]: offset.rollback(now) #向后偏移到上月月末
Out[62]: Timestamp('2011-10-31 00:00:00')


4 时区处理

待续……

4.1 本地化和转换

4.2 操作时区意识型 Timestamp 对象

4.3 不同时区之间的运算

5 时期及其算术运算

In [2]: p = pd.Period(2007, freq='A-DEC')
In [3]: p
Out[3]: Period('2007', 'A-DEC')
In [4]: p+5
Out[4]: Period('2012', 'A-DEC')
In [5]: pd.Period('2014', freq='A-DEC')-p
Out[5]: 7


period 表示的是时间区间,如数日、数月、数季、数年等。

In [6]: rng = pd.period_range('1/1/2000', '6/30/2000', freq='M')
In [7]: rng
Out[7]: PeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '2000-06'], dtype='int64', freq='M')
In [8]: Series(np.random.randn(6), index=rng)
Out[8]:
2000-01   -0.416612
2000-02   -0.174900
2000-03   -0.712479
2000-04   -1.538616
2000-05    0.525344
2000-06   -0.553198
Freq: M, dtype: float64


period_range 用于创建规则的时期范围,为 PeriodIndex 类,可以作为 pandas 数据结构的轴索引

In [13]: values = ['200103','200202','200301']
In [14]: pd.PeriodIndex(values, freq='Q-DEC')
Out[14]: PeriodIndex(['2003Q1', '2002Q1', '2001Q1'], dtype='int64', freq='Q-DEC')


PeriodIndex 的创造函数可以直接使用日期类型的字符串

5.1 时期的频率转换

Period.asfreq 和 PeriodIndex.asfreq 函数可以进行频率转换,其实本质上更像是对原来时期的扩大或缩小,超时期<-->子时期

In [18]: p = pd.Period('2007', freq='A-DEC')
In [19]: p.asfreq('M', how='start')
Out[19]: Period('2007-01', 'M')
In [20]: p.asfreq('M', how='end')
Out[20]: Period('2007-12', 'M')

In [21]: p = pd.Period('2007', freq='A-JUN') #截至到2007年6月30号的一整年时期区间,即p表示2006年7月1号至2007年6月30号这一年的时期!此时2007年7月-12月其实是属于周期“2008年”的
In [22]: p.asfreq('M', 'start')
Out[22]: Period('2006-07', 'M')
In [23]: p.asfreq('M', 'end')
Out[23]: Period('2007-06', 'M')

In [25]: p = pd.Period('2007-08', 'M') #2007年8月这一个月
In [26]: p.asfreq('A-JUN')
Out[26]: Period('2008', 'A-JUN') #子时期位于频率后面,所以超时期为截至到2008年6月30号的这一年区间
In [27]: p.asfreq('A-SEP')
Out[27]: Period('2007', 'A-SEP') #子时期位于频率前面,所以超时期为截至到2007年9月30号的这一年区间


如上所示,当高频率(每月)转换为低频率(每年)时,看子时期(2007年8月)位于频率指定日期(A-JUN)的前面还是后面

In [28]: rng = pd.period_range('2006', '2009', freq='A-DEC')
In [29]: ts = Series(np.random.randn(len(rng)), index=rng)
In [30]: ts
Out[30]:
2006    0.333219
2007    0.923277
2008   -0.476524
2009   -1.014863
Freq: A-DEC, dtype: float64

In [31]: ts.asfreq('M', how='start')
Out[31]:
2006-01    0.333219
2007-01    0.923277
2008-01   -0.476524
2009-01   -1.014863
Freq: M, dtype: float64

In [32]: ts.asfreq('B', how='end')
Out[32]:
2006-12-29    0.333219
2007-12-31    0.923277
2008-12-31   -0.476524
2009-12-31   -1.014863
Freq: B, dtype: float64


PeriodIndex 和 TimeSeries 频率转换同样如此。

5.2 按季度计算的时期频率

In [35]: p = pd.Period('2012Q3', freq='Q-JAN') #以1月(JAN)结束的年度(即2011年2月1号至2012年1月31号这一年)的第3季度(即2011年8月1号至2011年10月31号这三个月)
In [36]: p
Out[36]: Period('2012Q3', 'Q-JAN')
In [37]: p.asfreq('D', 'start')
Out[37]: Period('2011-08-01', 'D')
In [38]: p.asfreq('D', 'end')
Out[38]: Period('2011-10-31', 'D')

In [42]: p4pm = (p.asfreq('D','end')-1).asfreq('T','start') + 16*60 #获得该季度倒数第2个工作日下午4点的时间戳
In [43]: p4pm
Out[43]: Period('2011-10-30 16:00', 'T')
In [44]: p4pm.to_timestamp()
Out[44]: Timestamp('2011-10-30 16:00:00')


period_range 类型的同样可以如此运算

5.3 将 Timestamp 转换为 Period(及其反向过程)

TimeSeries 的 to_period 和 to_timestamp 方法

In [50]: rng = pd.date_range('1/1/2000', periods=3, freq='M')
In [51]: ts = Series(np.random.randn(3), index=rng)
In [52]: ts
Out[52]:
2000-01-31    0.139556
2000-02-29    1.085592
2000-03-31    0.846197
Freq: M, dtype: float64
In [53]: pts = ts.to_period()  #没有指定频率,此时为了让新的时期不重叠,新的频率默认从时间戳推断出来
In [54]: pts #为保证时间不重叠,新的频率为月
Out[54]:
2000-01    0.139556
2000-02    1.085592
2000-03    0.846197
Freq: M, dtype: float64
In [55]: pts.to_timestamp(how='end') #重新转换为时间戳,方式是之前时期的最后1天
Out[55]:
2000-01-31    0.139556
2000-02-29    1.085592
2000-03-31    0.846197
Freq: M, dtype: float64

In [56]: rng = pd.date_range('1/29/2000', periods=6, freq='D')
In [57]: ts2 = Series(np.random.randn(6), index=rng)
In [58]: ts2.to_period('M') #指定频率为月,此时存在重复时期
Out[58]:
2000-01    1.226179
2000-01   -1.177125
2000-01   -0.484311
2000-02   -0.312755
2000-02    0.410706
2000-02   -0.059527
Freq: M, dtype: float64


5.4 通过数组创建 PeriodIndex

利用 pd.PeriodIndex 函数可以把分开的 year 和 quarter 两组数组数据合并一起形成新的轴索引

In [59]: index = pd.PeriodIndex(year=data.year, quarter=data.quarter, freq='Q-DEC')


PeriodIndex 里同样可以指定 month, day, hour, minute, second 等参数

6 重采样及频率转换

降采样 downsampling:高频率数据聚合到低频率数据

升采样 upsampling:低频率数据转换到高频率数据

pandas对象都带有一个 resample 方法

6.1 降采样

In [11]: ts
Out[11]:
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

In [12]: ts.resample('5min', how='sum') #这三个结果一样,closed和label默认都是left,书上写错了!这种默认值使用起来最舒服!最好理解!
In [13]: ts.resample('5min', how='sum', closed='left')
In [14]: ts.resample('5min', how='sum', closed='left', label='left')
Out[14]:
2000-01-01 00:00:00    10   #0-4min的结果,closed='left':原时间戳0min为区间的左边界且闭合,即(0,5)-->[0,5)-->[0,4],label='left':以区间(0,5)的左边界(0min)为标签
2000-01-01 00:05:00    35   #(5,10)-->[5,10)-->[5,9]
2000-01-01 00:10:00    21
Freq: 5T, dtype: int32

In [15]: ts.resample('5min', how='sum', closed='left', label='right')
Out[15]:
2000-01-01 00:05:00    10   #0-4min的结果,closed='left':(0,5)-->[0,5)-->[0,4],label='right':以区间(0,5)的右边界(5min)为标签
2000-01-01 00:10:00    35   #(5,10)-->[5,10)-->[5,9]
2000-01-01 00:15:00    21
Freq: 5T, dtype: int32

In [16]: ts.resample('5min', how='sum', closed='right') #这两个结果一样!
In [17]: ts.resample('5min', how='sum', closed='right', label='left')
Out[17]:
1999-12-31 23:55:00     0   #56-0min的结果,closed='right':原时间戳0min为区间的右边界且闭合,即(55,0)-->(55,0]-->[56,0]
2000-01-01 00:00:00    15   #(0,5)-->(0,5]-->[1,5]
2000-01-01 00:05:00    40
2000-01-01 00:10:00    11
Freq: 5T, dtype: int32

In [18]: ts.resample('5min', how='sum', closed='right', label='right')
Out[18]:
2000-01-01 00:00:00     0   #56-0min的结果
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


注意: resample 的参数 closedlabel 默认的都是 left,书上是错误的!这种默认值使用起来最舒服!最好理解!要么 left+left 搭配,要么 right+right 搭配,其他搭配都不方便理解。BUT!!! 有时候默认值又都是 right,看样子 resample 比较智能,可以用最好理解的方式展示结果!

6.1.1 OHLC 重采样

金融领域一个常用的时间序列聚合方式是求各面元的4个值:第1个值(open,开盘),最后1个值(close,收盘),最大值(hight,最高)和最小值(low,最低),传入how=’ohlc’即可

In [19]: ts.resample('5min', how='ohlc')
Out[19]:
open  high  low  close
2000-01-01 00:00:00     0     4    0      4
2000-01-01 00:05:00     5     9    5      9
2000-01-01 00:10:00    10    11   10     11


6.1.2 通过 groupby 进行重采样

使用 pandas 的 groupby 功能,可以方便聚合即降采样!

In [20]: rng = pd.date_range('1/1/2000', periods=100, freq='D')
In [21]: ts = Series(np.arange(100), index=rng)
In [22]: ts.groupby(lambda x: x.month).mean()
Out[22]:
1    15
2    45
3    75
4    95
dtype: int32
In [23]: ts.resample('M', how='mean', kind='period') #与上面结果类似
Out[23]:
2000-01    15
2000-02    45
2000-03    75
2000-04    95
Freq: M, dtype: int32

In [24]: ts.groupby(lambda x: x.weekday).mean()
Out[24]:
0    47.5
1    48.5
2    49.5
3    50.5
4    51.5
5    49.0
6    50.0
dtype: float64


6.2 升采样和插值

In [5]: frame
Out[5]:
C         T         N         O
2000-01-05 -0.164698 -0.156805  0.613622 -0.046056
2000-01-12  1.146673  2.268121  0.786021 -1.792799
In [7]: frame.resample('D')
Out[7]:
C         T         N         O
2000-01-05 -0.164698 -0.156805  0.613622 -0.046056
2000-01-06       NaN       NaN       NaN       NaN
2000-01-07       NaN       NaN       NaN       NaN
2000-01-08       NaN       NaN       NaN       NaN
2000-01-09       NaN       NaN       NaN       NaN
2000-01-10       NaN       NaN       NaN       NaN
2000-01-11       NaN       NaN       NaN       NaN
2000-01-12  1.146673  2.268121  0.786021 -1.792799

In [8]: frame.resample('D', fill_method='ffill', limit=2)
Out[8]:
C         T         N         O
2000-01-05 -0.164698 -0.156805  0.613622 -0.046056
2000-01-06 -0.164698 -0.156805  0.613622 -0.046056
2000-01-07 -0.164698 -0.156805  0.613622 -0.046056
2000-01-08       NaN       NaN       NaN       NaN
2000-01-09       NaN       NaN       NaN       NaN
2000-01-10       NaN       NaN       NaN       NaN
2000-01-11       NaN       NaN       NaN       NaN
2000-01-12  1.146673  2.268121  0.786021 -1.792799


6.3 通过时期进行重采样

In [9]: frame = DataFrame(np.random.randn(24,4), index=pd.period_range('1-2000','12-2001',freq='M'), columns=['C','T','N','O'])
In [10]: frame[:5]
Out[10]:
C         T         N         O
2000-01 -1.160273  1.912955  0.987896 -1.270885
2000-02 -1.094754 -0.914040 -0.262379 -0.840917
2000-03 -0.384724 -1.448646  0.092836  1.249594
2000-04 -0.603648  1.061497  0.751424  0.080474
2000-05 -0.549866  0.334767  1.438030 -0.037338

In [11]: annual_frame = frame.resample('A-DEC', how='mean')
In [12]: annual_frame
Out[12]:
C         T         N         O
2000 -0.581455  0.361708  0.477365 -0.143055
2001 -0.215801  0.001828  0.072425  0.162908
In [14]: annual_frame.resample('Q-DEC', fill_method='ffill') #默认convention是start!而非书上写的end!
Out[14]:
C         T         N         O
2000Q1 -0.581455  0.361708  0.477365 -0.143055
2000Q2 -0.581455  0.361708  0.477365 -0.143055
2000Q3 -0.581455  0.361708  0.477365 -0.143055
2000Q4 -0.581455  0.361708  0.477365 -0.143055
2001Q1 -0.215801  0.001828  0.072425  0.162908
2001Q2 -0.215801  0.001828  0.072425  0.162908
2001Q3 -0.215801  0.001828  0.072425  0.162908
2001Q4 -0.215801  0.001828  0.072425  0.162908

In [16]: annual_frame.resample('Q-DEC', fill_method='ffill', convention='end')
Out[16]:
C         T         N         O
2000Q4 -0.581455  0.361708  0.477365 -0.143055
2001Q1 -0.581455  0.361708  0.477365 -0.143055
2001Q2 -0.581455  0.361708  0.477365 -0.143055
2001Q3 -0.581455  0.361708  0.477365 -0.143055
2001Q4 -0.215801  0.001828  0.072425  0.162908


7 时间序列绘图

In [20]: close_px
Out[20]:
AAPL   MSFT    XOM
2003-01-02    7.40  21.11  29.22
2003-01-03    7.45  21.14  29.24
2003-01-06    7.45  21.52  29.96
……
2011-10-12  402.19  26.96  77.16
2011-10-13  408.43  27.18  76.37
2011-10-14  422.00  27.27  78.11
[2292 rows x 3 columns]

In [21]: close_px.plot()




如上所示,所有时间序列都会被绘制在同一个 subplot 上并用一个图例来说明哪个是哪个。

In [25]: close_px['AAPL'].ix['01-2011':'03-2011'].plot()




In [27]: close_px['AAPL'].resample('Q-DEC', fill_method='ffill').ix['2009':].plot()




8 移动窗口函数

In [13]: close_px.AAPL.plot()
In [14]: pd.rolling_mean(close_px.AAPL, 250).plot()




In [17]: pd.rolling_mean(close_px, 60).plot()




8.1 指数加权函数

下面是苹果公司股份的60日移动平均和 span=60 的指数加权移动平均的对比。

In [25]: fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True, sharey=True, figsize=(12,7))
In [26]: aapl_px = close_px.AAPL['2005':'2009']
In [27]: ma60 = pd.rolling_mean(aapl_px, 60, min_periods=50)
In [28]: ewma60 = pd.ewma(aapl_px, span=60)

In [29]: aapl_px.plot(style='k-', ax=axes[0])
In [30]: ma60.plot(style='k--', ax=axes[0])
In [31]: aapl_px.plot(style='k-', ax=axes[1])
In [32]: ewma60.plot(style='k--', ax=axes[1])

In [33]: axes[0].set_title('Simple MA')
In [34]: axes[1].set_title('Exponentially-weighted MA')




8.2 二元移动窗口函数

有些统计运算(如相关系数和协方差)需要在两个时间序列上执行。下面是金融分析师对某只股票对某个参数指数的相关系数感兴趣从而做的分析。

In [35]: spx_px = close_px_all['SPX']
In [36]: spx_rets = spx_px/spx_px.shift(1)-1
In [37]: returns = close_px.pct_change()
In [38]: corrs = pd.rolling_corr(returns, spx_rets, 125, min_periods=100)
In [39]: corrs.plot()




8.3 用户定义的移动窗口函数

rolling_apply 函数能够在移动窗口上应用自己设计的数组函数,唯一的要求是该函数要能从数组的各个片段中产生单个值。下面是使用 scipy.stats.percentitleofscore 函数来用 rolling_quantitle 计算样本中特定值的百分等级。

In [43]: from scipy.stats import percentileofscore
In [44]: score_at_2percent = lambda x: percentileofscore(x, 0.02)
In [45]: result = pd.rolling_apply(returns.AAPL, 250, score_at_2percent)
In [46]: result.plot()




9 性能和内存使用方面的注意事项

省略
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 时间序列