一.日期和时间数据类型
1.相关模块:
参见 Python.内置模块.时间 部分
2.时间戳的Null值:
Pandas中用pd.NaT(Not a Time)表示时间戳数据的Null值:
>>> pd.NaT
NaT
>>> pd.to_datetime(['2011-07-06 12:00:00','2011-08-06 00:00:00',None,pd.NaT,"NaT",np.nan,""])
DatetimeIndex(['2011-07-06 12:00:00', '2011-08-06 00:00:00', 'NaT', 'NaT',
'NaT', 'NaT', 'NaT'],
dtype='datetime64[ns]', freq=None)
二.时间戳及以时间戳为索引的时间序列
1.时间戳
(1)概念:
参见 Python.内置模块.时间.一.1 部分
(2)创建:
pd.Timestamp([ts_input=None,freq=None,tz=None,unit=None,year=None,month=None,day=None,hour=0,minute=0,second=0,microsecond=0,nanosecond=0,tzinfo=None,fold=None])
#参数说明:
ts_input:指定时间点;为datetime-like/str/num
freq:指定频率;为str/DateOffset
tz:指定时区;为str/pytz.timezone/dateutil.tz.tzfile/None
year,month,day,hour,minute,second,microsecond,nanosecond:分别指定年/月/日/时/分/秒/毫秒/纳秒;为int
#实例:
>>> pd.Timestamp(ts_input="2001/01/01")
Timestamp('2001-01-01 00:00:00')
(3)函数:
转换为时期:<t>.to_period([freq=None,copy=True])
#参数说明:
t:指定时间戳;为pandas._libs.tslibs.timestamps.Timestamp
freq:指定频率;为str,默认为<t>的频率
#实例:
>>> pd.date_range('2000-01-01',periods=3,freq='M').to_period()
PeriodIndex(['2000-01', '2000-02', '2000-03'], dtype='period[M]', freq='M')
>>> pd.date_range('2000-01-01',periods=3,freq='M').to_period("D")
PeriodIndex(['2000-01-31', '2000-02-29', '2000-03-31'], dtype='period[D]', freq='D')
######################################################################################################################
格式化日期时间:<t>.apply(lambda x:datetime.datetime.strftime(x,"<format>"))
#参数说明:
format:指定输出格式;为str
#实例:
>>> import datetime
>>> dts=pd.Series(["2020-02-22 14:01:23.451000","2020-02-20 16:40:56.190000"])
>>> dtst=pd.to_datetime(dts)
>>> dtst
0 2020-02-22 14:01:23.451
1 2020-02-20 16:40:56.190
dtype: datetime64[ns]
>>> dtst.apply(lambda x:datetime.datetime.strftime(x,"%Y-%m-%d"))
0 2020-02-22
1 2020-02-20
dtype: object
######################################################################################################################
获取秒:<t>.dt.second
获取分:<t>.dt.minute
获取时:<t>.dt.hour
获取日:<t>.dt.day
获取星期几:<t>.dt.weekday
获取月:<t>.dt.month
获取年:<t>.dt.year
#实例:接上
>>> dtst.dt.second
0 23
1 56
dtype: int64
>>> dtst.dt.minute
0 1
1 40
dtype: int64
>>> dtst.dt.hour
0 14
1 16
dtype: int64
>>> dtst.dt.day
0 22
1 20
dtype: int64
>>> dtst.dt.weekday
0 5
1 3
dtype: int64
>>> dtst.dt.month
0 2
1 2
dtype: int64
>>> dtst.dt.year
0 2020
1 2020
dtype: int64
2.时间序列
(1)概念:
"时间序列"(Time Series)是1种重要的结构化数据形式.在多个时间点观测到的任何数据都可以形成一段时间序列.最简单常用的时间序列是以时间
戳为索引的Series/DataFrame.下面以Series为例,DataFrame与之相同
(2)创建:
这样的Series的index为DatetimeIndex,index中的每个元素均为Timestamp:
>>> dates=[datetime.datetime(2011,1,2),datetime.datetime(2011,1,5),datetime.datetime(2011,1,7),datetime.datetime(2011,1,8),datetime.datetime(2011,1,10),datetime.datetime(2011,1,12)]
>>> ts=pd.Series(np.random.randn(6),index=dates)
>>> ts
2011-01-02 -0.547157
2011-01-05 0.097721
2011-01-07 -0.535814
2011-01-08 0.294756
2011-01-10 -0.276985
2011-01-12 0.212698
dtype: float64
>>> ts.index
DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07', '2011-01-08',
'2011-01-10', '2011-01-12'],
dtype='datetime64[ns]', freq=None)
>>> ts.index.dtype
dtype('<M8[ns]')
>>> ts.index[0]
Timestamp('2011-01-02 00:00:00')
(3)使用:
可以使用时间戳str/datetime.datetime/int进行索引/切片:
>>> ts[0]
-0.5471567700646004
>>> ts[ts.index[0]]
-0.5471567700646004
>>> ts["2011-01-02"]
-0.5471567700646004
>>> ts["20110102"]
-0.5471567700646004
>>> ts["20110103":]#时间戳不需要存在于该时间序列中
2011-01-05 0.097721
2011-01-07 -0.535814
2011-01-08 0.294756
2011-01-10 -0.276985
2011-01-12 0.212698
dtype: float64
>>> ts["2011/01/02"]
-0.5471567700646004
>>> ts["01/02.2011"]
-0.5471567700646004
>>> ts["01/02/2011"]
-0.5471567700646004
>>> ts["01.02.2011"]
-0.5471567700646004
>>> ts[datetime.datetime(2011,1,2)]
-0.5471567700646004
还可以用只指定年或年月的字符串进行索引/切片
>>> ts["2011"]
2011-01-02 -0.547157
2011-01-05 0.097721
2011-01-07 -0.535814
2011-01-08 0.294756
2011-01-10 -0.276985
2011-01-12 0.212698
dtype: float64
>>> ts["2011-01"]
2011-01-02 -0.547157
2011-01-05 0.097721
2011-01-07 -0.535814
2011-01-08 0.294756
2011-01-10 -0.276985
2011-01-12 0.212698
dtype: float64
>>> ts["2011":]
2011-01-02 -0.547157
2011-01-05 0.097721
2011-01-07 -0.535814
2011-01-08 0.294756
2011-01-10 -0.276985
2011-01-12 0.212698
dtype: float64
通过切片产生的是视图,因此对通过切片得到的Series的修改也会反映在原Series上:
>>> ts2=ts["2011/01/10":]
>>> ts2[0]=1111111111111111111111
>>> ts
2011-01-02 -5.471568e-01
2011-01-05 9.772118e-02
2011-01-07 -5.358138e-01
2011-01-08 2.947563e-01
2011-01-10 1.111111e+21
2011-01-12 2.126976e-01
dtype: float64
如果2个时间序列的时区不同,则合并的结果是UTC:
>>> ts1=ts.tz_localize('Europe/London')
>>> ts2=ts1.tz_convert('Europe/Moscow')
>>> (ts1+ts2).index
DatetimeIndex(['2011-01-02 00:00:00+00:00', '2011-01-05 00:00:00+00:00',
'2011-01-07 00:00:00+00:00', '2011-01-08 00:00:00+00:00',
'2011-01-10 00:00:00+00:00', '2011-01-12 00:00:00+00:00'],
dtype='datetime64[ns, UTC]', freq=None)
(4)带有重复索引的时间序列:
>>> dates=pd.DatetimeIndex(['1/1/2000','1/2/2000','1/2/2000','1/2/2000','1/3/2000'])
>>> ts=pd.Series(np.arange(5),index=dates)
>>> ts.index.is_unique
False
索引时可能得到标量值或Series:
>>> ts["20000102"]
2000-01-02 1
2000-01-02 2
2000-01-02 3
dtype: int32
>>> ts["20000103"]
4
可以通过<S>.groupby()进行聚合:
>>> ts=ts.groupby(level=0)
>>> ts.mean()
2000-01-01 0
2000-01-02 2
2000-01-03 4
dtype: int32
(5)填充缺失值:
通过ffill插值来填充缺失值:<ts>.ffill([axis=None,inplace=False,limit=None,downcast=None])
#实例:
>>> ts=pd.Series(np.random.randn(5),index=pd.date_range('1/1/2000',periods=5,freq='M'))
>>> ts[1:4]=None
>>> ts.ffill()
2000-01-31 1.171863
2000-02-29 1.171863
2000-03-31 1.171863
2000-04-30 1.171863
2000-05-31 0.502951
Freq: M, dtype: float64
>>> ts.ffill(limit=2)
2000-01-31 1.171863
2000-02-29 1.171863
2000-03-31 1.171863
2000-04-30 NaN
2000-05-31 0.502951
Freq: M, dtype: float64
三.日期的频率及移动
1.固定频率的时间序列:
Pandas中的原生时间序列是不规则的,或者说没有固定的频率.但实际业务中常需要按固定的频率进行分析,而这会在时间序列中引入缺失值.因此需要
将原生时间序列转换为具有固定频率的时间序列.通过<ts>.resample()来实现(详情参见 5 部分):
>>> ts=pd.Series(np.random.randn(1000),index=pd.date_range('1/1/2000',periods=1000))
>>> ts
2000-01-01 0.014314
2000-01-02 -0.795310
2000-01-03 2.187410
2000-01-04 0.333970
2000-01-05 0.860134
...
2002-09-22 1.099824
2002-09-23 1.134697
2002-09-24 0.965782
2002-09-25 -1.523675
2002-09-26 -1.877816
Freq: D, Length: 1000, dtype: float64
>>> tsr=ts.resample('D')#转换成频率为1/day的时间序列
>>> i=0
>>> for j in tsr:
... if not i:
... print(j)
... i+=1
...
(Timestamp('2000-01-01 00:00:00', freq='D'), 2000-01-01 0.014314
Freq: D, dtype: float64)
2.基础频率:
Pandas中的频率由1个基础频率(Base Frequency)和1个乘数组成.基础频率通常用1个不区分大小写的str别名表示,如"M"表示每月/"H"表示每小
时.每个基础频率都与1个称为"日期偏移量"(Date Offset)的对象相对应,如"H"型频率可用pandas.tseries.offsets.Hour对象表示:
>>> from pandas.tseries.offsets import Hour,Minute,MonthEnd
>>> import datetime
>>> hour=Hour()
>>> hour
<Hour>
在创建日期偏移量对象时传入1个int即可定义乘数:
>>> four_hours=Hour(4)
>>> four_hours
<4 * Hours>
一般来说无需显式创建这样的对象,只需使用"H"/"4H"这样的别名即可.在基础频率的别名前加上1个int即可定义乘数:
>>> pd.date_range('2000-01-01','2000-01-03 23:59',freq='4H')
DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 04:00:00',
'2000-01-01 08:00:00', '2000-01-01 12:00:00',
'2000-01-01 16:00:00', '2000-01-01 20:00:00',
'2000-01-02 00:00:00', '2000-01-02 04:00:00',
'2000-01-02 08:00:00', '2000-01-02 12:00:00',
'2000-01-02 16:00:00', '2000-01-02 20:00:00',
'2000-01-03 00:00:00', '2000-01-03 04:00:00',
'2000-01-03 08:00:00', '2000-01-03 12:00:00',
'2000-01-03 16:00:00', '2000-01-03 20:00:00'],
dtype='datetime64[ns]', freq='4H')
也可以使用不同的别名和乘数进行组合:
>>> pd.date_range('2000-01-01','2000-01-01 11:00',freq='2h10t')
DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 02:10:00',
'2000-01-01 04:20:00', '2000-01-01 06:30:00',
'2000-01-01 08:40:00', '2000-01-01 10:50:00'],
dtype='datetime64[ns]', freq='130T')
日期偏移量对象可进行加法运算,也可和时间戳对象/日期时间对象进行加法:
>>> Hour(2)+Minute(30)
<150 * Minutes>
>>> offset=MonthEnd()
>>> now=datetime.datetime(2021,1,30)
>>> offset+now
Timestamp('2021-01-31 00:00:00')
时间戳对象/日期时间对象可减去日期偏移量对象:
>>> now-offset
Timestamp('2020-12-31 00:00:00')
>>> (now-offset)-offset
Timestamp('2020-11-30 00:00:00')
使用日期偏移量对象的rollback()/rollforward()方法可控制日期滚动的方向:
>>> offset.rollback(now)#向前滚动到每月的最后1天
Timestamp('2020-12-31 00:00:00')
>>> offset.rollforward(now)#向后滚动到每月的最后1天
Timestamp('2021-01-31 00:00:00')
有些基础频率对象所描述的时间点并不是均匀分隔的,称为"锚点偏移量"(Anchored Offset),如"M"/"BM"
3.WOM日期:
"WOM日期"(Week Of Month)是1种非常实用的频率类.它以WOM开头,使用户能获得诸如"每月第3个星期五"这样的日期:
>>> w=pd.date_range('2012-01-01','2012-09-01',freq='WOM-3FRI')
>>> list(w)
[Timestamp('2012-01-20 00:00:00', freq='WOM-3FRI'), Timestamp('2012-02-17 00:00:00', freq='WOM-3FRI'), Timestamp('2012-03-16 00:00:00', freq='WOM-3FRI'), Timestamp('2012-04-20 00:00:00', freq='WOM-3FRI'), Timestamp('2012-05-18 00:00:00', freq='WOM-3FRI'), Timestamp('2012-06-15 00:00:00', freq='WOM-3FRI'), Timestamp('2012-07-20 00:00:00', freq='WOM-3FRI'), Timestamp('2012-08-17 00:00:00', freq='WOM-3FRI')]
4.日期的移动:
将数据沿时间轴移动<ts>.shift([periods=1,freq=None,axis=0,fill_value=None])
#参数说明:
periods:指定移动几个周期;为int
#周期由freq决定,默认移动到其他索引处
freq:指定数据的频率;为str/DateOffset/tseries.offsets/timedelta
#指定频率后可以连同索引一起移动,而不是只移动数据
#实例:
>>> ts=pd.Series(np.random.randn(4),index=pd.date_range('1/1/2000',periods=4,freq='M'))
>>> ts
2000-01-31 -0.657145
2000-02-29 0.213665
2000-03-31 0.888562
2000-04-30 0.323581
Freq: M, dtype: float64
>>> ts.shift()
2000-01-31 NaN
2000-02-29 -0.657145
2000-03-31 0.213665
2000-04-30 0.888562
Freq: M, dtype: float64
>>> ts.shift(-1,fill_value=9999)
2000-01-31 0.213665
2000-02-29 0.888562
2000-03-31 0.323581
2000-04-30 9999.000000
Freq: M, dtype: float64
>>> ts.shift(1,freq="M")
2000-02-29 -0.657145
2000-03-31 0.213665
2000-04-30 0.888562
2000-05-31 0.323581
Freq: M, dtype: float64
>>> ts.shift(1,freq="D")
2000-02-01 -0.657145
2000-03-01 0.213665
2000-04-01 0.888562
2000-05-01 0.323581
dtype: float64
5.重采样
(1)概念:
"重采样"(Resampling)是指将时间序列从1个频率转换到另1个频率的过程.将高频数据聚合到低频称为"降采样"(Downsampling),而将低频数据转
换到高频则称为"升采样"(Upsampling).并非所有重采样都能被划分到这2类中,例如将W-WED转换为W-FRI既非降采样也非升采样
注意:时期对象也可重采样,但要求更严格:
①在降采样中,目标频率必须是原频率的"子时期"(Subperiod)
②在升采样中,目标频率必须是原频率的"超时期"(Superperiod)
(2)实现:
进行重采样:<ts>.resample(<rule>[,axis=0,closed=None,label=None,convention="start",kind=None,loffset=None,base=None,on=None,level=None,origin="start_day",offset=None])
#返回pandas.core.resample.DatetimeIndexResampler
#注意:升采样时会产生缺失值,需要使用其他方法进行填充
#参数说明:
rule:指定目标转换(转换后的频率);为str(见下图)/DateOffset/Timedelta
closed:指定区间的哪侧是闭合的;为'right'/'left'
#对频率'M'/'A'/'Q'/'BM'/'BA'/'BQ'/'W',默认为'right';对其他频率,默认为'right'
label:指定使用区间的哪侧作为标签;为'right'/'left'
#对频率'M'/'A'/'Q'/'BM'/'BA'/'BQ'/'W',默认为'right';对其他频率,默认为'right'
convention:指定升采样时原值放在哪侧;为'start'/'s'或'end'/'e'
loffset:指定1个标签的时间偏移量;为timedelta
#标签会在原标签的基础上进行偏移,以便于明白时间戳表示的是哪个区间;强烈不建议使用
#实例:接上
>>> ts=pd.Series(np.random.randn(20),index=pd.date_range('1/1/2000',periods=20,freq='M'))
>>> list(ts.resample("D"))
[(Timestamp('2000-01-31 00:00:00', freq='D'), 2000-01-31 -1.091545
Freq: M, dtype: float64)...(Timestamp('2001-08-31 00:00:00', freq='D'), 2001-08-31 1.705509
Freq: M, dtype: float64)]
>>> list(ts.resample("A-JAN"))
[(Timestamp('2000-01-31 00:00:00', freq='A-JAN'), 2000-01-31 -1.091545
Freq: M, dtype: float64)...(Timestamp('2002-01-31 00:00:00', freq='A-JAN'), 2001-02-28 -0.414356
2001-03-31 0.606156
2001-04-30 0.634820
2001-05-31 -0.044379
2001-06-30 0.891806
2001-07-31 -1.628353
2001-08-31 1.705509
Freq: M, dtype: float64)]
>>> list(ts.resample("A-JAN",label="left"))
[(Timestamp('1999-01-31 00:00:00', freq='A-JAN'), 2000-01-31 -1.091545
Freq: M, dtype: float64)...(Timestamp('2001-01-31 00:00:00', freq='A-JAN'), 2001-02-28 -0.414356
2001-03-31 0.606156
2001-04-30 0.634820
2001-05-31 -0.044379
2001-06-30 0.891806
2001-07-31 -1.628353
2001-08-31 1.705509
Freq: M, dtype: float64)]
>>> list(ts.resample("A-JAN",closed="left"))
[(Timestamp('2001-01-31 00:00:00', freq='A-JAN'), 2000-01-31 -1.091545
2000-02-29 0.549390
2000-03-31 1.590489
2000-04-30 -1.307500
2000-05-31 -1.616338
2000-06-30 -0.059134
2000-07-31 0.520718
2000-08-31 0.074347
2000-09-30 0.702702
2000-10-31 0.067281
2000-11-30 -1.099831
2000-12-31 0.382498
Freq: M, dtype: float64), (Timestamp('2002-01-31 00:00:00', freq='A-JAN'), 2001-01-31 -1.197313
2001-02-28 -0.414356
2001-03-31 0.606156
2001-04-30 0.634820
2001-05-31 -0.044379
2001-06-30 0.891806
2001-07-31 -1.628353
2001-08-31 1.705509
Freq: M, dtype: float64)]
(3)OHLC重采样:
>>> ts.resample("A-JAN").ohlc()
open high low close#分别表示期间中开始/最高/最低/结束时的值
2000-01-31 -1.091545 -1.091545 -1.091545 -1.091545
2001-01-31 0.549390 1.590489 -1.616338 -1.197313
2002-01-31 -0.414356 1.705509 -1.628353 1.705509
四.时区的处理
将无时区时间序列转换到指定时区:<ts>.tz_localize(<tz>[,axis=0,level=None,copy=True,ambiguous="raise",nonexistent="raise"])
#参数说明:
ts:指定时间序列;为Series/DataFrame/DatetimeIndex/Timestamp
tz:指定时区;为str/tzinfo
#实例:接 三.4 部分
>>> tsl=ts.tz_localize("UTC")
>>> tsl
2000-01-31 00:00:00+00:00 0.005343
2000-02-29 00:00:00+00:00 -1.224921
2000-03-31 00:00:00+00:00 1.107894
2000-04-30 00:00:00+00:00 0.231661
Freq: M, dtype: float64
>>> tsl.index.tz
<UTC>
######################################################################################################################
将有时区时间序列转换到指定时区:<ts>.tz_convert(<tz>[,axis=0,level=None,copy=True])
#实例:接上
>>> tsl.tz_convert("America/New_York")
2000-01-30 19:00:00-05:00 0.005343
2000-02-28 19:00:00-05:00 -1.224921
2000-03-30 19:00:00-05:00 1.107894
2000-04-29 20:00:00-04:00 0.231661
Freq: M, dtype: float64
六.时期及以时期为索引的时间序列
1.创建:
"时期"(Period)表示的是时间区间,如2001/01/01-2001/12/31或2001-2002年,在Pandas中使用Period类表示:
pd.Period([value=None,freq=None,ordinal=None,year=None,month=1,quarter=None,day=1,hour=0,minute=0,second=0])
#参数说明:
value:指定时期;为Period/str(如"4Q2005")
freq:指定频率;为str
ordinal:为int
year,month,quarter,day,hour,minute,second:分别指定年/月/季度/日/时/分/秒
#实例:
>>> pd.Period()
NaT
>>> pd.Period(value="2Q2020")
Period('2020Q2', 'Q-DEC')
>>> p=pd.Period(freq="A-DEC",year=2012)#表示整个2012年
>>> p
Period('2012', 'A-DEC')
2.运算:
对Period对象加上或减去1个int即可实现根据其频率进行位移:
>>> p+1
Period('2013', 'A-DEC')
>>> p-2
Period('2010', 'A-DEC')
如果2个Period对象拥有相同的频率,则它们的差就是它们相差的单位数量:
>>> p-pd.Period(freq="A-DEC",year=2006)
<6 * YearEnds: month=12>
>>> pd.Period(freq="A-DEC",year=2006)-p
<-6 * YearEnds: month=12>
3.日期索引:
>>> values=['2001Q3','2002Q2','2003Q1']
>>> pd.PeriodIndex(values,freq='Q-DEC')
PeriodIndex(['2001Q3', '2002Q2', '2003Q1'], dtype='period[Q-DEC]', freq='Q-DEC')
4.频率转换:
转换频率:<p>.asfreq([freq=None,how="E"])
#参数说明:
p:指定日期对象;为Period/PeriodIndex/Series
freq:指定频率;为str
how:指定使用原日期结尾还是开头;为"E"/"End"/"end"/"Finish"/"finish"(结尾)或"S"/"start"/"Start"/"Begin"/"begin"(开头)
#实例:接上
>>> p.asfreq("D")
Period('2012-12-31', 'D')
>>> p.asfreq("D","S")
Period('2012-01-01', 'D')
5.转换为时间戳:
将日期对象转换为时间戳:<p>.to_timestamp([freq=None,how="S"])
#参数说明:
p:指定日期对象;为Period/PeriodIndex/Series
freq:指定频率;为str/DateOffset
#若<p>的频率至少为week,则为"D";否则为"S"
how:指定使用原日期结尾还是开头;为"E"/"End"/"end"/"Finish"/"finish"(结尾)或"S"/"start"/"Start"/"Begin"/"begin"(开头)
#实例:接上
>>> p.to_timestamp()
Timestamp('2012-01-01 00:00:00')