《利用python进行数据分析》读书笔记--第十章 时间序列(一)

时间:2021-12-16 00:24:10

时间序列是很重要的。时间序列(time series)数据是一种重要的结构化数据格式。时间序列的意义取决于具体的应用场景,主要有以下几种:

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

pandas提供了一组标准的时间序列处理工具和数据算法。因此可以高效处理非常大的时间序列,轻松进行切片/切块、聚合、对定期/不定期的时间序列进行重采样等。也就是说,大部分都对金融和经济数据尤为有用,当然也可以用它们来分析服务器日志数据。

1、日期和时间数据类型及工具

Python标准库中包含用于日期(date)、时间(time)数据的数据类型。而且还有日历方面的功能。主要会用到datetime、time、calendar模块。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from datetime import datetime

now = datetime.now()
#datetime以毫秒形式储存时间
print now,now.year,now.month,now.day,now.microsecond,'\n'
#print datetime(2015,12,17,20,00,01,555555) #设置一个时间
#datetime.timedelta表示两个datetime对象之间的时间差
#换句话说,datetime格式可以相相减
delta = datetime(2011,1,7) - datetime(2008,6,24,8,15)
print delta
#把注意下面是days And seconds
print dt.timedelta(926,56700)
print delta.days
print delta.seconds
#下面是错误的
#print delta.minutes
start = datetime(2011,1,7)
#参数分别为days,seconds,microseconds(微秒),milliseconds(毫秒),minutes,hours,weeks,除了微秒小数自动四舍五入之外,其他的都能自动转换为其他度量
print start + dt.timedelta(1,20,0.5,5,10,10,0)
>>>
2015-12-17 20:24:21.829000 2015 12 17 829000 

926 days, 15:45:00
926 days, 15:45:00
926
56700
2011-01-08 10:10:20.005001
[Finished in 0.6s]

datetime中的数据类型有:

《利用python进行数据分析》读书笔记--第十章 时间序列(一)

  • 字符串和datetime的相互转换

利用str或者strftime方法(传入一个格式化字符串),datetime对象和pandas中timestamp对象就可以转换为字符串:

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from datetime import datetime
from dateutil.parser import parse

stamp = datetime(2011,1,3)
print str(stamp),'\n'
#看一下下面的字符,很有意思,自己不小心打错了,运行仍然是正确的
print stamp.strftime('&Y-%m-%d')
print stamp.strftime('%Y-%m-%d'),'\n'
value = '2011-01-03'
print datetime.strptime(value,'%Y-%m-%d') #注意这是datetime函数的函数,不是模块的函数
datestrs = ['7/6/2011','8/6/2011']
print [datetime.strptime(x,'%m/%d/%Y') for x in datestrs]
#上面将字符串转化为最常用的格式,但是米次都自己写出来有点麻烦,可以用dateutil这个第三方包中的parser.parse方法
print parse('2011-01-03')
#dateutil可以几乎解析所有能够理解的日期表示形式(很可惜中文不行)
#这个应该是很实用的
print parse('2011/01/03')
print parse('Jan 31,1997 10:45 PM')
#国际通用格式中,日出现在月的前面,传入dayfirst = True即可
print parse('6/12/2011',dayfirst = True),'\n'
#pandas通常是用于处理成组日期的,不管这些日期是DataFrame的行还是列。
print pd.to_datetime(datestrs),'\n'
idx = pd.to_datetime(datestrs + [None])
print idx
print idx[2] #这里应该是NaT(Not a Time)
print pd.isnull(idx)
#parse是一个不完美的工具,比如下面
print parse('42')
>>>
2011-01-03 00:00:00 

&Y-01-03
2011-01-03

2011-01-03 00:00:00
[datetime.datetime(2011, 7, 6, 0, 0), datetime.datetime(2011, 8, 6, 0, 0)]
2011-01-03 00:00:00
2011-01-03 00:00:00
1997-01-31 22:45:00
2011-12-06 00:00:00

<class 'pandas.tseries.index.DatetimeIndex'>
[2011-07-06 00:00:00, 2011-08-06 00:00:00]
Length: 2, Freq: None, Timezone: None

<class 'pandas.tseries.index.DatetimeIndex'>
[2011-07-06 00:00:00, ..., NaT]
Length: 3, Freq: None, Timezone: None
0001-255-255 00:00:00
[False False  True]
2042-12-17 00:00:00
[Finished in 0.6s]

下面是日期的一些格式:

《利用python进行数据分析》读书笔记--第十章 时间序列(一)

《利用python进行数据分析》读书笔记--第十章 时间序列(一)

datetime对象还有一些特定于当前环境(位于不同国家或使用不同语言系统)的格式化选项。估计用的少?

《利用python进行数据分析》读书笔记--第十章 时间序列(一)

2、时间序列基础

pandas最基本的时间序列类型就是以时间戳(通常用Python字符串或datatime对象表示)为索引的Series。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse

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)]
#print dates
ts = Series(np.random.randn(6),index = dates)
print ts,'\n'
#这些datetime对象实际上是被放在一个DatetimeIndex中的。现在,变量ts就成为了TimeSeries了。
print type(ts)
print ts.index,'\n'
#没必要显示使用TimeSeries的构造函数。当创建一个带有DatetimeIndex的Series时,pandas就会知道该对象是一个时间序列
print ts + ts[::2]
#pandas用NumPy的datetime64数据类型以纳秒形式存储时间戳:
print ts.index.dtype
#DatetimeIndex中的各个标量值是pandas的Timestamp
stamp = ts.index[0]
print stamp
#只要有需要,TimeStamp可以随时自动转换为datetime对象。此外,还可以存储频率信息,且知道如何执行时区转换以及其他操作
>>>
2011-01-02   -1.267108
2011-01-05   -0.450098
2011-01-07    0.784850
2011-01-08    0.024722
2011-01-10    0.638663
2011-01-12    0.246022

<class 'pandas.core.series.TimeSeries'>
<class 'pandas.tseries.index.DatetimeIndex'>
[2011-01-02 00:00:00, ..., 2011-01-12 00:00:00]
Length: 6, Freq: None, Timezone: None

2011-01-02   -2.534216
2011-01-05         NaN
2011-01-07    1.569701
2011-01-08         NaN
2011-01-10    1.277326
2011-01-12         NaN
datetime64[ns]
2011-01-02 00:00:00
[Finished in 0.7s]

索引、选取、子集构造

TimeSeries是Series的一个子类,所以在索引以及数据选取方面跟Series一样。

stamp = ts.index[2]
print ts[stamp],'\n'
#还有更方便的用法,传入可以被解释为日期的字符串
print ts['1/10/2011']
print ts['20110110'],'\n'
#对于较长的时间序列,只需传入“年”或“年月”即可轻松选取数据切片
long_ts = Series(np.random.randn(1000),
    index = pd.date_range('1/1/2000',periods = 1000))
#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time

print long_ts,'\n'
print long_ts['2001'],'\n'
print long_ts['2001-05'],'\n'
#通过日期进行切片的方式只对规则Series有效:
print ts[datetime(2011,1,7):],'\n'
#由于大部分时间序列数据都是按照时间先后排序的,因此你可以用不存在于该时间序列中的时间戳对其进行切片(即范围查询)
#就是说,本来1/6/2011不在index中,却可以用来当作范围
print ts['1/6/2011':'1/11/2011'],'\n' #这里可以传入字符串日期、datetime或者Timestamp

print 'This is time and localtime'
print "time.time(): %f " %  time.time()
print time.localtime( time.time() )
print time.asctime( time.localtime(time.time()) )
ltime=time.localtime(int(time.time()))   #time.time()不能直接运用strftime进行转换
print time.strftime("%Y-%m-%d %H:%M:%S", ltime)
#time asctime() 函数接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"
print 'over','\n'

#还有一个等价方法截取两个日期之间的TimeSeries.
print ts.truncate(after = '1/9/2011'),'\n'

#上面这些对DataFrame也有效
dates = pd.date_range('1/1/2000',periods = 100,freq = 'W-WED') #这里的freq是按照星期进行增加
long_df = DataFrame(np.random.randn(100,4),index = dates,columns = ['Colorado','Texas','New York','Ohio'])
print long_df.ix['2001-05']
>>>
0.0751316698811 
-0.622706612554
-0.622706612554

2000-01-01   -1.646726
2000-01-02    1.531423
2000-01-03    0.251503
2000-01-04    0.938951
2000-01-05    0.647967
2000-01-06    0.696173
2000-01-07   -1.372519
2000-01-08   -1.398277
2000-01-09   -0.679975
2000-01-10   -0.801375
2000-01-11   -0.241165
2000-01-12   -0.332811
2000-01-13   -0.337774
2000-01-14    0.826756
2000-01-15   -0.279239
...
2002-09-12   -0.097634
2002-09-13    2.222456
2002-09-14    0.042517
2002-09-15    0.266974
2002-09-16    0.038329
2002-09-17   -1.524744
2002-09-18    1.476706
2002-09-19    0.108336
2002-09-20    0.016759
2002-09-21   -0.072676
2002-09-22   -0.960545
2002-09-23    0.520699
2002-09-24   -1.188202
2002-09-25    1.669166
2002-09-26   -0.043997
Freq: D, Length: 1000

2001-01-01   -0.168866
2001-01-02   -0.273377
2001-01-03    0.094258
2001-01-04   -0.979666
2001-01-05    0.947706
2001-01-06    0.666709
2001-01-07    0.451145
2001-01-08   -0.301992
2001-01-09    0.272385
2001-01-10   -0.255775
2001-01-11   -0.321916
2001-01-12    1.894119
2001-01-13    0.582272
2001-01-14   -1.102707
2001-01-15    0.019423
...
2001-12-17   -0.243563
2001-12-18    1.757564
2001-12-19   -0.145106
2001-12-20   -0.579629
2001-12-21   -0.431069
2001-12-22    0.480805
2001-12-23   -0.651905
2001-12-24    0.702051
2001-12-25   -0.384549
2001-12-26   -1.077664
2001-12-27   -0.972768
2001-12-28    1.001220
2001-12-29    0.418016
2001-12-30    0.567361
2001-12-31   -0.811610
Freq: D, Length: 365

2001-05-01   -0.071521
2001-05-02    0.402344
2001-05-03   -0.568929
2001-05-04    0.227754
2001-05-05    0.194631
2001-05-06   -0.407669
2001-05-07   -1.407606
2001-05-08   -0.804147
2001-05-09    0.050445
2001-05-10   -0.604275
2001-05-11    0.270760
2001-05-12    0.000804
2001-05-13   -0.348938
2001-05-14   -1.626158
2001-05-15    0.084629
2001-05-16   -0.376655
2001-05-17    1.913789
2001-05-18    2.497594
2001-05-19    0.818446
2001-05-20    0.067115
2001-05-21   -0.993827
2001-05-22    0.940616
2001-05-23   -0.951763
2001-05-24   -0.806228
2001-05-25    0.441872
2001-05-26    0.067010
2001-05-27   -1.903360
2001-05-28   -0.400990
2001-05-29    0.257146
2001-05-30    0.785503
2001-05-31   -1.129024
Freq: D

2011-01-07    0.075132
2011-01-08   -0.985630
2011-01-10   -0.622707
2011-01-12   -1.356095

2011-01-07    0.075132
2011-01-08   -0.985630
2011-01-10   -0.622707

This is time and localtime
time.time(): 1450362054.149000
time.struct_time(tm_year=2015, tm_mon=12, tm_mday=17, tm_hour=22, tm_min=20, tm_sec=54, tm_wday=3, tm_yday=351, tm_isdst=0)
Thu Dec 17 22:20:54 2015
2015-12-17 22:20:54
over

2011-01-02   -0.772858
2011-01-05   -0.908074
2011-01-07    0.075132
2011-01-08   -0.985630

            Colorado     Texas  New York      Ohio
2001-05-02  0.303341  0.026978 -0.036389  0.463034
2001-05-09 -1.573227 -0.283074 -0.882382 -1.207936
2001-05-16  1.520804 -0.838297  0.725690  1.240092
2001-05-23  1.297194 -0.516198 -0.022075 -0.876630
2001-05-30 -1.629426  1.022547 -0.131823 -0.621269
[Finished in 0.7s]

  • 带有重复索引的时间序列
#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time

#注意下面的DatetimeIndex生成方式
dates = pd.DatetimeIndex(['1/1/2000','1/2/2000','1/2/2000','1/2/2000','1/3/2000'])
dup_ts = Series(np.arange(5),index = dates)
print dup_ts,'\n'
#通过检唯一的测is_unique属性,我们就可以知道它不是
print dup_ts.index.is_unique,'\n'
#此时若索引,得到的可能是标量值,也可能是切片
print dup_ts['1/2/2000'],'\n'
print dup_ts['1/3/2000']
#假如你想要对具有非
#唯一时间戳的数据进行聚合一个办法是使用groupby,并传入level = 0
grouped = dup_ts.groupby(level = 0)
print grouped.mean(),'\n'
print grouped.count()
>>>
2000-01-01    0
2000-01-02    1
2000-01-02    2
2000-01-02    3
2000-01-03    4

False

2000-01-02    1
2000-01-02    2
2000-01-02    3

4
2000-01-01    0
2000-01-02    2
2000-01-03    4

2000-01-01    1
2000-01-02    3
2000-01-03    1
[Finished in 1.3s]

3、日期的范围、频率以及移动

有时候需要用相对固定的频率对数据进行分析,比如每月、每天等。幸运的是,pandas有一整套标准时间序列频率以及用于重采样、频率推断、生成固定频率日期范围的工具。

#定义列表
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)]
#print dates
ts = Series(np.random.randn(6),index = dates)
#print ts
#下面进行重采样,得到具有固定时间频率(每天)的时间序列,当让这样的话就会产生缺失值
print ts.resample('D')#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time

#定义列表
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)]
#print dates
ts = Series(np.random.randn(6),index = dates)
#print ts
#下面进行重采样,得到具有固定时间频率(每天)的时间序列,当让这样的话就会产生缺失值
print ts.resample('D') #频率的转换(或重采样)主题较大,后面再说
>>>
2011-01-02   -0.956627
2011-01-03         NaN
2011-01-04         NaN
2011-01-05    0.130565
2011-01-06         NaN
2011-01-07    0.090270
2011-01-08    0.753881
2011-01-09         NaN
2011-01-10   -0.733514
2011-01-11         NaN
2011-01-12   -0.200039
Freq: D
[Finished in 1.2s]
  • 生成日期范围
#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time

#pandas.date_range会生成指定长度的DatetimeIndex
index = pd.date_range('4/1/2015','6/1/2015')
print index,'\n'
#默认情况下,date_range产生按天计算的时间点,当然可以传入开始或结束日期,还得传入一个表示一段时间的数字
print pd.date_range('1/1/2016',periods = 31),'\n'
#开始和结束定义了日期索引的严格边界,如果你想要生成一个由每月最后一个工作日组成的日期索引,可以传入‘BM’(business end of month)
#这样就只会包含时间间隔内(或者放好在时间边界上)符合频率要求的日期:
print pd.date_range('12/18/2015','1/1/2016',freq = 'BM'),'\n'
#date_range默认保留起始和结束时间戳信息
print pd.date_range('5/2/2015 12:12:12',periods = 5)
#有时,虽然起始和结束带有时间信息,但是可以用normalize = True把它们吧变为00:00:00
print pd.date_range('5/2/2015 12:12:12',periods = 5,normalize = True)
>>>
<class 'pandas.tseries.index.DatetimeIndex'>
[2015-04-01 00:00:00, ..., 2015-06-01 00:00:00]
Length: 62, Freq: D, Timezone: None

<class 'pandas.tseries.index.DatetimeIndex'>
[2016-01-01 00:00:00, ..., 2016-01-31 00:00:00]
Length: 31, Freq: D, Timezone: None

<class 'pandas.tseries.index.DatetimeIndex'>
[2015-12-31 00:00:00]
Length: 1, Freq: BM, Timezone: None

<class 'pandas.tseries.index.DatetimeIndex'>
[2015-05-02 12:12:12, ..., 2015-05-06 12:12:12]
Length: 5, Freq: D, Timezone: None
<class 'pandas.tseries.index.DatetimeIndex'>
[2015-05-02 00:00:00, ..., 2015-05-06 00:00:00]
Length: 5, Freq: D, Timezone: None
[Finished in 1.1s]

  • 频率和日期偏移量

有些频率所描述的时间点并不是均匀分隔的。例如'M'和'BM'就取决于每月的天数,对于后者,还要考虑月末是不是周末,将这些成为锚点偏移量(anchored offset)。pandas还允许自定义一些日期逻辑,但是暂且不表。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time
from pandas.tseries.offsets import Hour,Minute

#pandas中的频率是由一个基础频率和一个乘数组成的。基础的频率由字符串表示,比如‘M’表示月,‘H’表示小时
#对于每个基础频率,都有一个被称为日期偏移量(date offset)的对象与之对应。
hour = Hour()
print hour #感觉这个形式比较霸气
#传入整数可以自定义偏移量倍数
four_hours = Hour(4)
print four_hours
#一般而言,并不需要显示创建偏移量,只需创建时间序列时传入'H'或者'4h'即可
print pd.date_range('1/1/2016','1/2/2016',freq = '4h'),'\n'
#偏移量可以拼接
print Hour(1) + Minute(30)
#传入频率字符串('2h30min'),这种字符串可以被高效地解析为等效的表达式
print pd.date_range('1/1/2016',periods = 10,freq = '1h30min'),'\n'
#有些频率所描述的时间点并不是均匀分隔的。例如'M'和'BM'就取决于每月的天数,对于后者,还要考虑月末是不是周末,将这些成为锚点偏移量(anchored offset)
#WOM(Week Of Month)日期是一个非常常用的频率,以WOM开头,能产生诸如“每月第三个星期五”之类的信息
rng = pd.date_range('1/1/2016','9/1/2016',freq = 'WOM-3FRI')
print rng
>>>
<1 Hour>
<4 Hours>
<class 'pandas.tseries.index.DatetimeIndex'>
[2016-01-01 00:00:00, ..., 2016-01-02 00:00:00]
Length: 7, Freq: 4H, Timezone: None

<90 Minutes>
<class 'pandas.tseries.index.DatetimeIndex'>
[2016-01-01 00:00:00, ..., 2016-01-01 13:30:00]
Length: 10, Freq: 90T, Timezone: None

<class 'pandas.tseries.index.DatetimeIndex'>
[2016-01-15 00:00:00, ..., 2016-08-19 00:00:00]
Length: 8, Freq: WOM-3FRI, Timezone: None
[Finished in 1.1s]

下面是一些常用的基础频率,很多很详细。

《利用python进行数据分析》读书笔记--第十章 时间序列(一)
《利用python进行数据分析》读书笔记--第十章 时间序列(一)

  • 移动(超前和滞后)数据

移动(shifting)指的是沿着时间轴将数据前移或后移。Series和DataFrame都有一个shift方法用于执行单纯的前移或后移操作,保持索引不变。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time
from pandas.tseries.offsets import Hour,Minute

ts = Series(np.random.randn(4),index = pd.date_range('1/1/2016',periods = 4,freq = 'M'))
print ts
print ts.shift(2)
print ts.shift(-2),'\n'
#可以看到,shift通常用于计算一个时间序列或多个时间序列(如DataFrame列)中的百分比变化。
print ts / ts.shift(1) - 1
#单纯的移位操作不会修改索引,所以部分数据会被丢弃,如果频率已知,则可以将其传给shift以实现对时间戳进行位移而不是只对数据移位
print ts.shift(2,freq = 'M')  #时间戳移动,而数据不动
#当然也可以自己定义移动的频率
print ts.shift(3,freq = 'D'),'\n'  #时间的移动不是上下移动,而是将时间列的每个值进行移动
print ts.shift(1,freq = '3D')
print ts.shift(1,freq = '90T')
>>>
2016-01-31    0.721445
2016-02-29   -0.568200
2016-03-31   -0.945288
2016-04-30    0.198176
Freq: M
2016-01-31         NaN
2016-02-29         NaN
2016-03-31    0.721445
2016-04-30   -0.568200
Freq: M
2016-01-31   -0.945288
2016-02-29    0.198176
2016-03-31         NaN
2016-04-30         NaN
Freq: M

2016-01-31         NaN
2016-02-29   -1.787585
2016-03-31    0.663653
2016-04-30   -1.209646
Freq: M
2016-03-31    0.721445
2016-04-30   -0.568200
2016-05-31   -0.945288
2016-06-30    0.198176
Freq: M
2016-02-03    0.721445
2016-03-03   -0.568200
2016-04-03   -0.945288
2016-05-03    0.198176

2016-02-03    0.721445
2016-03-03   -0.568200
2016-04-03   -0.945288
2016-05-03    0.198176
2016-01-31 01:30:00    0.721445
2016-02-29 01:30:00   -0.568200
2016-03-31 01:30:00   -0.945288
2016-04-30 01:30:00    0.198176
[Finished in 0.7s]

  • 通过偏移量对日期进行位移

pandas的日期偏移量还可以用在datetime或Timestemp对象上。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time
from pandas.tseries.offsets import Hour,Minute,Day,MonthEnd

now = datetime(2011,11,29)
print type(now)
print now + Day(3),'\n'
#如果加的是锚点偏移量,第一次增量会将原日期向前滚动到符合频率规则的下一个日期
#如果本来就是锚点,那么下一个就是下一个锚点
print now + MonthEnd(),'\n'
print now + MonthEnd(2),'\n'
#通过锚点偏移量的rollforward和rollback方法,可显示地将日期向前或向后“滚动”
offset = MonthEnd()
print offset.rollforward(now),'\n'
print offset.rollback(now),'\n'
#日期偏移量还有一个巧妙的用法,即结合groupby使用这两个“滚动”方法
ts = Series(np.random.randn(20),index = pd.date_range('1/15/2000',periods = 20,freq = '4d'))
print ts,'\n'
#注意下面的方式,很隐晦
print ts.groupby(offset.rollforward).mean(),'\n'
#当然,更简单快速的方式是使用resample
print ts.resample('M',how = 'mean')
>>>
<type 'datetime.datetime'>
2011-12-02 00:00:00
2011-11-30 00:00:00 
2011-12-31 00:00:00 
2011-11-30 00:00:00 
2011-10-31 00:00:00 
2000-01-15   -1.234284
2000-01-19   -1.078641
2000-01-23   -0.727257
2000-01-27   -0.943798
2000-01-31    0.050586
2000-02-04    0.019833
2000-02-08   -1.407244
2000-02-12   -0.446414
2000-02-16   -0.521847
2000-02-20    0.066200
2000-02-24    1.604580
2000-02-28   -0.714762
2000-03-03    1.743459
2000-03-07    1.675388
2000-03-11    0.104701
2000-03-15    0.124533
2000-03-19   -1.113306
2000-03-23   -1.442906
2000-03-27   -0.489818
2000-03-31    0.344161
Freq: 4D
2000-01-31   -0.786679
2000-02-29   -0.199950
2000-03-31    0.118276
 
2000-01-31   -0.786679
2000-02-29   -0.199950
2000-03-31    0.118276
Freq: M
[Finished in 0.7s]

4、时区处理

时间序列最让人不爽的就是对时区的处理。很多人已经用协调世界时(UTC,格林尼治时间接替者,目前是国际标准)来处理时间序列。时区就是以UTC偏移量的形式表示的。

Python中,时区信息来自第三方库pytz,它可以使Python可以使用Olson数据库。pandas包装了pytz功能。因此不用记忆API,只要记得时区名称即可。时区名可以在文档中找到。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time
from pandas.tseries.offsets import Hour,Minute,Day,MonthEnd
import pytz

print pytz.common_timezones[-5:]
#要从pytz中获取时区对象,使用pytz.timezone即可
tz = pytz.timezone('US/Eastern')
print tz #这里的输出已经和课本上不一样,估计是进行了简化,使得更方便了
>>>
['US/Eastern', 'US/Hawaii', 'US/Mountain', 'US/Pacific', 'UTC']
US/Eastern
[Finished in 0.7s]
  • 本地化和转换

默认情况下,pandas中的序列是单纯的(naive[too young too simple!navie!])时区。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time
from pandas.tseries.offsets import Hour,Minute,Day,MonthEnd
import pytz

rng = pd.date_range('3/9/2012 9:30',periods = 6,freq = 'D')
ts = Series(np.random.randn(len(rng)),index = rng)
print ts,'\n'
print ts.index.tz,'\n'  #默认的时区字段为None
#在生成日期范围的时候还可以加上一个时区集
print pd.date_range('3/9/2012',periods = 10,freq = 'D',tz = 'UTC'),'\n'
#从单纯到本地化的转换是通过tz_localize方法处理的:
ts_utc = ts.tz_localize('US/Pacific')  #转换为美国太平洋时间
print ts_utc,'\n'
print ts_utc.index,'\n'
#一旦被转换为某个特定时期,就可以用tz_convert将其转换到其他时区了
print ts_utc.tz_convert('US/Eastern'),'\n'
#tz_localize和tz_convert是DatetimeIndex的实例方法,可以把一个DatetimeIndex转化为特定时区
print ts.index.tz_localize('Asia/Shanghai')
>>>
2012-03-09 09:30:00    0.079530
2012-03-10 09:30:00   -0.434450
2012-03-11 09:30:00    0.360739
2012-03-12 09:30:00    0.678065
2012-03-13 09:30:00   -0.705374
2012-03-14 09:30:00    0.684572
Freq: D

None

<class 'pandas.tseries.index.DatetimeIndex'>
[2012-03-09 00:00:00, ..., 2012-03-18 00:00:00]
Length: 10, Freq: D, Timezone: UTC

2012-03-09 09:30:00-08:00    0.079530
2012-03-10 09:30:00-08:00   -0.434450
2012-03-11 09:30:00-07:00    0.360739
2012-03-12 09:30:00-07:00    0.678065
2012-03-13 09:30:00-07:00   -0.705374
2012-03-14 09:30:00-07:00    0.684572
Freq: D

<class 'pandas.tseries.index.DatetimeIndex'>
[2012-03-09 09:30:00, ..., 2012-03-14 09:30:00]
Length: 6, Freq: D, Timezone: US/Pacific

2012-03-09 12:30:00-05:00    0.079530
2012-03-10 12:30:00-05:00   -0.434450
2012-03-11 12:30:00-04:00    0.360739
2012-03-12 12:30:00-04:00    0.678065
2012-03-13 12:30:00-04:00   -0.705374
2012-03-14 12:30:00-04:00    0.684572
Freq: D

<class 'pandas.tseries.index.DatetimeIndex'>
[2012-03-09 09:30:00, ..., 2012-03-14 09:30:00]
Length: 6, Freq: D, Timezone: Asia/Shanghai
[Finished in 0.6s]

  • 操作时区意识型(time zone-aware)Timestamp对象

跟时间序列和日期序列差不多,Timestamp对象也能被从单纯型(navie)本地化为time zone-aware,并从一个时区转换为另一个时区。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time
from pandas.tseries.offsets import Hour,Minute,Day,MonthEnd
import pytz

stamp = pd.Timestamp('2011-03-12 04:00')
print type(stamp),'\n'
stamp_utc = stamp.tz_localize('UTC')
print stamp_utc,'\n'
print stamp_utc.tz_convert('US/Eastern'),'\n'
stamp_moscow = pd.Timestamp('2011-03-12 04:00',tz = 'Europe/Moscow')
print stamp_moscow
#时区意识型Timestamp对象在内部保存了一个UTC时间戳值(自1970年1月1日起的纳秒数),这个UTC值在时区转换过程中是不会变化的
print stamp_utc.value
print stamp_utc.tz_convert('US/Eastern').value,'\n'
#当使用pandas的DataOffset对象执行运算时,会自动关注“夏时令”…………
>>>

<class 'pandas.lib.Timestamp'>

2011-03-12 04:00:00+00:00

2011-03-11 23:00:00-05:00

2011-03-12 04:00:00+03:00
1299902400000000000
1299902400000000000
[Finished in 0.7s]

  • 不同时区之间的运算

如果时间时间时区不同,那么结果就会是UTC时间,由于时间戳其实是以UTC储存的,索引计算很方便。

#-*- coding:utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
from pandas import Series,DataFrame
from datetime import datetime
from dateutil.parser import parse
import time
from pandas.tseries.offsets import Hour,Minute,Day,MonthEnd
import pytz

rng = pd.date_range('3/7/2012',periods = 10,freq = 'B')
ts = Series(np.random.randn(len(rng)),index = rng)
print ts
ts1 = ts[:7].tz_localize('Europe/London')
#注意naive是不能直接转换为时区的,必须先转换为localize再进行转换
ts2 = ts1[2:].tz_convert('Europe/Moscow')
result = ts1 + ts2
#转换为UTC
print result.index
>>>
2012-03-07   -0.591417
2012-03-08    1.009651
2012-03-09   -1.922004
2012-03-12    0.246206
2012-03-13    0.033430
2012-03-14    0.614911
2012-03-15    1.944014
2012-03-16   -2.349846
2012-03-19    0.425925
2012-03-20    1.941166
Freq: B
<class 'pandas.tseries.index.DatetimeIndex'>
[2012-03-07 00:00:00, ..., 2012-03-15 00:00:00]
Length: 7, Freq: B, Timezone: UTC
[Finished in 0.7s]