目录
1. 学习内容
1. 学会时序类型数据的创建
2. 了解时序数据的索引与属性
3. 对时序数据进行重采样
2. 准备工作
import pandas as pd
import numpy as np
3. 创建时序数据
3.1 时序数据类型

3.2 创建时间点
3.2.1 to_datetime()方法
pandas在时间点建立的输入格式规定上给了很大的自由度,很多表示时间的字符串都能直接且正确建立同一时间点。而对于无法直接建立的数据,也可以使用format参数来指定字符串格式从而建立时间点。
pd.to_datetime('2020\\1\\1', format = '%Y\\%m\\%d')
pd.to_datetime('2020`1`1', format = '%Y`%m`%d')
pd.to_datetime('2020.1 1', format = '%Y.%m %d')
pd.to_datetime('1 1.2020', format = '%d %m.%Y')
Timestamp('2020-01-01 00:00:00')
同时,使用列表可以将其转为时间点索引。
pd.Series(range(2), index = pd.to_datetime(['2020/1/1', '2020/1/2']))
2020-01-01 0
2020-01-02 1
dtype: int64
对于DataFrame而言,如果列已经按照时间顺序排好,则利用to_datetime可自动转换。
df = pd.DataFrame({'year': [2020, 2020], 'month': [1, 1], 'day': [1, 2]})
pd.to_datetime(df)
0 2020-01-01
1 2020-01-02
dtype: datetime64[ns]
事实上,Timestamp的精度远远不止day,可以最小到纳秒ns。当然,缩小精度的代价是缩小表示范围。
pd.to_datetime('2020/1/1 00:00:00.123456789')
Timestamp('2020-01-01 00:00:00.123456789')
print(pd.Timestamp.min)
print(pd.Timestamp.max)
1677-09-21 00:12:43.145225
2262-04-11 23:47:16.854775807
3.2.2 date_range()方法
一般来说,start/end/periods(时间点个数)/freq(间隔方法)是该方法最重要的参数,给定了其中的3个,剩下的一个就会被确定。
pd.date_range(start = '2020/1/1', end = '2020/1/10', periods = 3)
DatetimeIndex(['2020-01-01 00:00:00', '2020-01-05 12:00:00',
'2020-01-10 00:00:00'],
dtype='datetime64[ns]', freq=None)
其中freq参数有许多选项,下面将常用部分罗列如下:

3.2.3 bdate_range()方法
bdate_range是一个类似与date_range的方法,特点在于可以在自带的工作日间隔设置上,再选择weekmask参数和holidays参数。它的freq中有一个特殊的'C'/'CBM'/'CBMS'选项,表示定制,需要联合weekmask参数和holidays参数使用。
# 例如现在需要将工作日中的周一、周二、周五3天保留,并将部分holidays剔除
weekmask = 'Mon Tue Fri'
holidays = [pd.Timestamp('2020/1/%s' %i) for i in range(7, 13)]
#注意holidays
pd.bdate_range(start = '2020-1-1',end = '2020-1-15', freq = 'C', \
weekmask = weekmask, holidays = holidays)
DatetimeIndex(['2020-01-03', '2020-01-06', '2020-01-13', '2020-01-14'], dtype='datetime64[ns]', freq='C')
3.3 DateOffset对象
3.3.1 DateOffset与Timedelta的区别
Timedelta绝对时间差的特点指无论是冬令时还是夏令时,增减1day都只计算24小时。DateOffset相对时间差指,无论一天是23\24\25小时,增减1day都与当天相同的时间保持一致。例如,英国当地时间 2020年03月29日,01:00:00 时钟向前调整 1 小时 变为 2020年03月29日,02:00:00,开始夏令时。
4. 时序的索引及属性
4.1 索引切片
rng = pd.date_range('2020', '2021', freq = 'W')
ts = pd.Series(np.random.randn(len(rng)), index = rng)
ts.head()
2020-01-05 -0.355636
2020-01-12 2.377644
2020-01-19 -0.918134
2020-01-26 -0.136201
2020-02-02 -1.320732
Freq: W-SUN, dtype: float64
# 合法字符自动转换为时间点
ts['2020-01-26': '20200726'].head()
2020-01-26 -0.136201
2020-02-02 -1.320732
2020-02-09 -0.180517
2020-02-16 -0.346887
2020-02-23 0.663864
Freq: W-SUN, dtype: float64
4.2 子集索引
ts['2020-7'].head()
2020-07-05 0.911370
2020-07-12 -0.949458
2020-07-19 0.754932
2020-07-26 0.014313
Freq: W-SUN, dtype: float64
# 支持混合形态索引
ts['2011-1': '20200726'].head()
2020-01-05 -0.355636
2020-01-12 2.377644
2020-01-19 -0.918134
2020-01-26 -0.136201
2020-02-02 -1.320732
Freq: W-SUN, dtype: float64
4.3 时间点的属性
采用dt对象可以轻松获得关于时间的信息。
pd.Series(ts.index).dt.week.head()
0 1
1 2
2 3
3 4
4 5
dtype: int64
pd.Series(ts.index).dt.day.head()
0 5
1 12
2 19
3 26
4 2
dtype: int64
利用strftime可重新修改时间格式。
pd.Series(ts.index).dt.strftime('%Y-间隔1-%m-间隔2-%d').head()
0 2020-01-05 00:00:00
1 2020-01-12 00:00:00
2 2020-01-19 00:00:00
3 2020-01-26 00:00:00
4 2020-02-02 00:00:00
dtype: object
对于datetime对象可以直接通过属性获取信息。
pd.date_range('2020', '2021', freq = 'W').month
Int64Index([ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4,
5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12,
12],
dtype='int64')
pd.date_range('2020', '2021', freq = 'W').weekday
Int64Index([6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6],
dtype='int64')
5. 重采样
所谓重采样,就是指resample函数,它可以看做时序版本的groupby函数。
5.1 一般用法
采样频率一般设置为上面提到的offset字符。
df_r = pd.DataFrame(np.random.randn(1000, 3), \
index = pd.date_range('1/1/2020', freq = 'S', periods = 1000), \
columns = ['A', 'B', 'C'])
r = df_r.resample('3min')
r.sum()
A B C
2020-01-01 00:00:00 2.357245 -16.806970 7.988968
2020-01-01 00:03:00 -10.272145 15.806088 -25.279588
2020-01-01 00:06:00 2.544400 12.645482 -3.338972
2020-01-01 00:09:00 -0.872054 12.439237 15.888432
2020-01-01 00:12:00 -1.293978 16.658237 16.703369
2020-01-01 00:15:00 9.466841 -5.728703 2.354720
df_r2 = pd.DataFrame(np.random.randn(200, 3), \
index = pd.date_range('1/1/2020', freq = 'D', periods = 200), \
columns = ['A', 'B', 'C'])
r = df_r2.resample('CBMS')
r.sum()
A B C
2020-01-01 -0.990115 -0.460111 -3.429150
2020-02-03 7.131371 0.498715 2.033721
2020-03-02 4.153905 -3.487473 10.328431
2020-04-01 -0.583855 -3.208713 -6.282215
2020-05-01 2.083735 1.235905 1.326104
2020-06-01 2.496877 -0.014361 -3.089092
2020-07-01 -3.104211 7.385895 -1.652805
5.2 采样聚合
r = df_r.resample('3T')
r['A'].mean()
2020-01-01 00:00:00 -0.035590
2020-01-01 00:03:00 -0.134152
2020-01-01 00:06:00 -0.009651
2020-01-01 00:09:00 0.037712
2020-01-01 00:12:00 0.024681
2020-01-01 00:15:00 0.018664
Freq: 3T, Name: A, dtype: float64
5.3 采样组的迭代
采样组的迭代和groupby迭代完全类似,对于每一个组都可以分别做相应操作。
date_list = ['2020-01-01 00:00:00', '2020-01-01 00:30:00', \
'2020-01-01 00:31:00', '2020-01-01 01:00:00', \
'2020-01-01 03:00:00', '2020-01-01 03:05:00']
small = pd.Series(range(6),index = pd.to_datetime(date_list))
resampled = small.resample('H')
for name, group in resampled:
print("Group: ", name)
print("-" * 27)
print(group, end = "\n\n")
Group: 2020-01-01 00:00:00
---------------------------
2020-01-01 00:00:00 0
2020-01-01 00:30:00 1
2020-01-01 00:31:00 2
dtype: int64
Group: 2020-01-01 01:00:00
---------------------------
2020-01-01 01:00:00 3
dtype: int64
Group: 2020-01-01 02:00:00
---------------------------
Series([], dtype: int64)
Group: 2020-01-01 03:00:00
---------------------------
2020-01-01 03:00:00 4
2020-01-01 03:05:00 5
dtype: int64
6. 窗口函数
下面主要介绍pandas中两类主要的窗口(window)函数:rolling/expanding。
s = pd.Series(np.random.randn(1000), \
index = pd.date_range('1/1/2020', periods = 1000))
s.head()
2020-01-01 -0.346470
2020-01-02 -1.372667
2020-01-03 -0.026028
2020-01-04 0.696390
2020-01-05 0.684197
Freq: D, dtype: float64
6.1 Rolling
6.1.1 常用聚合
所谓rolling方法,就是规定一个窗口,它和groupby对象一样,本身不会进行操作,需要配合聚合函数才能计算结果。
s.rolling(window = 50)
Rolling [window=50,center=False,axis=0]
s.rolling(window = 50).mean()
2020-01-01 NaN
2020-01-02 NaN
2020-01-03 NaN
2020-01-04 NaN
2020-01-05 NaN
...
2022-09-22 -0.131049
2022-09-23 -0.121698
2022-09-24 -0.111032
2022-09-25 -0.096440
2022-09-26 -0.113077
Freq: D, Length: 1000, dtype: float64
min_periods参数是指需要的非缺失数据点数量阀值。
s.rolling(window = 50, min_periods = 3).mean().head()
2020-01-01 NaN
2020-01-02 NaN
2020-01-03 -0.581722
2020-01-04 -0.262194
2020-01-05 -0.072916
Freq: D, dtype: float64
6.1.2 rolling的apply聚合
使用apply聚合时,只需记住传入的是window大小的Series,输出的必须是标量即可,比如如下计算变异系数。
s.rolling(window = 50, min_periods = 3).apply(lambda x: x.std() / x.mean()).head()
2020-01-01 NaN
2020-01-02 NaN
2020-01-03 -1.209285
2020-01-04 -3.277140
2020-01-05 -11.740584
Freq: D, dtype: float64
6.1.3 基于时间的rolling
s.rolling('15D').mean().head()
2020-01-01 -0.346470
2020-01-02 -0.859568
2020-01-03 -0.581722
2020-01-04 -0.262194
2020-01-05 -0.072916
Freq: D, dtype: float64
可选closed='right'(默认)'left''both''neither'参数,决定端点的包含情况。
s.rolling('15D', closed = 'right').sum().head()
2020-01-01 -0.346470
2020-01-02 -1.719137
2020-01-03 -1.745165
2020-01-04 -1.048775
2020-01-05 -0.364578
Freq: D, dtype: float64
6.2 Expanding
6.2.1 expanding函数
普通的expanding函数等价与rolling(window=len(s),min_periods=1),是对序列的累计计算。
s.rolling(window = len(s), min_periods = 1).sum().head()
2020-01-01 -0.346470
2020-01-02 -1.719137
2020-01-03 -1.745165
2020-01-04 -1.048775
2020-01-05 -0.364578
Freq: D, dtype: float64
s.expanding().sum().head()
2020-01-01 -0.346470
2020-01-02 -1.719137
2020-01-03 -1.745165
2020-01-04 -1.048775
2020-01-05 -0.364578
Freq: D, dtype: float64
s.expanding().apply(lambda x: sum(x)).head()
2020-01-01 -0.346470
2020-01-02 -1.719137
2020-01-03 -1.745165
2020-01-04 -1.048775
2020-01-05 -0.364578
Freq: D, dtype: float64
6.2.2 几个特别的Expanding类型函数
cumsum/cumprod/cummax/cummin都是特殊expanding累计计算方法。
s.cumsum().head()
2020-01-01 -0.346470
2020-01-02 -1.719137
2020-01-03 -1.745165
2020-01-04 -1.048775
2020-01-05 -0.364578
Freq: D, dtype: float64
shift/diff/pct_change都是涉及到了元素关系:
shift是指序列索引不变,但值向后移动;
diff是指前后元素的差,period参数表示间隔,默认为1,并且可以为负;
pct_change是值前后元素的变化百分比,period参数与diff类似。
s.shift(2).head()
2020-01-01 NaN
2020-01-02 NaN
2020-01-03 -0.346470
2020-01-04 -1.372667
2020-01-05 -0.026028
Freq: D, dtype: float64
s.diff(3).head()
2020-01-01 NaN
2020-01-02 NaN
2020-01-03 NaN
2020-01-04 1.042860
2020-01-05 2.056864
Freq: D, dtype: float64
s.pct_change(3).head()
2020-01-01 NaN
2020-01-02 NaN
2020-01-03 NaN
2020-01-04 -3.009957
2020-01-05 -1.498444
Freq: D, dtype: float64