• Pandas基础(十一)时间序列


    pandas提供了一套标准的时间序列处理工具和算法,使得我们可以非常高效的处理时间序列,比如切片、聚合、重采样等等。

    本节我们讨论以下三种时间序列:

    • 时间戳(time stamp):一个特定的时间点,比如2018年4月2日。
    • 时间间隔和时期(time period):两个特定的时间起点和终点,比如2018年4月2日-2019年4月2日。
    • 时间差或持续时间(time deltas):特定的时间长度,比如20秒。

    Python的时间序列处理是一个很重要的话题,尤其在金融领域有着非常重要的应用。本节只做简单的介绍,如果以后有需要的话再深入学习。本文参考的Working with Time Series对python原生的日期时间处理模块datetime,numpy日期时间处理模块datetime64,以及第三方日期时间处理模块dateutil等都做了介绍,我们这里跳过这些,直接进入pandas的时间序列。

    1. import numpy as np
    2. import pandas as pd

    1. pandas时间序列:时间索引

    当你使用时间戳作为数据索引的时候,pandas的时间序列工具就变得是非有用。

    1. index = pd.DatetimeIndex(['2014-07-04', '2014-08-04',
    2. '2015-07-04', '2015-08-04'])
    3. data = pd.Series([0, 1, 2, 3], index=index)
    4. data
    5. #> 2014-07-04 0
    6. #> 2014-08-04 1
    7. #> 2015-07-04 2
    8. #> 2015-08-04 3
    9. #> dtype: int64
    10. data['2014-07-04':'2015-07-04']
    11. #> 2014-07-04 0
    12. #> 2014-08-04 1
    13. #> 2015-07-04 2
    14. #> dtype: int64
    15. data['2015']
    16. #> 2015-07-04 2
    17. #> 2015-08-04 3
    18. #> dtype: int64

    2. pandas时间序列数据结构

    • 对于时间戳,pandas提供了Timestamp类型。它基于numpy.datetime64数据类型,与与之相关的索引类型是DatetimeIndex

    • 对于时间时期,pandas提供了Period类型,它是基于numpy.datetime64编码的固定频率间隔。与之相关的索引类型是PeriodIndex

    • 对于时间差,pandas提供了Timedelta类型,同样基于numpy.datetime64。与之相关的索引结构是TimedeltaIndex

    最基本的时间/日期是时间戳TimestampDatetimeIndex。但是我们可以直接使用pd.to_datetime()函数来直接解析不同的格式。传入单个日期给pd.to_datetime()返回一个Timestamp,传入一系列日期默认返回DatetimeIndex

    1. dates = pd.to_datetime([datetime(2015, 7, 3), '4th of July, 2015',
    2. '2015-Jul-6', '07-07-2015', '20150708'])
    3. dates
    4. #> DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-06', '2015-07-07',
    5. #> '2015-07-08'],
    6. #> dtype='datetime64[ns]', freq=None)

    当一个日期减去另一个日期的时候,TimedeltaIndex就会被创建:

    1. dates - dates[0]
    2. #> TimedeltaIndex(['0 days', '1 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq=None)

    2.1 定期序列

    为了方便地创建定期的日期序列,pandas提供了一些函数:pd.date_range()创建时间戳,pd.period_range()创建时期,pd.timedelta_range()创建时间差。默认情况下频率间隔是一天。

    1. pd.date_range('2015-07-03', '2015-07-10')
    2. #> DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
    3. #> '2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
    4. #> dtype='datetime64[ns]', freq='D')

    另外可以不指定终点,而是提供一个时期数:

    1. pd.date_range('2015-07-03', periods=8)
    2. #> DatetimeIndex(['2015-07-03', '2015-07-04', '2015-07-05', '2015-07-06',
    3. #> '2015-07-07', '2015-07-08', '2015-07-09', '2015-07-10'],
    4. #> dtype='datetime64[ns]', freq='D')

    频率间隔可以通过freq参数设置,默认是D:

    1. pd.date_range('2015-07-03', periods=8, freq='H')
    2. #> DatetimeIndex(['2015-07-03 00:00:00', '2015-07-03 01:00:00',
    3. #> '2015-07-03 02:00:00', '2015-07-03 03:00:00',
    4. #> '2015-07-03 04:00:00', '2015-07-03 05:00:00',
    5. #> '2015-07-03 06:00:00', '2015-07-03 07:00:00'],
    6. #> dtype='datetime64[ns]', freq='H')

    类似的

    1. pd.period_range('2015-07', periods=8, freq='M')
    2. #> PeriodIndex(['2015-07', '2015-08', '2015-09', '2015-10', '2015-11', '2015-12',
    3. #> '2016-01', '2016-02'],
    4. #> dtype='period[M]', freq='M')
    5. pd.timedelta_range(0, periods=10, freq='H')
    6. #> TimedeltaIndex(['00:00:00', '01:00:00', '02:00:00', '03:00:00', '04:00:00',
    7. #> '05:00:00', '06:00:00', '07:00:00', '08:00:00', '09:00:00'],
    8. #> dtype='timedelta64[ns]', freq='H')

    3. 频率和偏移

    以上这些时间序列处理工具最基础的概念是频率和时间偏移。下表总结了主要的代码表示:

    代码描述代码描述
    D 日历天 L 厘秒
    W 星期 U 毫秒
    M 月末 N 纳秒
    Q 季度末 B 工作日
    A 年末 BM 工作月末
    H 小时 BQ 工作季末
    T 分钟 BA 工作年末
    S BH 工作时

    上面的月,季度以及周期性频率都被标记为这一时期的终点,如果想变成起始点可以加S后缀。

    代码描述代码描述
    MS 月初 BMS 工作月初
    QS 季度初 BQS 工作季初
    AS 年初 BAS 工作年初

    另外,还可以通过加后缀的方法改变季度或年的月份等。

    • Q-JANBQ-FEBBQS-APR等等;
    • A-JANBA-FEBBAS-APR等等。

      类似的,星期也可以分解成周一,周二…

    • W-SUNW-MONW-WED等等

      以上这些代码还可以与数字结合来表示频率, 例如我们要表示2小时30分的频率:

    1. pd.timedelta_range(0, periods=9, freq="2H30T")
    2. #> TimedeltaIndex(['00:00:00', '02:30:00', '05:00:00', '07:30:00', '10:00:00',
    3. #> '12:30:00', '15:00:00', '17:30:00', '20:00:00'],
    4. #> dtype='timedelta64[ns]', freq='150T')

    以上这些代码表示的含义还可以通过pd.tseries.offsets模块表示:

    1. from pandas.tseries.offsets import BDay
    2. pd.date_range('2015-07-01', periods=5, freq=BDay())
    3. #> DatetimeIndex(['2015-07-01', '2015-07-02', '2015-07-03', '2015-07-06',
    4. #> '2015-07-07'],
    5. #> dtype='datetime64[ns]', freq='B')

    更加详细的讨论可以看DateOffset

    4. 重采样,转移,加窗口

    我们以一些真实的金融数据为例讲解,pandas-datareader(可通过conda install pandas-datareader来安装, 如果)包包含谷歌,雅虎等公司的金融数据。

    1. from pandas_datareader import data
    2. goog = data.DataReader('GOOG', start='2004', end='2016',
    3. data_source='google')
    4. goog.head()
    5. #> Open High Low Close Volume
    6. #> Date
    7. #> 2004-08-19 49.96 51.98 47.93 50.12 NaN
    8. #> 2004-08-20 50.69 54.49 50.20 54.10 NaN
    9. #> 2004-08-23 55.32 56.68 54.47 54.65 NaN
    10. #> 2004-08-24 55.56 55.74 51.73 52.38 NaN
    11. #> 2004-08-25 52.43 53.95 51.89 52.95 NaN

    简单起见,我们只是用收盘价格

    1. goog = goog['Close']

    为了更好的说明问题,我们可以利用matplotlib对数据进行可视化,关于matplotlib可以通过Python for Data Analysis这本书进行学习,也可以通过官网的gallary进行学习,我个人当时是在官网看了大量的实例,有什么需求直接去官网找例子,都很方便。本系列学习笔记不会包含数据可视化部分。

    1. import matplotlib.pyplot as plt
    2. goog.plot()

    4.1 重采样及频率转换

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

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

    1. goog.plot(alpha=0.5, style='-')
    2. goog.resample('BA').mean().plot(style=':')
    3. goog.asfreq('BA').plot(style='--');
    4. plt.legend(['input', 'resample', 'asfreq'],
    5. loc='upper left')

    resample()的处理过程是取整年的数据的平均值,而asfreq()是选取年末的时间点的数据。

    对于升采样来说,resample()asfreq()是等效的。对于空置采样点都会用NaN来进行填充。就像pd.fillna()方法,asfreq()接受一个参数method可指定缺失值的填充方法。ffill表示与前面的值保持一致,bfill表示与后面的值保持一致等等。

    1. fig, ax = plt.subplots(2, sharex=True)
    2. data = goog.iloc[:10]
    3. data.asfreq('D').plot(ax=ax[0], marker='o')
    4. data.asfreq('D', method='bfill').plot(ax=ax[1], style='-o')
    5. data.asfreq('D', method='ffill').plot(ax=ax[1], style='--o')
    6. ax[1].legend(["back-fill", "forward-fill"])

    4.2 时间移动

    另一个与时间序列相关的操作是数据在时间轴的移动。pandas提供了两个相关的函数:shift()tshift(),两者的不同是shift()移动的是数据,tshift()移动的是时间轴(索引)。

    1. fig, ax = plt.subplots(3, sharey=True)
    2. # apply a frequency to the data
    3. goog = goog.asfreq('D', method='pad')
    4. goog.plot(ax=ax[0])
    5. goog.shift(900).plot(ax=ax[1])
    6. goog.tshift(900).plot(ax=ax[2])
    7. # legends and annotations
    8. local_max = pd.to_datetime('2007-11-05')
    9. offset = pd.Timedelta(900, 'D')
    10. ax[0].legend(['input'], loc=2)
    11. ax[0].get_xticklabels()[2].set(weight='heavy', color='red')
    12. ax[0].axvline(local_max, alpha=0.3, color='red')
    13. ax[1].legend(['shift(900)'], loc=2)
    14. ax[1].get_xticklabels()[2].set(weight='heavy', color='red')
    15. ax[1].axvline(local_max + offset, alpha=0.3, color='red')
    16. ax[2].legend(['tshift(900)'], loc=2)
    17. ax[2].get_xticklabels()[1].set(weight='heavy', color='red')
    18. ax[2].axvline(local_max + offset, alpha=0.3, color='red')

    时移一个重要作用即使计算一段时间内的差别,比如计算谷歌股票一年的投资回报率:

    1. ROI = 100 * (goog.tshift(-365) / goog - 1)
    2. ROI.plot()
    3. plt.ylabel('% Return on Investment')

    4.3 滚动窗口

    滚动统计是时间序列的又一重要的操作。可以通过SeriesDataFramerolling()方法实现,返回类似于groupby的操作,同样也有许多数据聚合的操作。

    1. rolling = goog.rolling(365, center=True)
    2. data = pd.DataFrame({'input': goog,
    3. 'one-year rolling_mean': rolling.mean(),
    4. 'one-year rolling_std': rolling.std()})
    5. ax = data.plot(style=['-', '--', ':'])
    6. ax.lines[0].set_alpha(0.3)

  • 相关阅读:
    shell tr命令的使用
    linux find prune排除某目录或文件
    在vue中使用axios发送post请求,参数方式
    webpack官网demo起步中遇到的问题
    css中盒子模型与box-sizing属性
    jquery获得 url的变量
    17-js观察者模式
    基于Jquery ui 可复用的酒店 web页面选择入住日期插件
    webkit浏览器下改变滚动条样式
    用户登录时,禁止chrome提示用户保存密码
  • 原文地址:https://www.cnblogs.com/zknublx/p/10243273.html
Copyright © 2020-2023  润新知