pandas之时间操作
顾名思义,时间序列(time series),就是由时间构成的序列,它指的是在一定时间内按照时间顺序测量的某个变量的取值序列,比如一天内的温度会随时间而发生变化,或者股票的价格会随着时间不断的波动,这里用到的一系列时间,就可以看做时间序列。时间序列包含三种应用场景,分别是:
- 特定的时刻(timestamp),也就是时间戳;
- 固定的日期(period),比如某年某月某日;
- 时间间隔(interval),每隔一段时间具有规律性;
在处理时间序列的过程中,我们一般会遇到两个问题,第一,如何创建时间序列;第二,如何更改已生成时间序列的频率。 Pandas 为解决上述问题提供了一套简单、易用的方法。
下面用 Python 内置的 datetime 模块来获取当前时间,通过该模块提供的now()
方法即可实现。
- from datetime import datetime
- #数据类型为datetime
- print(datetime.now())
输出结果:
2020-12-16 16:36:18.791297
创建时间戳
TimeStamp(时间戳) 是时间序列中的最基本的数据类型,它将数值与时间点完美结合在一起。Pandas 使用下列方法创建时间戳:
- import pandas as pd
- print (pd.Timestamp('2017-03-01'))
输出结果:
2017-03-01 00:00:00
同样,可以将整型或浮点型表示的时间转换为时间戳。默认的单位是纳秒(时间戳单位),示例如下:
- import pandas as pd
- print(pd.Timestamp(1587687255,unit='s'))
输出结果:
2022-03-19 14:26:39
创建时间范围
通过 date_range() 方法可以创建某段连续的时间或者固定间隔的时间时间段。该函数提供了三个参数,分别是:
- start:开始时间
- end:结束时间
- freq:时间频率,默认为 "D"(天)
示例如下:
- import pandas as pd
- #freq表示时间频率,每30min变化一次
- print(pd.date_range("9:00", "18:10", freq="30min").time)
输出结果:
[datetime.time(9, 0) datetime.time(9, 30) datetime.time(10, 0) datetime.time(10, 30) datetime.time(11, 0) datetime.time(11, 30) datetime.time(12, 0) datetime.time(12, 30) datetime.time(13, 0) datetime.time(13, 30) datetime.time(14, 0) datetime.time(14, 30) datetime.time(15, 0) datetime.time(15, 30) datetime.time(16, 0) datetime.time(16, 30) datetime.time(17, 0) datetime.time(17, 30) datetime.time(18, 0)]
更改时间频率
- import pandas as pd
- #修改为按小时
- print(pd.date_range("6:10", "11:45", freq="H").time)
输出结果:
[datetime.time(6, 10) datetime.time(7, 10) datetime.time(8, 10) datetime.time(9, 10) datetime.time(10, 10) datetime.time(11, 10)]
转化为时间戳
您可以使用 to_datetime() 函数将 series 或 list 转换为日期对象,其中 list 会转换为DatetimeIndex
。示例如下:
- import pandas as pd
- print(pd.to_datetime(pd.Series(['Jun 3, 2020','2020-12-10', None])))
输出结果:
- 0 2020-06-03
- 1 2020-12-10
- 2 NaT
- dtype: datetime64[ns]
注意:NaT 表示的不是时间 ,它等效于 NaN。
最后再来看一个示例:
- import pandas as pd
- #传入list,生成Datetimeindex
- print(pd.to_datetime(['Jun 31, 2020','2020-12-10', None]))
输出结果:
DatetimeIndex(['2020-06-03', '2020-12-10', 'NaT'], dtype='datetime64[ns]', freq=None)
频率和周期转换
Time Periods 表示时间跨度,一段时间周期,它被定义在 Pandas Periods 类中,通过该类提供的方法可以实现将频率转换为周期。比如 Periods() 方法,可以将频率 "M"(月)转换为 Period(时间段)。
下面示例,使用 asfreq() 和 start 参数,打印 "01" ,若使用 end 参数,则打印 "31"。示例如下:
- import pandas as pd
- x = pd.Period('2014', freq='M')
- #start参数
- x.asfreq('D', 'start')
- #end参数
- x.asfreq('D', 'end')
输出结果:
Period('2014-01-01', 'D') Period('2014-01-31', 'D')
对于常用的时间序列频率,Pandas 为其规定了一些字符串别名,我们将这些别名称为“offset(偏移量)”。如下表所示:
别名 | 描述 | 别名 | 描述 |
---|---|---|---|
B | 工作日频率 | BQS | 工作季度开始频率 |
D | 日历日频率 | A | 年终频率 |
W | 每周频率 | BA | 工作年度结束频率 |
M | 月末频率 | BAS | 工作年度开始频率 |
SM | 半月结束频率 | BH | 营业时间频率 |
BM | 工作月结束频率 | H | 小时频率 |
MS | 月开始频率 | T,min | 每分钟频率 |
SMS | 半月开始频率 | S | 每秒钟频率 |
BMS | 工作月开始频率 | L,ms | 毫秒 |
Q | 季末频率 | U,us | 微妙 |
BQ | 工作季度结束频率 | N | 纳秒 |
QS | 季度开始频率 |
时间周期计算
周期计算,指的是对时间周期进行算术运算,所有的操作将在“频率”的基础上执行。
- import pandas as pd
- #S表示秒
- x = pd.Period('2014', freq='S')
- x
输出结果:
Period('2014-01-01 00:00:00', 'S')
执行计算示例:
- import pandas as pd
- x = pd.Period('2014', freq='S')
- #加1s的时间
- print(x+1)
输出结果:
Period('2014-01-01 00:00:01', 'S')
再看一组完整的示例:
- #定义时期period,默认freq="Y"年份
- p1=pd.Period('2020')
- p2=pd.Period('2019')
- #使用f''格式化输出
- print(f'p1={p1}年')
- print(f'p2={p2}年')
- print(f'p1和p2间隔{p1-p2}年')
- #f''表示字符串格式化输出
- print(f'五年前是{p1-5}年')
输出结果:
p1=2020年 p2=2019年 p1和p2间隔<YearEnd: month=12>年 五年前是2015年
创建时间周期
我们可以使用 period_range() 方法来创建时间周期范围。示例如下:
- import pandas as pd
- #Y表示年
- p = pd.period_range('2016','2018', freq='Y')
- p
输出结果:
PeriodIndex(['2016', '2017', '2018'], dtype='period[A-DEC]', freq='A-DEC')
时间序列转换
如果想要把字符串日期转换为 Period,首先需要将字符串转换为日期格式,然后再将日期转换为 Period。示例如下:
- # 创建时间序列
- index=pd.date_range("2020-03-17","2020-03-30",freq="1.5H")
- #随机选取4个互不相同的数
- loc=np.random.choice(np.arange(len(index)),size=4,replace=False)
- loc.sort()
- ts_index=index[loc]
- ts_index
- pd_index=ts_index.to_periods('D')
- pd_index()
输出结果:
DatetimeIndex(['2020-03-17 12:00:00', '2020-03-22 04:30:00', '2020-03-27 03:00:00', '2020-03-30 00:00:00'], dtype='datetime64[ns]', freq=None) PeriodIndex(['2020-03-17', '2020-03-19', '2020-03-19', '2020-03-27'], dtype='period[D]', freq='D')
使用 to_timestamp() 能够将 Period 时期转换为时间戳(timestamp),示例如下:
- import pandas as pd
- p1=pd.Periods("2020-2-3")
- p1.to_timestamp()
输出结果:
Timestamp('2020-02-03 00:00:00')
创建日期范围
Pandas 提供了用来创建日期序列的函数 date_range(),该函数的默认频率为 "D", 也就是“天”。日期序列只包含年、月、日,不包含时、分、秒。
下面是一组简单的示例,如下所示:
- import pandas as pd
- print(pd.date_range('12/15/2020', periods=10))
输出结果:
DatetimeIndex(['2020-12-15', '2020-12-16', '2020-12-17', '2020-12-18', '2020-12-19', '2020-12-20', '2020-12-21', '2020-12-22', '2020-12-23', '2020-12-24'], dtype='datetime64[ns]', freq='D')
当我们使用 date_range() 来创建日期范围时,该函数包含结束的日期,用数学术语来说就是区间左闭右闭,即包含起始值,也包含结束值。示例如下:
- import pandas as pd
- #建议使用Python的datetime模块创建时间
- start = pd.datetime(2019, 1, 1)
- end = pd.datetime(2019, 1, 5)
- print pd.date_range(start,end)
输出结果:
DatetimeIndex(['2019-01-01', '2019-01-02', '2019-01-03', '2019-01-04','2019-01-05'] ,dtype='datetime64[ns]', freq='D')
更改日频率
使用下列方法可以修改频率,比如按“天”为按“月”,示例如下:
- import pandas as pd
- print(pd.date_range('12/15/2011', periods=5,freq='M'))
输出结果:
DatetimeIndex(['2020-12-31', '2021-01-31', '2021-02-28', '2021-03-31', '2021-04-30'],dtype='datetime64[ns]', freq='M')
工作日时间
bdate_range() 表示创建工作日的日期范围,它与 date_range() 不同,它不包括周六、周日。
- import pandas as pd
- print(pd.date_range('11/25/2020', periods=8))
输出结果:
DatetimeIndex(['2020-11-25', '2020-11-26', '2020-11-27', '2020-11-28','2020-11-29', '2020-11-30', '2020-12-01', '2020-12-02'],dtype='datetime64[ns]', freq='D')
上述方法中,date_range() 默认频率是日历日,而 bdate_range() 的默认频率是工作日。
----------------------------------------------------------------‘’
日期格式化符号
在对时间进行格式化处理时,它们都有固定的表示格式,比如小时的格式化符号为%H
,分钟简写为%M
,秒简写为%S
。下表对常用的日期格式化符号做了总结:
符号 | 说明 |
---|---|
%y | 两位数的年份表示(00-99) |
%Y | 四位数的年份表示(000-9999) |
%m | 月份(01-12) |
%d | 月内中的一天(0-31) |
%H | 24小时制小时数(0-23) |
%I | 12小时制小时数(01-12) |
%M | 分钟数(00=59) |
%S | 秒(00-59) |
%a | 本地英文缩写星期名称 |
%A | 本地英文完整星期名称 |
%b | 本地缩写英文的月份名称 |
%B | 本地完整英文的月份名称 |
%w | 星期(0-6),星期天为星期的开始 |
%W | 一年中的星期数(00-53)星期一为星期的开始 |
%x | 本地相应的日期表示 |
%X | 本地相应的时间表示 |
%Z | 当前时区的名称 |
%U | 一年中的星期数(00-53)星期天为星期的开始 |
%j | 年内的一天(001-366) |
%c | 本地相应的日期表示和时间表示 |
Python处理
Python 内置的 strptime() 方法能够将字符串日期转换为 datetime 类型,下面看一组示例:
- from datetime import datetime
- #将日期定义为字符串
- date_str1 = 'Wednesday, July 18, 2020'
- date_str2 = '18/7/20'
- date_str3 = '18-07-2020'
- #将日期转化为datetime对象
- dmy_dt1 = datetime.strptime(date_str1, '%A,%B%d,%Y')
- dmy_dt2 = datetime.strptime(date_str2, '%d/%m/%y')
- dmy_dt3 = datetime.strptime(date_str3, '%d-%m-%Y')
- #处理为相同格式,并打印输出
- print(dmy_dt1)
- print(dmy_dt2)
- print(dmy_dt3)
输出结果:
2020-07-18 00:00:00 2020-07-18 00:00:00 2020-07-18 00:00:00
注意:strftime() 可以将 datetime 类型转换为字符串类型,恰好与 strptime() 相反。
Pandas处理
除了使用 Python 内置的 strptime() 方法外,你还可以使用 Pandas 模块的 pd.to_datetime() 和 pd.DatetimeIndex() 进行转换。
1) to_datetime()
通过 to_datetime() 直接转换为 datetime 类型
- import pandas as pd
- import numpy as np
- date = ['2012-05-06 11:00:00','2012-05-16 11:00:00']
- pd_date=pd.to_datetime(date)
- df=pd.Series(np.random.randn(2),index=pd_date)
输出结果:
2012-05-06 11:00:00 0.189865 2012-05-16 11:00:00 1.052456 dtype: float64
2) DatetimeIndex()
使用 Datetimeindex() 函数设置时间序,示例如下:
- date = pd.DatetimeIndex(['1/1/2008', '1/2/2008', '1/3/2008', '1/4/2008', '1/5/2008'])
- dt = pd.Series(np.random.randn(5),index = date)
- print(dt)
输出结果:
2008-01-01 1.965619 2008-01-02 -2.897374 2008-01-03 0.625929 2008-01-04 1.204926 2008-01-05 1.755680 dtype: float6-----------------------------------------------------------
Timedelta 表示时间差(或者时间增量),我们可以使用不同的时间单位来表示它,比如,天、小时、分、秒。时间差的最终的结果可以是正时间差,也可以是负时间差。
本节主要介绍创建 Timedelta (时间差)的方法以及与时间差相关的运算法则。
字符串
通过传递字符串可以创建 Timedelta 对象,示例如下:
- import pandas as pd
- print(pd.Timedelta('5 days 8 hours 6 minutes 59 seconds'))
输出结果:
5 days 08:06:59
整数
通过传递整数值和unit
参数也可以创建一个 Timedelta 对象。
- import pandas as pd
- print(pd.Timedelta(19,unit='h'))
输出结果:
0 days 19:00:00
数据偏移量
数据偏移量, 比如,周(weeks)、天(days)、小时(hours)、分钟(minutes)、秒(milliseconds)、毫秒、微秒、纳秒都可以使用。
- import pandas as pd
- print (pd.Timedelta(days=2,hours=6))
输出结果:
2 days 06:00:00
to_timedelta()
您可以使用pd.to_timedelta()
方法,将具有 timedelta 格式的值 (标量、数组、列表或 Series)转换为 Timedelta 类型。如果输入是 Series,则返回 Series;如果输入是标量,则返回值也为标量,其他情况输出 TimedeltaIndex。示例如下:
- import pandas as pd
- print(pd.to_timedelta(['1 days 06:05:01.00003', '15.5us', 'nan']))
- print(pd.to_timedelta(np.arange(5), unit='s'))
输出结果:
TimedeltaIndex(['1 days 06:05:01.000030', '0 days 00:00:00.000015', NaT],dtype='timedelta64[ns]', freq=None) TimedeltaIndex(['0 days 00:00:00', '0 days 00:00:01', '0 days 00:00:02','0 days 00:00:03', '0 days 00:00:04'],dtype='timedelta64[ns]', freq=None)
算术操作
datetime64[ns] datetime64[ns]
- import pandas as pd
- s = pd.Series(pd.date_range('2020-1-1', periods=5, freq='D'))
- #推导式用法
- td = pd.Series([ pd.Timedelta(days=i) for i in range(5)])
- df = pd.DataFrame(dict(A = s, B = td))
- print(df)
输出结果:
A B 0 2020-01-01 0 days 1 2020-01-02 1 days 2 2020-01-03 2 days 3 2020-01-04 3 days 4 2020-01-05 4 days
加法运算
- import pandas as pd
- s = pd.Series(pd.date_range('20120-1-1', periods=3, freq='D'))
- td = pd.Series([ pd.Timedelta(days=i) for i in range(3) ])
- df = pd.DataFrame(dict(A = s, B = td))
- #加法运算
- df['C']=df['A']+df['B']
- print(df)
输出结果:
A B C 0 2020-01-01 0 days 2020-01-01 1 2020-01-02 1 days 2020-01-03 2 2020-01-03 2 days 2020-01-05
减法运算
- import pandas as pd
- s = pd.Series(pd.date_range('2012-1-1', periods=3, freq='D'))
- td = pd.Series([ pd.Timedelta(days=i) for i in range(3) ])
- df = pd.DataFrame(dict(A = s, B = td))
- df['C']=df['A']+df['B']
- df['D']=df['C']-df['B']
- print(df)
输出结果:
A B C D 0 2019-01-01 0 days 2019-01-01 2019-01-01 1 2019-01-02 1 days 2019-01-03 2019-01-02 2 2019-01-03 2 days 2019-01-05 2019-01-03
------------------------------------------------------
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了