时间序列
时间序列是指将同一统计指标的数值按其发生的时间先后顺序排列而成的数列。时间序列分析的主要目的是根据已有的历史数据对未来进行预测。
日期和时间数据类型及工具
Python标准库包含用于日期(data)和时间(time)数据的数据类型,而且还有日历方面的功能。我们主要会用到datetime、time以及calendar模块。datetime.datetime(也可以简写为datetime)是用得最多的数据类型:
from datetime import datetime
now = datetime.now()
now
out:datetime.datetime(2018, 4, 30, 15, 8, 59, 633049)
#为年月日时分秒毫秒,分别可以用
#(year,month,day,hour,minute,second,microsecond)
#获取,例如:
now.second
out:59
datetime以毫秒的形式存储日期和时间,datetime.timedelta表示两个datetime对象之间的时间差:
delta = datetime(2011,1,7)-datetime(2008,6,24,8,15)
out:datetime.timedelta(926, 56700)
delta.days
out:926
delta.seconds
out:56700
也可以在datetime对象上加或减一个或多个timedelta,产生一个新对象:
from datetime import timedelta
start = datetime(2011,1,7)
start + 2*timedelta(12)
out:datetime.datetime(2011, 1, 31, 0, 0)
datetime中的数据类型
字符串和datetime相互转换
利用str或strftime方法,datetime对象和pandas的Timestamp对象就可以被格式化为字符串。
stamp = datetime(2011,1,3)
str(stamp)
out:'2011-01-03 00:00:00'
stamp.strftime('%Y-%m-%d')
out:'2011-01-03'
datetime.strptime可以用这些格式化编码将字符串转换为日期:
[datetime.strptime(x,'%m/%d/%Y') for x in datestrs]
out:[datetime.datetime(2011, 7, 6, 0, 0), datetime.datetime(2011, 8, 6, 0, 0)]
由于用这种格式定义是很麻烦的,所以dateutil这个第三方包中的parser.parse方法就显得格外简洁:
parse('Jan 31,1997 10:45 PM')
out:datetime.datetime(2018, 1, 31, 22, 45)
默认认为月是出现在日之前的,而如果是国际通用格式,日在月之前,传入dayfirst=True即可。
但是parser.parse很实用,但是并不严谨,比如对于一个42数字,它会理解为2042年的今天。
pandas的to_datetime方法可以解析多种不同的日期表示形式。对标准的日期格式解析非常快。
datestrs
out:['7/6/2011', '8/6/2011']
pd.to_datetime(datestrs)
out:DatetimeIndex(['2011-07-06', '2011-08-06'], dtype='datetime64[ns]', freq=None)
#处理缺失值
idx = pd.to_datetime(datestrs + [None])
idx
#NaT是pandas中时间戳数据的NA值。
out:DatetimeIndex(['2011-07-06', '2011-08-06', 'NaT'], dtype='datetime64[ns]', freq=None)
pd.isnull(idx)
out:array([False, False, True], dtype=bool)
datetime格式定义
时间序列基础
以时间戳为索引的Series就叫时间序列。
ts = Series(np.random.randn(3),index = idx)
ts
out:
2011-07-06 -0.764380
2011-08-06 -1.918176
NaT -0.036477
dtype: float64
ts.index
out:DatetimeIndex(['2011-07-06', '2011-08-06', 'NaT'], dtype='datetime64[ns]', freq=None)
DatetimeIndex中各个标量值是pandas的Timestamp对象:
ts.index[0]
out:Timestamp('2011-07-06 00:00:00')
索引、选取、子集构造
索引:
ts[ts.index[0]]
out:-0.76438007139328756
#还可以直接传入一个日期的字符串
ts['7/6/2011']
out:2011-07-06 -0.76438
dtype: float64
对于较长的时间序列,只需传入“年”或“年月”即可轻松选取数据切片。与之前类似,可以把字符串日期、datetime或Timestamp传入用来切片。
ts[datetime(2011,1,7):]
ts['1/6/2011':'1/11/2011']
还有一个等价的方法也可以截取两个日期之间的TimeSeries:
ts.truncate(after='1/9/2011')
以上操作对DataFrame同样有效。
带有重复索引的时间序列
当某个时间点是有多个数据时,就会出现索引重复的情况。用is_unique属性可以判断索引是否唯一。
ts.index.is_unique
out:True
如果想要对具有非唯一时间戳的数据进行聚合。一个方法是使用groupby,并传入level=0(索引唯一一层):
grouped=ts.groupby(level=0)
grouped.mean()
grouped.count()
日期的范围、频率以及移动
时间序列一般是无规则的,但是有时候它需要以某种特定的频率进行分析(而且很常见),比如每日、每月。例如想要将时间序列转换成每日的时间序列,只需要调用resample:
ts.resample('D')
生成日期范围
pandas.date_range可用于生成指定长度的DatetimeIndex:
index = pd.date_range('4/1/2012','6/1/2012')
index
out:
DatetimeIndex(['2012-04-01', '2012-04-02'........'2012-05-31', '2012-06-01'], dtype='datetime64[ns]', freq='D')
默认情况下是按天计算的,如果只传入起始或结束日期(start='4/1/2012'),则还需要传入一个代表时间长度的值(periods=20):则代表了2012/4/1-2012/4/20。
如果你想要生成一个由每月的最后一个工作日组成的日期索引,可以传入“BM”频率(表示bussiness end of month),这样就只包含时间间隔内(或刚好在边界上的)符合频率要求的日期:
pd.date_range('1/1/2000','12/1/2000',freq='BM')
date_range默认保留起始和结束时间戳的时间信息(如果有的话,即保留时分秒等)
如果不想要这部分,可以传入normallize=True实现。
频率和日期偏移量
pandas中的频率是由一个基础频率和一个乘数组成的,基础频率通常以一个字符串别名表示,比如“M”表示每月,“H”表示每小时。对于每个基础频率,都有一个被称为日期偏移量的对象与之对应。例如,按小时计算的频率可以用Hour类表示:
from pandas.tseries.offsets import Hour,Minute
hour = Hour()
hour
out:<Hour>
four_hours = Hour(4)
four_hours
out:<4 * Hours>
#当然一般不需要创建这种对象,只需要传入“H”或者“4H”即可。例如freq='4h'
而偏移量也可以通过加法连接:
Hour(2)+Minute(30)
同理,你甚至可以传入freq='1h30min'。
时间序列的基础频率
WOM日期
WOM(Week Of Month)是一种非常实用的频率类,它以WOM开头。它使你能获得诸如“每个月第三个星期五”之类的日期:
rng = pd.date_range('1/1/2012','9/1/2012',freq='WOM-3FRI')
移动(超前和滞后)数据
移动(shifting)是沿着时间轴将数据前移或后移。Series和DateFrame都有shift方法用于执行单纯的前移或后移操作,保持索引不变:
ts = Series(np.random.randn(4),index=pd.date_range('1/1/2000',periods=4,freq='M'))
ts
out:
2000-01-31 0.770290
2000-02-29 1.295360
2000-03-31 0.208220
2000-04-30 -0.534682
Freq: M, dtype: float64
ts.shift(2)
out:
2000-01-31 NaN
2000-02-29 NaN
2000-03-31 0.77029
2000-04-30 1.29536
Freq: M, dtype: float64
ts.shift(-2)
out:
2000-01-31 0.208220
2000-02-29 -0.534682
2000-03-31 NaN
2000-04-30 NaN
Freq: M, dtype: float64
shift通常用于计算一个时间序列或多个时间序列(如DataFrame的列)中百分比变化。可以这样表达ts/ts.shift(1)-1
前面是将数据进行移动,我们也可以对时间戳进行位移:
ts.shift(2,freq='3d')
out:
2000-02-06 0.770290
2000-03-06 1.295360
2000-04-06 0.208220
2000-05-06 -0.534682
dtype: float64
通过偏移量对日期进行偏移
pandas的日期偏移量还可以用在datetime或Timestamp对象上:
#偏移到月底(加个2说明第二个月底)
from pandas.tseries.offsets import Day,MonthEnd
now = datetime(2011,11,17)
now + MonthEnd(2)
out:Timestamp('2011-12-31 00:00:00')
通过rollforward和rollback方法,可显式地将日期往前或往后“滚动”
offset = MonthEnd()
offset.rollforward(now)
out:Timestamp('2011-11-30 00:00:00')
offset.rollback(now)
out:Timestamp('2011-10-31 00:00:00')
通过groupby可以更巧妙的使用:
ts = Series(np.random.randn(20),index=pd.date_range('1/15/2000',periods=20,freq='4d'))
ts
out:
2000-01-15 1.173212
2000-01-19 0.505253
2000-01-23 -0.755936
2000-01-27 -0.496667
2000-01-31 -0.500123
2000-02-04 -0.305935
2000-02-08 -0.637126
2000-02-12 -0.437494
2000-02-16 0.429528
2000-02-20 1.174809
2000-02-24 -0.028770
2000-02-28 0.174715
2000-03-03 1.118145
2000-03-07 -0.813746
2000-03-11 0.729049
2000-03-15 -0.005806
2000-03-19 -3.121317
2000-03-23 -2.352350
2000-03-27 -1.460366
2000-03-31 -0.775489
Freq: 4D, dtype: float64
ts.groupby(offset.rollforward).mean()
out:
2000-01-31 -0.014852
2000-02-29 0.052818
2000-03-31 -0.835235
dtype: float64
巧妙的通过groupby将当月的值都统计到月底了。
当然更简单,快速的方法是使用resample:
ts.resample('M').mean()
最后,假期愉快!
微信公众号:BrainZou
欢迎关注,一起学习。
网友评论