如何在 pandas 中创建图表?
原文:
pandas.pydata.org/docs/getting_started/intro_tutorials/04_plotting.html
In [1]: import pandas as pd
In [2]: import matplotlib.pyplot as plt
本教程使用的数据:
-
空气质量数据
本教程使用关于(NO_2)的空气质量数据,由OpenAQ提供,并使用py-openaq包。
air_quality_no2.csv
数据集提供了分别来自巴黎、安特卫普和伦敦的测量站FR04014、BETR801和London 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_col
和parse_dates
参数,将第一(0)列定义为生成的DataFrame
的索引,并将列中的日期转换为Timestamp
对象。 -
我想快速地对数据进行可视化检查。
In [5]: air_quality.plot() Out[5]: <Axes: xlabel='datetime'> In [6]: plt.show()
使用
DataFrame
,pandas 默认为具有数值数据的每列创建一条线图。 -
我只想绘制数据表中来自巴黎的列。
In [7]: air_quality["station_paris"].plot() Out[7]: <Axes: xlabel='datetime'> In [8]: plt.show()
要绘制特定列,请结合子集数据教程中的选择方法和
plot()
方法。因此,plot()
方法适用于Series
和DataFrame
。 -
我想要直观比较伦敦和巴黎测得的(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()
在使用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()
用户指南
除了默认的折线图之外,有关支持的绘图样式的介绍,请参阅用户指南中关于支持的绘图样式的部分。
-
我想将每列分别放在单独的子图中。
In [14]: axs = air_quality.plot.area(figsize=(12, 4), subplots=True) In [15]: plt.show()
通过
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()
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
-
我想要以 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。您不需要使用循环迭代每一行!
-
我想检查巴黎与安特卫普的比值,并将结果保存在一个新列中。
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
聚合统计
-
泰坦尼克号乘客的平均年龄是多少?
In [4]: titanic["Age"].mean() Out[4]: 29.69911764705882
可用不同统计数据并可以应用于具有数值数据的列。一般情况下,操作将排除缺失数据,并默认跨行操作。
-
泰坦尼克号乘客的中位年龄和票价是多少?
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
到用户指南
在用户指南中提供了关于描述性统计的详细信息,参见描述性统计一节。
按类别分组的聚合统计
-
泰坦尼克号乘客的男性与女性的平均年龄是多少?
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=True
将mean
方法应用于包含数字列的每一列:
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
注意
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()
方法。
转至用户指南
关于分组-应用-合并方法的全面描述可在用户指南的分组操作部分找到。
按类别计算记录数
-
在每个舱位等级中的乘客数量是多少?
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
注意
size
和 count
都可以与 groupby
结合使用。而 size
包括 NaN
值并且仅提供行数(表的大小),count
则排除缺失值。在 value_counts
方法中,使用 dropna
参数来包含或排除 NaN
值。
转至用户指南
用户指南有一个专门介绍 value_counts
的部分,请参阅离散化页面。
记住
-
聚合统计可以在整个列或行上计算。
-
groupby
提供了 分组-应用-合并 模式的强大功能。 -
value_counts
是一个方便的快捷方式,用于计算变量的每个类别中的条目数。
转至用户指南
关于分组-应用-合并方法的全面描述可在用户指南的分组操作页面找到。
聚合统计
-
泰坦尼克号乘客的平均年龄是多少?
In [4]: titanic["Age"].mean() Out[4]: 29.69911764705882
不同的统计数据可用,并且可以应用于具有数字数据的列。操作通常会排除缺失数据,并默认跨行操作。
-
泰坦尼克号乘客的中位年龄和票价是多少?
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
转到用户指南
关于描述性统计的详细信息,请参阅用户指南中的描述性统计部分。
按类别分组的聚合统计
-
泰坦尼克号男性与女性乘客的平均年龄分别是多少?
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=True
将mean
方法应用于包含数值列的每列:
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
注意
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()
方法。
用户指南
关于分组操作的分割-应用-组合方法的完整描述,请参阅用户指南中的分组操作部分。
按类别计算记录数
-
每个客舱等级的乘客数量是多少?
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
注意
size
和 count
都可以与 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]
-
我想要三个站点的值分别作为相邻的列。
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'>
注意
当未定义index
参数时,将使用现有索引(行标签)。
用户指南
有关pivot()
的更多信息,请参阅用户指南中关于数据透视表对象的部分。
透视表
-
我想要表格形式中每个站点的(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()
相关联。可以通过在parameter
和location
上进行分组来得到相同的结果:
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
-
我想将所有空气质量(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_name
和var_name
只是两个生成列的用户定义名称。要融合的列由id_vars
和value_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]
-
我想要三个站点的值作为相邻的单独列��
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'>
注意
当未定义index
参数时,将使用现有的索引(行标签)。
转到用户指南
有关pivot()
的更多信息,请参阅用户指南中关于数据透视表 DataFrame 对象的部分。
数据透视表
-
我想要每个站点中(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()
。相同的结果可以通过在parameter
和location
上进行分组来得到:
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
-
我想将所有空气质量( N O 2 NO_2 NO2)测量值收集到单独的一列中(长格式)。
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_name
和var_name
只是生成的用户定义列 ocolumns 的名称。要融合的列由id_vars
和value_vars
定义。
转到用户指南
使用pandas.melt()
从宽格式转换为长格式在用户指南中有详细说明,参见通过 melt 进行重塑部分。
记住
-
通过
sort_values
支持按一个或多个列进行排序。 -
pivot
函数纯粹是数据重构,pivot_table
支持聚合。 -
pivot
的反向(从长到宽格式)是melt
(从宽到长格式)。
转到用户指南
完整概述可在关于重塑和旋转的用户指南页面上找到。
如何合并来自多个表的数据
原文:
pandas.pydata.org/docs/getting_started/intro_tutorials/08_combine_dataframes.html
连接对象
-
我想将(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
定义在其他轴上的索引逻辑(并集或交集)。
使用共同标识符连接表
-
将由站点元数据表提供的站点坐标添加到测量表中的相应行。
警告
空气质量测量站坐标存储在数据文件
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_on
和right_on
参数(而不仅仅是on
)来建立两个表之间的链接。
用户指南
pandas 还支持内部、外部和右连接。有关表的连接/合并的更多信息,请参阅用户指南中关于数据库风格表合并的部分。或者查看与 SQL 的比较页面。
记住
-
可以使用
concat
函数沿着列或行将多个表连接起来。 -
对于类似数据库的表合并/连接,请使用
merge
函数。
用户指南
查看用户指南,了解各种合并数据表的方法的详细描述。
连接对象
-
我想将(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
来定义索引在其他轴上的逻辑(并集或交集)的信息,请参阅对象连接部分。
使用共同标识符连接表
-
将由站点元数据表提供的站点坐标添加到测量表中的相应行中。
警告
空气质量测量站点坐标存储在数据文件
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_on
和right_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.datetime
的pandas.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.timedelta
的pandas.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
,还有year
,quarter
���。所有这些属性都可以通过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))每个工作日和每个测量位置的数据。为了按工作日分组,我们使用 pandasTimestamp
的 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³)$");
与前一个案例类似,我们想要计算给定统计量(例如均值(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();
通过提供一个解析为日期时间的字符串,可以在
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()
方法类似于分组操作:
-
它提供基于时间的分组,通过使用定义目标频率的字符串(例如
M
、5H
,…) -
它需要聚合函数,如
mean
、max
等
到用户指南
有关用于定义时间序列频率的别名的概述可在偏移别名概述表中找到。
在定义时,时间序列的频率由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));
到用户指南
有关时间序列重采样
强大功能的更多细节,请参阅用户指南中关于重采样的部分。
记住
-
有效的日期字符串可以使用
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.datetime
的pandas.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
,还有year
,quarter
等等。所有这些属性都可以通过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))。为了按工作日分组,我们使用 pandasTimestamp
的日期时间属性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³)$");
类似于前面的情况,我们想要计算每个小时的给定统计量(例如平均(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();
通过提供解析为日期时间的字符串,可以在
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()
方法类似于分组操作:
-
它提供了基于时间的分组,通过使用定义目标频率的字符串(例如
M
、5H
等) -
它需要一个聚合函数,如
mean
、max
等。
到用户指南
在偏移别名概述表中提供了用于定义时间序列频率的别名的概述。
当定义时,时间序列的频率由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));
到用户指南
更多关于时间序列重新采样
强大功能的详细信息,请参阅用户指南中的重新采样部分。
记住
-
有效的日期字符串可以使用
to_datetime
函数或作为读取函数的一部分转换为日期时间对象。 -
pandas 中的日期时间对象支持使用
dt
访问器进行计算、逻辑操作和方便的与日期相关的属性。 -
DatetimeIndex
包含这些与日期相关的属性,并支持方便的切片。 -
Resample
是改变时间序列频率的强大方法。
用户指南
有关时间序列的完整概述可在时间序列和日期功能页面上找到。