pandas 时间序列操作.docx
《pandas 时间序列操作.docx》由会员分享,可在线阅读,更多相关《pandas 时间序列操作.docx(11页珍藏版)》请在冰豆网上搜索。
![pandas 时间序列操作.docx](https://file1.bdocx.com/fileroot1/2022-12/1/6674c0da-7f49-4abc-b60f-efaec17ce741/6674c0da-7f49-4abc-b60f-efaec17ce7411.gif)
pandas时间序列操作
数据类型及操作
Python标准库的datetime
datetime模块中的datetime、time、calendar等类都可以用来存储时间类型以及进行一些转换和运算操作。
>>>fromdatetimeimportdatetime
>>>now=datetime.now()
>>>now
datetime.datetime(2014,6,17,15,56,19,313193)
>>>delta=datetime(2010,2,2)-datetime(2010,2,1)
>>>delta
datetime.timedelta
(1)
>>>now+delta
datetime.datetime(2014,6,18,15,56,19,313193)
datetime对象间的减法运算会得到一个timedelta对象,表示一个时间段。
datetime对象与它所保存的字符串格式时间戳之间可以互相转换。
str() 函数是可用的,但更推荐 datetime.strptime() 方法。
这个方法可以实现双向转换。
>>>str(now)
'2014-06-1715:
56:
19.313193'
>>>now.strftime('%Y-%m-%d')
'2014-06-17'
>>>datetime.strptime('2010-01-01','%Y-%m-%d')
datetime.datetime(2010,1,1,0,0)
如 %Y 这种格式代表了某种具体的意义,但用着很麻烦。
因此可以使用一个名为dateutil第三方包的parser.parse()函数实现自动转义,它几乎可以解析任何格式(这也可能会带来麻烦)。
>>>fromdateutil.parserimportparse
>>>parse('01-02-2010',dayfirst=True)
datetime.datetime(2010,2,1,0,0)
>>>parse('01-02-2010')
datetime.datetime(2010,1,2,0,0)
>>>parse('55')
datetime.datetime(2055,6,17,0,0)
pandas的TimeStamp
pandas最基本的时间日期对象是一个从Series派生出来的子类TimeStamp,这个对象与datetime对象保有高度兼容性,可通过 pd.to_datetime() 函数转换。
(一般是从datetime转换为Timestamp)
>>>pd.to_datetime(now)
Timestamp('2014-06-1715:
56:
19.313193',tz=None)
>>>pd.to_datetime(np.nan)
NaT
pandas的时间序列
pandas最基本的时间序列类型就是以时间戳(TimeStamp)为index元素的Series类型。
>>>dates=[datetime(2011,1,1),datetime(2011,1,2),datetime(2011,1,3)]
>>>ts=Series(np.random.randn(3),index=dates)
>>>ts
2011-01-010.362289
2011-01-020.586695
2011-01-03-0.154522
dtype:
float64
>>>type(ts)
>>>ts.index
[2011-01-01,...,2011-01-03]
Length:
3,Freq:
None,Timezone:
None
>>>ts.index[0]
Timestamp('2011-01-0100:
00:
00',tz=None)
时间序列之间的算术运算会自动按时间对齐。
索引、选取、子集构造
时间序列只是index比较特殊的Series,因此一般的索引操作对时间序列依然有效。
其特别之处在于对时间序列索引的操作优化。
如使用各种字符串进行索引:
>>>ts['20110101']
0.36228897878097266
>>>ts['2011-01-01']
0.36228897878097266
>>>ts['01/01/2011']
0.36228897878097266
对于较长的序列,还可以只传入“年”或“年月”选取切片:
>>>ts
2011-01-010.362289
2011-01-020.586695
2011-01-03-0.154522
2012-12-250.111869
dtype:
float64
>>>ts['2012']
2012-12-250.111869
dtype:
float64
>>>ts['2011-1-2':
'2012-12']
2011-01-020.586695
2011-01-03-0.154522
2012-12-250.111869
dtype:
float64
除了这种字符串切片方式外,还有一种实例方法可用:
ts.truncate(after='2011-01-03')。
值得注意的是,切片时使用的字符串时间戳并不必存在于index之中,如 ts.truncate(before='3055') 也是合法的。
日期的范围、频率以及移动
pandas中的时间序列一般被默认为不规则的,即没有固定的频率。
但出于分析的需要,我们可以通过插值的方式将序列转换为具有固定频率的格式。
一种快捷方式是使用 .resample(rule) 方法:
>>>ts
2011-01-010.362289
2011-01-020.586695
2011-01-03-0.154522
2011-01-060.222958
dtype:
float64
>>>ts.resample('D')
2011-01-010.362289
2011-01-020.586695
2011-01-03-0.154522
2011-01-04NaN
2011-01-05NaN
2011-01-060.222958
Freq:
D,dtype:
float64
生成日期范围
pd.date_range() 可用于生成指定长度的DatetimeIndex。
参数可以是起始结束日期,或单给一个日期,加一个时间段参数。
日期是包含的。
>>>pd.date_range('20100101','20100110')
[2010-01-01,...,2010-01-10]
Length:
10,Freq:
D,Timezone:
None
>>>pd.date_range(start='20100101',periods=10)
[2010-01-01,...,2010-01-10]
Length:
10,Freq:
D,Timezone:
None
>>>pd.date_range(end='20100110',periods=10)
[2010-01-01,...,2010-01-10]
Length:
10,Freq:
D,Timezone:
None
默认情况下,date_range会按天计算时间点。
这可以通过freq参数进行更改,如“BM”代表bussinessendofmonth。
>>>pd.date_range('20100101','20100601',freq='BM')
[2010-01-29,...,2010-05-31]
Length:
5,Freq:
BM,Timezone:
None
频率和日期偏移量
pandas中的频率是由一个基础频率和一个乘数组成的。
基础频率通常以一个字符串别名表示,如上例中的“BM”。
对于每个基础频率,都有一个被称为日期偏移量(dateoffset)的对象与之对应。
可以通过实例化日期偏移量来创建某种频率:
>>>Hour()
>>>Hour
(2)
<2*Hours>
>>>Hour
(1)+Minute(30)
<90*Minutes>
但一般来说不必这么麻烦,使用前面提过的字符串别名来创建频率就可以了:
>>>pd.date_range('00:
00','12:
00',freq='1h20min')
[2014-06-1700:
00:
00,...,2014-06-1712:
00:
00]
Length:
10,Freq:
80T,Timezone:
None
可用的别名,可以通过help()或文档来查询,这里就不写了。
移动(超前和滞后)数据
移动(shifting)指的是沿着时间轴将数据前移或后移。
Series和DataFrame都有一个 .shift() 方法用于执行单纯的移动操作,index维持不变:
>>>ts
2011-01-010.362289
2011-01-020.586695
2011-01-03-0.154522
2011-01-060.222958
dtype:
float64
>>>ts.shift
(2)
2011-01-01NaN
2011-01-02NaN
2011-01-030.362289
2011-01-060.586695
dtype:
float64
>>>ts.shift(-2)
2011-01-01-0.154522
2011-01-020.222958
2011-01-03NaN
2011-01-06NaN
dtype:
float64
上例中因为移动操作产生了NA值,另一种移动方法是移动index,而保持数据不变。
这种移动方法需要额外提供一个freq参数来指定移动的频率:
>>>ts.shift(2,freq='D')
2011-01-030.362289
2011-01-040.586695
2011-01-05-0.154522
2011-01-080.222958
dtype:
float64
>>>ts.shift(2,freq='3D')
2011-01-070.362289
2011-01-080.586695
2011-01-09-0.154522
2011-01-120.222958
dtype:
float64
时期及其算术运算
本节使用的时期(period)概念不同于前面的时间戳(timestamp),指的是一个时间段。
但在使用上并没有太多不同,pd.Period 类的构造函数仍需要一个时间戳,以及一个freq参数。
freq用于指明该period的长度,时间戳则说明该period在公园时间轴上的位置。
>>>p=pd.Period(2010,freq='M')
>>>p
Period('2010-01','M')
>>>p+2
Period('2010-03','M')
上例中我给period的构造器传了一个“年”单位的时间戳和一个“Month”的freq,pandas便自动把2010解释为了2010-01。
period_range函数可用于创建规则的时间范围:
>>>pd.period_range('2010-01','2010-05',freq='M')
freq:
M
[2010-01,...,2010-05]
length:
5
PeriodIndex类保存了一组period,它可以在任何pandas数据结构中被用作轴索引:
>>>Series(np.random.randn(5),index=pd.period_range('201001','201005',freq='M'))
2010-010.755961
2010-02-1.074492
2010-03-0.379719
2010-040.153662
2010-05-0.291157
Freq:
M,dtype:
float64
时期的频率转换
Period和PeriodIndex对象都可以通过其 .asfreq(freq,method=None,how=None) 方法被转换成别的频率。
>>>p=pd.Period('2007',freq='A-DEC')
>>>p.asfreq('M',how='start')
Period('2007-01','M')
>>>p.asfreq('M',how='end')
Period('2007-12','M')
>>>ts=Series(np.random.randn
(1),index=[p])
>>>ts
2007-0.112347
Freq:
A-DEC,dtype:
float64
>>>ts.asfreq('M',how='start')
2007-01-0.112347
Freq:
M,dtype:
float64
时间戳与时期间相互转换
以时间戳和以时期为index的Series和DataFrame都有一对 .to_period() 和 to_timestamp(how='start') 方法用于互相转换index的类型。
因为从period到timestamp的转换涉及到一个取端值的问题,所以需要一个额外的how参数,默认为'start':
>>>ts=Series(np.random.randn(5),index=pd.period_range('201001','201005',freq='M'))
>>>ts
2010-01-0.312160
2010-020.962652
2010-03-0.959478
2010-041.240236
2010-05-0.916218
Freq:
M,dtype:
float64
>>>ts.to_timestamp()
2010-01-01-0.312160
2010-02-010.962652
2010-03-01-0.959478
2010-04-011.240236
2010-05-01-0.916218
Freq:
MS,dtype:
float64
>>>ts.to_timestamp(how='end')
2010-01-31-0.312160
2010-02-280.962652
2010-03-31-0.959478
2010-04-301.240236
2010-05-31-0.916218
Freq:
M,dtype:
float64
>>>ts.to_timestamp().to_period()
2010-01-0100:
00:
00.000-0.312160
2010-02-0100:
00:
00.0000.962652
2010-03-0100:
00:
00.000-0.959478
2010-04-0100:
00:
00.0001.240236
2010-05-0100:
00:
00.000-0.916218
Freq:
L,dtype:
float64
>>>ts.to_timestamp().to_period('M')
2010-01-0.312160
2010-020.962652
2010-03-0.959478
2010-041.240236
2010-05-0.916218
Freq:
M,dtype:
float64
重采样及频率转换
重采样(resampling)指的是将时间序列从一个频率转换到另一个频率的过程。
pandas对象都含有一个 .resample(freq,how=None,axis=0,fill_method=None,closed=None,label=None,convention='start',kind=None,loffset=None,limit=None,base=0) 方法用于实现这个过程。
本篇最前面曾用resample规整化过时间序列。
当时进行的是插值操作,因为原索引的频率与给出的freq参数相同。
resample方法更多的应用场合是freq发生改变的时候,这时操作就分为升采样(upsampling)和降采样(downsampling)两种。
具体的区别都体现在参数里。
>>>ts
2010-01-0.312160
2010-020.962652
2010-03-0.959478
2010-041.240236
2010-05-0.916218
Freq:
M,dtype:
float64
>>>ts.resample('D',fill_method='ffill')#升采样
2010-01-01-0.31216
2010-01-02-0.31216
2010-01-03-0.31216
2010-01-04-0.31216
2010-01-05-0.31216
2010-01-06-0.31216
2010-01-07-0.31216
2010-01-08-0.31216
2010-01-09-0.31216
2010-01-10-0.31216
2010-01-11-0.31216
2010-01-12-0.31216
2010-01-13-0.31216
2010-01-14-0.31216
2010-01-15-0.31216
...
2010-05-17-0.916218
2010-05-18-0.916218
2010-05-19-0.916218
2010-05-20-0.916218
2010-05-21-0.916218
2010-05-22-0.916218
2010-05-23-0.916218
2010-05-24-0.916218
2010-05-25-0.916218
2010-05-26-0.916218
2010-05-27-0.916218
2010-05-28-0.916218
2010-05-29-0.916218
2010-05-30-0.916218
2010-05-31-0.916218
Freq:
D,Length:
151
>>>ts.resample('A-JAN',how='sum')#降采样
2010-0.312160
20110.327191
Freq:
A-JAN,dtype:
float64