千锋教育-做有情怀、有良心、有品质的职业教育机构

手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

当前位置:首页  >  技术干货  > Pandas时间序列详解

Pandas时间序列详解

来源:千锋教育
发布人:qyf
时间: 2022-08-12 17:30:18 1660296618

  在使用Python进行数据分析时,经常会遇到时间日期格式处理、转换和时间索引,Pandas作为Python环境下的数据分析库,提供了一套标准的时间序列处理工具和算法,使我们可以非常高效的处理时间序列,比如切片、聚合、重采样等等。这些强大的日期数据处理功能,是处理日期时间序列的利器。

  pandas 支持 4 种常见时间概念:

  > 1. 日期时间(Datetime):带时区的日期时间,类似于标准库的 `datetime.datetime` 。

  > 2. 时间差(Timedelta):绝对时间周期,类似于标准库的 `datetime.timedelta`。

  > 3. 时间段(Timespan):在某一时点以指定频率定义的时间跨度。

  > 4. 日期偏移(Dateoffset):与日历运算对应的时间段,类似于 `dateutil` 的 `dateutil.relativedelta.relativedelta`。

1

  一般情况下,时间序列主要是 Series 或 DataFrame的时间型索引,可以用时间元素进行操控。

  时间戳

  时间戳是最基本的时间序列数据,用于把数值与时点关联在一起。Pandas 对象通过时间戳调用时点数据。

  时间戳的创建

  在pandas中提供了Timestamp()可以用于创建一个时间戳对象。

  import datetime

  import pandas as pd

  # 三种方式

  pd.Timestamp(datetime.datetime(2021, 8, 16)) # 结果: Timestamp('2021-08-16 00:00:00')

  pd.Timestamp(2021, 8, 16) # 结果: Timestamp('2021-08-16 00:00:00')

  pd.Timestamp('2021-08-16') # 结果: Timestamp('2021-08-16 00:00:00')

  to_datetime()转换得到时间戳

  import pandas as pd

  pd.to_datetime('2021/08/08') # 结果:Timestamp('2021-08-08 00:00:00')

  to_datetime` 转换单个字符串时,返回的是单个 `Timestamp`。`Timestamp` 仅支持字符串输入,不支持 `dayfirst`、`format` 等字符串解析选项,如果要使用这些选项,就要用 `to_datetime`。

  要实现精准转换,除了传递 `datetime` 字符串,还要指定 `format` 参数,指定此参数还可以加速转换速度。

  pd.to_datetime('2021/08/08', format='%Y/%m/%d')

  pd.to_datetime('08-08-2021 00:00', format='%d-%m-%Y %H:%M')

  返回结果也是一个`Timestamp`类型。当然如果不可解析则出发错误

  pd.to_datetime(['2021/08/31', 'abc'], errors='raise') # 报错ValueError: Unknown string format

  转换多个时间序列

  import pandas as pd

  pd.to_datetime(pd.Series(["Aug 16, 2021", "2021-08-17", None]))

  结果(其中Pandas 用 `NaT` 表示日期时间、时间差及时间段的空值,代表了缺失日期或空日期的值,类似于浮点数的 `np.nan`)

  0 2021-08-16

  1 2021-08-17

  2 NaT

  dtype: datetime64[ns]

  当然也可以使用如下方式:

  pd.to_datetime(["2021/08/16", "2021.08.17"]) #也可以转成时间戳的格式

  返回结果与上面的有所不同,返回值不是一个序列而是一个DatetimeIndex类型

  DatetimeIndex(['2021-08-16', '2021-08-17'], dtype='datetime64[ns]', freq=None)

  date_range()获取时间戳范围

  实际工作中,经常要生成含大量时间戳的超长索引,一个个输入时间戳又枯燥,又低效。如果时间戳是定频的,用 `date_range()`与 `bdate_range()`函数即可创建 `DatetimeIndex`。`date_range` 默认的频率是**日历日**,`bdate_range` 的默认频率是**工作日

  pd.date_range(start=None,end=None,periods=None,freq=None)

  Return a fixed frequency DatetimeIndex.

  start:表示起始

  end:表示结尾

  periods:表示时间段

  freq:表示有倍数的频率字符串,e.g. '5H'.

  pd.date_range("2021-8-8", periods=8) # 表示从2021-8-8开始到现在日期的8个时间

  输出结果:

  DatetimeIndex(['2021-08-08', '2021-08-09', '2021-08-10', '2021-08-11',

  '2021-08-12', '2021-08-13', '2021-08-14', '2021-08-15'],

  dtype='datetime64[ns]', freq='D')

  如果使用bdate_range()则获取的日期是工作日的日期时间

  pd.bdate_range("2021-8-8", periods=8)

  结果:

  DatetimeIndex(['2021-08-09', '2021-08-10', '2021-08-11', '2021-08-12',

  '2021-08-13', '2021-08-16', '2021-08-17', '2021-08-18'],

  dtype='datetime64[ns]', freq='B')

  如果指定freq,date_range 默认使用的频率是 日历日即`D`,也可以通过freq修改成周的。

  pd.date_range("2021-8-8", periods=8, freq="W")

  输出结果下:

  DatetimeIndex(['2021-08-08', '2021-08-15', '2021-08-22', '2021-08-29',

  '2021-09-05', '2021-09-12', '2021-09-19', '2021-09-26'],

  dtype='datetime64[ns]', freq='W-SUN')

  时间序列的频率表:

屏幕快照 2021-08-17 下午12.14.23

  时间段

  时间段的创建

  pandas提供了`Period`类型,它是基于`numpy.datetime64`编码的固定频率间隔。与之相关的索引类型是`PeriodIndex`。`Period` 表示的时间段更直观,还可以用日期时间格式的字符串进行推断。默认是月`M`,也可以是天`D`

  pd.Period('2021-08')

  pd.Period('2021-05', freq='D')

  返回:

  Period('2021-08', 'M')

  Period('2021-05-01', 'D')

  时间段的范围创建

  pd.period_range('2020-08',periods=5,freq='M')

  pd.period_range('2020-08',periods=5,freq='D')

  结果是时间段序列:

  PeriodIndex(['2020-08', '2020-09', '2020-10', '2020-11', '2020-12'], dtype='period[M]', freq='M')

  PeriodIndex(['2020-08-01', '2020-08-02', '2020-08-03', '2020-08-04',

  '2020-08-05'],

  dtype='period[D]', freq='D')

  Pandas 可以识别两种表现形式,并在两者之间进行转化。Pandas 后台用 `Timestamp` 实例代表时间戳,用 `DatetimeIndex` 实例代表时间戳序列。pandas 用 `Period` 对象表示符合规律的时间段标量值,用 `PeriodIndex` 表示时间段序列。

  时间索引

  `DatetimeIndex` 主要用作 pandas 对象的索引。`DatetimeIndex` 类为时间序列做了很多优化:

  1. 预计算了各种偏移量的日期范围,并在后台缓存,让后台生成后续日期范围的速度非常快(仅需抓取切片)。

  2. 在 pandas 对象上使用 `shift` 与 `tshift` 方法进行快速偏移。

  3. 合并具有相同频率的重叠 `DatetimeIndex` 对象的速度非常快(这点对快速数据对齐非常重要)。

  4. 通过 `year`、`month` 等属性快速访问日期字段。

  5. `snap` 等正则函数与超快的 `asof` 逻辑

  DatetimeIndex` 可以当作常规索引,支持选择、切片等方法。

  index = pd.date_range('2020-12-01','2021-10-01' , freq='BM') # 指定范围内的每个月的最后一个工作日

  ts = pd.Series(np.random.randn(len(index)), index=index)

  输出结果:

  2020-12-31 -0.351660

  2021-01-29 0.358744

  2021-02-26 0.746602

  2021-03-31 0.178684

  2021-04-30 -0.408984

  2021-05-31 0.117038

  2021-06-30 0.661603

  2021-07-30 0.655608

  2021-08-31 -0.207675

  2021-09-30 -0.023105

  Freq: BM, dtype: float64

  可以支持获取index和索引切片

  display(ts.index)

  display(ts[:4].index)

  display(ts[::2].index)

  输出结果:

  DatetimeIndex(['2020-12-31', '2021-01-29', '2021-02-26', '2021-03-31',

  '2021-04-30', '2021-05-31', '2021-06-30', '2021-07-30',

  '2021-08-31', '2021-09-30'],

  dtype='datetime64[ns]', freq='BM')

  DatetimeIndex(['2020-12-31', '2021-01-29', '2021-02-26', '2021-03-31'], dtype='datetime64[ns]', freq='BM')

  DatetimeIndex(['2020-12-31', '2021-02-26', '2021-04-30', '2021-06-30',

  '2021-08-31'],

  dtype='datetime64[ns]', freq='2BM')

  当然也可以按照年、月、日获取时间索引,或者获取所有的索引的年月等

  display(ts['2020'])

  display(ts['2021-06'])

  display(ts['2020-12':'2021-05'])

  display(ts.index.year)

  2020-12-31 -0.35166

  Freq: BM, dtype: float64

  2021-06-30 0.661603

  Freq: BM, dtype: float64

  2020-12-31 -0.351660

  2021-01-29 0.358744

  2021-02-26 0.746602

  2021-03-31 0.178684

  2021-04-30 -0.408984

  2021-05-31 0.117038

  Freq: BM, dtype: float64

  Int64Index([2020, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021], dtype='int64')

  当然,`Series` 的值为 `datetime` 时,还可以用 `.dt` 访问这些属性。

  df = pd.DataFrame(np.random.randn(10000, 2),columns=['A','B'])

  df['datetime'] = pd.date_range('20180101', periods=10000, freq='H')

  df

  输出结果:

屏幕快照 2021-08-17 下午2.50.59

  此时可以通过datetime列获取年月日时分秒等

  df['datetime'].dt.year

  # df['datetime'].dt.month

  # df['datetime'].dt.day

  dt的属性如下表:

屏幕快照 2021-08-17 下午2.54.14

  时间差

  使用pandas中的Timedelta()函数表示时间差,这个方法与Python基础中datetime.timedelta是等效的可以互换的在大多数情况下。

  以时间差为数据的 `Series` 与 `DataFrame` 支持各种运算,`datetime64 [ns]` 序列或 `Timestamps` 减法运算生成的是`timedelta64 [ns]` 序列

  直接使用符号获取时间差

  pd.to_datetime('2021-8-14') - pd.to_datetime('2021-6-1')

  返回值就是一个Timedelta类型的

  Timedelta('74 days 00:00:00')

  如果想在当前的日期前三天或者后5天的值,则需要创建时间差对象

  from datetime import datetime

  delta = pd.Timedelta('3 days')

  display(datetime.now()-delta)

  delta1 = pd.Timedelta('30 days')

  display(datetime.now()+delta1)

  输出结果(默认是ns作为单位):

  datetime.datetime(2021, 8, 14, 15, 21, 9, 875792)

  datetime.datetime(2021, 9, 16, 15, 21, 9, 877299)

  创建Timedelta()可以支持的unit参数值

屏幕快照 2021-08-17 下午3.22.50

  delta1 = pd.Timedelta(5,unit='W') # 表示5周后的时间

  display(datetime.now()+delta1)

  简单应用:获取1998-12-20到现在的年龄

  age = (datetime.now()- pd.to_datetime('1998-12-20')) / pd.Timedelta(days=365)

  print(age)

  结果:

  22.675737040520104

  时间偏移

  `DateOffset` 类似于时间差 `Timedelta` ,但遵循指定的日历日规则。例如,`Timedelta` 表示的每日时间差一直都是 24 小时,而 `DateOffset` 的每日偏移量则是与下一天相同的时间差,使用夏时制时,每日偏移时间有可能是 23 或 24 小时,甚至还有可能是 25 小时。不过,`DateOffset` 子类只能是等于或小于**小时**的时间单位(`Hour`、`Minute`、`Second`、`Milli`、`Micro`、`Nano`),操作类似于 `Timedelta`及对应的绝对时间。

  DateOffset` 基础操作类似于 `dateutil.relativedelta`可按指定的日历日时间段偏移日期时间。

  ts = pd.Timestamp('2016-10-30 00:00:00', tz='Europe/Helsinki') # 其中 tz='Europe/Helsinki'表示夏时制时区

  display(ts + pd.Timedelta(days=1))

  display(ts + pd.DateOffset(days=1))

  输出结果:

  Timestamp('2016-10-30 23:00:00+0200', tz='Europe/Helsinki')

  Timestamp('2016-10-31 00:00:00+0200', tz='Europe/Helsinki')

  `DateOffset`可用算数运算符(+)或 `apply` 方法执行日期偏移操作。

  d = pd.Timestamp('2021-08-15')

  two_business_days = 2 * pd.offsets.BDay() # BDay()表示工作日

  two_business_days.apply(d) # Timestamp('2021-08-17 00:00:00')

  `DateOffset` 还支持 `rollforward()` 与 `rollback()` 方法,按偏移量把某一日期**向前**或**向后**移动至有效偏移日期。例如,工作日偏移滚动日期时会跳过周末(即,星期六与星期日),直接到星期一,因为工作日偏移针对的是工作日。

  可以为 `Series` 或 `DatetimeIndex` 里的每个元素应用偏移。

  rng = pd.date_range('2021-01-01', '2021-08-16')

  s = pd.Series(rng)

  s + pd.DateOffset(days=2) # 或者使用s+pd.offsets.Day(2)

  输出结果:

  0 2021-01-03

  1 2021-01-04

  2 2021-01-05

  3 2021-01-06

  4 2021-01-07

 

  223 2021-08-14

  224 2021-08-15

  225 2021-08-16

  226 2021-08-17

  227 2021-08-18

  Length: 228, dtype: datetime64[ns]

  与时间序列相关的方法

  在做时间序列相关的工作时,经常要对时间做一些移动/滞后、频率转换、采样等相关操作,我们来看下这些操作如何使用吧。

  移动

  如果你想移动或滞后时间序列,你可以使用 shift 方法。

  ts = pd.Series(np.random.randn(4), index = pd.date_range('2012-01-01',periods =4, freq ='M'))

  print(ts)

  2012-01-31 1.132395

  2012-02-29 0.740404

  2012-03-31 0.154164

  2012-04-30 -0.487571

  Freq: M, dtype: float64

  print(ts.shift(2)) #将数据往后移动, 往前移动则为 ts.shift(-2)

  2012-01-31 NaN

  2012-02-29 NaN

  2012-03-31 1.132395

  2012-04-30 0.740404

  Freq: M, dtype: float64

  当然也可以结合频度

  print(ts.shift(2, freq='M')) # 此时时间增加了2个月

  结果:

  2012-03-31 1.132395

  2012-04-30 0.740404

  2012-05-31 0.154164

  2012-06-30 -0.487571

  Freq: M, dtype: float64

  改变频率

  使用函数 `asfreq()`。对于 `DatetimeIndex`,这就是一个调用 `reindex()`,并生成 `date_range` 的便捷打包器。

  from pandas.tseries.offsets import *

  ts = pd.Series(np.random.randn(2), index = pd.date_range('2021-06-01',periods =2, freq ='w'))

  ts.asfreq(Day())

  结果(即原来是周显示,现在将频率由周转为了天):

  2021-06-06 0.362032

  2021-06-07 NaN

  2021-06-08 NaN

  2021-06-09 NaN

  2021-06-10 NaN

  2021-06-11 NaN

  2021-06-12 NaN

  2021-06-13 -1.720824

  Freq: D, dtype: float64

  你会发现出现了缺失值,因此 Pandas 为你提供了 method 参数来填充缺失值。几种不同的填充方法参考 Pandas 缺失值处理 中 fillna 介绍

  ts.asfreq(Day(), method="pad") # 即使用0.362032填充了NaN的值

  结果:

  2021-06-06 0.362032

  2021-06-07 0.362032

  2021-06-08 0.362032

  2021-06-09 0.362032

  2021-06-10 0.362032

  2021-06-11 0.362032

  2021-06-12 0.362032

  2021-06-13 -1.720824

  Freq: D, dtype: float64

  重采样

  Pandas 有一个虽然简单,但却强大、高效的功能,可在频率转换时执行重采样,如,将秒数据转换为 5 分钟数据,这种操作在金融等领域里的应用非常广泛。

  重采样(resampling)指的是将时间序列从一个频率转换到另一个频率的处理过程。将高频数据聚合到低频称为降采样(downsampling),将低频数据转换到高频则称为升采样(upsampling)。除此以外还存在一种采样方式既不是升采样,也不是降采样,比如`W-WED`转换成`W-FRI`。

  可以通过`resample()`函数来实现,也可以通过更简单的方式`asfreq()`函数来实现。两者基本的不同点在于`resample()`是一种数据聚合方式`asfreq()`是一种数据选取方式。

  resample() 是基于时间的分组操作,每个组都遵循归纳方法。可以按照分钟、小时、工作日、周、月、年等来作为日期维度

  # 获取7月1日到7月31日的时间区间

  rng = pd.date_range(start='2021/07/1',end='2021/07/31',freq='D')

  # 使用此时间区间构建一个Series对象

  ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)

  # 获取此Series对象每5天的数据总和

  ts.resample('5D').sum()

屏幕快照 2021-08-17 下午5.20.58

  案例演示

  AAPL = pd.read_csv('AAPL.csv')

  AAPL.Date = pd.to_datetime(AAPL.Date)

  AAPL.head()

屏幕快照 2021-08-17 下午5.09.42

  AAPL['month'] = AAPL.Date.dt.month

  AAPL.groupby('month')['Adj Close'].mean() # 每个月份的Adj Close的均值

屏幕快照 2021-08-17 下午5.11.06

  AAPL.set_index('Date').resample('Y')['Adj Close'].mean() # 获取每年的Adj Close的均值

屏幕快照 2021-08-17 下午5.29.06

  如果使用resample进行重采样,获取日期每十年`Adj Close`的均值

  AAPL.set_index('Date').resample('10Y')['Adj Close'].mean()

屏幕快照 2021-08-17 下午5.13.34

  AAPL.csv数据回复`时间序列重采样`获取

  更多关于“Python培训”的问题,欢迎咨询千锋教育在线名师。千锋教育多年办学,课程大纲紧跟企业需求,更科学更严谨,每年培养泛IT人才近2万人。不论你是零基础还是想提升,都可以找到适合的班型,千锋教育随时欢迎你来试听。

tags:
声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。
10年以上业内强师集结,手把手带你蜕变精英
请您保持通讯畅通,专属学习老师24小时内将与您1V1沟通
免费领取
今日已有369人领取成功
刘同学 138****2860 刚刚成功领取
王同学 131****2015 刚刚成功领取
张同学 133****4652 刚刚成功领取
李同学 135****8607 刚刚成功领取
杨同学 132****5667 刚刚成功领取
岳同学 134****6652 刚刚成功领取
梁同学 157****2950 刚刚成功领取
刘同学 189****1015 刚刚成功领取
张同学 155****4678 刚刚成功领取
邹同学 139****2907 刚刚成功领取
董同学 138****2867 刚刚成功领取
周同学 136****3602 刚刚成功领取
相关推荐HOT