Pandas 2.2 中文官方教程和指南(二)

原文:pandas.pydata.org/docs/

如何在 pandas 中创建图表?

原文:pandas.pydata.org/docs/getting_started/intro_tutorials/04_plotting.html

../../_images/04_plot_overview.svg

In [1]: import pandas as pd

In [2]: import matplotlib.pyplot as plt 

本教程使用的数据:

  • 空气质量数据

    本教程使用关于(NO_2)的空气质量数据,由OpenAQ提供,并使用py-openaq包。air_quality_no2.csv数据集提供了分别来自巴黎、安特卫普和伦敦的测量站FR04014BETR801London Westminster的(NO_2)值。

    原始数据

    In [3]: air_quality = pd.read_csv("data/air_quality_no2.csv", index_col=0, parse_dates=True)
    
    In [4]: air_quality.head()
    Out[4]: 
     station_antwerp  station_paris  station_london
    datetime 
    2019-05-07 02:00:00              NaN            NaN            23.0
    2019-05-07 03:00:00             50.5           25.0            19.0
    2019-05-07 04:00:00             45.0           27.7            19.0
    2019-05-07 05:00:00              NaN           50.4            16.0
    2019-05-07 06:00:00              NaN           61.9             NaN 
    

    注意

    使用read_csv函数的index_colparse_dates参数,将第一(0)列定义为生成的DataFrame的索引,并将列中的日期转换为Timestamp对象。

  • 我想快速地对数据进行可视化检查。

    In [5]: air_quality.plot()
    Out[5]: <Axes: xlabel='datetime'>
    
    In [6]: plt.show() 
    

    ../../_images/04_airqual_quick.png

    使用DataFrame,pandas 默认为具有数值数据的每列创建一条线图。

  • 我只想绘制数据表中来自巴黎的列。

    In [7]: air_quality["station_paris"].plot()
    Out[7]: <Axes: xlabel='datetime'>
    
    In [8]: plt.show() 
    

    ../../_images/04_airqual_paris.png

    要绘制特定列,请结合子集数据教程中的选择方法和plot()方法。因此,plot()方法适用于SeriesDataFrame

  • 我想要直观比较伦敦和巴黎测得的(NO_2)值。

    In [9]: air_quality.plot.scatter(x="station_london", y="station_paris", alpha=0.5)
    Out[9]: <Axes: xlabel='station_london', ylabel='station_paris'>
    
    In [10]: plt.show() 
    

    ../../_images/04_airqual_scatter.png

在使用plot函数时,除了默认的line图之外,还有许多可用于绘制数据的替代方法。让我们使用一些标准 Python 来了解可用绘图方法:

In [11]: [
 ....:    method_name
 ....:    for method_name in dir(air_quality.plot)
 ....:    if not method_name.startswith("_")
 ....: ]
 ....: 
Out[11]: 
['area',
 'bar',
 'barh',
 'box',
 'density',
 'hexbin',
 'hist',
 'kde',
 'line',
 'pie',
 'scatter'] 

注意

在许多开发环境以及 IPython 和 Jupyter Notebook 中,使用 TAB 键可以获得可用方法的概览,例如 air_quality.plot. + TAB。

其中一个选项是DataFrame.plot.box(),它指的是箱线图box方法适用于空气质量示例数据:

In [12]: air_quality.plot.box()
Out[12]: <Axes: >

In [13]: plt.show() 

../../_images/04_airqual_boxplot.png用户指南

除了默认的折线图之外,有关支持的绘图样式的介绍,请参阅用户指南中关于支持的绘图样式的部分。

  • 我想将每列分别放在单独的子图中。

    In [14]: axs = air_quality.plot.area(figsize=(12, 4), subplots=True)
    
    In [15]: plt.show() 
    

    ../../_images/04_airqual_area_subplot.png

    通过plot函数的subplots参数支持为每个数据列创建单独的子图。值得回顾每个 pandas 绘图函数中提供的内置选项。

到用户指南

更多格式选项在用户指南的绘图格式化部分有详细说明。

  • 我想进一步自定义、扩展或保存生成的图。

    In [16]: fig, axs = plt.subplots(figsize=(12, 4))
    
    In [17]: air_quality.plot.area(ax=axs)
    Out[17]: <Axes: xlabel='datetime'>
    
    In [18]: axs.set_ylabel("NO$_2$ concentration")
    Out[18]: Text(0, 0.5, 'NO$_2$ concentration')
    
    In [19]: fig.savefig("no2_concentrations.png")
    
    In [20]: plt.show() 
    

    ../../_images/04_airqual_customized.png

pandas 创建的每个绘图对象都是一个Matplotlib对象。由于 Matplotlib 提供了大量自定义绘图的选项,使 pandas 和 Matplotlib 之间的链接明确,可以将 Matplotlib 的所有功能应用于绘图。这种策略在前面的示例中应用:

fig, axs = plt.subplots(figsize=(12, 4))        # Create an empty Matplotlib Figure and Axes
air_quality.plot.area(ax=axs)                   # Use pandas to put the area plot on the prepared Figure/Axes
axs.set_ylabel("NO$_2$ concentration")          # Do any Matplotlib customization you like
fig.savefig("no2_concentrations.png")           # Save the Figure/Axes using the existing Matplotlib method.
plt.show()                                      # Display the plot 

记住

  • .plot.*方法适用于 Series 和 DataFrames。

  • 默认情况下,每列都被绘制为不同的元素(线条、箱线图等)。

  • 由 pandas 创建的任何绘图都是一个 Matplotlib 对象。

到用户指南

在可视化页面中提供了 pandas 绘图的完整概述。

如何从现有列派生新列

原文:pandas.pydata.org/docs/getting_started/intro_tutorials/05_add_columns.html

../../_images/05_newcolumn_1.svg

  • 我想要以 mg/m(³)表示伦敦站的(NO_2)浓度。

    (如果我们假设温度为 25 摄氏度,压力为 1013 百帕,转换系数为 1.882)

    In [4]: air_quality["london_mg_per_cubic"] = air_quality["station_london"] * 1.882
    
    In [5]: air_quality.head()
    Out[5]: 
     station_antwerp  ...  london_mg_per_cubic
    datetime                              ... 
    2019-05-07 02:00:00              NaN  ...               43.286
    2019-05-07 03:00:00             50.5  ...               35.758
    2019-05-07 04:00:00             45.0  ...               35.758
    2019-05-07 05:00:00              NaN  ...               30.112
    2019-05-07 06:00:00              NaN  ...                  NaN
    
    [5 rows x 4 columns] 
    

    要创建新列,请使用[]括号,新列名称位于赋值的左侧。

注意

值的计算是逐元素进行的。这意味着给定列中的所有值一次性乘以值 1.882。您不需要使用循环迭代每一行!

../../_images/05_newcolumn_2.svg

  • 我想检查巴黎与安特卫普的比值,并将结果保存在一个新列中。

    In [6]: air_quality["ratio_paris_antwerp"] = (
     ...:    air_quality["station_paris"] / air_quality["station_antwerp"]
     ...: )
     ...: 
    
    In [7]: air_quality.head()
    Out[7]: 
     station_antwerp  ...  ratio_paris_antwerp
    datetime                              ... 
    2019-05-07 02:00:00              NaN  ...                  NaN
    2019-05-07 03:00:00             50.5  ...             0.495050
    2019-05-07 04:00:00             45.0  ...             0.615556
    2019-05-07 05:00:00              NaN  ...                  NaN
    2019-05-07 06:00:00              NaN  ...                  NaN
    
    [5 rows x 5 columns] 
    

    计算再次逐元素进行,因此/适用于每行的值

还有其他数学运算符(+-*/,…)或逻辑运算符(<>==,…)逐元素工作。后者在子集数据教程中已经用于使用条件表达式过滤表的行。

如果您需要更高级的逻辑,可以通过apply()使用任意 Python 代码。

  • 我想将数据列重命名为由OpenAQ使用的相应站点标识符。

    In [8]: air_quality_renamed = air_quality.rename(
     ...:    columns={
     ...:        "station_antwerp": "BETR801",
     ...:        "station_paris": "FR04014",
     ...:        "station_london": "London Westminster",
     ...:    }
     ...: )
     ...: 
    
    In [9]: air_quality_renamed.head()
    Out[9]: 
     BETR801  FR04014  ...  london_mg_per_cubic  ratio_paris_antwerp
    datetime                               ... 
    2019-05-07 02:00:00      NaN      NaN  ...               43.286                  NaN
    2019-05-07 03:00:00     50.5     25.0  ...               35.758             0.495050
    2019-05-07 04:00:00     45.0     27.7  ...               35.758             0.615556
    2019-05-07 05:00:00      NaN     50.4  ...               30.112                  NaN
    2019-05-07 06:00:00      NaN     61.9  ...                  NaN                  NaN
    
    [5 rows x 5 columns] 
    

    rename()函数可用于行标签和列标签。提供一个字典,键是当前名称,值是要更新的新名称以更新相应的名称。

映射不应仅限于固定名称,还可以是映射函数。例如,也可以使用函数将列名称转换为小写字母:

In [10]: air_quality_renamed = air_quality_renamed.rename(columns=str.lower)

In [11]: air_quality_renamed.head()
Out[11]: 
 betr801  fr04014  ...  london_mg_per_cubic  ratio_paris_antwerp
datetime                               ... 
2019-05-07 02:00:00      NaN      NaN  ...               43.286                  NaN
2019-05-07 03:00:00     50.5     25.0  ...               35.758             0.495050
2019-05-07 04:00:00     45.0     27.7  ...               35.758             0.615556
2019-05-07 05:00:00      NaN     50.4  ...               30.112                  NaN
2019-05-07 06:00:00      NaN     61.9  ...                  NaN                  NaN

[5 rows x 5 columns] 

到用户指南

有关列或行标签重命名的详细信息,请参阅用户指南中的重命名标签部分。

记住

  • 通过在[]之间的新列名称处将输出分配给 DataFrame 来创建新列。

  • 运算是逐元素进行的,不需要循环遍历行。

  • 使用字典或函数与rename一起重命名行标签或列名称。

到用户指南

用户指南中有一个独立的部分介绍了列的添加和删除。

如何计算摘要统计信息

原文:pandas.pydata.org/docs/getting_started/intro_tutorials/06_calculate_statistics.html

聚合统计

../../_images/06_aggregate.svg

  • 泰坦尼克号乘客的平均年龄是多少?

    In [4]: titanic["Age"].mean()
    Out[4]: 29.69911764705882 
    

可用不同统计数据并可以应用于具有数值数据的列。一般情况下,操作将排除缺失数据,并默认跨行操作。

../../_images/06_reduction.svg

  • 泰坦尼克号乘客的中位年龄和票价是多少?

    In [5]: titanic[["Age", "Fare"]].median()
    Out[5]: 
    Age     28.0000
    Fare    14.4542
    dtype: float64 
    

    对于DataFrame的多列(选择两列返回一个DataFrame,参见子集数据教程)应用的统计数据针对每个数字列进行计算。

聚合统计信息可以同时计算多列。记得从第一个教程中的describe函数吗?

In [6]: titanic[["Age", "Fare"]].describe()
Out[6]: 
 Age        Fare
count  714.000000  891.000000
mean    29.699118   32.204208
std     14.526497   49.693429
min      0.420000    0.000000
25%     20.125000    7.910400
50%     28.000000   14.454200
75%     38.000000   31.000000
max     80.000000  512.329200 

与预定义的统计数据不同,可以使用DataFrame.agg()方法定义给定列的特定组合的聚合统计信息:

In [7]: titanic.agg(
 ...:    {
 ...:        "Age": ["min", "max", "median", "skew"],
 ...:        "Fare": ["min", "max", "median", "mean"],
 ...:    }
 ...: )
 ...: 
Out[7]: 
 Age        Fare
min      0.420000    0.000000
max     80.000000  512.329200
median  28.000000   14.454200
skew     0.389108         NaN
mean          NaN   32.204208 

到用户指南

在用户指南中提供了关于描述性统计的详细信息,参见描述性统计一节。

按类别分组的聚合统计

../../_images/06_groupby.svg

  • 泰坦尼克号乘客的男性与女性的平均年龄是多少?

    In [8]: titanic[["Sex", "Age"]].groupby("Sex").mean()
    Out[8]: 
     Age
    Sex 
    female  27.915709
    male    30.726645 
    

    由于我们的兴趣是每个性别的平均年龄,首先对这两列进行子选择:titanic[["Sex", "Age"]]。然后,在Sex列上应用groupby()方法,以每个类别生成一个分组。计算并返回每个性别的平均年龄。

计算给定统计数据(例如mean年龄)对于列中的每个类别(例如Sex列中的男性/女性)是一种常见模式。groupby方法用于支持这种类型的操作。这适用于更通用的split-apply-combine模式:

  • 拆分数据成组

  • 对每个组独立应用一个函数

  • 合并结果到一个数据结构中

在 pandas 中,应用和合并步骤通常一起完成。

在前面的示例中,我们明确选择了前两列。如果没有,将通过传递numeric_only=Truemean方法应用于包含数字列的每一列:

In [9]: titanic.groupby("Sex").mean(numeric_only=True)
Out[9]: 
 PassengerId  Survived    Pclass  ...     SibSp     Parch       Fare
Sex                                      ... 
female   431.028662  0.742038  2.159236  ...  0.694268  0.649682  44.479818
male     454.147314  0.188908  2.389948  ...  0.429809  0.235702  25.523893

[2 rows x 7 columns] 

获取Pclass的平均值并没有太多意义。如果我们只对每个性别的平均年龄感兴趣,那么在分组数据上也支持对列的选择(如通常所见的方括号[]):

In [10]: titanic.groupby("Sex")["Age"].mean()
Out[10]: 
Sex
female    27.915709
male      30.726645
Name: Age, dtype: float64 

../../_images/06_groupby_select_detail.svg

注意

Pclass 列包含数字数据,但实际上代表着 3 个类别(或因子),分别标有 '1'、'2' 和 '3' 的标签。对这些数据进行统计计算并不太合理。因此,pandas 提供了 Categorical 数据类型来处理这种类型的数据。更多信息请参阅用户指南中的分类数据部分。

  • 在性别和舱位等级组合中,票价的平均值是多少?

    In [11]: titanic.groupby(["Sex", "Pclass"])["Fare"].mean()
    Out[11]: 
    Sex     Pclass
    female  1         106.125798
     2          21.970121
     3          16.118810
    male    1          67.226127
     2          19.741782
     3          12.661633
    Name: Fare, dtype: float64 
    

    可以同时通过多列进行分组。将列名作为列表提供给 groupby() 方法。

转至用户指南

关于分组-应用-合并方法的全面描述可在用户指南的分组操作部分找到。

按类别计算记录数

../../_images/06_valuecounts.svg

  • 在每个舱位等级中的乘客数量是多少?

    In [12]: titanic["Pclass"].value_counts()
    Out[12]: 
    Pclass
    3    491
    1    216
    2    184
    Name: count, dtype: int64 
    

    value_counts() 方法计算列中每个类别的记录数。

该函数是一个快捷方式,因为实际上是一个组合了分组操作和对每个组内记录数进行计数的操作:

In [13]: titanic.groupby("Pclass")["Pclass"].count()
Out[13]: 
Pclass
1    216
2    184
3    491
Name: Pclass, dtype: int64 

注意

sizecount 都可以与 groupby 结合使用。而 size 包括 NaN 值并且仅提供行数(表的大小),count 则排除缺失值。在 value_counts 方法中,使用 dropna 参数来包含或排除 NaN 值。

转至用户指南

用户指南有一个专门介绍 value_counts 的部分,请参阅离散化页面。

记住

  • 聚合统计可以在整个列或行上计算。

  • groupby 提供了 分组-应用-合并 模式的强大功能。

  • value_counts 是一个方便的快捷方式,用于计算变量的每个类别中的条目数。

转至用户指南

关于分组-应用-合并方法的全面描述可在用户指南的分组操作页面找到。

聚合统计

../../_images/06_aggregate.svg

  • 泰坦尼克号乘客的平均年龄是多少?

    In [4]: titanic["Age"].mean()
    Out[4]: 29.69911764705882 
    

不同的统计数据可用,并且可以应用于具有数字数据的列。操作通常会排除缺失数据,并默认跨行操作。

../../_images/06_reduction.svg

  • 泰坦尼克号乘客的中位年龄和票价是多少?

    In [5]: titanic[["Age", "Fare"]].median()
    Out[5]: 
    Age     28.0000
    Fare    14.4542
    dtype: float64 
    

    DataFrame的多列(选择两列返回一个DataFrame,参见子集数据教程)应用的统计量是针对每个数值列进行计算的。

聚合统计可以同时针对多列进行计算。还记得第一个教程中的describe函数吗?

In [6]: titanic[["Age", "Fare"]].describe()
Out[6]: 
 Age        Fare
count  714.000000  891.000000
mean    29.699118   32.204208
std     14.526497   49.693429
min      0.420000    0.000000
25%     20.125000    7.910400
50%     28.000000   14.454200
75%     38.000000   31.000000
max     80.000000  512.329200 

可以使用DataFrame.agg()方法定义给定列的特定聚合统计量组合,而不是预定义的统计量:

In [7]: titanic.agg(
 ...:    {
 ...:        "Age": ["min", "max", "median", "skew"],
 ...:        "Fare": ["min", "max", "median", "mean"],
 ...:    }
 ...: )
 ...: 
Out[7]: 
 Age        Fare
min      0.420000    0.000000
max     80.000000  512.329200
median  28.000000   14.454200
skew     0.389108         NaN
mean          NaN   32.204208 

转到用户指南

关于描述性统计的详细信息,请参阅用户指南中的描述性统计部分。

按类别分组的聚合统计

../../_images/06_groupby.svg

  • 泰坦尼克号男性与女性乘客的平均年龄分别是多少?

    In [8]: titanic[["Sex", "Age"]].groupby("Sex").mean()
    Out[8]: 
     Age
    Sex 
    female  27.915709
    male    30.726645 
    

    由于我们感兴趣的是每个性别的平均年龄,首先对这两列进行了子选择:titanic[["Sex", "Age"]]。然后,应用groupby()方法在Sex列上进行分组,以每个类别创建一个组。计算并返回每个性别的平均年龄。

对于某一列中的每个类别(例如Sex列中的男性/女性)计算给定统计量(例如mean年龄)是一种常见模式。groupby方法用于支持此类操作。这符合更一般的split-apply-combine模式:

  • 将数据分割成组

  • 对每个分组独立应用一个函数

  • 将结果合并成数据结构

在 pandas 中,应用和合并步骤通常一起完成。

在前面的示例中,我们首先明确选择了 2 列。如果没有,则通过传递numeric_only=Truemean方法应用于包含数值列的每列:

In [9]: titanic.groupby("Sex").mean(numeric_only=True)
Out[9]: 
 PassengerId  Survived    Pclass  ...     SibSp     Parch       Fare
Sex                                      ... 
female   431.028662  0.742038  2.159236  ...  0.694268  0.649682  44.479818
male     454.147314  0.188908  2.389948  ...  0.429809  0.235702  25.523893

[2 rows x 7 columns] 

获取Pclass的平均值并没有太多意义。如果我们只对每个性别的平均年龄感兴趣,那么在分组数据上也支持对列(如常规的方括号[])进行选择:

In [10]: titanic.groupby("Sex")["Age"].mean()
Out[10]: 
Sex
female    27.915709
male      30.726645
Name: Age, dtype: float64 

../../_images/06_groupby_select_detail.svg

注意

Pclass列包含数值数据,但实际上表示 3 个类别(或因子),分别具有标签‘1’、‘2’和‘3’。对这些进行统计没有太多意义。因此,pandas 提供了Categorical数据类型来处理这种类型的数据。更多信息请参阅用户指南中的分类数据部分。

  • 每个性别和舱位等级组合的平均票价是多少?

    In [11]: titanic.groupby(["Sex", "Pclass"])["Fare"].mean()
    Out[11]: 
    Sex     Pclass
    female  1         106.125798
     2          21.970121
     3          16.118810
    male    1          67.226127
     2          19.741782
     3          12.661633
    Name: Fare, dtype: float64 
    

    分组可以同时按多个列进行。将列名作为列表提供给 groupby() 方法。

用户指南

关于分组操作的分割-应用-组合方法的完整描述,请参阅用户指南中的分组操作部分。

按类别计算记录数

../../_images/06_valuecounts.svg

  • 每个客舱等级的乘客数量是多少?

    In [12]: titanic["Pclass"].value_counts()
    Out[12]: 
    Pclass
    3    491
    1    216
    2    184
    Name: count, dtype: int64 
    

    value_counts() 方法计算列中每个类别的记录数。

该函数是一个快捷方式,实际上是一个组合操作,结合了每个组内记录数的分组操作:

In [13]: titanic.groupby("Pclass")["Pclass"].count()
Out[13]: 
Pclass
1    216
2    184
3    491
Name: Pclass, dtype: int64 

注意

sizecount 都可以与 groupby 结合使用。而 size 包括 NaN 值并且仅提供行数(表的大小),count 排除缺失值。在 value_counts 方法中,使用 dropna 参数来包含或排除 NaN 值。

用户指南

用户指南有一个专门介绍value_counts的部分,请参阅离散化页面。

记住

  • 可以在整个列或行上计算聚合统计信息。

  • groupby 提供了分割-应用-组合模式的强大功能。

  • value_counts 是计算变量每个类别中条目数量的便捷快捷方式。

用户指南

关于分组操作的用户指南页面中提供了关于分割-应用-组合方法的完整描述。

如何重新排列表格布局

原文:pandas.pydata.org/docs/getting_started/intro_tutorials/07_reshape_table_layout.html

排序表格行

  • 我想根据乘客的年龄对泰坦尼克号数据进行排序。

    In [6]: titanic.sort_values(by="Age").head()
    Out[6]: 
     PassengerId  Survived  Pclass  ...     Fare Cabin  Embarked
    803          804         1       3  ...   8.5167   NaN         C
    755          756         1       2  ...  14.5000   NaN         S
    644          645         1       3  ...  19.2583   NaN         C
    469          470         1       3  ...  19.2583   NaN         C
    78            79         1       2  ...  29.0000   NaN         S
    
    [5 rows x 12 columns] 
    
  • 我想根据舱位等级和年龄按降序对泰坦尼克号数据进行排序。

    In [7]: titanic.sort_values(by=['Pclass', 'Age'], ascending=False).head()
    Out[7]: 
     PassengerId  Survived  Pclass  ...    Fare Cabin  Embarked
    851          852         0       3  ...  7.7750   NaN         S
    116          117         0       3  ...  7.7500   NaN         Q
    280          281         0       3  ...  7.7500   NaN         Q
    483          484         1       3  ...  9.5875   NaN         S
    326          327         0       3  ...  6.2375   NaN         S
    
    [5 rows x 12 columns] 
    

    使用DataFrame.sort_values(),表格中的行根据定义的列进行排序。索引将遵循行顺序。

用户指南

有关表格排序的更多详细信息,请参阅用户指南中关于数据排序的部分。

从长表格格式到宽表格格式

让我们使用空气质量数据集的一个小子集。我们关注(NO_2)数据,并仅使用每个位置的前两个测量值(即每个组���头部)。数据子集将被称为no2_subset

# filter for no2 data only
In [8]: no2 = air_quality[air_quality["parameter"] == "no2"] 
# use 2 measurements (head) for each location (groupby)
In [9]: no2_subset = no2.sort_index().groupby(["location"]).head(2)

In [10]: no2_subset
Out[10]: 
 city country  ... value   unit
date.utc                                      ... 
2019-04-09 01:00:00+00:00  Antwerpen      BE  ...  22.5  µg/m³
2019-04-09 01:00:00+00:00      Paris      FR  ...  24.4  µg/m³
2019-04-09 02:00:00+00:00     London      GB  ...  67.0  µg/m³
2019-04-09 02:00:00+00:00  Antwerpen      BE  ...  53.5  µg/m³
2019-04-09 02:00:00+00:00      Paris      FR  ...  27.4  µg/m³
2019-04-09 03:00:00+00:00     London      GB  ...  67.0  µg/m³

[6 rows x 6 columns] 

../../_images/07_pivot.svg

  • 我想要三个站点的值分别作为相邻的列。

    In [11]: no2_subset.pivot(columns="location", values="value")
    Out[11]: 
    location                   BETR801  FR04014  London Westminster
    date.utc 
    2019-04-09 01:00:00+00:00     22.5     24.4                 NaN
    2019-04-09 02:00:00+00:00     53.5     27.4                67.0
    2019-04-09 03:00:00+00:00      NaN      NaN                67.0 
    

    pivot()函数纯粹是对数据的重新排列:每个索引/列组合需要一个单一值。

由于 pandas 支持多列绘图(参见绘图教程),因此从表格格式转换为表格格式可以同时绘制不同时间序列的图表:

In [12]: no2.head()
Out[12]: 
 city country location parameter  value   unit
date.utc 
2019-06-21 00:00:00+00:00  Paris      FR  FR04014       no2   20.0  µg/m³
2019-06-20 23:00:00+00:00  Paris      FR  FR04014       no2   21.8  µg/m³
2019-06-20 22:00:00+00:00  Paris      FR  FR04014       no2   26.5  µg/m³
2019-06-20 21:00:00+00:00  Paris      FR  FR04014       no2   24.9  µg/m³
2019-06-20 20:00:00+00:00  Paris      FR  FR04014       no2   21.4  µg/m³ 
In [13]: no2.pivot(columns="location", values="value").plot()
Out[13]: <Axes: xlabel='date.utc'> 

../../_images/7_reshape_columns.png

注意

当未定义index参数时,将使用现有索引(行标签)。

用户指南

有关pivot()的更多信息,请参阅用户指南中关于数据透视表对象的部分。

透视表

../../_images/07_pivot_table.svg

  • 我想要表格形式中每个站点的(NO_2)和(PM_{2.5})的平均浓度。

    In [14]: air_quality.pivot_table(
     ....:    values="value", index="location", columns="parameter", aggfunc="mean"
     ....: )
     ....: 
    Out[14]: 
    parameter                 no2       pm25
    location 
    BETR801             26.950920  23.169492
    FR04014             29.374284        NaN
    London Westminster  29.740050  13.443568 
    

    pivot()的情况下,数据只是重新排列。当需要聚合多个值(在这种特定情况下,不同时间步长上的值)时,可以使用pivot_table(),提供一个聚合函数(例如均值)来组合这些值。

透视表是电子表格软件中一个众所周知的概念。当对每个变量的行/列边距(小计)感兴趣时,请将margins参数设置为True

In [15]: air_quality.pivot_table(
 ....:    values="value",
 ....:    index="location",
 ....:    columns="parameter",
 ....:    aggfunc="mean",
 ....:    margins=True,
 ....: )
 ....: 
Out[15]: 
parameter                 no2       pm25        All
location 
BETR801             26.950920  23.169492  24.982353
FR04014             29.374284        NaN  29.374284
London Westminster  29.740050  13.443568  21.491708
All                 29.430316  14.386849  24.222743 

用户指南

有关pivot_table()的更多信息,请参阅用户指南中关于数据透视表的部分。

注意

如果你在想,pivot_table()确实直接与groupby()相关联。可以通过在parameterlocation上进行分组来得到相同的结果:

air_quality.groupby(["parameter", "location"])[["value"]].mean() 

转至用户指南

从宽格式到长格式

从前一节创建的宽格式表重新开始,我们使用reset_index()DataFrame添加新索引。

In [16]: no2_pivoted = no2.pivot(columns="location", values="value").reset_index()

In [17]: no2_pivoted.head()
Out[17]: 
location                  date.utc  BETR801  FR04014  London Westminster
0        2019-04-09 01:00:00+00:00     22.5     24.4                 NaN
1        2019-04-09 02:00:00+00:00     53.5     27.4                67.0
2        2019-04-09 03:00:00+00:00     54.5     34.2                67.0
3        2019-04-09 04:00:00+00:00     34.5     48.5                41.0
4        2019-04-09 05:00:00+00:00     46.5     59.5                41.0 

../../_images/07_melt.svg

  • 我想将所有空气质量(NO_2)测量值收集到单独的一列中(长格式)。

    In [18]: no_2 = no2_pivoted.melt(id_vars="date.utc")
    
    In [19]: no_2.head()
    Out[19]: 
     date.utc location  value
    0 2019-04-09 01:00:00+00:00  BETR801   22.5
    1 2019-04-09 02:00:00+00:00  BETR801   53.5
    2 2019-04-09 03:00:00+00:00  BETR801   54.5
    3 2019-04-09 04:00:00+00:00  BETR801   34.5
    4 2019-04-09 05:00:00+00:00  BETR801   46.5 
    

    DataFrame上调用pandas.melt()方法将数据表从宽格式转换为长格式。列标题变为新创建列中的变量名。

解决方案是如何应用pandas.melt()的简短版本。该方法将所有未在id_vars中提及的列融合成两列:一列是列标题名称,另一列是值本身。后一列默认名称为value

传递给pandas.melt()的参数可以更详细地定义:

In [20]: no_2 = no2_pivoted.melt(
 ....:    id_vars="date.utc",
 ....:    value_vars=["BETR801", "FR04014", "London Westminster"],
 ....:    value_name="NO_2",
 ....:    var_name="id_location",
 ....: )
 ....: 

In [21]: no_2.head()
Out[21]: 
 date.utc id_location  NO_2
0 2019-04-09 01:00:00+00:00     BETR801  22.5
1 2019-04-09 02:00:00+00:00     BETR801  53.5
2 2019-04-09 03:00:00+00:00     BETR801  54.5
3 2019-04-09 04:00:00+00:00     BETR801  34.5
4 2019-04-09 05:00:00+00:00     BETR801  46.5 

附加参数具有以下效果:

  • value_vars定义要融合在一起的列

  • value_name为值列提供自定义列名,而不是默认列名value

  • var_name为收集列标题名称的列提供自定义列名。否则,它将采用索引名称或默认值variable

因此,参数value_namevar_name只是两个生成列的用户定义名称。要融合的列由id_varsvalue_vars定义。

转至用户指南

使用pandas.melt()将数据从宽格式转换为长格式在用户指南中有详细说明,参见融合重塑部分。

记住

  • 支持按一个或多个列排序的sort_values

  • pivot函数纯粹是数据重构,pivot_table支持聚合。

  • pivot的反向操作(从长格式到宽格式)是melt(从宽格式到长格式)。

转至用户指南

用户指南中关于重塑和数据透视的页面提供了完整的概述。

排序表行

  • 我想根据乘客的年龄对泰坦尼克号数据进行排序。

    In [6]: titanic.sort_values(by="Age").head()
    Out[6]: 
     PassengerId  Survived  Pclass  ...     Fare Cabin  Embarked
    803          804         1       3  ...   8.5167   NaN         C
    755          756         1       2  ...  14.5000   NaN         S
    644          645         1       3  ...  19.2583   NaN         C
    469          470         1       3  ...  19.2583   NaN         C
    78            79         1       2  ...  29.0000   NaN         S
    
    [5 rows x 12 columns] 
    
  • 我想根据船舱等级和年龄按降序对泰坦尼克号数据进行排序。

    In [7]: titanic.sort_values(by=['Pclass', 'Age'], ascending=False).head()
    Out[7]: 
     PassengerId  Survived  Pclass  ...    Fare Cabin  Embarked
    851          852         0       3  ...  7.7750   NaN         S
    116          117         0       3  ...  7.7500   NaN         Q
    280          281         0       3  ...  7.7500   NaN         Q
    483          484         1       3  ...  9.5875   NaN         S
    326          327         0       3  ...  6.2375   NaN         S
    
    [5 rows x 12 columns] 
    

    使用DataFrame.sort_values(),表中的行将根据定义的列进行排序。索引将遵循行顺序。

转到用户指南

有关表格排序的更多详细信息,请参阅用户指南中关于数据排序的部分。

从长表格格式到宽表格格式

让我们使用空气质量数据集的一个小子集。我们关注(NO_2)数据,并且只使用每个位置的前两次测量(即每个组的头部)。数据子集将被称为no2_subset

# filter for no2 data only
In [8]: no2 = air_quality[air_quality["parameter"] == "no2"] 
# use 2 measurements (head) for each location (groupby)
In [9]: no2_subset = no2.sort_index().groupby(["location"]).head(2)

In [10]: no2_subset
Out[10]: 
 city country  ... value   unit
date.utc                                      ... 
2019-04-09 01:00:00+00:00  Antwerpen      BE  ...  22.5  µg/m³
2019-04-09 01:00:00+00:00      Paris      FR  ...  24.4  µg/m³
2019-04-09 02:00:00+00:00     London      GB  ...  67.0  µg/m³
2019-04-09 02:00:00+00:00  Antwerpen      BE  ...  53.5  µg/m³
2019-04-09 02:00:00+00:00      Paris      FR  ...  27.4  µg/m³
2019-04-09 03:00:00+00:00     London      GB  ...  67.0  µg/m³

[6 rows x 6 columns] 

../../_images/07_pivot.svg

  • 我想要三个站点的值作为相邻的单独列��

    In [11]: no2_subset.pivot(columns="location", values="value")
    Out[11]: 
    location                   BETR801  FR04014  London Westminster
    date.utc 
    2019-04-09 01:00:00+00:00     22.5     24.4                 NaN
    2019-04-09 02:00:00+00:00     53.5     27.4                67.0
    2019-04-09 03:00:00+00:00      NaN      NaN                67.0 
    

    pivot()函数纯粹是对数据的重塑:每个索引/列组合需要一个单一值。

由于 pandas 支持多列的绘图(请参阅绘图教程),因此从表格格式转换为表格格式使得可以同时绘制不同时间序列:

In [12]: no2.head()
Out[12]: 
 city country location parameter  value   unit
date.utc 
2019-06-21 00:00:00+00:00  Paris      FR  FR04014       no2   20.0  µg/m³
2019-06-20 23:00:00+00:00  Paris      FR  FR04014       no2   21.8  µg/m³
2019-06-20 22:00:00+00:00  Paris      FR  FR04014       no2   26.5  µg/m³
2019-06-20 21:00:00+00:00  Paris      FR  FR04014       no2   24.9  µg/m³
2019-06-20 20:00:00+00:00  Paris      FR  FR04014       no2   21.4  µg/m³ 
In [13]: no2.pivot(columns="location", values="value").plot()
Out[13]: <Axes: xlabel='date.utc'> 

../../_images/7_reshape_columns.png

注意

当未定义index参数时,将使用现有的索引(行标签)。

转到用户指南

有关pivot()的更多信息,请参阅用户指南中关于数据透视表 DataFrame 对象的部分。

数据透视表

../../_images/07_pivot_table.svg

  • 我想要每个站点中(NO_2)和(PM_{2.5})的平均浓度以表格形式呈现。

    In [14]: air_quality.pivot_table(
     ....:    values="value", index="location", columns="parameter", aggfunc="mean"
     ....: )
     ....: 
    Out[14]: 
    parameter                 no2       pm25
    location 
    BETR801             26.950920  23.169492
    FR04014             29.374284        NaN
    London Westminster  29.740050  13.443568 
    

    pivot()的情况下,数据仅被重新排列。当需要聚合多个值(在这种特定情况下,不同时间步的值)时,可以使用pivot_table(),提供一个聚合函数(例如平均值)来组合这些值。

数据透视表是电子表格软件中一个众所周知的概念。当对每个变量的行/列边距(小计)感兴趣时,请将margins参数设置为True

In [15]: air_quality.pivot_table(
 ....:    values="value",
 ....:    index="location",
 ....:    columns="parameter",
 ....:    aggfunc="mean",
 ....:    margins=True,
 ....: )
 ....: 
Out[15]: 
parameter                 no2       pm25        All
location 
BETR801             26.950920  23.169492  24.982353
FR04014             29.374284        NaN  29.374284
London Westminster  29.740050  13.443568  21.491708
All                 29.430316  14.386849  24.222743 

转到用户指南

欲了解有关pivot_table()的更多信息,请参阅用户指南中关于数据透视表的部分。

注意

如果你在想,pivot_table()确实直接链接到groupby()。相同的结果可以通过在parameterlocation上进行分组来得到:

air_quality.groupby(["parameter", "location"])[["value"]].mean() 

转到用户指南

宽到长格式

从前一节创建的宽格式表重新开始,我们使用reset_index()DataFrame添加新索引。

In [16]: no2_pivoted = no2.pivot(columns="location", values="value").reset_index()

In [17]: no2_pivoted.head()
Out[17]: 
location                  date.utc  BETR801  FR04014  London Westminster
0        2019-04-09 01:00:00+00:00     22.5     24.4                 NaN
1        2019-04-09 02:00:00+00:00     53.5     27.4                67.0
2        2019-04-09 03:00:00+00:00     54.5     34.2                67.0
3        2019-04-09 04:00:00+00:00     34.5     48.5                41.0
4        2019-04-09 05:00:00+00:00     46.5     59.5                41.0 

../../_images/07_melt.svg

  • 我想将所有空气质量(\(NO_2\))测量值收集到单独的一列中(长格式)。

    In [18]: no_2 = no2_pivoted.melt(id_vars="date.utc")
    
    In [19]: no_2.head()
    Out[19]: 
     date.utc location  value
    0 2019-04-09 01:00:00+00:00  BETR801   22.5
    1 2019-04-09 02:00:00+00:00  BETR801   53.5
    2 2019-04-09 03:00:00+00:00  BETR801   54.5
    3 2019-04-09 04:00:00+00:00  BETR801   34.5
    4 2019-04-09 05:00:00+00:00  BETR801   46.5 
    

    pandas.melt()方法在DataFrame上将数据表从宽格式转换为长格式。列标题变为新创建列中的变量名称。

解决方案是如何应用pandas.melt()的简短版本。该方法将未在id_vars中提及的所有列融合到两列中:一列是列标题名称,另一列是值本身。后一列默认命名为value

传递给pandas.melt()的参数可以更详细地定义:

In [20]: no_2 = no2_pivoted.melt(
 ....:    id_vars="date.utc",
 ....:    value_vars=["BETR801", "FR04014", "London Westminster"],
 ....:    value_name="NO_2",
 ....:    var_name="id_location",
 ....: )
 ....: 

In [21]: no_2.head()
Out[21]: 
 date.utc id_location  NO_2
0 2019-04-09 01:00:00+00:00     BETR801  22.5
1 2019-04-09 02:00:00+00:00     BETR801  53.5
2 2019-04-09 03:00:00+00:00     BETR801  54.5
3 2019-04-09 04:00:00+00:00     BETR801  34.5
4 2019-04-09 05:00:00+00:00     BETR801  46.5 

附加参数具有以下效果:

  • value_vars定义要融合在一起的列

  • value_name为值列提供自定义列名,而不是默认列名value

  • var_name为收集列标题名称的列提供自定义列名。否则,它将采用索引名称或默认的variable

因此,参数value_namevar_name只是生成的用户定义列 ocolumns 的名称。要融合的列由id_varsvalue_vars定义。

转到用户指南

使用pandas.melt()从宽格式转换为长格式在用户指南中有详细说明,参见通过 melt 进行重塑部分。

记住

  • 通过sort_values支持按一个或多个列进行排序。

  • pivot函数纯粹是数据重构,pivot_table支持聚合。

  • pivot的反向(从长到宽格式)是melt(从宽到长格式)。

转到用户指南

完整概述可在关于重塑和旋转的用户指南页面上找到。

如何合并来自多个表的数据

原文:pandas.pydata.org/docs/getting_started/intro_tutorials/08_combine_dataframes.html

连接对象

../../_images/08_concat_row.svg

  • 我想将(NO_2)和(PM_{25})的测量数据,两个结构相似的表,合并到一个表中。

    In [8]: air_quality = pd.concat([air_quality_pm25, air_quality_no2], axis=0)
    
    In [9]: air_quality.head()
    Out[9]: 
     date.utc location parameter  value
    0  2019-06-18 06:00:00+00:00  BETR801      pm25   18.0
    1  2019-06-17 08:00:00+00:00  BETR801      pm25    6.5
    2  2019-06-17 07:00:00+00:00  BETR801      pm25   18.5
    3  2019-06-17 06:00:00+00:00  BETR801      pm25   16.0
    4  2019-06-17 05:00:00+00:00  BETR801      pm25    7.5 
    

    concat()函数执行多个表沿一个轴(行或列)的连接操作。

默认情况下,沿轴 0 进行连接,因此结果表合并了输入表的行。让我们检查原始表和连接表的形状以验证操作:

In [10]: print('Shape of the ``air_quality_pm25`` table: ', air_quality_pm25.shape)
Shape of the ``air_quality_pm25`` table:  (1110, 4)

In [11]: print('Shape of the ``air_quality_no2`` table: ', air_quality_no2.shape)
Shape of the ``air_quality_no2`` table:  (2068, 4)

In [12]: print('Shape of the resulting ``air_quality`` table: ', air_quality.shape)
Shape of the resulting ``air_quality`` table:  (3178, 4) 

因此,结果表有 3178 行= 1110 + 2068 行。

注意

axis参数将返回可以沿着应用的多个 pandas 方法。DataFrame有两个对应的轴:第一个沿着行垂直向下运行(轴 0),第二个沿着列水平运行(轴 1)。大多数操作(如连接或汇总统计)默认跨行(轴 0),但也可以跨列应用。

根据日期时间信息对表进行排序也说明了两个表的组合,parameter列定义了表的来源(来自air_quality_no2表的no2或来自air_quality_pm25表的pm25):

In [13]: air_quality = air_quality.sort_values("date.utc")

In [14]: air_quality.head()
Out[14]: 
 date.utc            location parameter  value
2067  2019-05-07 01:00:00+00:00  London Westminster       no2   23.0
1003  2019-05-07 01:00:00+00:00             FR04014       no2   25.0
100   2019-05-07 01:00:00+00:00             BETR801      pm25   12.5
1098  2019-05-07 01:00:00+00:00             BETR801       no2   50.5
1109  2019-05-07 01:00:00+00:00  London Westminster      pm25    8.0 

在这个具体的例子中,数据提供的parameter列确保可以识别原始表中的每个表。这并不总是这样。concat函数提供了一个方便的解决方案,使用keys参数添加一个额外的(分层)行索引。例如:

In [15]: air_quality_ = pd.concat([air_quality_pm25, air_quality_no2], keys=["PM25", "NO2"])

In [16]: air_quality_.head()
Out[16]: 
 date.utc location parameter  value
PM25 0  2019-06-18 06:00:00+00:00  BETR801      pm25   18.0
 1  2019-06-17 08:00:00+00:00  BETR801      pm25    6.5
 2  2019-06-17 07:00:00+00:00  BETR801      pm25   18.5
 3  2019-06-17 06:00:00+00:00  BETR801      pm25   16.0
 4  2019-06-17 05:00:00+00:00  BETR801      pm25    7.5 

注意

在这些教程中尚未提到同时存在多个行/列索引。分层索引MultiIndex是一个用于分析更高维数据的高级且强大的 pandas 功能。

多重索引超出了本 pandas 入门范围。暂时记住函数reset_index可以用于将索引的任何级别转换为列,例如air_quality.reset_index(level=0)

到用户指南

随意深入探讨高级索引用户指南部分中的多重索引世界。

到用户指南

在对象连接部分提供了有关表连接(行和列连接)的更多选项以及如何使用concat定义在其他轴上的索引逻辑(并集或交集)。

使用共同标识符连接表

../../_images/08_merge_left.svg

  • 将由站点元数据表提供的站点坐标添加到测量表中的相应行。

    警告

    空气质量测量站坐标存储在数据文件air_quality_stations.csv中,使用py-openaq包下载。

    In [17]: stations_coord = pd.read_csv("data/air_quality_stations.csv")
    
    In [18]: stations_coord.head()
    Out[18]: 
     location  coordinates.latitude  coordinates.longitude
    0  BELAL01              51.23619                4.38522
    1  BELHB23              51.17030                4.34100
    2  BELLD01              51.10998                5.00486
    3  BELLD02              51.12038                5.02155
    4  BELR833              51.32766                4.36226 
    

    注意

    此示例中使用的站点(FR04014、BETR801 和 London Westminster)只是元数据表中列出的三个条目。我们只想将这三个站点的坐标添加到测量表中,每个站点对应air_quality表的相应行。

    In [19]: air_quality.head()
    Out[19]: 
     date.utc            location parameter  value
    2067  2019-05-07 01:00:00+00:00  London Westminster       no2   23.0
    1003  2019-05-07 01:00:00+00:00             FR04014       no2   25.0
    100   2019-05-07 01:00:00+00:00             BETR801      pm25   12.5
    1098  2019-05-07 01:00:00+00:00             BETR801       no2   50.5
    1109  2019-05-07 01:00:00+00:00  London Westminster      pm25    8.0 
    
    In [20]: air_quality = pd.merge(air_quality, stations_coord, how="left", on="location")
    
    In [21]: air_quality.head()
    Out[21]: 
     date.utc  ... coordinates.longitude
    0  2019-05-07 01:00:00+00:00  ...              -0.13193
    1  2019-05-07 01:00:00+00:00  ...               2.39390
    2  2019-05-07 01:00:00+00:00  ...               2.39390
    3  2019-05-07 01:00:00+00:00  ...               4.43182
    4  2019-05-07 01:00:00+00:00  ...               4.43182
    
    [5 rows x 6 columns] 
    

    使用merge()函数,对于air_quality表中的每一行,从air_quality_stations_coord表中添加相应的坐标。这两个表都有一个共同的location列,用作组合信息的键。通过选择left连接,最终表中只包含air_quality(左)表中可用的位置,即 FR04014、BETR801 和 London Westminster。merge函数支持类似数据库风格操作的多个连接选项。

  • 将参数元数据表提供的参数完整描述和名称添加到测量表中。

    警告

    空气质量参数元数据存储在数据文件air_quality_parameters.csv中,使用py-openaq包下载。

    In [22]: air_quality_parameters = pd.read_csv("data/air_quality_parameters.csv")
    
    In [23]: air_quality_parameters.head()
    Out[23]: 
     id                                        description  name
    0    bc                                       Black Carbon    BC
    1    co                                    Carbon Monoxide    CO
    2   no2                                   Nitrogen Dioxide   NO2
    3    o3                                              Ozone    O3
    4  pm10  Particulate matter less than 10 micrometers in...  PM10 
    
    In [24]: air_quality = pd.merge(air_quality, air_quality_parameters,
     ....:                       how='left', left_on='parameter', right_on='id')
     ....: 
    
    In [25]: air_quality.head()
    Out[25]: 
     date.utc  ...   name
    0  2019-05-07 01:00:00+00:00  ...    NO2
    1  2019-05-07 01:00:00+00:00  ...    NO2
    2  2019-05-07 01:00:00+00:00  ...    NO2
    3  2019-05-07 01:00:00+00:00  ...  PM2.5
    4  2019-05-07 01:00:00+00:00  ...    NO2
    
    [5 rows x 9 columns] 
    

    与前面的示例相比,没有共同的列名。但是,在air_quality表中的parameter列和air_quality_parameters_name中的id列都以共同格式提供了测量变量。这里使用left_onright_on参数(而不仅仅是on)来建立两个表之间的链接。

用户指南

pandas 还支持内部、外部和右连接。有关表的连接/合并的更多信息,请参阅用户指南中关于数据库风格表合并的部分。或者查看与 SQL 的比较页面。

记住

  • 可以使用concat函数沿着列或行将多个表连接起来。

  • 对于类似数据库的表合并/连接,请使用merge函数。

用户指南

查看用户指南,了解各种合并数据表的方法的详细描述。

连接对象

../../_images/08_concat_row.svg

  • 我想将(NO_2)和(PM_{25})的测量值,两个结构相似的表,合并到一个表中。

    In [8]: air_quality = pd.concat([air_quality_pm25, air_quality_no2], axis=0)
    
    In [9]: air_quality.head()
    Out[9]: 
     date.utc location parameter  value
    0  2019-06-18 06:00:00+00:00  BETR801      pm25   18.0
    1  2019-06-17 08:00:00+00:00  BETR801      pm25    6.5
    2  2019-06-17 07:00:00+00:00  BETR801      pm25   18.5
    3  2019-06-17 06:00:00+00:00  BETR801      pm25   16.0
    4  2019-06-17 05:00:00+00:00  BETR801      pm25    7.5 
    

    concat()函数执行多个表沿一个轴(行或列)的连接操作。

默认情况下,沿轴 0 进行连接,因此生成的表将合并输入表的行。让我们检查原始表和连接表的形状以验证操作:

In [10]: print('Shape of the ``air_quality_pm25`` table: ', air_quality_pm25.shape)
Shape of the ``air_quality_pm25`` table:  (1110, 4)

In [11]: print('Shape of the ``air_quality_no2`` table: ', air_quality_no2.shape)
Shape of the ``air_quality_no2`` table:  (2068, 4)

In [12]: print('Shape of the resulting ``air_quality`` table: ', air_quality.shape)
Shape of the resulting ``air_quality`` table:  (3178, 4) 

因此,结果表有 3178 = 1110 + 2068 行。

注意

axis参数将返回一些可以沿着轴应用的 pandas 方法。DataFrame有两个对应的轴:第一个沿着行垂直向下运行(轴 0),第二个沿着列水平运行(轴 1)。大多数操作(如连接或汇总统计)默认是沿着行(轴 0)进行的,但也可以沿着列进行。

根据日期时间信息对表进行排序也说明了两个表的组合,其中parameter列定义了表的来源(air_quality_no2表中的no2air_quality_pm25表中的pm25):

In [13]: air_quality = air_quality.sort_values("date.utc")

In [14]: air_quality.head()
Out[14]: 
 date.utc            location parameter  value
2067  2019-05-07 01:00:00+00:00  London Westminster       no2   23.0
1003  2019-05-07 01:00:00+00:00             FR04014       no2   25.0
100   2019-05-07 01:00:00+00:00             BETR801      pm25   12.5
1098  2019-05-07 01:00:00+00:00             BETR801       no2   50.5
1109  2019-05-07 01:00:00+00:00  London Westminster      pm25    8.0 

在这个特定示例中,数据提供的parameter列确保可以识别原始表中的每个表。这并非总是如此。concat函数提供了一个方便的解决方案,使用keys参数添加一个额外的(分层)行索引。例如:

In [15]: air_quality_ = pd.concat([air_quality_pm25, air_quality_no2], keys=["PM25", "NO2"])

In [16]: air_quality_.head()
Out[16]: 
 date.utc location parameter  value
PM25 0  2019-06-18 06:00:00+00:00  BETR801      pm25   18.0
 1  2019-06-17 08:00:00+00:00  BETR801      pm25    6.5
 2  2019-06-17 07:00:00+00:00  BETR801      pm25   18.5
 3  2019-06-17 06:00:00+00:00  BETR801      pm25   16.0
 4  2019-06-17 05:00:00+00:00  BETR801      pm25    7.5 

注意

在这些教程中没有提到同时存在多个行/列索引。层次化索引MultiIndex是用于分析高维数据的高级且强大的 pandas 功能。

多重索引超出了本 pandas 介绍的范围。暂时记住函数reset_index可用于将索引的任何级别转换为列,例如air_quality.reset_index(level=0)

用户指南

随时深入研究用户指南中关于高级索引的多重索引世界。

用户指南

提供了有关表连接的更多选项(按行和列)以及如何使用concat来定义索引在其他轴上的逻辑(并集或交集)的信息,请参阅对象连接部分。

使用共同标识符连接表

../../_images/08_merge_left.svg

  • 将由站点元数据表提供的站点坐标添加到测量表中的相应行中。

    警告

    空气质量测量站点坐标存储在数据文件air_quality_stations.csv中,使用py-openaq包下载。

    In [17]: stations_coord = pd.read_csv("data/air_quality_stations.csv")
    
    In [18]: stations_coord.head()
    Out[18]: 
     location  coordinates.latitude  coordinates.longitude
    0  BELAL01              51.23619                4.38522
    1  BELHB23              51.17030                4.34100
    2  BELLD01              51.10998                5.00486
    3  BELLD02              51.12038                5.02155
    4  BELR833              51.32766                4.36226 
    

    注意

    此示例中使用的站点(FR04014、BETR801 和 London Westminster)只是元数据表中列出的三个条目。我们只想将这三个站点的坐标添加到测量表中,每个站点对应air_quality表的相应行。

    In [19]: air_quality.head()
    Out[19]: 
     date.utc            location parameter  value
    2067  2019-05-07 01:00:00+00:00  London Westminster       no2   23.0
    1003  2019-05-07 01:00:00+00:00             FR04014       no2   25.0
    100   2019-05-07 01:00:00+00:00             BETR801      pm25   12.5
    1098  2019-05-07 01:00:00+00:00             BETR801       no2   50.5
    1109  2019-05-07 01:00:00+00:00  London Westminster      pm25    8.0 
    
    In [20]: air_quality = pd.merge(air_quality, stations_coord, how="left", on="location")
    
    In [21]: air_quality.head()
    Out[21]: 
     date.utc  ... coordinates.longitude
    0  2019-05-07 01:00:00+00:00  ...              -0.13193
    1  2019-05-07 01:00:00+00:00  ...               2.39390
    2  2019-05-07 01:00:00+00:00  ...               2.39390
    3  2019-05-07 01:00:00+00:00  ...               4.43182
    4  2019-05-07 01:00:00+00:00  ...               4.43182
    
    [5 rows x 6 columns] 
    

    使用merge()函数,对于air_quality表中的每一行,从air_quality_stations_coord表中添加相应的坐标。这两个表格都有一个名为location的列,用作合并信息的关键。通过选择left连接,只有在air_quality(左)表中可用的位置,即 FR04014、BETR801 和 London Westminster,最终出现在结果表中。merge函数支持类似数据库操作的多个连接选项。

  • 将参数元数据表提供的参数完整描述和名称添加到测量表中。

    警告

    空气质量参数元数据存储在数据文件air_quality_parameters.csv中,使用py-openaq包下载。

    In [22]: air_quality_parameters = pd.read_csv("data/air_quality_parameters.csv")
    
    In [23]: air_quality_parameters.head()
    Out[23]: 
     id                                        description  name
    0    bc                                       Black Carbon    BC
    1    co                                    Carbon Monoxide    CO
    2   no2                                   Nitrogen Dioxide   NO2
    3    o3                                              Ozone    O3
    4  pm10  Particulate matter less than 10 micrometers in...  PM10 
    
    In [24]: air_quality = pd.merge(air_quality, air_quality_parameters,
     ....:                       how='left', left_on='parameter', right_on='id')
     ....: 
    
    In [25]: air_quality.head()
    Out[25]: 
     date.utc  ...   name
    0  2019-05-07 01:00:00+00:00  ...    NO2
    1  2019-05-07 01:00:00+00:00  ...    NO2
    2  2019-05-07 01:00:00+00:00  ...    NO2
    3  2019-05-07 01:00:00+00:00  ...  PM2.5
    4  2019-05-07 01:00:00+00:00  ...    NO2
    
    [5 rows x 9 columns] 
    

    与前面的示例相比,这里没有共同的列名。然而,在air_quality表中的parameter列和air_quality_parameters_name中的id列都以共同的格式提供了测量变量。这里使用left_onright_on参数(而不仅仅是on)来建立两个表格之间的链接。

至用户指南

pandas 还支持内部、外部和右连接。有关表格连接/合并的更多信息,请参阅用户指南中关于数据库风格表格合并的部分。或查看与 SQL 的比较页面。

记住

  • 可以使用concat函数在列方向和行方向上连接多个表格。

  • 对于类似数据库的表格合并/连接,请使用merge函数。

至用户指南

请参阅用户指南,了解各种数据表合并设施的详细描述。

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

原文:pandas.pydata.org/docs/getting_started/intro_tutorials/09_timeseries.html

使用 pandas 日期时间属性

  • 我想将列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.datetimepandas.Timestamp

注意

由于许多数据集中的一列包含日期时间信息,pandas 输入函数如pandas.read_csv()pandas.read_json()在读取数据时可以使用parse_dates参数和要读取为 Timestamp 的列的列表进行日期转换:

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处理日期时间使我们能够计算日期信息并使其可比较。因此,我们可以用这个来获取时间序列的长度:

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

结果是一个类似于标准 Python 库中的datetime.timedeltapandas.Timedelta对象,定义了一个时间持续。

转到用户指南

pandas 支持的各种时间概念在用户指南的时间相关概念部分中有解释。

  • 我想向DataFrame添加一个只包含测量月份的新列

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

    通过使用日期的Timestamp对象,pandas 提供了许多与时间相关的属性。例如month,还有yearquarter���。所有这些属性都可以通过dt访问器访问。

转到用户指南

现有日期属性的概述在时间和日期组件概述表中给出。关于dt访问器返回类似日期时间的属性的更多细节在 dt 访问器的专用部分中有解释。

  • 每天每个测量位置的平均(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 
    

    还记得来自统计计算教程的groupby提供的分割-应用-合并模式吗?在这里,我们想要计算给定统计量(例如均值(NO_2))每个工作日每个测量位置的数据。为了按工作日分组,我们使用 pandas Timestamp 的 datetime 属性weekday(星期一=0,星期日=6),该属性也可以通过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]: <Axes: xlabel='datetime'>
    
    In [16]: plt.xlabel("Hour of the day");  # custom x label using Matplotlib
    
    In [17]: plt.ylabel("$NO_2 (µg/m³)$"); 
    

    ../../_images/09_bar_chart.png

    与前一个案例类似,我们想要计算给定统计量(例如均值(NO_2))每小时的数据,并且我们可以再次使用分割-应用-合并的方法。对于这种情况,我们使用 pandas Timestamp 的 datetime 属性hour,该属性也可以通过dt访问器访问。

日期时间作为索引

在重塑教程中,介绍了pivot()用于将数据表重塑,使每个测量位置成为单独的列:

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]: 
(Index([2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019,
 ...
 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019],
 dtype='int32', name='datetime', length=1033),
 Index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 ...
 3, 3, 3, 3, 3, 3, 3, 3, 3, 4],
 dtype='int32', name='datetime', length=1033)) 

其他一些优点包括方便的时间段子集或图表上的调整时间刻度。让我们在我们的数据上应用这个。

  • 创建一个图表,显示从 5 月 20 日到 5 月 21 日结束的不同站点的(NO_2)值

    In [21]: no_2["2019-05-20":"2019-05-21"].plot(); 
    

    ../../_images/09_time_section.png

    通过提供一个解析为日期时间的字符串,可以在DatetimeIndex上选择特定的数据子集。

用户指南

有关DatetimeIndex和使用字符串进行切片的更多信息,请参阅时间序列索引部分。

将时间序列重新采样为另一个频率

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

    In [22]: monthly_max = no_2.resample("ME").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 
    

    在具有日期时间索引的时间序列数据上,一种非常强大的方法是能够将时间序列重采样()到另一个频率(例如,将每秒数据转换为每 5 分钟数据)。

resample()方法类似于分组操作:

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

  • 它需要聚合函数,如meanmax

到用户指南

有关用于定义时间序列频率的别名的概述可在偏移别名概述表中找到。

在定义时,时间序列的频率由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_datetime函数或作为读取函数的一部分转换为日期时间对象。

  • pandas 中的日期时间对象支持计算、逻辑操作和使用dt访问器的便捷日期相关属性。

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

  • 重采样是一种强大的方法,可以改变时间序列的频率。

到用户指南

有关时间序列的完整概述可在时间序列和日期功能页面上找到。

使用 pandas 日期时间属性

  • 我想要将列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.datetimepandas.Timestamp

注意

由于许多数据集中的一列包含日期时间信息,因此 pandas 输入函数如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来处理日期时间使我们能够计算日期信息并使其可比较。因此,我们可以用它来获取时间序列的长度:

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

结果是一个pandas.Timedelta对象,类似于标准 Python 库中的datetime.timedelta,定义了时间持续。

用户指南

pandas 支持的各种时间概念在时间相关概念的用户指南部分有详细解释。

  • 我想要向DataFrame添加一个只包含测量月份的新列

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

    通过使用Timestamp对象作为日期,pandas 提供了许多与时间相关的属性。例如month,还有yearquarter等等。所有这些属性都可以通过dt访问器访问。

用户指南

时间和日期组件概览表中提供了现有日期属性的概述。关于dt访问器返回类似日期时间属性的更多细节在 dt 访问器的专门部分有解释。

  • 每周每天每个测量位置的平均(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 
    

    还记得groupby提供的分割-应用-合并模式吗?在统计计算教程中,我们想要计算每个工作日和每个测量位置的给定统计量(例如平均(NO_2))。为了按工作日分组,我们使用 pandas Timestamp的日期时间属性weekday(星期一=0,星期日=6),这也可以通过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]: <Axes: xlabel='datetime'>
    
    In [16]: plt.xlabel("Hour of the day");  # custom x label using Matplotlib
    
    In [17]: plt.ylabel("$NO_2 (µg/m³)$"); 
    

    ../../_images/09_bar_chart.png

    类似于前面的情况,我们想要计算每个小时的给定统计量(例如平均(NO_2)),我们可以再次使用分割-应用-合并方法。对于这种情况,我们使用 pandas Timestamp的日期时间属性hour,这也可以通过dt访问器访问。

日期时间作为索引

在重塑教程中,介绍了使用pivot()来将数据表重塑,使每个测量位置成为单独的列:

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]: 
(Index([2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019,
 ...
 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019],
 dtype='int32', name='datetime', length=1033),
 Index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 ...
 3, 3, 3, 3, 3, 3, 3, 3, 3, 4],
 dtype='int32', name='datetime', length=1033)) 

其他一些优点是方便地对时间段进行子集划分或在图表上调整时间刻度。让我们在我们的数据上应用这个方法。

  • 创建从 5 月 20 日到 5 月 21 日结束的不同站点(NO_2)值的图表。

    In [21]: no_2["2019-05-20":"2019-05-21"].plot(); 
    

    ../../_images/09_time_section.png

    通过提供解析为日期时间的字符串,可以在DatetimeIndex上选择特定的数据子集。

到用户指南

更多关于DatetimeIndex和使用字符串进行切片的信息,请参阅时间序列索引部分。

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

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

    In [22]: monthly_max = no_2.resample("ME").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 
    

    在具有日期时间索引的时间序列数据上非常强大的方法是能够将时间序列resample()到另一个频率(例如,将每秒数据转换为每 5 分钟的数据)。

resample()方法类似于分组操作:

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

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

到用户指南

在偏移别名概述表中提供了用于定义时间序列频率的别名的概述。

当定义时,时间序列的频率由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_datetime函数或作为读取函数的一部分转换为日期时间对象。

  • pandas 中的日期时间对象支持使用dt访问器进行计算、逻辑操作和方便的与日期相关的属性。

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

  • Resample 是改变时间序列频率的强大方法。

用户指南

有关时间序列的完整概述可在时间序列和日期功能页面上找到。

posted @ 2024-04-24 11:26  绝不原创的飞龙  阅读(29)  评论(0编辑  收藏  举报