目录
1. 学习内容
2. 准备工作
3. 创建时序数据
3.1 时序数据类型
3.2 创建时间点
3.2.1 to_datetime()方法
3.2.2 date_range()方法
3.2.3 bdate_range()方法
3.3 DateOffset对象
3.3.1 DateOffset与Timedelta的区别
4. 时序的索引及属性
4.1 索引切片
4.2 子集索引
4.3 时间点的属性
5. 重采样
5.1 一般用法
5.2 采样聚合
5.3 采样组的迭代
6. 窗口函数
6.1 Rolling
6.1.1 常用聚合
6.1.2 rolling的apply聚合
6.1.3 基于时间的rolling
6.2 Expanding
6.2.1 expanding函数
6.2.2 几个特别的Expanding类型函数
1. 学会时序类型数据的创建
2. 了解时序数据的索引与属性
3. 对时序数据进行重采样
本项目参见https://github.com/datawhalechina/team-learning/tree/master/Pandas%E6%95%99%E7%A8%8B%EF%BC%88%E4%B8%8A%EF%BC%89
import pandas as pd
import numpy as np
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
一般来说,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参数有许多选项,下面将常用部分罗列如下:
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')
Timedelta绝对时间差的特点指无论是冬令时还是夏令时,增减1day都只计算24小时。DateOffset相对时间差指,无论一天是23\24\25小时,增减1day都与当天相同的时间保持一致。例如,英国当地时间 2020年03月29日,01:00:00 时钟向前调整 1 小时 变为 2020年03月29日,02:00:00,开始夏令时。
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
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
采用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')
所谓重采样,就是指resample函数,它可以看做时序版本的groupby函数。
采样频率一般设置为上面提到的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
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
采样组的迭代和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
下面主要介绍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
所谓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
使用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
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
普通的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
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