呆鸟云:”这篇是 Pandas 时间序列的第 001 篇,昨天发的《Pandas 时间序列 - 日期时间索引》是第 002 篇。大家阅读时,请注意顺序。"
时间序列纵览时间戳 vs. 时间段转换时间戳提供格式参数用多列组合日期时间无效数据纪元时间戳把时间戳转换为纪元应用 `origin` 参数生成时间戳范围自定义频率范围时间戳的界限
依托 NumPy 的 datetime64、timedelta64 等数据类型,pandas 可以处理各种时间序列数据,还能调用 scikits.timeseries 等 Python 支持库的时间序列功能。
Pandas 支持以下操作:
解析时间格式字符串、np.datetime64、
datetime.datetime 等多种时间序列数据。
In[1]:importdatetime
In[2]:dti=pd.to_datetime(['1/1/',np.datetime64('-01-01'),
...:datetime.datetime(,1,1)])
...:
In[3]:dti
Out[3]:DatetimeIndex(['-01-01','-01-01','-01-01'],dtype='datetime64[ns]',freq=None)
生成 DatetimeIndex、TimedeltaIndex、
PeriodIndex 等定频日期与时间段序列。
In[4]:dti=pd.date_range('-01-01',periods=3,freq='H')
In[5]:dti
Out[5]:
DatetimeIndex(['-01-0100:00:00','-01-0101:00:00',
'-01-0102:00:00'],
dtype='datetime64[ns]',freq='H')
处理、转换带时区的日期时间数据。
In[6]:dti=dti.tz_localize('UTC')
In[7]:dti
Out[7]:
DatetimeIndex(['-01-0100:00:00+00:00','-01-0101:00:00+00:00',
'-01-0102:00:00+00:00'],
dtype='datetime64[ns,UTC]',freq='H')
In[8]:dti.tz_convert('US/Pacific')
Out[8]:
DatetimeIndex(['-12-3116:00:00-08:00','-12-3117:00:00-08:00',
'-12-3118:00:00-08:00'],
dtype='datetime64[ns,US/Pacific]',freq='H')
按指定频率重采样,并转换为时间序列。
In[9]:idx=pd.date_range('-01-01',periods=5,freq='H')
In[10]:ts=pd.Series(range(len(idx)),index=idx)
In[11]:ts
Out[11]:
-01-0100:00:000
-01-0101:00:001
-01-0102:00:002
-01-0103:00:003
-01-0104:00:004
Freq:H,dtype:int64
In[12]:ts.resample('2H').mean()
Out[12]:
-01-0100:00:000.5
-01-0102:00:002.5
-01-0104:00:004.0
Freq:2H,dtype:float64
用绝对或相对时间差计算日期与时间。
In[13]:friday=pd.Timestamp('-01-05')
In[14]:friday.day_name()
Out[14]:'Friday'
#添加1个日历日
In[15]:saturday=friday+pd.Timedelta('1day')
In[16]:saturday.day_name()
Out[16]:'Saturday'
#添加1个工作日,从星期五跳到星期一
In[17]:monday=friday+pd.offsets.BDay()
In[18]:monday.day_name()
Out[18]:'Monday'
Pandas 提供了一组精悍、实用的工具集以完成上述操作。
时间序列纵览
Pandas 支持 4 种常见时间概念:
日期时间(Datetime):带时区的日期时间,类似于标准库的 datetime.datetime 。
时间差(Timedelta):绝对时间周期,类似于标准库的 datetime.timedelta。
时间段(Timespan):在某一时点以指定频率定义的时间跨度。
日期偏移(Dateoffset):与日历运算对应的时间段,类似dateutil 的 dateutil.relativedelta.relativedelta。
一般情况下,时间序列主要是 Series 或 DataFrame 的时间型索引,可以用时间元素进行操控。
In[19]:pd.Series(range(3),index=pd.date_range('2000',freq='D',periods=3))
Out[19]:
2000-01-010
2000-01-021
2000-01-032
Freq:D,dtype:int64
当然,Series 与 DataFrame 也可以直接把时间序列当成数据。
In[20]:pd.Series(pd.date_range('2000',freq='D',periods=3))
Out[20]:
02000-01-01
12000-01-02
22000-01-03
dtype:datetime64[ns]
Series 与 DataFrame 提供了 datetime、timedelta 、Period 扩展类型与专有用法,不过,Dateoffset 则保存为 object。
In[21]:pd.Series(pd.period_range('1/1/',freq='M',periods=3))
Out[21]:
0-01
1-02
2-03
dtype:period[M]
In[22]:pd.Series([pd.DateOffset(1),pd.DateOffset(2)])
Out[22]:
0<DateOffset>
1<2*DateOffsets>
dtype:object
In[23]:pd.Series(pd.date_range('1/1/',freq='M',periods=3))
Out[23]:
0-01-31
1-02-28
2-03-31
dtype:datetime64[ns]
Pandas 用 NaT 表示日期时间、时间差及时间段的空值,代表了缺失日期或空日期的值,类似于浮点数的 np.nan。
In[24]:pd.Timestamp(pd.NaT)
Out[24]:NaT
In[25]:pd.Timedelta(pd.NaT)
Out[25]:NaT
In[26]:pd.Period(pd.NaT)
Out[26]:NaT
#与np.nan一样,pd.NaT不等于pd.NaT
In[27]:pd.NaT==pd.NaT
Out[27]:False
时间戳 vs. 时间段
时间戳是最基本的时间序列数据,用于把数值与时点关联在一起。Pandas 对象通过时间戳调用时点数据。
In[28]:pd.Timestamp(datetime.datetime(,5,1))
Out[28]:Timestamp('-05-0100:00:00')
In[29]:pd.Timestamp('-05-01')
Out[29]:Timestamp('-05-0100:00:00')
In[30]:pd.Timestamp(,5,1)
Out[30]:Timestamp('-05-0100:00:00')
不过,大多数情况下,用时间段改变变量更自然。Period 表示的时间段更直观,还可以用日期时间格式的字符串进行推断。
示例如下:
In[31]:pd.Period('-01')
Out[31]:Period('-01','M')
In[32]:pd.Period('-05',freq='D')
Out[32]:Period('-05-01','D')
Timestamp 与 Period 可以用作索引。作为索引的 Timestamp 与 Period 列表则被强制转换为对应的 DatetimeIndex 与 PeriodIndex。
In[33]:dates=[pd.Timestamp('-05-01'),
....:pd.Timestamp('-05-02'),
....:pd.Timestamp('-05-03')]
....:
In[34]:ts=pd.Series(np.random.randn(3),dates)
In[35]:type(ts.index)
Out[35]:pandas.core.indexes.datetimes.DatetimeIndex
In[36]:ts.index
Out[36]:DatetimeIndex(['-05-01','-05-02','-05-03'],dtype='datetime64[ns]',freq=None)
In[37]:ts
Out[37]:
-05-010.469112
-05-02-0.282863
-05-03-1.509059
dtype:float64
In[38]:periods=[pd.Period('-01'),pd.Period('-02'),pd.Period('-03')]
In[39]:ts=pd.Series(np.random.randn(3),periods)
In[40]:type(ts.index)
Out[40]:pandas.core.indexes.period.PeriodIndex
In[41]:ts.index
Out[41]:PeriodIndex(['-01','-02','-03'],dtype='period[M]',freq='M')
In[42]:ts
Out[42]:
-01-1.135632
-021.212112
-03-0.173215
Freq:M,dtype:float64
Pandas 可以识别这两种表现形式,并在两者之间进行转化。Pandas 后台用 Timestamp 实例代表时间戳,用 DatetimeIndex 实例代表时间戳序列。pandas 用 Period 对象表示符合规律的时间段标量值,用 PeriodIndex 表示时间段序列。未来版本将支持用任意起止时间实现不规律时间间隔。
转换时间戳
to_datetime 函数用于转换字符串、纪元式及混合的日期 Series 或日期列表。转换的是 Series 时,返回的是具有相同的索引的 Series,日期时间列表则会被转换为 DatetimeIndex:
In[43]:pd.to_datetime(pd.Series(['Jul31,','-01-10',None]))
Out[43]:
0-07-31
1-01-10
2NaT
dtype:datetime64[ns]
In[44]:pd.to_datetime(['/11/23','.12.31'])
Out[44]:DatetimeIndex(['-11-23','-12-31'],dtype='datetime64[ns]',freq=None)
解析欧式日期(日-月-年),要用 dayfirst 关键字参数:
In[45]:pd.to_datetime(['04-01-10:00'],dayfirst=True)
Out[45]:DatetimeIndex(['-01-0410:00:00'],dtype='datetime64[ns]',freq=None)
In[46]:pd.to_datetime(['14-01-','01-14-'],dayfirst=True)
Out[46]:DatetimeIndex(['-01-14','-01-14'],dtype='datetime64[ns]',freq=None)
警告:从上例可以看出,dayfirst 并没有那么严苛,如果不能把第一个数解析为日,就会以 dayfirst 为 False 进行解析。
to_datetime 转换单个字符串时,返回的是单个 Timestamp。Timestamp 仅支持字符串输入,不支持 dayfirst、format 等字符串解析选项,如果要使用这些选项,就要用 to_datetime。
In[47]:pd.to_datetime('/11/12')
Out[47]:Timestamp('-11-1200:00:00')
In[48]:pd.Timestamp('/11/12')
Out[48]:Timestamp('-11-1200:00:00')
Pandas 还支持直接使用 DatetimeIndex 构建器:
In[49]:pd.DatetimeIndex(['-01-01','-01-03','-01-05'])
Out[49]:DatetimeIndex(['-01-01','-01-03','-01-05'],dtype='datetime64[ns]',freq=None)
创建 DatetimeIndex 时,传递字符串 infer 即可推断索引的频率。
In[50]:pd.DatetimeIndex(['-01-01','-01-03','-01-05'],freq='infer')
Out[50]:DatetimeIndex(['-01-01','-01-03','-01-05'],dtype='datetime64[ns]',freq='2D')
提供格式参数
要实现精准转换,除了传递 datetime 字符串,还要指定 format 参数,指定此参数还可以加速转换速度。
In[51]:pd.to_datetime('/11/12',format='%Y/%m/%d')
Out[51]:Timestamp('-11-1200:00:00')
In[52]:pd.to_datetime('12-11-00:00',format='%d-%m-%Y%H:%M')
Out[52]:Timestamp('-11-1200:00:00')
要了解更多 format 选项,请参阅 Python 日期时间文档。
用多列组合日期时间
0.18.1 版新增。
Pandas 还可以把 DataFrame 里的整数或字符串列组合成 Timestamp Series。
In[53]:df=pd.DataFrame({'year':[,],
....:'month':[2,3],
....:'day':[4,5],
....:'hour':[2,3]})
....:
In[54]:pd.to_datetime(df)
Out[54]:
0-02-0402:00:00
1-03-0503:00:00
dtype:datetime64[ns]
只传递组合所需的列也可以。
In[55]:pd.to_datetime(df[['year','month','day']])
Out[55]:
0-02-04
1-03-05
dtype:datetime64[ns]
pd.to_datetime 查找列名里日期时间组件的标准名称,包括:
必填:year、month、day
可选:
hour、minute、second、
millisecond、microsecond、
nanosecond
无效数据
不可解析时,默认值 errors='raise' 会触发错误:
In[2]:pd.to_datetime(['/07/31','asd'],errors='raise')
ValueError:Unknownstringformat
errors='ignore' 返回原始输入:
In[56]:pd.to_datetime(['/07/31','asd'],errors='ignore')
Out[56]:Index(['/07/31','asd'],dtype='object')
errors='coerce' 把无法解析的数据转换为 NaT,即不是时间(Not a Time):
In[57]:pd.to_datetime(['/07/31','asd'],errors='coerce')
Out[57]:DatetimeIndex(['-07-31','NaT'],dtype='datetime64[ns]',freq=None)
纪元时间戳
pandas 支持把整数或浮点数纪元时间转换为 Timestamp 与 DatetimeIndex。鉴于 Timestamp 对象内部存储方式,这种转换的默认单位是纳秒。不过,一般都会用指定其它时间单位 unit 来存储纪元数据,纪元时间从 origin 参数指定的时点开始计算。
In[58]:pd.to_datetime([134975,1349806505,1349892905,
....:1349979305,1350065705],unit='s')
....:
Out[58]:
DatetimeIndex(['-10-0818:15:05','-10-0918:15:05',
'-10-1018:15:05','-10-1118:15:05',
'-10-1218:15:05'],
dtype='datetime64[ns]',freq=None)
In[59]:pd.to_datetime([134975100,134975200,134975300,
....:134975400,134975500],unit='ms')
....:
Out[59]:
DatetimeIndex(['-10-0818:15:05.100000','-10-0818:15:05.200000',
'-10-0818:15:05.300000','-10-0818:15:05.400000',
'-10-0818:15:05.500000'],
dtype='datetime64[ns]',freq=None)
用带 tz 参数的纪元时间戳创建 Timestamp 或 DatetimeIndex 时,要先把纪元时间戳转化为 UTC,然后再把结果转换为指定时区。不过这种操作方式现在已经废弃了,对于其它时区 Wall Time 里的纪元时间戳,建议先把纪元时间戳转换为无时区时间戳,然后再把时区本地化。
In[60]:pd.Timestamp(1262347200000000000).tz_localize('US/Pacific')
Out[60]:Timestamp('-01-0112:00:00-0800',tz='US/Pacific')
In[61]:pd.DatetimeIndex([1262347200000000000]).tz_localize('US/Pacific')
Out[61]:DatetimeIndex(['-01-0112:00:00-08:00'],dtype='datetime64[ns,US/Pacific]',freq=None)
注意:纪元时间取整到最近的纳秒。
警告:
Python 浮点数只精确到 15 位小数,因此,转换浮点纪元时间可能会导致不精准或失控的结果。转换过程中,免不了会对高精度 Timestamp 取整,只有用 int64 等定宽类型才有可能实现极其精准的效果。
In[62]:pd.to_datetime([1490195805.433,1490195805.433502912],unit='s')
Out[62]:DatetimeIndex(['-03-2215:16:45.433000088','-03-22>15:16:45.433502913'],dtype='datetime64[ns]',freq=None)
In[63]:pd.to_datetime(1490195805433502912,unit='ns')
Out[63]:Timestamp('-03-2215:16:45.433502912')
参阅:应用 origin 参数
把时间戳转换为纪元
反转上述操作,把 Timestamp 转换为 unix 纪元:
In[64]:stamps=pd.date_range('-10-0818:15:05',periods=4,freq='D')
In[65]:stamps
Out[65]:
DatetimeIndex(['-10-0818:15:05','-10-0918:15:05',
'-10-1018:15:05','-10-1118:15:05'],
dtype='datetime64[ns]',freq='D')
首先与纪元开始时点(1970 年 1 月 1 日午夜,UTC)相减,然后以 1 秒为时间单位(unit='1s')取底整除。
In[66]:(stamps-pd.Timestamp("1970-01-01"))//pd.Timedelta('1s')
Out[66]:Int64Index([134975,1349806505,1349892905,1349979305],dtype='int64')
应用 `origin` 参数
0.20.0 版新增。
origin 参数可以指定 DatetimeIndex 的备选开始时点。例如,把1960-01-01 作为开始日期:
In[67]:pd.to_datetime([1,2,3],unit='D',origin=pd.Timestamp('1960-01-01'))
Out[67]:DatetimeIndex(['1960-01-02','1960-01-03','1960-01-04'],dtype='datetime64[ns]',freq=None)
默认值为 origin='unix',即 1970-01-01 00:00:00,一般把这个时点称为 unix 纪元 或 POSIX 时间。
In[68]:pd.to_datetime([1,2,3],unit='D')
Out[68]:DatetimeIndex(['1970-01-02','1970-01-03','1970-01-04'],dtype='datetime64[ns]',freq=None)
生成时间戳范围
DatetimeIndex、Index 构建器可以生成时间戳索引,此处要提供 datetime 对象列表。
In[69]:dates=[datetime.datetime(,5,1),
....:datetime.datetime(,5,2),
....:datetime.datetime(,5,3)]
....:
#注意频率信息
In[70]:index=pd.DatetimeIndex(dates)
In[71]:index
Out[71]:DatetimeIndex(['-05-01','-05-02','-05-03'],dtype='datetime64[ns]',freq=None)
#自动转换为DatetimeIndex
In[72]:index=pd.Index(dates)
In[73]:index
Out[73]:DatetimeIndex(['-05-01','-05-02','-05-03'],dtype='datetime64[ns]',freq=None)
实际工作中,经常要生成含大量时间戳的超长索引,一个个输入时间戳又枯燥,又低效。如果时间戳是定频的,用 date_range() 与 bdate_range() 函数即可创建 DatetimeIndex。date_range 默认的频率是日历日,bdate_range 的默认频率是工作日:
In[74]:start=datetime.datetime(,1,1)
In[75]:end=datetime.datetime(,1,1)
In[76]:index=pd.date_range(start,end)
In[77]:index
Out[77]:
DatetimeIndex(['-01-01','-01-02','-01-03','-01-04',
'-01-05','-01-06','-01-07','-01-08',
'-01-09','-01-10',
...
'-12-23','-12-24','-12-25','-12-26',
'-12-27','-12-28','-12-29','-12-30',
'-12-31','-01-01'],
dtype='datetime64[ns]',length=366,freq='D')
In[78]:index=pd.bdate_range(start,end)
In[79]:index
Out[79]:
DatetimeIndex(['-01-03','-01-04','-01-05','-01-06',
'-01-07','-01-10','-01-11','-01-12',
'-01-13','-01-14',
...
'-12-19','-12-20','-12-21','-12-22',
'-12-23','-12-26','-12-27','-12-28',
'-12-29','-12-30'],
dtype='datetime64[ns]',length=260,freq='B')
date_range、bdate_range 等便捷函数可以调用各种频率别名:
In[80]:pd.date_range(start,periods=1000,freq='M')
Out[80]:
DatetimeIndex(['-01-31','-02-28','-03-31','-04-30',
'-05-31','-06-30','-07-31','-08-31',
'-09-30','-10-31',
...
'2093-07-31','2093-08-31','2093-09-30','2093-10-31',
'2093-11-30','2093-12-31','2094-01-31','2094-02-28',
'2094-03-31','2094-04-30'],
dtype='datetime64[ns]',length=1000,freq='M')
In[81]:pd.bdate_range(start,periods=250,freq='BQS')
Out[81]:
DatetimeIndex(['-01-03','-04-01','-07-01','-10-03',
'-01-02','-04-02','-07-02','-10-01',
'-01-01','-04-01',
...
'2071-01-01','2071-04-01','2071-07-01','2071-10-01',
'2072-01-01','2072-04-01','2072-07-01','2072-10-03',
'2073-01-02','2073-04-03'],
dtype='datetime64[ns]',length=250,freq='BQS-JAN')
date_range 与 bdate_range 通过指定 start、end、period 与 freq 等参数,简化了生成日期范围这项工作。开始与结束日期是必填项,因此,不会生成指定范围之外的日期。
In[82]:pd.date_range(start,end,freq='BM')
Out[82]:
DatetimeIndex(['-01-31','-02-28','-03-31','-04-29',
'-05-31','-06-30','-07-29','-08-31',
'-09-30','-10-31','-11-30','-12-30'],
dtype='datetime64[ns]',freq='BM')
In[83]:pd.date_range(start,end,freq='W')
Out[83]:
DatetimeIndex(['-01-02','-01-09','-01-16','-01-23',
'-01-30','-02-06','-02-13','-02-20',
'-02-27','-03-06','-03-13','-03-20',
'-03-27','-04-03','-04-10','-04-17',
'-04-24','-05-01','-05-08','-05-15',
'-05-22','-05-29','-06-05','-06-12',
'-06-19','-06-26','-07-03','-07-10',
'-07-17','-07-24','-07-31','-08-07',
'-08-14','-08-21','-08-28','-09-04',
'-09-11','-09-18','-09-25','-10-02',
'-10-09','-10-16','-10-23','-10-30',
'-11-06','-11-13','-11-20','-11-27',
'-12-04','-12-11','-12-18','-12-25',
'-01-01'],
dtype='datetime64[ns]',freq='W-SUN')
In[84]:pd.bdate_range(end=end,periods=20)
Out[84]:
DatetimeIndex(['-12-05','-12-06','-12-07','-12-08',
'-12-09','-12-12','-12-13','-12-14',
'-12-15','-12-16','-12-19','-12-20',
'-12-21','-12-22','-12-23','-12-26',
'-12-27','-12-28','-12-29','-12-30'],
dtype='datetime64[ns]',freq='B')
In[85]:pd.bdate_range(start=start,periods=20)
Out[85]:
DatetimeIndex(['-01-03','-01-04','-01-05','-01-06',
'-01-07','-01-10','-01-11','-01-12',
'-01-13','-01-14','-01-17','-01-18',
'-01-19','-01-20','-01-21','-01-24',
'-01-25','-01-26','-01-27','-01-28'],
dtype='datetime64[ns]',freq='B')
0.23.0 版新增。
指定 start、end、periods 即可生成从 start 开始至 end 结束的等距日期范围,这个日期范围包含了 start 与 end,生成的 DatetimeIndex 里的元素数量为 periods 的值。
In[86]:pd.date_range('-01-01','-01-05',periods=5)
Out[86]:
DatetimeIndex(['-01-01','-01-02','-01-03','-01-04',
'-01-05'],
dtype='datetime64[ns]',freq=None)
In[87]:pd.date_range('-01-01','-01-05',periods=10)
Out[87]:
DatetimeIndex(['-01-0100:00:00','-01-0110:40:00',
'-01-0121:20:00','-01-0208:00:00',
'-01-0218:40:00','-01-0305:20:00',
'-01-0316:00:00','-01-0402:40:00',
'-01-0413:20:00','-01-0500:00:00'],
dtype='datetime64[ns]',freq=None)
自定义频率范围
设定 weekmask 与 holidays 参数,bdate_range 还可以生成自定义频率日期范围。这些参数只用于传递自定义字符串。
In[88]:weekmask='MonWedFri'
In[89]:holidays=[datetime.datetime(,1,5),datetime.datetime(,3,14)]
In[90]:pd.bdate_range(start,end,freq='C',weekmask=weekmask,holidays=holidays)
Out[90]:
DatetimeIndex(['-01-03','-01-07','-01-10','-01-12',
'-01-14','-01-17','-01-19','-01-21',
'-01-24','-01-26',
...
'-12-09','-12-12','-12-14','-12-16',
'-12-19','-12-21','-12-23','-12-26',
'-12-28','-12-30'],
dtype='datetime64[ns]',length=154,freq='C')
In[91]:pd.bdate_range(start,end,freq='CBMS',weekmask=weekmask)
Out[91]:
DatetimeIndex(['-01-03','-02-02','-03-02','-04-01',
'-05-02','-06-01','-07-01','-08-01',
'-09-02','-10-03','-11-02','-12-02'],
dtype='datetime64[ns]',freq='CBMS')
参阅:自定义工作日
时间戳的界限
Pandas 时间戳的最低单位为纳秒,64 位整数显示的时间跨度约为 584 年,这就是 Timestamp 的界限:
In[92]:pd.Timestamp.min
Out[92]:Timestamp('1677-09-2100:12:43.145225')
In[93]:pd.Timestamp.max
Out[93]:Timestamp('2262-04-1123:47:16.854775807')
参阅:时间段越界展示
如果觉得《Pandas 时间序列1 - 纵览与时间戳》对你有帮助,请点赞、收藏,并留下你的观点哦!