#!/usr/bin/env python # coding: utf-8 # # 11.3 Date Ranges, Frequencies, and Shifting(日期范围,频度,和位移) # # 普通的时间序列通常是不规律的,但我们希望能有一个固定的频度,比如每天,每月,或没15分钟,即使有一些缺失值也没关系。幸运的是,pandas中有一套方法和工具来进行重采样,推断频度,并生成固定频度的日期范围。例如,我们可以把样本时间序列变为固定按日的频度,需要调用resample: # In[2]: import pandas as pd import numpy as np 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)] ts = pd.Series(np.random.randn(6), index=dates) ts # In[3]: resampler = ts.resample('D') # 这里的'D'表示按日的频度(daily frequency)。 # # 关于频度(frequency)和重采样(resampling)的转换,会在11.6进行具体介绍,这里我们展示一些基本的用法。 # # # 1 Generating Date Ranges(生成日期范围) # # 之前虽然用过,但没有做解释,其实pandas.date_range是用来生成DatetimeIndex的,使用时要根据频度来指明长度: # In[4]: index = pd.date_range('2012-04-01', '2012-06-01') index # 默认,date_range会生成按日频度的时间戳。如果我们只传入一个开始或一个结束时间,还必须传入一个数字来表示时期: # In[5]: pd.date_range(start='2012-04-01', periods=20) # In[6]: pd.date_range(end='2012-06-01', periods=20) # 开始和结束的日期,严格指定了用于生成日期索引(date index)的边界。例如,如果我们希望日期索引包含每个月的最后一个工作日,我们要设定频度为'BM'(business end of month,每个月的最后一个工作日,更多频度可以看下面的表格),而且只有在这个日期范围内的日期会被包含进去: # In[7]: pd.date_range('2000-01-01', '2000-12-01', freq='BM') # 时间序列频度: # # ![](http://oydgk2hgw.bkt.clouddn.com/pydata-book/v4ae4.png) # # date_range会默认保留开始或结束的时间戳: # In[8]: pd.date_range('2012-05-02 12:56:31', periods=5) # 有些时候我们的时间序列数据带有小时,分,秒这样的信息,但我们想要让这些时间戳全部归一化到午夜(normalized to midnight, 即晚上0点),这个时候要用到normalize选项: # In[15]: nor_date = pd.date_range('2012-05-02 12:56:31', periods=5, normalize=True) nor_date # In[16]: nor_date[0] # 可以看到小时,分,秒全部变为0 # # # 2 Frequencies and Date Offsets(频度和日期偏移) # # pandas中的频度由一个基本频度(base frequency)和一个乘法器(multiplier)组成。基本频度通常用一个字符串别名(string alias)来代表,比如'M'表示月,'H'表示小时。对每一个基本频度,还有一个被称之为日期偏移(date offset)的对象。例如,小时频度能用Hour类来表示: # In[18]: from pandas.tseries.offsets import Hour, Minute # In[19]: hour = Hour() hour # 通过传入一个整数,我们可以定义一个乘以偏移的乘法(a multiple of an offset): # In[20]: four_hours = Hour(4) four_hours # 在很多情况下,我们不需要创建这些对象,而是使用字符串别名,比如'H'或'4H'。在频度前加一个整数,就能作为一个乘法器: # In[21]: pd.date_range('2000-01-01', '2000-01-03 23:59', freq='4H') # 很多偏移(offset)还能和加法结合: # In[22]: Hour(2) + Minute(30) # 同样的,我们可以传入频度字符串,比如'1h30min',这种表达也能被解析: # In[23]: pd.date_range('2000-01-01', periods=10, freq='1h30min') # ### Week of month dates(月中的第几周日期) # # 一个有用的类(class)是月中的第几周(Week of month),用WOM表示。丽日我们想得到每个月的第三个星期五: # # In[26]: rng = pd.date_range('2012-01-01', '2012-09-01', freq='WOM-3FRI') rng # In[27]: list(rng) # # 3 Shifting (Leading and Lagging) Data (偏移(提前与推后)数据) # # 偏移(shifting)表示按照时间把数据向前或向后推移。Series和DataFrame都有一个shift方法实现偏移,索引(index)不会被更改: # In[28]: ts = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2000', periods=4, freq='M')) ts # In[29]: ts.shift(2) # In[30]: ts.shift(-2) # 当我们进行位移的时候,就像上面这样会引入缺失值。 # # shift的一个普通的用法是计算时间序列的百分比变化,可以表示为: # In[31]: ts / ts.shift(1) - 1 # 因为普通的shift不会对index进行修改,一些数据会被丢弃。因此如果频度是已知的,可以把频度传递给shift,这样的话时间戳会自动变化: # In[33]: ts # In[34]: ts.shift(2) # In[32]: ts.shift(2, freq='M') # 其他一些频度也可以导入,能让我们前后移动数据: # In[35]: ts.shift(3, freq='D') # In[36]: ts.shift(1, freq='90T') # T表示分钟。 # # # ### Shifting dates with offsets(用偏移量来移动日期) # # pandas的日期偏移(date offset)能被用于datetime或Timestamp对象: # In[37]: from pandas.tseries.offsets import Day, MonthEnd # In[38]: now = datetime(2011, 11, 17) # In[39]: now + 3 * Day() # 如果我们添加一个像MonthEnd这样的anchored offset(依附偏移;锚点位置),日期会根据频度规则进行递增: # In[40]: now + MonthEnd() # In[41]: now + MonthEnd(2) # 依附偏移可以让日期向前或向后滚动,利用rollforward和rollback方法: # In[43]: offset = MonthEnd() # In[44]: offset.rollforward(now) # In[45]: offset.rollback(now) # 一个比较创造性的日期偏移(date offset)用法是配合groupby一起用: # In[46]: ts = pd.Series(np.random.randn(20), index=pd.date_range('1/15/2000', periods=20, freq='4d')) ts # In[47]: ts.groupby(offset.rollforward).mean() # 一个简单且快捷的方式是用resample(11.6会进行更详细的介绍): # In[48]: ts.resample('M').mean()