In [1]: import pandas as pd

In [2]: import matplotlib.pyplot as plt
Data used for this tutorial:
  • 在本教程中,空气质量数据关于 \(NO_2\) 使用小于2.5微米的颗粒物,由 OpenAQ 并使用 py-openaq 包裹。这个 air_quality_no2_long.csv" 数据集提供 \(NO_2\) 测量站的值 FR04014BETR801伦敦威斯敏斯特 分别在巴黎、安特卫普和伦敦。

    To raw data
    In [3]: air_quality = pd.read_csv("data/air_quality_no2_long.csv")
    
    In [4]: air_quality = air_quality.rename(columns={"date.utc": "datetime"})
    
    In [5]: air_quality.head()
    Out[5]: 
        city country                   datetime location parameter  value   unit
    0  Paris      FR  2019-06-21 00:00:00+00:00  FR04014       no2   20.0  µg/m³
    1  Paris      FR  2019-06-20 23:00:00+00:00  FR04014       no2   21.8  µg/m³
    2  Paris      FR  2019-06-20 22:00:00+00:00  FR04014       no2   26.5  µg/m³
    3  Paris      FR  2019-06-20 21:00:00+00:00  FR04014       no2   24.9  µg/m³
    4  Paris      FR  2019-06-20 20:00:00+00:00  FR04014       no2   21.4  µg/m³
    
    In [6]: air_quality.city.unique()
    Out[6]: array(['Paris', 'Antwerpen', 'London'], dtype=object)
    

如何轻松处理时间序列数据?#

使用Pandas日期时间属性#

  • 我想使用列中的日期 datetime 作为DateTime对象,而不是纯文本

    In [7]: air_quality["datetime"] = pd.to_datetime(air_quality["datetime"])
    
    In [8]: air_quality["datetime"]
    Out[8]: 
    0      2019-06-21 00:00:00+00:00
    1      2019-06-20 23:00:00+00:00
    2      2019-06-20 22:00:00+00:00
    3      2019-06-20 21:00:00+00:00
    4      2019-06-20 20:00:00+00:00
                      ...           
    2063   2019-05-07 06:00:00+00:00
    2064   2019-05-07 04:00:00+00:00
    2065   2019-05-07 03:00:00+00:00
    2066   2019-05-07 02:00:00+00:00
    2067   2019-05-07 01:00:00+00:00
    Name: datetime, Length: 2068, dtype: datetime64[ns, UTC]
    

    最初,中的值 datetime 是字符串,不提供任何日期时间操作(例如提取年份、星期几、…)。通过应用 to_datetime 函数时,Pandas解释字符串并将其转换为日期时间(即 datetime64[ns, UTC] )对象。在Pandas中,我们将这些DateTime对象称为类似于 datetime.datetime 来自标准库的 pandas.Timestamp

备注

由于许多数据集在其中一列中确实包含DATETIME信息,因此PANDA输入函数如下 pandas.read_csv()pandas.read_json() 方法读取数据时,可以执行日期转换。 parse_dates 参数,其中包含要读取为时间戳的列的列表:

pd.read_csv("../data/air_quality_no2_long.csv", parse_dates=["datetime"])

为什么会有这些? pandas.Timestamp 物件有用吗?让我们用一些示例案例来说明它的附加值。

我们正在处理的时间序列数据集的开始和结束日期是什么时候?

In [9]: air_quality["datetime"].min(), air_quality["datetime"].max()
Out[9]: 
(Timestamp('2019-05-07 01:00:00+0000', tz='UTC'),
 Timestamp('2019-06-21 00:00:00+0000', tz='UTC'))

使用 pandas.Timestamp For DateTime使我们能够使用日期信息进行计算,并使它们具有可比性。因此,我们可以使用它来获得时间序列的长度:

In [10]: air_quality["datetime"].max() - air_quality["datetime"].min()
Out[10]: Timedelta('44 days 23:00:00')

结果是一个 pandas.Timedelta 对象,类似于 datetime.timedelta 并定义一个持续时间。

To user guide

上的用户指南部分解释了Pandas支持的各种时间概念 time related concepts

  • 我想将一个新专栏添加到 DataFrame 仅包含测量的月份

    In [11]: air_quality["month"] = air_quality["datetime"].dt.month
    
    In [12]: air_quality.head()
    Out[12]: 
        city country                  datetime location parameter  value   unit  month
    0  Paris      FR 2019-06-21 00:00:00+00:00  FR04014       no2   20.0  µg/m³      6
    1  Paris      FR 2019-06-20 23:00:00+00:00  FR04014       no2   21.8  µg/m³      6
    2  Paris      FR 2019-06-20 22:00:00+00:00  FR04014       no2   26.5  µg/m³      6
    3  Paris      FR 2019-06-20 21:00:00+00:00  FR04014       no2   24.9  µg/m³      6
    4  Paris      FR 2019-06-20 20:00:00+00:00  FR04014       no2   21.4  µg/m³      6
    

    通过使用 Timestamp 作为日期的对象,Pandas提供了许多与时间相关的属性。例如, month ,但也 yearweekofyearquarter ,…所有这些属性都可以通过 dt 访问者。

To user guide

中提供了现有Date属性的概述 time and date components overview table 。有关的更多详细信息 dt 类返回DateTime属性的访问器在 dt accessor

  • 平均是多少? \(NO_2\) 每个测量地点一周中每天的浓度?

    In [13]: air_quality.groupby(
       ....:     [air_quality["datetime"].dt.weekday, "location"])["value"].mean()
       ....: 
    Out[13]: 
    datetime  location          
    0         BETR801               27.875000
              FR04014               24.856250
              London Westminster    23.969697
    1         BETR801               22.214286
              FR04014               30.999359
                                      ...    
    5         FR04014               25.266154
              London Westminster    24.977612
    6         BETR801               21.896552
              FR04014               23.274306
              London Westminster    24.859155
    Name: value, Length: 21, dtype: float64
    

    记住由提供的拆分-应用-组合模式 groupbytutorial on statistics calculation ?在这里,我们想要计算给定的统计数据(例如,平均值 \(NO_2\) ) 每个工作日对于每个测量位置 。要在工作日分组,我们使用DateTime属性 weekday (周一=0,周日=6) Timestamp ,它也可以通过 dt 访问者。可以对地点和工作日进行分组,以拆分这些组合中每一个的平均值计算。

    危险

    由于我们在这些示例中使用的是非常短的时间序列,因此分析不会提供具有长期代表性的结果!

  • 绘制典型的 \(NO_2\) 我们一天中所有台站的时间序列都是这样的。换句话说,一天中每个小时的平均值是多少?

    In [14]: fig, axs = plt.subplots(figsize=(12, 4))
    
    In [15]: air_quality.groupby(air_quality["datetime"].dt.hour)["value"].mean().plot(
       ....:     kind='bar', rot=0, ax=axs
       ....: )
       ....: 
    Out[15]: <AxesSubplot:xlabel='datetime'>
    
    In [16]: plt.xlabel("Hour of the day");  # custom x label using Matplotlib
    
    In [17]: plt.ylabel("$NO_2 (µg/m^3)$");
    
    ../../_images/09_bar_chart.png

    与前面的情况类似,我们希望计算给定的统计数据(例如,平均值 \(NO_2\) ) 一天中的每个小时 我们可以再次使用拆分-应用-合并的方法。对于本例,我们使用DateTime属性 hour 大Pandas的 Timestamp ,它也可以通过 dt 访问者。

将DateTime作为索引#

tutorial on reshapingpivot() 用于重塑数据表,将每个测量位置作为单独的列:

In [18]: no_2 = air_quality.pivot(index="datetime", columns="location", values="value")

In [19]: no_2.head()
Out[19]: 
location                   BETR801  FR04014  London Westminster
datetime                                                       
2019-05-07 01:00:00+00:00     50.5     25.0                23.0
2019-05-07 02:00:00+00:00     45.0     27.7                19.0
2019-05-07 03:00:00+00:00      NaN     50.4                19.0
2019-05-07 04:00:00+00:00      NaN     61.9                16.0
2019-05-07 05:00:00+00:00      NaN     72.4                 NaN

备注

通过透视数据,日期时间信息成为表的索引。通常,将列设置为索引可以通过 set_index 功能。

使用日期时间索引(即 DatetimeIndex )提供了强大的功能。例如,我们不需要 dt 访问器来获取时间序列属性,但可以直接在索引上使用这些属性:

In [20]: no_2.index.year, no_2.index.weekday
Out[20]: 
(Int64Index([2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019,
             ...
             2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019],
            dtype='int64', name='datetime', length=1033),
 Int64Index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
             ...
             3, 3, 3, 3, 3, 3, 3, 3, 3, 4],
            dtype='int64', name='datetime', length=1033))

其他一些优点是方便地细分时间段或调整曲线图上的时间尺度。让我们将这一点应用于我们的数据。

  • 创建一个情节的 \(NO_2\) 从5月20日到5月21日底不同站点的数值

    In [21]: no_2["2019-05-20":"2019-05-21"].plot();
    
    ../../_images/09_time_section.png

    通过提供一个 解析为日期时间的字符串 ,则可以在 DatetimeIndex

To user guide

有关 DatetimeIndex 中的一节中提供了使用字符串的切片 time series indexing

将时间序列重采样到另一个频率#

  • 将每个站点的当前每小时时间序列值聚合为每月最大值。

    In [22]: monthly_max = no_2.resample("M").max()
    
    In [23]: monthly_max
    Out[23]: 
    location                   BETR801  FR04014  London Westminster
    datetime                                                       
    2019-05-31 00:00:00+00:00     74.5     97.0                97.0
    2019-06-30 00:00:00+00:00     52.5     84.7                52.0
    

    使用DateTime索引处理时间序列数据的一种非常强大的方法是能够 resample() 将时间序列转换为另一频率(例如,将第二数据转换为5分钟数据)。

这个 resample() 方法类似于GROUP BY操作:

  • 它提供基于时间的分组,使用字符串(例如 M5H ,…)它定义了目标频率

  • 它需要一个聚合函数,如 meanmax ,…

To user guide

中给出了用于定义时间序列频率的别名的概述 offset aliases overview table

定义后,时间序列的频率由 freq 属性:

In [24]: monthly_max.index.freq
Out[24]: <MonthEnd>
  • 画一张日均的曲线图 \(NO_2\) 每个站点的价值。

    In [25]: no_2.resample("D").mean().plot(style="-o", figsize=(10, 5));
    
    ../../_images/09_resample_mean.png
To user guide

关于时间序列的力量的更多细节 resampling 上的用户指南部分提供了 resampling

REMEMBER

  • 有效的日期字符串可以使用以下命令转换为DateTime对象 to_datetime 函数或作为读取函数的一部分。

  • 属性支持计算、逻辑运算和方便的与日期相关的属性。 dt 访问者。

  • A DatetimeIndex 包含这些与日期相关的属性,并支持方便的切片。

  • Resample 是改变时间序列频率的一种强有力的方法。

To user guide

有关时间序列的完整概述,请参阅 time series and date functionality