Pandas 时间戳索引的使用(pandas 查找字符串)
Pandas时间戳索引-DatetimeIndex
pd.DatetimeIndex()与TimeSeries时间序列
pd.DatetimeIndex()
可以直接生成时间戳索引,支持使用str、datetime.datetime。 单个时间戳的类型为Timestamp
,多个时间戳的类型为DatetimeIndex
,示例如下:
rng = pd.DatetimeIndex(['12/1/2017','12/2/2017','12/3/2017','12/4/2017','12/5/2017']) print(rng,type(rng)) print(rng[0],type(rng[0])) >>> DatetimeIndex(['2017-12-01', '2017-12-02', '2017-12-03', '2017-12-04', '2017-12-05'], dtype='datetime64[ns]', freq=None) <class 'pandas.core.indexes.datetimes.DatetimeIndex'> 2017-12-01 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'> 复制代码
什么是TimeSeries时间序列?
以DatetimeIndex
为index的Series,为TimeSries时间序列 举个栗子:
st = pd.Series(np.random.rand(len(rng)), index = rng) print(st,type(st)) print(st.index) >>> 2017-12-01 0.081920 2017-12-02 0.921781 2017-12-03 0.489779 2017-12-04 0.257632 2017-12-05 0.805373 dtype: float64 <class 'pandas.core.series.Series'> DatetimeIndex(['2017-12-01', '2017-12-02', '2017-12-03', '2017-12-04', '2017-12-05'], dtype='datetime64[ns]', freq=None) 复制代码
pd.date_range()-生成日期范围
pd.date_range()生成日期范围有两种生成方式(默认频率是day):
起始时间(start) + 结束时间(end)
起始时间(start)/结束时间(end) + 偏移量(periods)
举个栗子:
date1 = pd.date_range('2017/1/1','2017/10/1',normalize=True) print(date1) date2 = pd.date_range(start = '1/1/2017', periods = 10) print(date2) date3 = pd.date_range(end = '1/30/2017 15:00:00', periods = 10,normalize=True) # 增加了时、分、秒 print(date3) >>> DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04', '2017-01-05', '2017-01-06', '2017-01-07', '2017-01-08', '2017-01-09', '2017-01-10', ... '2017-09-22', '2017-09-23', '2017-09-24', '2017-09-25', '2017-09-26', '2017-09-27', '2017-09-28', '2017-09-29', '2017-09-30', '2017-10-01'], dtype='datetime64[ns]', length=274, freq='D') DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04', '2017-01-05', '2017-01-06', '2017-01-07', '2017-01-08', '2017-01-09', '2017-01-10'], dtype='datetime64[ns]', freq='D') DatetimeIndex(['2017-01-21', '2017-01-22', '2017-01-23', '2017-01-24', '2017-01-25', '2017-01-26', '2017-01-27', '2017-01-28', '2017-01-29', '2017-01-30'], dtype='datetime64[ns]', freq='D') 复制代码
pd.date_range(start=None, end=None, periods=None, freq='D', tz=None, normalize=False, name=None, closed=None, **kwargs) 复制代码
其中常用参数含义如下:
start:开始时间
end:结束时间
periods:偏移量
freq:频率,默认天,pd.date_range()默认频率为日历日,pd.bdate_range()默认频率为工作日
tz:时区
normalize:时间参数值正则化到午夜时间戳
closed:默认为None的情况下,左闭右闭,left则左闭右开,right则左开右闭
举个栗子对normalize
参数进行实际运用:
rng4 = pd.date_range(start = '1/1/2017 15:30', periods = 10, name = 'hello world!', normalize = True) print(rng4) >>> DatetimeIndex(['2017-01-01', '2017-01-02', '2017-01-03', '2017-01-04', '2017-01-05', '2017-01-06', '2017-01-07', '2017-01-08', '2017-01-09', '2017-01-10'], dtype='datetime64[ns]', name='hello world!', freq='D') 复制代码
freq的使用(1) - 固定频率时间序列的生成
基础使用如下:
print(pd.date_range('2017/1/1','2017/1/4')) # 默认freq = 'D':每日历日 print(pd.date_range('2017/1/1','2017/1/4', freq = 'B')) # B:每工作日 print(pd.date_range('2017/1/1','2017/1/2', freq = 'H')) # H:每小时 print(pd.date_range('2017/1/1 12:00','2017/1/1 12:10', freq = 'T')) # T/MIN:每分 print(pd.date_range('2017/1/1 12:00:00','2017/1/1 12:00:10', freq = 'S')) # S:每秒 print(pd.date_range('2017/1/1 12:00:00','2017/1/1 12:00:10', freq = 'L')) # L:每毫秒(千分之一秒) print(pd.date_range('2017/1/1 12:00:00','2017/1/1 12:00:10', freq = 'U')) # U:每微秒(百万分之一秒) 复制代码
进阶使用如下:
print(pd.date_range('2017/1/1','2017/2/1', freq = 'W-MON')) # W-MON:从指定星期几开始算起,每周 # 星期几缩写:MON/TUE/WED/THU/FRI/SAT/SUN print(pd.date_range('2017/1/1','2017/5/1', freq = 'WOM-2MON')) # WOM-2MON:每月的第几个星期几开始算,这里是每月第二个星期一 复制代码
freq的使用(2) - 多样化生成需要的时间序列
生成指定频率的日历日:
print(pd.date_range('2017','2018', freq = 'M')) print(pd.date_range('2017','2020', freq = 'Q-DEC')) print(pd.date_range('2017','2020', freq = 'A-DEC')) print('------') # M:每月最后一个日历日 # Q-月:指定月为季度末,每个季度末最后一月的最后一个日历日 # A-月:每年指定月份的最后一个日历日 # 月缩写:JAN/FEB/MAR/APR/MAY/JUN/JUL/AUG/SEP/OCT/NOV/DEC # 所以Q-月只有三种情况:1-4-7-10,2-5-8-11,3-6-9-12 复制代码
生成指定频率的工作日:
print(pd.date_range('2017','2018', freq = 'BM')) print(pd.date_range('2017','2020', freq = 'BQ-DEC')) print(pd.date_range('2017','2020', freq = 'BA-DEC')) print('------') # BM:每月最后一个工作日 # BQ-月:指定月为季度末,每个季度末最后一月的最后一个工作日 # BA-月:每年指定月份的最后一个工作日 复制代码
生成指定规律的特殊时间:
print(pd.date_range('2017','2018', freq = 'MS')) print(pd.date_range('2017','2020', freq = 'QS-DEC')) print(pd.date_range('2017','2020', freq = 'AS-DEC')) print('------') # M:每月第一个日历日 # QS-月:指定月为季度末,每个季度末最后一月的第一个日历日 # AS-月:每年指定月份的第一个日历日 print(pd.date_range('2017','2018', freq = 'BMS')) print(pd.date_range('2017','2020', freq = 'BQS-DEC')) print(pd.date_range('2017','2020', freq = 'BAS-DEC')) print('------') # BMS:每月第一个工作日 # BQS-月:指定月为季度末,每个季度末最后一月的第一个工作日 # BAS-月:每年指定月份的第一个工作日 复制代码
freq的使用(3) - 复合频率的使用
生成指定复合频率的时间序列:
print(pd.date_range('2017/1/1','2017/2/1', freq = '7D')) # 7天 print(pd.date_range('2017/1/1','2017/1/2', freq = '2h30min')) # 2小时30分钟 print(pd.date_range('2017','2018', freq = '2M')) # 每间隔2个月的第一个日历日 复制代码
asfreq - 时期频率转换
以天为间隔频率的时间序列如何修改为更小单位间隔的时间序列?
ts = pd.Series(np.random.rand(4), index = pd.date_range('20170101','20170104')) print(ts) print(ts.asfreq('4H',method = 'ffill')) # 改变频率,这里是D改为4H # method:插值模式,None不插值,ffill用之前值填充,bfill用之后值填充 复制代码
如何超前/滞后数据?
下面栗子超前/滞后的数据移动的是数值:
ts = pd.Series(np.random.rand(4), index = pd.date_range('20170101','20170104')) print(ts) print(ts.shift(2)) print(ts.shift(-2)) print('------') # 正数:数值后移(滞后);负数:数值前移(超前) >>> 2017-01-01 0.575076 2017-01-02 0.514981 2017-01-03 0.221506 2017-01-04 0.410396 Freq: D, dtype: float64 2017-01-01 NaN 2017-01-02 NaN 2017-01-03 0.575076 2017-01-04 0.514981 Freq: D, dtype: float64 2017-01-01 0.221506 2017-01-02 0.410396 2017-01-03 NaN 2017-01-04 NaN Freq: D, dtype: float64 复制代码
而加上freq偏移参数则偏移的的是前面的索引时间戳而不是数值:
print(ts.shift(2, freq = 'D')) print(ts.shift(2, freq = 'T')) # 加上freq参数:对时间戳进行位移,而不是对数值进行位移 复制代码
Pandas时期 - Period
pd.Period()创建时期
生成一个以2017-01开始,月为频率的时间构造器:
p = pd.Period('2017', freq = 'M') print(p, type(p)) >>> 2017-01 <class 'pandas._period.Period'> 复制代码
我们可以通过加减整数,将周期整体移动:
p = pd.Period('2017', freq = 'M') print(p, type(p)) print(p + 1) print(p - 2) >>> 2017-02 2016-11 复制代码
pd.period_range()创建时期范围
创建指定时期范围:
prng = pd.period_range('1/1/2011', '1/1/2012', freq='M') print(prng,type(prng)) >>> PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06', '2011-07', '2011-08', '2011-09', '2011-10', '2011-11', '2011-12', '2012-01'], dtype='int64', freq='M') <class 'pandas.tseries.period.PeriodIndex'> 复制代码
结合上面的时期序列,创建时间序列:
ts = pd.Series(np.random.rand(len(prng)), index = prng) print(ts,type(ts)) print(ts.index) >>> 2011-01 0.342571 2011-02 0.826151 2011-03 0.370505 2011-04 0.137151 2011-05 0.679976 2011-06 0.265928 2011-07 0.416502 2011-08 0.874078 2011-09 0.112801 2011-10 0.112504 2011-11 0.448408 2011-12 0.851046 2012-01 0.370605 Freq: M, dtype: float64 <class 'pandas.core.series.Series'> PeriodIndex(['2011-01', '2011-02', '2011-03', '2011-04', '2011-05', '2011-06', '2011-07', '2011-08', '2011-09', '2011-10', '2011-11', '2011-12', '2012-01'], dtype='int64', freq='M') 复制代码
pd.period - asfreq:频率转换
通过.asfreq(freq, method=None, how=None)方法可以将之前生成的频率转换成别的频率
p = pd.Period('2017','A-DEC') print(p) print(p.asfreq('M', how = 'start')) # 也可写 how = 's' print(p.asfreq('D', how = 'end')) # 也可写 how = 'e' >>> 2017 2017-01 2017-12-31 复制代码
asfreq也可以转换TIMESeries的index:
prng = pd.period_range('2017','2018',freq = 'M') ts1 = pd.Series(np.random.rand(len(prng)), index = prng) ts2 = pd.Series(np.random.rand(len(prng)), index = prng.asfreq('D', how = 'start')) print(ts1.head(),len(ts1)) print(ts2.head(),len(ts2)) 复制代码
时间戳与时期之间的转换
使用pd.to_period()、pd.to_timestamp()可以实现时间戳与时期之间的转换。
rng = pd.date_range('2017/1/1', periods = 10, freq = 'M') prng = pd.period_range('2017','2018', freq = 'M') ts1 = pd.Series(np.random.rand(len(rng)), index = rng) print(ts1.head()) print(ts1.to_period().head()) # 每月最后一日,转化为每月 ts2 = pd.Series(np.random.rand(len(prng)), index = prng) print(ts2.head()) print(ts2.to_timestamp().head()) # 每月,转化为每月第一天 >>> 2017-01-31 0.125288 2017-02-28 0.497174 2017-03-31 0.573114 2017-04-30 0.665665 2017-05-31 0.263561 Freq: M, dtype: float64 2017-01 0.125288 2017-02 0.497174 2017-03 0.573114 2017-04 0.665665 2017-05 0.263561 Freq: M, dtype: float64 2017-01 0.748661 2017-02 0.095891 2017-03 0.280341 2017-04 0.569813 2017-05 0.067677 Freq: M, dtype: float64 2017-01-01 0.748661 2017-02-01 0.095891 2017-03-01 0.280341 2017-04-01 0.569813 2017-05-01 0.067677 Freq: MS, dtype: float64 复制代码
时间序列的索引与切片
索引
时间序列的索引方法同样是适用于Dataframe,而且在时间序列中由于按照时间先后排序,故不用考虑顺序问题。
基本位置索引,使用的方法和列表类似:
from datetime import datetime rng = pd.date_range('2017/1','2017/3') ts = pd.Series(np.random.rand(len(rng)), index = rng) print(ts.head()) print(ts[0]) print(ts[:2]) >>> 2017-01-01 0.107736 2017-01-02 0.887981 2017-01-03 0.712862 2017-01-04 0.920021 2017-01-05 0.317863 Freq: D, dtype: float64 0.107735945027 2017-01-01 0.107736 2017-01-02 0.887981 Freq: D, dtype: float64 复制代码
除了基本位置索引之外还有时间序列标签索引:
from datetime import datetime rng = pd.date_range('2017/1','2017/3') ts = pd.Series(np.random.rand(len(rng)), index = rng) print(ts['2017/1/2']) print(ts['20170103']) print(ts['1/10/2017']) print(ts[datetime(2017,1,20)]) >>> 0.887980757812 0.712861778966 0.788336674948 0.93070380011 复制代码
切片
切片的使用操作在上面索引部分的基本位置索引中有提到和Series按照index索引原理一样,也是末端包含。
rng = pd.date_range('2017/1','2017/3',freq = '12H') ts = pd.Series(np.random.rand(len(rng)), index = rng) print(ts['2017/1/5':'2017/1/10']) >>> 2017-01-05 00:00:00 0.462085 2017-01-05 12:00:00 0.778637 2017-01-06 00:00:00 0.356306 2017-01-06 12:00:00 0.667964 2017-01-07 00:00:00 0.246857 2017-01-07 12:00:00 0.386956 2017-01-08 00:00:00 0.328203 2017-01-08 12:00:00 0.260853 2017-01-09 00:00:00 0.224920 2017-01-09 12:00:00 0.397457 2017-01-10 00:00:00 0.158729 2017-01-10 12:00:00 0.501266 Freq: 12H, dtype: float64 # 在这里我们可以传入月份可以直接获取整个月份的切片 print(ts['2017/2'].head()) >>> 2017-02-01 00:00:00 0.243932 2017-02-01 12:00:00 0.220830 2017-02-02 00:00:00 0.896107 2017-02-02 12:00:00 0.476584 2017-02-03 00:00:00 0.515817 Freq: 12H, dtype: float64 复制代码
重复索引的时间序列
dates = pd.DatetimeIndex(['1/1/2015','1/2/2015','1/3/2015','1/4/2015','1/1/2015','1/2/2015']) ts = pd.Series(np.random.rand(6), index = dates) print(ts) # 我们可以通过is_unique检查值或index是否重复 print(ts.is_unique,ts.index.is_unique) >>> 2015-01-01 0.300286 2015-01-02 0.603865 2015-01-03 0.017949 2015-01-04 0.026621 2015-01-01 0.791441 2015-01-02 0.526622 dtype: float64 True False 复制代码
按照上面的结果,可以看出在上面的时间序列中,出现了index(ts.index.is_unique)重复但值(ts.is_unique)不重复的情况。
我们可以通过时间序列把重复索引对应的值取平均值来解决索引重复的问题:
print(ts.groupby(level = 0).mean()) # 通过groupby做分组,重复的值这里用平均值处理 >>> 2015-01-01 0.545863 2015-01-02 0.565244 2015-01-03 0.017949 2015-01-04 0.026621 dtype: float64
作者:咸鱼学Python
链接:https://juejin.cn/post/7035457592860590116