您的位置:首页 > 编程语言 > Python开发

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

2017-12-27 09:10 375 查看
时间序列是很重要的。时间序列(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中的数据类型有:





字符串和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]

下面是日期的一些格式:









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





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]

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








移动(超前和滞后)数据

移动(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]


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  时间序列