时间序列数据的意义取决于具体的应用场景,主要有以下几种:
pandas提供了一组标准的时间序列,轻松地进行切片/切块、聚合、对定期/不定期的时间序列进行重采样等。
可以你已经猜到了,这些工具中大部分都对金融和经济数据尤其有用,但你当然也可以用它们来分析服务器日志数据。
1.日期和时间数据类型及工具:
from datetime import datetime
now=datetime.now()
print now
delta=datetime(2017,8,4)-datetime(2017,6,24)
print now.year,now.month,now.day
print delta
print delta.days
print delta.seconds
#输出结果如下:
# 2018-07-26 15:37:20.905441
# 2018 7 26
# 41 days, 0:00:00
# 41
# 0
#(1)可以给datetime对象加上(或减去)一个或多个timedelta,这样会产生一个新对象:
from datetime import timedelta
start=datetime(2011,1,7)
print start+timedelta(12)
#输出:2011-01-19 00:00:00
print start-2*timedelta(12)
#输出:2010-12-14 00:00:00
datetime模块中的数据类型
类型 说明
date 以公历形式存储日历日期(年、月、日)
time 将时间存储为时、分、秒、毫秒
datetime 存储日期和时间
timedelta 表示两个datetime值之间的差(日、秒、毫秒)
注:datetime是python的模块函数,但pandas一定会用到。
字符串和datetime的相互转换:str/strftime,time.strptime,parser,pd.to_datetime
import datetime
import time
import pandas as pd
#(1)时间转化为字符串:str;
# 格式化不籽符串:strftime('%Y-%m-%d')
# stamp=datetime(2017,1,3)
# print str(stamp) #输出:2017-01-03 00:00:00
# print stamp.strftime('%Y-%m-%d') #输出:2017-01-03
#(2) 将字符串转化为时间
value='2011-01-03'
print time.strptime(value,'%Y-%m-%d')
datestrs=['7/6/2011','8/6/2011']
print [time.strptime(x,'%m/%d/%Y') for x in datestrs]
#输出结果如下:
# [time.struct_time(tm_year=2011, tm_mon=7, tm_mday=6, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=187, tm_isdst=-1), time.struct_time(tm_year=2011, tm_mon=8, tm_mday=6, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=218, tm_isdst=-1)]
#(3)time.strptime是通过已知格式进行日期解析的最佳方式。但是每次都要编写格式定义是很麻烦的事情,
#尤其是对于一些常见的日期格式。这种情况下,可以用dateutil这个第三方包中的parser.parse方法
from dateutil.parser import parse
print parse('2017-01-03')
#输出结果如下:
# 2017-01-03 00:00:00
#dateutil可以解析几乎所有人类能够理解的日期表示形式:
print parse('Jan 31,2017 10:45 PM') #输出:2017-01-31 22:45:00
#(4)在国际通用的格式中,日通常出现在月的前面,传入dayfirst=True即可解决这个问题:
print parse('6/12/2011',dayfirst=True) #输出:2011-12-06 00:00:00
#注意:dateutil.parser是一个实用但不完美的工具。比如,它会把一些原本不是日期的字符串认作是日期
#(比如"42"会被解析为2042年的今天)
#(5)pandas通常是用于处理成组日期的,不管这些日期是DataFrame的轴索引还是列。
#to_datetime方法可以解析多种不同的日期表示形式。对标准日期格式的解析非常快。
print datestrs #输出:['7/6/2011', '8/6/2011']
print pd.to_datetime(datestrs)
#输出结果:
# DatetimeIndex(['2011-07-06', '2011-08-06'], dtype='datetime64[ns]', freq=None)
#(5)它还可以处理缺失值(None,空字符串等):NAT(Not a Time)是pandas中时间戳数据的NA值。
idx=pd.to_datetime(datestrs+[None])
print idx
#输出结果如下:
# DatetimeIndex(['2011-07-06', '2011-08-06', 'NaT'], dtype='datetime64[ns]', freq=None)
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,60]
%w 用整数表示的星期几[0(星期天),6]
%U 每年的第几周[00,53].星期天被认为是每周的第一天,第年第一个星期天之前的那几天被认为是“第0周”
%W 每年的第几周[00,53]。星期一被认为是每周的第一天,每年第一个星期一之前的那几天被认为是“第0周”
%z 以+HHMM或-HHMM表示的UTC时区偏移量,如果时区为naive,则返回空字符串
%F %Y-%m-%d简写形式,例如2017-4-18
%D %m/%d/%y简写形式,例如04/18/12
datetime对象还有一些特定于当前环境(位于不同国家或使用不同语言的系统)
特定于当前环境的日期格式:
代码 说明
%a 星期几的简写
%A 星期几的全称
%b 月份的简写
%B 月份的全称
%c 完整的日期和时间,例如:“Tue 01 May 2017 04:20:57 PM”
%p 不同环境中的AM或PM
%x 适合于当前环境的日期格式。例如,在美国,“May 1,2017”会产生"05/01/2017"
%X 适合于当前环境的时间格式,例如“04:24:12PM”
2.时间序列基础:
pandas最基本的时间序列类型就是以时间戳(通常以python字符串或datetime对象表示)为索引的series:
from datetime import datetime
import pandas as pd
from pandas import DataFrame,Series
import numpy as np
dates=[datetime(2017,1,2),datetime(2017,1,5),datetime(2017,1,7),
datetime(2017,1,8),datetime(2017,1,10),datetime(2017,1,12)]
ts=Series(np.random.randn(6),index=dates)
print ts
#输出结果如下:
# 2017-01-02 -1.618294
# 2017-01-05 1.838125
# 2017-01-07 0.491439
# 2017-01-08 0.329895
# 2017-01-10 1.541238
# 2017-01-12 0.917043
#这些datetime对象实际上是被放在一个DatetimeIndex中的,现在,变量ts就成为一个TimeSeries了。
print type(ts) #输出结果:
print ts.index
#输出结果如下:
# DatetimeIndex(['2017-01-02', '2017-01-05', '2017-01-07', '2017-01-08',
# '2017-01-10', '2017-01-12'],
# dtype='datetime64[ns]', freq=None)
#(1)时间序列的Series跟其它Series一样,不同索引的时间序列之间的算术运算会自动按日期对齐
print ts+ts[::2]
#输出结果如下:
# 2017-01-02 -2.109843
# 2017-01-05 NaN
# 2017-01-07 -4.117566
# 2017-01-08 NaN
# 2017-01-10 0.976704
# 2017-01-12 NaN
# dtype: float64
#pandas用Numpy的datetime64数据类型以纳秒形式存储时间戳:
print ts.index.dtype #输出结果:datetime64[ns]
#DatetimeIndex中的各个标量值是pandas的Timestamp对象:
stamp=ts.index[0]
print stamp #输出结果如下:2017-01-02 00:00:00
索引、选取、子集构造:
由于TimeSeries是Series的一个子类,所以在索引以及数据选取方面它们的行为是一样的。
ts.index[2]; ts['1/10/2017']; index=pd.date_range('1/1/2017',periods=10) ; ts[datetime('1/1/2017')] ; ts.truncate(after='1/1/2017'); ts['1/1/2017','2/1/2017']
from datetime import datetime
import pandas as pd
from pandas import DataFrame,Series
import numpy as np
#(1) index的下标取值
dates=[datetime(2017,1,2),datetime(2017,1,5),datetime(2017,1,7),
datetime(2017,1,8),datetime(2017,1,10),datetime(2017,1,12)]
ts=Series(np.random.randn(6),index=dates)
print ts
stamp=ts.index[2] #下标从0开始,即第三行数据
print ts[stamp]
#输出结果:0.376927445233
#(2)还有一个更为方便的用法:传入一个可以被解释为日期的字符串。
print ts['1/10/2017'] #取2017年1月10号的数据
#(3)对于较长的时间序列,只需传入"年"或"年月"即可轻松选取数据的切片:
longer_ts=Series(np.random.randn(1000),index=pd.date_range('1/1/2017',periods=1000))
# print longer_ts
#输出结果如下:index从2017年1月1日开始,1000天。故到2019年9月27日
# 2017-01-01 -0.825255
# 2017-01-02 0.024477
# 2017-01-03 -1.299953
# ......................
# 2019-09-26 -0.132728
# 2019-09-27 -1.187214
print longer_ts['2017-05'] #取2017年所有5月份的数据
#(4)通过日期进行切片的方式只对规则Series有效:
print ts[datetime(2017,1,7):] #取datetime(2017,1,7)后面的数据
#输出结果如下:
# 2017-01-07 -0.227280
# 2017-01-08 -0.307934
# 2017-01-10 0.923490
# 2017-01-12 -0.165595
# dtype: float64
#(5)由于大部分时间序列数据都是按照时间先后排序的,因此你也可以用不存在于该时间序列中的时间戳对基进行切片(即范围查询):
print ts
#输出结果如下:
# 2017-01-02 0.148366
# 2017-01-05 0.159362
# 2017-01-07 0.760312
# 2017-01-08 -0.185600
# 2017-01-10 0.310633
# 2017-01-12 1.879177
# dtype: float64
print ts['1/6/2017':'1/11/2017'] #取从2017年1月6号到2017年1月11号之间的数据
#输出结果如下:
# 2017-01-07 0.760312
# 2017-01-08 -0.185600
# 2017-01-10 0.310633
# dtype: float64
#(6)跟之前一样,这里可以传入字符串日期、datetime或Timestamp.
#注意,这样切片所产生的是源时间序列的视图,跟Numpy数组的切片运算是一样的。此外,还有一个等价的实例方法
#也可以截取两个日期之间TimeSeries.
print ts.truncate(after='1/9/2017') #将'1/9/2017'之后的数据截取掉,取'1/9/2017'之前的数据
#输出结果如下:
# 2017-01-02 -0.529299
# 2017-01-05 1.041631
# 2017-01-07 -0.791962
# 2017-01-08 -0.774595
#(7)上面这些操作对DataFrame也有效,例如,对DataFrmae的行进行索引。
dates=pd.date_range('1/1/2017',periods=100,freq='W-WED')
long_df=DataFrame(np.random.randn(100,4),index=dates,columns=['Colorado','Texas','New York','Ohio'])
print long_df.ix['5-2017']
带有重复索引的时间序列:
在某些应用场景中,可能会存在多个观测数据落在同一个时间点上的情况。
from datetime import datetime
import pandas as pd
from pandas import DataFrame,Series
import numpy as np
#下面就是一个例子
dates=pd.DatetimeIndex(['1/1/2017','1/2/2017','1/2/2017','1/2/2017','1/3/2017'])
dup_ts=Series(np.arange(5),index=dates)
print dup_ts
#输出结果如下:
# 2017-01-01 0
# 2017-01-02 1
# 2017-01-02 2
# 2017-01-02 3
# 2017-01-03 4
# dtype: int64
#(1)通过检查索引的is_unique属性,我们就可以知道它是不是唯一的:
print dup_ts.is_unique #输出:True,它这个是查看整条数据的
#(2)对这个时间序列进行索引,要么产生标量值,要么产生切片,具体要看所选的时间点是否重复:
print dup_ts['1/3/2017'] #不重复
print dup_ts['1/2/2017'] #重复
#输出结果如下:
# 2017-01-02 1
# 2017-01-02 2
# 2017-01-02 3
#(3)假设你想要对具有非唯一时间戳的数据进行聚合。一个办法是使用groupby,并传入level=0(索引的唯一一层)
grouped=dup_ts.groupby(level=0)
print grouped.mean()
print grouped.count()
#输出结果如下:
# 2017-01-01 0
# 2017-01-02 2
# 2017-01-03 4
# dtype: int64
# 2017-01-01 1
# 2017-01-02 3
# 2017-01-03 1
# dtype: int64
3.日期的范围、频率以及移动:
pandas中的时间序列一般被认为是不规则的,也就是说,它们没有固定的频率。
对于大部分应用程序而言,这是无所谓的。但是,它常常需要以某种相对固定的频率进行分析,比如每日、
每月、每15分钟等(这样自然会在时间序列中引入缺失值)。幸运的是,pandas有一整套标准时间序列频率
以及用于重采样、频率推断、生成固定频率日期范围的工具。
生成日期范围:
from datetime import datetime
import pandas as pd
from pandas import DataFrame,Series
import numpy as np
#(1) pandas.date_range可用于生成指定长度的DatetimeIndex:
index=pd.date_range('4/1/2017','4/5/2017')
print index
#输出结果如下:freq='D'表示具有固定频率的时间序列。
# DatetimeIndex(['2017-04-01', '2017-04-02', '2017-04-03', '2017-04-04',
# '2017-04-05'],
# dtype='datetime64[ns]', freq='D')
#(2)默认情况下,date_range会产生按天计算的时间点。如果只传入起始或结束日期,那就还得传入一个表示一段时间的数字。
print pd.date_range(start='4/1/2017',periods=10)
#输出结果如下:
# DatetimeIndex(['2017-04-01', '2017-04-02', '2017-04-03', '2017-04-04',
# '2017-04-05', '2017-04-06', '2017-04-07', '2017-04-08',
# '2017-04-09', '2017-04-10'],
# dtype='datetime64[ns]', freq='D')
print pd.date_range(end='4/10/2017',periods=8)
#输出结果如下:
# DatetimeIndex(['2017-04-03', '2017-04-04', '2017-04-05', '2017-04-06',
# '2017-04-07', '2017-04-08', '2017-04-09', '2017-04-10'],
# dtype='datetime64[ns]', freq='D')
#(3)起始和结束日期定义了日期索引的严格边界。例如,如果你想要生成一个由每月最后一个工作日组成的日期索引,可以传入
# "BM"频率,这样就只会包含时间间隔内(或刚好在边界上的)符合频率要求的日期:
print pd.date_range('1/1/2017','12/1/2017',freq='BM')
#输出结果如下:
# DatetimeIndex(['2017-01-31', '2017-02-28', '2017-03-31', '2017-04-28',
# '2017-05-31', '2017-06-30', '2017-07-31', '2017-08-31',
# '2017-09-29', '2017-10-31', '2017-11-30'],
# dtype='datetime64[ns]', freq='BM')
#date_range默认会保留起始和结束时间戳的时间信息(如果有的话):
print pd.date_range('5/2/2017 12:56:31',periods=5)
#输出结果如下:
# DatetimeIndex(['2017-05-02 12:56:31', '2017-05-03 12:56:31',
# '2017-05-04 12:56:31', '2017-05-05 12:56:31',
# '2017-05-06 12:56:31'],
# dtype='datetime64[ns]', freq='D')
#(4)有时,虽然起始和结束日期带有时间信息,但你希望产生一组被规范化到午夜的时间戳。
#normalize选项即可实现该功能:
print pd.date_range('5/2/2017 12:56:31',periods=5,normalize=True)
#输出结果如下:
# DatetimeIndex(['2017-05-02', '2017-05-03', '2017-05-04', '2017-05-05',
# '2017-05-06'],
# dtype='datetime64[ns]', freq='D')
频率和日期偏移量:
pandas中的频率是由一个基础频率(base frequency)和一个乘数组成。基础频率通常以一个字符串别名表示,比如‘M’表示每月,
‘H’表示每小时。对于每个基础频率都有一个被称为日期偏移量的对象与之对应。例如,按小时计算的频率可以用Hour类表示。
from datetime import datetime
import pandas as pd
from pandas import DataFrame,Series
import numpy as np
from pandas.tseries.offsets import Hour,Minute
hour=Hour()
print hour #输出结果:
#(1)传入一个整数即可定义偏移量的倍数:
four_hours=Hour(4)
print four_hours #输出:<4 * Hours>
#(2)一般来说,无需显式创建这样的对象,只需使用诸如"H"或"4H"这样的字符串别名即可。在基础频率前面放上一个整数即可创建倍数:
print pd.date_range('1/1/2018','1/3/2018',freq='4h')
#输出结果如下:
# DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 04:00:00',
# '2018-01-01 08:00:00', '2018-01-01 12:00:00',
# '2018-01-01 16:00:00', '2018-01-01 20:00:00',
# '2018-01-02 00:00:00', '2018-01-02 04:00:00',
# '2018-01-02 08:00:00', '2018-01-02 12:00:00',
# '2018-01-02 16:00:00', '2018-01-02 20:00:00',
# '2018-01-03 00:00:00'],
# dtype='datetime64[ns]', freq='4H') #4H是每隔4Hours创建一个数
#(3)大部分偏移量对象都可通过加法进行连接:
print Hour(2)+Minute(30) #输出结果:<150 * Minutes>
#同理,你也可以传入频率字符串(如'2h30min),这种字符串可以被高效地解析为等效的表达式:
print pd.date_range('1/1/2018',periods=10,freq='1h30min')
#输出结果如下:
# DatetimeIndex(['2018-01-01 00:00:00', '2018-01-01 01:30:00',
# '2018-01-01 03:00:00', '2018-01-01 04:30:00',
# '2018-01-01 06:00:00', '2018-01-01 07:30:00',
# '2018-01-01 09:00:00', '2018-01-01 10:30:00',
# '2018-01-01 12:00:00', '2018-01-01 13:30:00'],
# dtype='datetime64[ns]', freq='90T')
#(4)有些频率所描述的时间点并不是均匀分隔的。例如,"M"(日历月末)和"BM"(每月最后一个工作日)就取决于每月的天数,
#对于后者,还要考虑月末是不是周末。由于没有列好的术语,我将这些称为锚点偏移量。
时间序列的基础频率
别名 偏移量类型 说明
D Day 每日历日
B BussinessDay 每工作日
H Hour 每小时
T或min Minute 每分
S Second 每秒
L或ms Milli 每毫秒(即每千分之一秒)
U Micro 每微秒(即每百万分之一秒)
M MonthEnd 每月最后一个日历日
BM BusinessMonthEnd 每月最后一个工作日
MS MonthBegin 每月第一个日历日
BMS BusinessMothBegin 每月第一个工作日
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 对于以指定月份结束的年度,每季度最后一月的最后一个工作日
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 每年指定月份的第一个工作日
WOM日期:
from datetime import datetime
import pandas as pd
from pandas import DataFrame,Series
import numpy as np
#WOM日期
#WOM(Week Of Month)是一种非常实用的频率类。它以WOM开头。它使你能获得诸如"每月第3个星期五"之类的日期:
rng=pd.date_range('1/1/2017','9/1/2017',freq='WOM-3FRI')
print rng
#输出结果如下:若要将Datetime改成List输出,则list(rng)
# DatetimeIndex(['2017-01-20', '2017-02-17', '2017-03-17', '2017-04-21',
# '2017-05-19', '2017-06-16', '2017-07-21', '2017-08-18'],
# dtype='datetime64[ns]', freq='WOM-3FRI')
#美国的股票期权交易人会意识到这些日子就是标准的月度到期日
移动(超前和泄后)数据: