In [1]: import pandas as pd
本教程使用的数据
  • 本教程使用泰坦尼克号数据集,存储为 CSV。数据包含以下数据列

    • PassengerId:每个乘客的 ID。

    • Survived:指示乘客是否幸存。 0 表示是,1 表示否。

    • Pclass:3 个票价等级之一:等级 1、等级 2 和等级 3

    • Name:乘客姓名。

    • Sex:乘客性别。

    • Age:乘客年龄(以年为单位)。

    • SibSp:船上兄弟姐妹或配偶的人数。

    • Parch:船上父母或子女的人数。

    • 票号:乘客的票号。

    • 票价:表示票价。

    • 客舱:乘客的客舱号。

    • 登船港口:登船港口。

    到原始数据
    In [2]: titanic = pd.read_csv("data/titanic.csv")
    
    In [3]: titanic.head()
    Out[3]: 
       PassengerId  Survived  Pclass  ...     Fare Cabin  Embarked
    0            1         0       3  ...   7.2500   NaN         S
    1            2         1       1  ...  71.2833   C85         C
    2            3         1       3  ...   7.9250   NaN         S
    3            4         1       1  ...  53.1000  C123         S
    4            5         0       3  ...   8.0500   NaN         S
    
    [5 rows x 12 columns]
    
  • 本教程使用由 OpenAQ 提供的关于 \(NO_2\) 和小于 2.5 微米的颗粒物的空气质量数据,并使用 py-openaq 包。 air_quality_long.csv 数据集分别为巴黎、安特卫普和伦敦的 FR04014BETR801伦敦威斯敏斯特 测量站提供了 \(NO_2\)\(PM_{25}\) 值。

    空气质量数据集包含以下列

    • 城市:传感器使用所在的城市,巴黎、安特卫普或伦敦

    • 国家:传感器使用所在的国家,FR、BE 或 GB

    • 位置:传感器的 ID,FR04014BETR801伦敦威斯敏斯特

    • 参数:传感器测量的参数,\(NO_2\) 或颗粒物

    • 值:测量的值

    • 单位:测量参数的单位,在本例中为 ‘µg/m³’

    以及 DataFrame 的索引为 datetime,即测量的时间。

    注意

    空气质量数据以所谓的长格式数据表示形式提供,每个观测值在单独的行上,每个变量在数据表的单独列中。长/窄格式也称为 整洁数据格式

    到原始数据
    In [4]: air_quality = pd.read_csv(
       ...:     "data/air_quality_long.csv", index_col="date.utc", parse_dates=True
       ...: )
       ...: 
    
    In [5]: air_quality.head()
    Out[5]: 
                                    city country location parameter  value   unit
    date.utc                                                                     
    2019-06-18 06:00:00+00:00  Antwerpen      BE  BETR801      pm25   18.0  µg/m³
    2019-06-17 08:00:00+00:00  Antwerpen      BE  BETR801      pm25    6.5  µg/m³
    2019-06-17 07:00:00+00:00  Antwerpen      BE  BETR801      pm25   18.5  µg/m³
    2019-06-17 06:00:00+00:00  Antwerpen      BE  BETR801      pm25   16.0  µg/m³
    2019-06-17 05:00:00+00:00  Antwerpen      BE  BETR801      pm25    7.5  µg/m³
    

如何重塑表格的布局#

排序表格行#

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

    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
    

    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(宽格式到长格式)。

到用户指南

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