pandas 时间序列

时间序列数据的意义取决于具体的应用场景,主要有以下几种:

  • 时间戳(timestamp):特定的时刻。
  • 固定时期(period):如2017年1月或2018年全年
  • 时间间隔(interval):由起始和结束时间戳表示。时期(period)可以被看做间隔(interval)的特例。
  • 实验或过程时间:每个时间点都是相对于特定起始时间的一个度量。例如,从放入烤箱时起,每秒钟饼干的直径。

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) #输出结果:<class 'pandas.core.series.Series'>
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    #输出结果:<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')
#美国的股票期权交易人会意识到这些日子就是标准的月度到期日

移动(超前和泄后)数据:
 

猜你喜欢

转载自blog.csdn.net/u012474716/article/details/81219331