文章目录
一、datetime库
datetime库中有以下几种类型:
from datetime import datetime
from datetime import timedelta
1. 字符串与datetime相互转换
datetime转换为字符串:
stamp = datetime(2022, 4, 8)
str(stamp) # 结果:'2022-04-08 00:00:00'
stamp.strftime('%Y-%m-%d') # 结果:'2022-04-08'
将字符串转化为datetime:
datetime.strptime
是在已知格式的情况下转换日期的好方式:
s = '2022-04-09'
datetime.strptime(s, '%Y-%m-%d') # 结果:datetime.datetime(2022, 4, 9, 0, 0)
每次都必须编写一个格式代码可能有点烦人,特别是对于通用日期格式。在这种情况下,你可以使用第三方dateutil包的parser.parse
方法(这个包在安装pandas时已经自动安装,所以不需额外安装):
from dateutil.parser import parse
s = '2022-04-09'
parse(s) # 结果:datetime.datetime(2022, 4, 9, 0, 0)
dateutil能够解析大部分人类可理解的日期表示:
parse('Jan 31, 2022 10:45 PM') # 结果:datetime.datetime(2022, 1, 31, 22, 45)
如果日期出现在月份之前,可以传递dayfirst=True
来表明这种情况:
parse('9/4/2022', dayfirst=True) # 结果:datetime.datetime(2022, 4, 9, 0, 0)
二、pandas中的时间序列数据
1. to_datetime方法
pandas中的to_datetime
方法可以转换很多不同的日期表示格式:
s = '2022-04-09'
pd.to_datetime(s) # 结果:Timestamp('2022-04-09 00:00:00')
同时转换多个日期,即传入列表(datetime.strptime
和parse
不能):
dates = ['2022-04-01', '2022-04-03','2022-04-05','2022-04-07','2022-04-09']
pd.to_datetime(dates) # 结果:DatetimeIndex(['2022-04-01', '2022-04-03', '2022-04-05', '2022-04-07', '2022-04-09'], dtype='datetime64[ns]', freq=None)
to_datetime
方法生成的是Timestamp
对象。
同样的,也可以传递dayfirst=True
。
另外,to_datetime
方法还可以处理那些被认为是缺失值的值:
dt = ['2022-04-09', '2022-04-10', None]
pd.to_datetime(dt) # 结果:DatetimeIndex(['2022-04-09', '2022-04-10', 'NaT'], dtype='datetime64[ns]', freq=None)
其中,NaT
(Not a time)是pandas中时间戳数据的是null值。
2. 作为索引
时间序列数据可作为series和dataframe的索引,以Series为例。
2.1 短的序列
dates = pd.to_datetime(['2022-04-01', '2022-04-03','2022-04-05','2022-04-07','2022-04-09'])
ds = pd.Series(np.random.uniform(0, 1, 5), index=dates)
ds:
2022-04-01 0.089425
2022-04-03 0.917701
2022-04-05 0.222676
2022-04-07 0.450355
2022-04-09 0.125723
dtype: float64
- 可以像普通的Series一样进行选择:
ds[1] # 结果:0.9177005610779598
- 也可以传入相应的时间序列数据:
ds['2022-04-03'] # 结果:0.9177005610779598
- 还可以传递一个能解释为日期的字符串:
ds['2022/04/03'] # 结果:0.9177005610779598
2.2 长的序列
longer_ds = pd.Series(data=np.random.uniform(0, 1, 1000), index=pd.date_range('2022-01-01', periods=1000))
longer_ds:
2022-01-01 0.807610
2022-01-02 0.559262
2022-01-03 0.216209
2022-01-04 0.577930
2022-01-05 0.674460
...
2024-09-22 0.407143
2024-09-23 0.730174
2024-09-24 0.803955
2024-09-25 0.647067
2024-09-26 0.338940
Freq: D, Length: 1000, dtype: float64
- 可以传递一个年份来选择数据:
longer_ds['2022']
输出结果:
2022-01-01 0.807610
2022-01-02 0.559262
2022-01-03 0.216209
2022-01-04 0.577930
2022-01-05 0.674460
...
2022-12-27 0.000907
2022-12-28 0.532692
2022-12-29 0.765496
2022-12-30 0.319450
2022-12-31 0.746440
Freq: D, Length: 365, dtype: float64
这里的字符串'2022'
被解释为了年份。
- 也可以传递年份+月份,但不能仅传递月份:
longer_ds['2022-04']
输出结果:
2022-04-01 0.050926
2022-04-02 0.442934
2022-04-03 0.753951
2022-04-04 0.069381
2022-04-05 0.136684
...
2022-04-25 0.585125
2022-04-26 0.612559
2022-04-27 0.180499
2022-04-28 0.184621
2022-04-29 0.614060
2022-04-30 0.778345
Freq: D, dtype: float64
- 也可以传递
datetime
对象:
longer_ds[datetime(2022, 1 , 1)] # 结果:0.8076095055689887
- 使用
truncate
方法:
使用truncate时的索引必须先进行排序,不然会报错
详见:Python pandas.DataFrame.truncate函数方法的使用
longer_ds.truncate(after='2022-04-01')
输出结果:
2022-01-01 0.807610
2022-01-02 0.559262
2022-01-03 0.216209
2022-01-04 0.577930
2022-01-05 0.674460
...
2022-03-28 0.303901
2022-03-29 0.892455
2022-03-30 0.206263
2022-03-31 0.899058
2022-04-01 0.050926
Freq: D, Length: 91, dtype: float64
2.3 含有重复索引的序列
dates = pd.to_datetime(['2022-01-01', '2022-01-01', '2022-01-03', '2022-01-03', '2022-01-05'])
ds = pd.Series(np.random.uniform(0, 1, 5), index=dates)
ds:
2022-01-01 0.905035
2022-01-01 0.486505
2022-01-03 0.707109
2022-01-03 0.016314
2022-01-05 0.554270
dtype: float64
可以通过索引的is_unique
属性查看索引是否含有重复值:
ds.index.is_unique # 结果:False
ds['2022-01-05'] # 结果:0.5542699289639651,为标量指
ds['2022-01-01']
输出结果:
2022-01-01 0.905035
2022-01-01 0.486505
dtype: float64
结果为Series切片。
3. 日期范围、频率和移位
3.1 生成日期范围
pandas.date_range
可用于根据特定频率生成指定长度的DatetimeIndex,默认情况下,date_range
生成的是每日的时间戳:
index = pd.date_range(start='2022-04-01', end='2022-04-30')
index:
DatetimeIndex(['2022-04-01', '2022-04-02', '2022-04-03', '2022-04-04',
'2022-04-05', '2022-04-06', '2022-04-07', '2022-04-08',
'2022-04-09', '2022-04-10', '2022-04-11', '2022-04-12',
'2022-04-13', '2022-04-14', '2022-04-15', '2022-04-16',
'2022-04-17', '2022-04-18', '2022-04-19', '2022-04-20',
'2022-04-21', '2022-04-22', '2022-04-23', '2022-04-24',
'2022-04-25', '2022-04-26', '2022-04-27', '2022-04-28',
'2022-04-29', '2022-04-30'],
dtype='datetime64[ns]', freq='D')
- 如果只传递一个起始或结尾日期,必须传递一个用于生成范围的数字:
index = pd.date_range(start='2022-04-01', periods=30)
index:
DatetimeIndex(['2022-04-01', '2022-04-02', '2022-04-03', '2022-04-04',
'2022-04-05', '2022-04-06', '2022-04-07', '2022-04-08',
'2022-04-09', '2022-04-10', '2022-04-11', '2022-04-12',
'2022-04-13', '2022-04-14', '2022-04-15', '2022-04-16',
'2022-04-17', '2022-04-18', '2022-04-19', '2022-04-20',
'2022-04-21', '2022-04-22', '2022-04-23', '2022-04-24',
'2022-04-25', '2022-04-26', '2022-04-27', '2022-04-28',
'2022-04-29', '2022-04-30'],
dtype='datetime64[ns]', freq='D')
- 特殊的频率,比如需要一个包含每月最后业务日期的时间索引:
index = pd.date_range(start='2022-01-01', end='2022-12-01', freq='BM')
index:
DatetimeIndex(['2022-01-31', '2022-02-28', '2022-03-31', '2022-04-29',
'2022-05-31', '2022-06-30', '2022-07-29', '2022-08-31',
'2022-09-30', '2022-10-31', '2022-11-30'],
dtype='datetime64[ns]', freq='BM')
- 固定间隔:
index = pd.date_range(start='2022-04-01', end='2022-04-07', freq='4h')
index:
DatetimeIndex(['2022-04-01 00:00:00', '2022-04-01 04:00:00',
'2022-04-01 08:00:00', '2022-04-01 12:00:00',
'2022-04-01 16:00:00', '2022-04-01 20:00:00',
'2022-04-02 00:00:00', '2022-04-02 04:00:00',
'2022-04-02 08:00:00', '2022-04-02 12:00:00',
'2022-04-02 16:00:00', '2022-04-02 20:00:00',
'2022-04-03 00:00:00', '2022-04-03 04:00:00',
'2022-04-03 08:00:00', '2022-04-03 12:00:00',
'2022-04-03 16:00:00', '2022-04-03 20:00:00',
'2022-04-04 00:00:00', '2022-04-04 04:00:00',
'2022-04-04 08:00:00', '2022-04-04 12:00:00',
'2022-04-04 16:00:00', '2022-04-04 20:00:00',
'2022-04-05 00:00:00', '2022-04-05 04:00:00',
'2022-04-05 08:00:00', '2022-04-05 12:00:00',
'2022-04-05 16:00:00', '2022-04-05 20:00:00',
'2022-04-06 00:00:00', '2022-04-06 04:00:00',
'2022-04-06 08:00:00', '2022-04-06 12:00:00',
'2022-04-06 16:00:00', '2022-04-06 20:00:00',
'2022-04-07 00:00:00'],
dtype='datetime64[ns]', freq='4H')
- 生成的是标准化为零点的时间戳:
index = pd.date_range(start='2022-04-01 10:23:42', periods=7, normalize=True)
index:
DatetimeIndex(['2022-04-01', '2022-04-02', '2022-04-03', '2022-04-04',
'2022-04-05', '2022-04-06', '2022-04-07'],
dtype='datetime64[ns]', freq='D')
3.2 移位
移位"是指将日期按时间向前移动或向后移动。Series和DataFrame都有一个shift
方法用于简单的前向或后向移位,而不改变索引:
ds = pd.Series(data=np.random.randn(5), index=pd.date_range(start='2022-04-01', periods=5))
ds:
2022-04-01 0.169925
2022-04-02 -0.178289
2022-04-03 0.181091
2022-04-04 -0.373990
2022-04-05 0.060268
Freq: D, dtype: float64
前向移位:
ds.shift(2)
输出结果:
2022-04-01 NaN
2022-04-02 NaN
2022-04-03 0.169925
2022-04-04 -0.178289
2022-04-05 0.181091
Freq: D, dtype: float64
后向移位:
ds.shift(-2)
输出结果:
2022-04-01 0.181091
2022-04-02 -0.373990
2022-04-03 0.060268
2022-04-04 NaN
2022-04-05 NaN
Freq: D, dtype: float64
4. 重新采样与频率转换
重新采样是指将时间序列从一个频率转换为另一个频率的过程。将更高频率的数据聚合到低频率被称为向下采样,而从低频率转换到高频率称为向上采样。并不是所有的重新采样都属于上面说的两类;例如,将W-WED(weekly onWednesday,每周三)转换到W-FRI(每周五)既不是向上采样也不是向下采样。
可先调用resample
对数据分组,之后再调用聚合函数:
ds = pd.Series(data=np.random.randn(100), index=pd.date_range(start='2022-01-01', periods=100))
ds:
2022-01-01 -2.884805
2022-01-02 0.786080
2022-01-03 -1.191273
2022-01-04 0.468861
2022-01-05 0.462229
...
2022-04-06 0.753766
2022-04-07 0.827008
2022-04-08 -0.193957
2022-04-09 1.098543
2022-04-10 1.593474
Freq: D, Length: 100, dtype: float64
ds.resample('M').mean()
输出结果:
2022-01-31 -0.025061
2022-02-28 0.302152
2022-03-31 0.209327
2022-04-30 0.599747
Freq: M, dtype: float64
resample方法参数:
4.1 下采样
使用resample进行向下采样数据时有些事情需要考虑:
- 每段间隔的哪一边是闭合的
- 如何在间隔的起始或结束位置标记每个已聚合的箱体
以分钟维度的数据为例:
ds = pd.Series(data=np.arange(12), index=pd.date_range(start='2022-01-01', periods=12, freq='T'))
ds:
2022-01-01 00:00:00 0
2022-01-01 00:01:00 1
2022-01-01 00:02:00 2
2022-01-01 00:03:00 3
2022-01-01 00:04:00 4
2022-01-01 00:05:00 5
2022-01-01 00:06:00 6
2022-01-01 00:07:00 7
2022-01-01 00:08:00 8
2022-01-01 00:09:00 9
2022-01-01 00:10:00 10
2022-01-01 00:11:00 11
Freq: T, dtype: int32
计算5分钟的和:
ds.resample('5min', closed='right', label='right').sum()
输出结果:
2022-01-01 00:00:00 0
2022-01-01 00:05:00 15
2022-01-01 00:10:00 40
2022-01-01 00:15:00 11
Freq: 5T, dtype: int32
可以将结果索引移动一定的数量,例如从右边缘减去一秒,以使其更清楚地表明时间戳所指的间隔:
ds.resample('5min', closed='right', label='right', loffset='-1s').sum()
结果输出:
2021-12-31 23:59:59 0
2022-01-01 00:04:59 15
2022-01-01 00:09:59 40
2022-01-01 00:14:59 11
Freq: 5T, dtype: int32
为每个桶计算第一个值(开端)、最后一个值(结束)、最大值(峰值)和最小值(谷值):
ds.resample('5min', closed='right', label='right', loffset='-1s').ohlc()
输出结果:
open high low close
2021-12-31 23:59:59 0 0 0 0
2022-01-01 00:04:59 1 5 1 5
2022-01-01 00:09:59 6 10 6 10
2022-01-01 00:14:59 11 11 11 11