使用 Pandas 通过重叠窗口技术识别时间序列数据中的模式

2025 年 3 月 3 日 | 阅读 12 分钟

正如您可能预料的那样,Pandas 包含一套相当广泛的用于处理日期、时间和时间索引数据的工具,因为它是在为金融建模而构建的。

  • 时间戳指示特定时间点,例如 2016 年 7 月 14 日上午 7:00。
  • 周期和时间间隔描述了在特定开始和结束点之间经过的时间量,例如 2015 年。周期通常与特定类型的时间间隔相关,例如包含日期的 24 小时周期,其中每个间隔具有恒定的持续时间且不重叠。
  • 时间差和持续时间,例如 22.56 秒,提供了精确的时间量。

为了利用重叠窗口技术识别时间序列数据中的模式,必须利用统计方法来平滑和分析特定时期的过时数据。通过使用滚动计算(包括标准差和移动平均值),分析师可以发现数据中的差异和周期性模式。这些方法有助于减少噪声、突出底层模式并检测可能的异常。例如,滚动标准差可以显示波动性,而滚动平均值可以显示数据趋势的总体方向。在时间序列分析中,重叠窗口提供了更详细的视角,能够更精确地检测模式并进行持续监控。

在本节中,我们将介绍如何使用 Pandas 来处理这些类型的日期/时间数据。这篇简短的文章旨在为用户如何处理时间序列提供一个总体概述,绝不是 Python 或 Pandas 中所有可用时间序列工具的综合教程。在深入研究 Pandas 提供的功能之前,让我们快速看一下其他一些处理日期和时间的 Python 工具。我们将首先介绍一些深入的资源,然后再快速浏览一些使用 Pandas 处理时间序列数据的简短示例。

Python 中的日期和时间

Python 社区提供了许多表示日期、时间和时间差的表示方法。虽然 Pandas 的时间序列功能对于数据科学应用通常最有益,但了解它们与其他 Python 包的关系很重要。

原生 Python 日期和时间:datetime 和 dateutil

datetime 模块是 Python 自带的,包含处理日期和时间的基本对象。您可以将其与第三方 dateutil 模块结合使用,以快速执行各种有用的日期和时间函数。例如,您可以使用 datetime 类型手动创建日期。

示例 1

输出

 
datetime.datetime(2016, 7, 14, 0, 0)   

示例 2

输出

 
datetime.datetime(2016, 7, 14, 0, 0)   

一旦您有了 datetime 对象,您就可以打印星期几,例如。

输出

 
'Thursday'   

最后一行使用日期打印的常用字符串格式代码(“%A”);您可以在 Python 的 strftime 部分的 datetime 文档中了解有关这些代码的更多信息。dateutil 的在线文档包含有关更多有用日期实用程序的材料。Pytz 是一个您应该了解的相关包,因为它提供了处理时区(时间序列数据中最具迁移性的部分)的工具。

datetime 和 dateutil 的多功能性和简单的语法使您能够轻松完成几乎任何您想做的操作。这些对象是一个强大的组合。当您尝试处理大量日期和时间时,它们会失效:与类型化的编码日期数组相比,Python datetime 对象列表效率低下,就像与 NumPy 样式的类型化数值数组相比,Python 数值变量列表效率低下一样。

时间类型的数组:NumPy 的 datetime64

NumPy 团队添加了一系列原生时间序列数据类型到 NumPy,以应对 Python datetime 格式的不足。由于 datetime64 数据类型能够将日期编码为 64 位整数,因此日期数组可以非常紧凑地表示。datetime64 需要一种非常特殊的输入格式。

输出

 
array(datetime.date(2016, 7, 14), dtype='datetime64[D]')   

然而,一旦格式化,我们就可以在此日期上快速执行向量化操作。

输出

 
array(['2016-07-14' '2016-07-15' '2016-07-16' '2016-07-17' '2016-07-18' '2016-07-19' '2016-07-20' '2016-07-21' '2016-07-22' '2016-07-23' '2016-07-24' '2016-07-25'], dtype='datetime64   

datetime64 和 timedelta64 对象使用基本时间单位构建是它们的一些细节。由于 datetime64 对象具有 64 位精度限制,因此可编码时间的范围是该基本单位的 264 倍。换句话说,datetime64 需要在最大时间跨度和时间分辨率之间进行权衡。

例如,如果您想要一纳秒的时间分辨率,您将只有足够的数据来编码 264 纳秒的范围,或者略少于 600 年。从输入中,NumPy 将确定所需单位;例如,以下是基于日期的 datetime:

输出

 
numpy.datetime64('2016-07-14')   

这是基于分钟的 datetime:

输出

 
numpy.datetime64('2016-07-14T12:00')   

请注意,在运行代码的机器上,时区会自动设置为本地时间。多种可用格式代码中的一种允许您强制使用任何选定的基本单位;在这种情况下,我们将强制使用基于纳秒的时间。

输出

 
numpy.datetime64('2016-07-14T12:59:59.500000000')   
 
代码含义时间跨度(相对)时间跨度(绝对)
Y年份± 9.2e18 年[9.2e18 BC, 9.2e18 AD]
M月份± 7.6e17 年[7.6e17 BC, 7.6e17 AD]
W± 1.7e17 年[1.7e17 BC, 1.7e17 AD]
DDay± 2.5e16 年[2.5e16 BC, 2.5e16 AD]
h小时± 1.0e15 年[1.0e15 BC, 1.0e15 AD]
m分钟± 1.7e13 年[1.7e13 BC, 1.7e13 AD]
s第二个± 2.9e12 年[2.9e9 BC, 2.9e9 AD]
ms毫秒± 2.9e9 年[2.9e6 BC, 2.9e6 AD]
us微秒± 2.9e6 年[290301 BC, 294241 AD]
ns纳秒 ± 292 年[1678 AD, 2262 AD] 
ps皮秒± 106 天[1969 AD, 1970 AD]
fs飞秒 ± 2.6 小时[1969 AD, 1970 AD] 
as阿秒± 9.2 秒[1969 AD, 1970 AD]

对于我们在现实世界中遇到的数据类型,Datetime64[ns] 是一个不错的默认选择,因为它能够以适当的精度编码合理范围的当前日期。

总而言之,应该注意的是,虽然 datetime64 数据类型改进了内置 Python datetime 类型的一些不足之处,但它缺少 datetime 和 dateutil 提供的一些有用的函数和方法。NumPy 的 datetime64 文档提供了更多详细信息。

Pandas 中的日期和时间:两全其美

利用我们刚才介绍的所有技术,Pandas 创建了一个 Timestamp 对象,该对象结合了 NumPy 的向量化接口和高效存储以及 datetime 和 dateutil.datetime64 的用户友好性。Pandas 可以从这些 Timestamp 对象的集合中创建 DatetimeIndex,该对象可用于索引 Series 或 DataFrame 中的数据;稍后我们将看到几个示例。

例如,我们可以使用 Pandas 工具来重现之前的演示。可以通过解析格式灵活的字符串日期并使用格式代码来获取星期几。

代码

输出

 
Timestamp('2016-07-14 00:00:00')   

代码

输出

 
'Thursday'   

此外,我们可以直接在此对象上执行 NumPy 风格的向量化操作。

输出

 
DatetimeIndex(['2016-07-14' '2016-07-15' '2016-07-16' '2016-07-17' '2016-07-18' '2016-07-19' '2016-07-20' '2016-07-21' '2016-07-22' '2016-07-23' '2016-07-24' '2016-07-25'],   dtype='datetime64[ns]', freq=None)   

Pandas 时间序列:按时间索引

当您开始根据时间戳索引数据时,Pandas 时间序列工具的真正价值就体现出来了。例如,我们可以创建一个带有时间索引数据的 Series 对象。

输出

 
2015-07-14    0
2015-08-14    1
2016-07-14    2
2016-08-14    3
dtype: int64   

现在我们将数据放入 Series 中,我们可以使用我们在前面章节中介绍的任何 Series 索引技术来传递可以强制转换为日期 的值。

输出

 
2015-07-14    0
2015-08-14    1
2016-07-14    2
dtype: int64   

还存在其他独特的仅日期索引方法,包括传递年份以获取该年份所有数据的某个部分。

输出

 
2016-07-14    2
2016-08-14    3
dtype: int64   

我们将在后面看到更多日期作为索引的便利性示例。首先,让我们更深入地研究当前可用的时间序列数据结构。

Pandas 时间序列数据结构

本节将介绍处理时间序列数据的基本 Pandas 数据结构。

  • Pandas 为时间戳提供 Timestamp 类型。如前所述,它充当 Python 内置 datetime 函数的替代品,并基于更有效的 numpy.datetime64 数据类型。DatetimeIndex 是相关的索引结构。
  • Pandas 为时间段提供 Period 类型。这使用 numpy.datetime64 来编码固定频率的间隔。PeriodIndex 是相关的索引结构。
  • Pandas 为持续时间或时间差提供 Timedelta 类型。基于 numpy.timedelta64,Timedelta 是 Python 内置 datetime.timedelta 类的更有效的替代品。TimedeltaIndex 是与之关联的索引结构。

DatetimeIndex 和 Timestamp 对象是这些日期/时间对象中最基础的。虽然可以单独使用这些类对象,但 pd.to_datetime() 函数(能够解析大量格式)的使用更为频繁。

输出

 
DatetimeIndex(['2015-07-03', '2016-07-14', '2015-07-06', '2015-07-07',
               '2015-07-08'],
              dtype='datetime64[ns]', freq=None)   

to_period() 方法可以通过添加频率代码将任何 DatetimeIndex 转换为 PeriodIndex;在这种情况下,我们将使用“D”表示每日频率。

输出

 
PeriodIndex(['2015-07-03', '2016-07-14', '2015-07-06', '2015-07-07',
             '2015-07-08'],
            dtype='int64', freq='D')   

例如,当一个日期减去另一个日期时,会生成一个 TimedeltaIndex。

输出

 
TimedeltaIndex(['0 days', '1 days', '3 days', '4 days', '5 days'], dtype='timedelta64[ns]', freq=None)   

规则序列:pd.date_range()

Pandas 提供了许多方法来促进规则日期序列的构建:同样,pd.date_range() 通过接受开始日期、结束日期和可选的频率代码来生成规则日期序列。默认情况下,频率设置为一天。

您可以使用 freq 参数(默认为 D)来更改间隔。例如,在这里,我们将创建一个小时时间戳的范围。

输出

 
DatetimeIndex(['2016-07-13 00:00:00', '2016-07-13 01:00:00',
               '2016-07-13 02:00:00', '2016-07-13 03:00:00',
               '2016-07-13 04:00:00', '2016-07-13 05:00:00',
               '2016-07-13 06:00:00', '2016-07-13 07:00:00'],
              dtype='datetime64[ns]', freq='H')   

以下是一些月度周期。

输出

 
PeriodIndex(['2016-07', '2016-08', '2016-09', '2016-10', '2016-11', '2016-12',
             '2017-01', '2017=02'],
            dtype='int64', freq='M')   

以及一个以小时为增量的持续时间序列。

输出

 
TimedeltaIndex(['0 days 00:00:00', '0 days 01:00:00', '0 days 02:00:00',
                '0 days 03:00:00', '0 days 04:00:00', '0 days 05:00:00',
                '0 days 06:00:00', '0 days 07:00:00', '0 days 08:00:00',
                '0 days 09:00:00'],
               dtype='timedelta64[ns]', freq='H')   

频率和偏移量

这些 Pandas 时间序列工具的基础是频率或日期偏移量的概念。使用这些代码,我们可以定义任何所需的频率间隔,就像我们之前使用的 D(日)和 H(小时)代码一样。下面表格中汇总了主要可用代码。

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

在指定期限结束时,每月、每季度和每年的频率都表示出来。这些中任何一个加上 S 后缀的,都将表示在开始时。

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

此外,通过添加一个三字母的月份代码作为后缀,您可以修改用于表示任何季度或年度代码的月份。

  • Q-JAN, BQ-FEB, QS-MAR, BQS-APR 等。
  • A-JAN, BA-FEB, AS-MAR, BAS-APR 等。

同样,可以添加一个三字母的星期代码来更改每周频率的分割点。

  • W-SUN, W-MON, W-TUE, W-WED 等。

此外,还可以连接代码和数字来表示其他频率。例如,我们可以将小时(H)和分钟(T)代码组合起来,表示每两个半小时的周期。

输出

 
TimedeltaIndex(['0 days 00:00:00', '0 days 02:30:00', '0 days 05:00:00',
                '0 days 07:30:00', '0 days 10:00:00', '0 days 12:30:00',
                '0 days 15:00:00', '0 days 17:30:00', '0 days 20:00:00'],
               dtype='timedelta64[ns]', freq='150T')   

pd.tseries.offsets 模块包含 Pandas 时间序列偏移量的特定实例,这些实例由每个简短代码引用。

重采样、移位和窗口

Pandas 时间序列工具的一个关键组成部分是能够利用日期和时间作为索引来以可访问的方式组织和检索数据。Pandas 提供了许多其他时间序列特定的操作,同时基本索引数据的优势(操作期间自动对齐、轻松的数据切片和访问等)也同样适用。

在这里,我们将使用股票价格数据作为示例来检查其中一些。Pandas 包含一些非常专业的功能来处理金融数据,因为它主要是在金融领域设计的。例如,相关的 pandas-datareader 包可以从 Yahoo Finance 和 Google Finance 等各种来源导入金融数据,并可以使用命令 conda install pandas-datareader 进行安装。我们在这里加载谷歌的收盘价历史。

输出

 
                            Open      High            Low        Close            Adj  Close     Volume
Date                                                                    
2004-08-19  2.490664  2.591785  2.390042  2.499133   2.496292  897427216
2004-08-20  2.515820  2.716817  2.503118  2.697639   2.694573  458857488
2004-08-23  2.758411  2.826406  2.716070  2.724787   2.721690  366857939
2004-08-24  2.770615  2.779581  2.579581  2.611960   2.608991  306396159
2004-08-25  2.614201  2.689918  2.587302  2.640104   2.637103  184645512   

为简单起见,我们将只使用收盘价。

输出

Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas

重采样和转换频率

以不同频率重采样时间序列数据是一项经常需要的任务。可以使用更简单的 asfreq() 函数或 resample() 方法来完成此操作。两者之间的主要区别在于,asfreq() 基本上是一种数据选择,而 resample() 基本上是一种数据聚合。

让我们检查一下谷歌的收盘价,看看当数据被降采样时这两个的效果。在这里,我们将在财政年度结束时对数据进行重采样。

输出

Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas
Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas
Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas

请注意区别:asfreq 在年末提供值,而 resample 在每个点报告上一年的平均值。

对于上采样,asfreq() 和 resample() 几乎相似,但 resample 提供了更多选项。在这种情况下,这两种方法的默认设置是将上采样点留空,或者用 NA 值填充。与前面提到的 pd.fillna() 函数一样,asfreq() 接受一个 method 参数来确定插补值。在这里,我们将数据从工作日重采样到每日(即,包括周末)。

输出

Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas

非工作日默认留为空 NA 值,并且不绘制。底部面板显示了前向填充和后向填充的间隙填充过程之间的区别。

时间偏移

按时间移动数据是另一种常见的特定于时间序列的技术。Shift() 和 tshift() 是两个非常相似的 Pandas 算法,用于计算此操作。两者之间的主要区别在于 tshift() 移动索引,而 shift() 移动数据。在两种情况下,偏移量都表示为频率的倍数。

在这里,我们将同时使用 shift() 和 tshift() 移动 900 天;

输出

Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas

在这里,我们可以看到 tshift(900) 将索引值移动了 900 天,而 shift(900) 将数据移动了 900 天,导致一部分数据移出了图的边缘(并在另一端留下了 NA 值)。

这种类型的移动在计算时间差的上下文中经常遇到。例如,我们使用移位值来计算数据集中谷歌股票的一年投资回报率。

输出

Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas

这使得我们更容易理解谷歌股票的总体模式:迄今为止,投资谷歌的最佳时机是——不出所料——在其 IPO 之后不久以及 2009 年衰退高峰期。

滚动窗口

第三种 Pandas 特定的时间序列操作是滚动统计。可以使用 Series 和 DataFrame 对象的 rolling() 功能来完成此操作,它会产生一个与我们使用 groupby 方法时看到的视图相似的视图(参见聚合和分组)。默认情况下,此滚动视图提供了一些聚合操作。

以下是谷歌股票价格一年期居中滚动平均值和标准差的示例。

输出

Identify Patterns in Time-Series Data with Overlapping Window Techniques Using Pandas

与groupby操作一样,可以使用aggregate()和apply()方法执行自定义滚动计算。