import pandas as pd
import numpy as np
from datetime import datetime
WOM日期
- WOM(Week of Month)是一种非常实用的频率类
# 每个月的第三个星期五
rng = pd.date_range('2012-01-01','2012-09-01',freq='WOM-3FRI')
list(rng)
[Timestamp('2012-01-20 00:00:00', freq='WOM-3FRI'),
Timestamp('2012-02-17 00:00:00', freq='WOM-3FRI'),
Timestamp('2012-03-16 00:00:00', freq='WOM-3FRI'),
Timestamp('2012-04-20 00:00:00', freq='WOM-3FRI'),
Timestamp('2012-05-18 00:00:00', freq='WOM-3FRI'),
Timestamp('2012-06-15 00:00:00', freq='WOM-3FRI'),
Timestamp('2012-07-20 00:00:00', freq='WOM-3FRI'),
Timestamp('2012-08-17 00:00:00', freq='WOM-3FRI')]
移动(超前或滞后)数据
- 移动shifting指的是沿着时间轴将数据前移或者后移。
ts = pd.Series(np.random.randn(4),index=pd.date_range('1/1/2000',periods=4,freq='M'))
ts
2000-01-31 0.743071
2000-02-29 -0.242931
2000-03-31 -1.089458
2000-04-30 1.526272
Freq: M, dtype: float64
ts.shift(2)#后移
2000-01-31 NaN
2000-02-29 NaN
2000-03-31 0.743071
2000-04-30 -0.242931
Freq: M, dtype: float64
ts.shift(-2)
2000-01-31 -1.089458
2000-02-29 1.526272
2000-03-31 NaN
2000-04-30 NaN
Freq: M, dtype: float64
ts
2000-01-31 0.743071
2000-02-29 -0.242931
2000-03-31 -1.089458
2000-04-30 1.526272
Freq: M, dtype: float64
- shift通常用于计算一个时间序列或多个时间序列(如DataFrame的列)中的百分比变化。可以这样表达:
ts / ts.shift(1) -1
2000-01-31 NaN
2000-02-29 -1.326928
2000-03-31 3.484645
2000-04-30 -2.400946
Freq: M, dtype: float64
- 由于单纯的移位操作不会修改索引,所以部分数据会被丢弃。因此,如果频率已知,则可以将其传给shift以便实现对时间戳进行位移而不是对数据进行简单位移:
ts.shift(2,freq='M')
2000-03-31 0.743071
2000-04-30 -0.242931
2000-05-31 -1.089458
2000-06-30 1.526272
Freq: M, dtype: float64
- 也可以使用其他的频率:
ts.shift(2,freq="D")
2000-02-02 0.743071
2000-03-02 -0.242931
2000-04-02 -1.089458
2000-05-02 1.526272
dtype: float64
ts.shift(1,freq='90T')
2000-01-31 01:30:00 0.743071
2000-02-29 01:30:00 -0.242931
2000-03-31 01:30:00 -1.089458
2000-04-30 01:30:00 1.526272
Freq: M, dtype: float64
通过偏移量对日期进行移位
- pandas的日期偏移量还可以用在datetime和Timestamp对象上:
from pandas.tseries.offsets import Day,MonthEnd
now = datetime(2011,1,17)
now + 3 * Day()
Timestamp('2011-01-20 00:00:00')
- 加的是锚点偏移量(比如MonthEnd),第一次增量会将原日期向前滚动到符合频率规则的下一个日期:
now + MonthEnd()
Timestamp('2011-01-31 00:00:00')
now + MonthEnd(2)
Timestamp('2011-02-28 00:00:00')
- 通过锚点偏移量的rollforward和rollback方法,可明确地将日期向前或向后“滚动”:
offset = MonthEnd()
offset.rollforward(now)
Timestamp('2011-01-31 00:00:00')
offset.rollback(now)
Timestamp('2010-12-31 00:00:00')
- 日期偏移量还有一个巧妙地用法,即结合groupby使用两个“滚动”方法
ts = pd.Series(np.random.randn(20),index=pd.date_range('1/15/2000',periods=20, freq='4d'))
ts
2000-01-15 -1.668172
2000-01-19 -0.322175
2000-01-23 -1.243946
2000-01-27 -0.364190
2000-01-31 -0.342462
2000-02-04 -0.017388
2000-02-08 -0.820895
2000-02-12 -1.134715
2000-02-16 -1.117935
2000-02-20 1.271664
2000-02-24 -0.761732
2000-02-28 1.303958
2000-03-03 0.758311
2000-03-07 0.192461
2000-03-11 0.562556
2000-03-15 1.346408
2000-03-19 -1.205374
2000-03-23 -0.014696
2000-03-27 -0.547406
2000-03-31 -0.350570
Freq: 4D, dtype: float64
ts.groupby(offset.rollforward).mean()
2000-01-31 -0.788189
2000-02-29 -0.182435
2000-03-31 0.092711
dtype: float64
ts.resample('M').mean()
2000-01-31 -0.788189
2000-02-29 -0.182435
2000-03-31 0.092711
Freq: M, dtype: float64
时区处理
import pytz
pytz.common_timezones[-5:]
['US/Eastern', 'US/Hawaii', 'US/Mountain', 'US/Pacific', 'UTC']
#要从pytz中获取时区对象,使用pytz.timezone即可:
tz = pytz.timezone('America/New_York')
tz
<DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>
时区本地化和转换
- 默认情况下,pandas中的时间序列是单纯(naive)的时区
rng = pd.date_range('3/9/2012 9:30',periods=6, freq='D')
ts = pd.Series(np.random.randn(len(rng)),index = rng)
ts
2012-03-09 09:30:00 0.424951
2012-03-10 09:30:00 -1.164876
2012-03-11 09:30:00 0.506353
2012-03-12 09:30:00 0.455973
2012-03-13 09:30:00 -0.377689
2012-03-14 09:30:00 0.690384
Freq: D, dtype: float64
#其索引的tz字段为None:
print(ts.index.tz)
None
- 可以用时区集生成日期范围:
pd.date_range('3/9/2012 9:30',periods=6, freq='D',tz='UTC')
DatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00',
'2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00',
'2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00'],
dtype='datetime64[ns, UTC]', freq='D')
ts
2012-03-09 09:30:00 0.424951
2012-03-10 09:30:00 -1.164876
2012-03-11 09:30:00 0.506353
2012-03-12 09:30:00 0.455973
2012-03-13 09:30:00 -0.377689
2012-03-14 09:30:00 0.690384
Freq: D, dtype: float64
ts_utc = ts.tz_localize('UTC')
ts_utc
2012-03-09 09:30:00+00:00 0.424951
2012-03-10 09:30:00+00:00 -1.164876
2012-03-11 09:30:00+00:00 0.506353
2012-03-12 09:30:00+00:00 0.455973
2012-03-13 09:30:00+00:00 -0.377689
2012-03-14 09:30:00+00:00 0.690384
Freq: D, dtype: float64
ts_utc.index
DatetimeIndex(['2012-03-09 09:30:00+00:00', '2012-03-10 09:30:00+00:00',
'2012-03-11 09:30:00+00:00', '2012-03-12 09:30:00+00:00',
'2012-03-13 09:30:00+00:00', '2012-03-14 09:30:00+00:00'],
dtype='datetime64[ns, UTC]', freq='D')
- 一旦时间序列被本地化到某个特定时区,就可以用tz_convert将其转换到别的时区了
ts_utc.tz_convert('America/New_York')
2012-03-09 04:30:00-05:00 0.424951
2012-03-10 04:30:00-05:00 -1.164876
2012-03-11 05:30:00-04:00 0.506353
2012-03-12 05:30:00-04:00 0.455973
2012-03-13 05:30:00-04:00 -0.377689
2012-03-14 05:30:00-04:00 0.690384
Freq: D, dtype: float64
ts.index.tz_localize("Asia/Shanghai")
DatetimeIndex(['2012-03-09 09:30:00+08:00', '2012-03-10 09:30:00+08:00',
'2012-03-11 09:30:00+08:00', '2012-03-12 09:30:00+08:00',
'2012-03-13 09:30:00+08:00', '2012-03-14 09:30:00+08:00'],
dtype='datetime64[ns, Asia/Shanghai]', freq='D')
操作时区意识型Timestamp对象
- 跟时间序列和日期范围差不多,独立的Timestamp对象也能被从单纯型(naive)本地化为时区意识型(time zone-aware),并从一个时区转换到另一个时区
stamp = pd.Timestamp('2011-03-12 04:00')
stamp
Timestamp('2011-03-12 04:00:00')
stamp_utc = stamp.tz_localize('UTC')
stamp_utc
Timestamp('2011-03-12 04:00:00+0000', tz='UTC')
stamp_utc.tz_convert('America/New_York')
Timestamp('2011-03-11 23:00:00-0500', tz='America/New_York')
- 在创建Timestamp时,还可以传入一个时区信息:
stamp_mosco = pd.Timestamp('2011-03-12 04:30', tz="Europe/Moscow")
stamp_mosco
Timestamp('2011-03-12 04:30:00+0300', tz='Europe/Moscow')
- 时区意识型Timestamp对象在内部保存了一个UTC时间戳值(自UNIX纪元(1970年1月1日)算起的纳秒数)。这个UTC值在时区转换过程中是不会发生变化的:
stamp_utc.value
1299902400000000000
stamp_utc.tz_convert('America/New_York').value
1299902400000000000
时期以及其算术运算
- 时期(period)表示的是时间区间,比如数日、数月、数季、数年等。Period类所表示的就是这种数据类型,其构造函数需要用到一个字符串或整数,以及表11-4中的频率:
p = pd.Period(2007,freq="A-DEC")
p
Period('2007', 'A-DEC')
p1 = p+5
p1
Period('2012', 'A-DEC')
p2 = p - 2
p2
Period('2005', 'A-DEC')
p1 - p2
7
- period_range函数可用于创建规则的时期范围:
rng = pd.period_range('2000-01-01','2000-06-30',freq='M')
rng
PeriodIndex(['2000-01', '2000-02', '2000-03', '2000-04', '2000-05', '2000-06'], dtype='period[M]', freq='M')
pd.Series(np.random.randn(6),index=rng)
2000-01 1.642249
2000-02 1.086168
2000-03 -1.572164
2000-04 0.362395
2000-05 -0.361680
2000-06 -0.676564
Freq: M, dtype: float64
重采样及频率转换
- 重采样(resampling)指的是将时间序列从一个频率转换到另一个频率的处理过程。将高频率数据聚合到低频率称为降采样(downsampling),而将低频率数据转换到高频率则称为升采样(upsampling)。并不是所有的重采样都能被划分到这两个大类中。
rng = pd.date_range('2000-01-01',periods=100,freq='D')
ts = pd.Series(np.random.randn(100),index=rng)
ts.resample('M').mean()
2000-01-31 0.286363
2000-02-29 -0.045181
2000-03-31 0.209406
2000-04-30 0.065586
Freq: M, dtype: float64
ts.resample('M',kind='period').mean()
2000-01 0.286363
2000-02 -0.045181
2000-03 0.209406
2000-04 0.065586
Freq: M, dtype: float64
降采样
-
数据聚合到规律的低频率是一件非常普通的时间序列处理任务。待聚合的数据不必拥有固定的频率,期望的频率会自动定义聚合的面元边界,这些面元用于将时间序列拆分为多个片段。例如,要转换到月度频率(‘M’或’BM’),数据需要被划分到多个单月时间段中。各时间段都是半开放的。一个数据点只能属于一个时间段,所有时间段的并集必须能组成整个时间帧。在用resample对数据进行降采样时,需要考虑两样东西:
- 各区间哪边是闭合的。
- 如何标记各个聚合面元,用区间的开头还是末尾。
rng = pd.date_range('2000-01-01',periods=12,freq='T')
ts = pd.Series(np.arange(12),index=rng)
ts
2000-01-01 00:00:00 0
2000-01-01 00:01:00 1
2000-01-01 00:02:00 2
2000-01-01 00:03:00 3
2000-01-01 00:04:00 4
2000-01-01 00:05:00 5
2000-01-01 00:06:00 6
2000-01-01 00:07:00 7
2000-01-01 00:08:00 8
2000-01-01 00:09:00 9
2000-01-01 00:10:00 10
2000-01-01 00:11:00 11
Freq: T, dtype: int32
#通过求和的方式将这些数据聚合到“5分钟”块中:
ts.resample('5min',closed='right').sum()
1999-12-31 23:55:00 0
2000-01-01 00:00:00 15
2000-01-01 00:05:00 40
2000-01-01 00:10:00 11
Freq: 5T, dtype: int32
ts.resample('5min',closed='left').sum()
2000-01-01 00:00:00 10
2000-01-01 00:05:00 35
2000-01-01 00:10:00 21
Freq: 5T, dtype: int32