5 Getting Started with pandas
pandas 将成为本书其余大部分内容中令人感兴趣的主要工具。它包含数据结构和数据操作工具,旨在使 Python 中的数据清理和分析变得快速、方便。pandas 通常与 NumPy 和 SciPy 等数值计算工具、statsmodels 和 scikit-learn 等分析库以及 matplotlib 等数据可视化库配合使用。pandas 采用了 NumPy 的基于数组的计算惯用风格的重要部分,特别是基于数组的函数以及对不使用 for
循环的数据处理的偏好。
虽然 pandas 采用了 NumPy 的许多编码习惯,但最大的区别是 pandas 是为处理表格或异构数据而设计的。相比之下,NumPy 最适合处理同质类型的数值数组数据。
自 2010 年成为开源项目以来,pandas 已经发展成为一个相当大的库,适用于广泛的实际用例。开发者社区已发展到超过 2,500 名不同的贡献者,他们一直在帮助构建该项目,并使用该项目解决日常数据问题。充满活力的 pandas 开发者和用户社区是其成功的关键部分。
很多人不知道,自 2013 年以来,我就没有积极参与 pandas 的日常开发;从那时起,它就成为一个完全由社区管理的项目。请务必向核心开发人员和所有贡献者的辛勤工作表示感谢!
在本书的其余部分中,我对 NumPy 和 pandas 使用以下导入约定:
1]: import numpy as np
In [
2]: import pandas as pd In [
因此,每当你在代码中看到 pd.
,它指的是 pandas。您可能还会发现将 Series 和 DataFrame 导入本地命名空间更容易,因为它们使用得非常频繁:
3]: from pandas import Series, DataFrame In [
5.1 Introduction to pandas Data Structures
要开始使用 pandas,您需要熟悉它的两种主力数据结构:Series 和 DataFrame。虽然它们并不是解决所有问题的通用解决方案,但它们为各种数据任务提供了坚实的基础。
5.1.1 Series
Series 是一个类似一维数组的对象,包含相同类型的值序列(与 NumPy 类型类似)和关联的数据标签数组(称为 index)。最简单的 Series 仅由数据数组组成:
14]: obj = pd.Series([4, 7, -5, 3])
In [
15]: obj
In [15]:
Out[0 4
1 7
2 -5
3 3
dtype: int64
以交互方式显示的 Series 的字符串表示形式在左侧显示 index,在右侧显示值。由于我们没有为数据指定 index,因此会创建一个由整数 0
到 N - 1
(其中 N
是数据的长度)组成的默认索引。您可以分别通过其 array
和 index
属性获取 Series 的 array 表示形式和 index 对象:
16]: obj.array
In [16]:
Out[<PandasArray>
4, 7, -5, 3]
[4, dtype: int64
Length:
17]: obj.index
In [17]: RangeIndex(start=0, stop=4, step=1) Out[
.array
属性的结果是一个 PandasArray
,它通常包装一个 NumPy 数组,但也可以包含特殊的扩展数组类型,这将在 Ch 7.3: Extension Data Types 中详细讨论。
通常,您需要创建一个带有 index 的 Series,该 index 用标签标识每个数据点:
18]: obj2 = pd.Series([4, 7, -5, 3], index=["d", "b", "a", "c"])
In [
19]: obj2
In [19]:
Out[4
d 7
b -5
a 3
c
dtype: int64
20]: obj2.index
In [20]: Index(['d', 'b', 'a', 'c'], dtype='object') Out[
与 NumPy 数组相比,在选择单个值或一组值时可以在 index 中使用标签:
21]: obj2["a"]
In [21]: -5
Out[
22]: obj2["d"] = 6
In [
23]: obj2[["c", "a", "d"]]
In [23]:
Out[3
c -5
a 6
d dtype: int64
这里 ["c", "a", "d"]
被解释为索引列表,即使它包含字符串而不是整数。
使用 NumPy 函数或类似 NumPy 的操作(例如使用布尔数组进行过滤、标量乘法或应用数学函数)将保留索引值链接:
24]: obj2[obj2 > 0]
In [24]:
Out[6
d 7
b 3
c
dtype: int64
25]: obj2 * 2
In [25]:
Out[12
d 14
b -10
a 6
c
dtype: int64
26]: import numpy as np
In [
27]: np.exp(obj2)
In [27]:
Out[403.428793
d 1096.633158
b 0.006738
a 20.085537
c dtype: float64
将 Series 视为固定长度的有序字典的另一种方式,因为它是索引值到数据值的映射。它可以在许多可能使用字典的情况下使用:
28]: "b" in obj2
In [28]: True
Out[
29]: "e" in obj2
In [29]: False Out[
如果 Python 字典中包含数据,则可以通过传递字典来创建 Series:
30]: sdata = {"Ohio": 35000, "Texas": 71000, "Oregon": 16000, "Utah": 5000}
In [
31]: obj3 = pd.Series(sdata)
In [
32]: obj3
In [32]:
Out[35000
Ohio 71000
Texas 16000
Oregon 5000
Utah dtype: int64
Series 可以使用其 to_dict
方法转换回字典:
33]: obj3.to_dict()
In [33]: {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000} Out[
当您仅传递字典时,结果 Series 中的索引将根据字典的键方法遵循键的顺序,这取决于键插入顺序。您可以通过传递带有字典键的索引来覆盖它,按照您希望它们在结果系列中出现的顺序:
34]: states = ["California", "Ohio", "Oregon", "Texas"]
In [
35]: obj4 = pd.Series(sdata, index=states)
In [
36]: obj4
In [36]:
Out[
California NaN35000.0
Ohio 16000.0
Oregon 71000.0
Texas dtype: float64
在这里,在 sdata
中找到的三个值被放置在适当的位置,但由于没有找到 "California"
的值,因此它显示为 NaN
(Not a Number),这在 pandas 中被视为标记缺失或 NA
值。由于 "Utah"
未包含在 states
中,因此它被排除在结果对象之外。
我将交替使用术语 “missing”、“NA” 或 “null” 来指代缺失的数据。pandas 中的 isna
和 notna
函数应该用于检测丢失的数据:
37]: pd.isna(obj4)
In [37]:
Out[True
California False
Ohio False
Oregon False
Texas bool
dtype:
38]: pd.notna(obj4)
In [38]:
Out[False
California True
Ohio True
Oregon True
Texas bool dtype:
Series 还有这些实例方法:
39]: obj4.isna()
In [39]:
Out[True
California False
Ohio False
Oregon False
Texas bool dtype:
我在 Ch 7: Data Cleaning and Preparation 中更详细地讨论了如何处理丢失的数据。
对于许多应用程序来说,一个有用的 Series 功能是它在算术运算中按索引标签自动对齐:
40]: obj3
In [40]:
Out[35000
Ohio 71000
Texas 16000
Oregon 5000
Utah
dtype: int64
41]: obj4
In [41]:
Out[
California NaN35000.0
Ohio 16000.0
Oregon 71000.0
Texas
dtype: float64
42]: obj3 + obj4
In [42]:
Out[
California NaN70000.0
Ohio 32000.0
Oregon 142000.0
Texas
Utah NaN dtype: float64
稍后将更详细地讨论数据对齐功能。如果您有数据库方面的经验,您可以将其视为类似于连接操作。
Series 对象本身及其索引都有一个 name
属性,它与 pandas 功能的其他区域集成:
43]: obj4.name = "population"
In [
44]: obj4.index.name = "state"
In [
45]: obj4
In [45]:
Out[
state
California NaN35000.0
Ohio 16000.0
Oregon 71000.0
Texas Name: population, dtype: float64
Series 的索引可以通过赋值来改变:
46]: obj
In [46]:
Out[0 4
1 7
2 -5
3 3
dtype: int64
47]: obj.index = ["Bob", "Steve", "Jeff", "Ryan"]
In [
48]: obj
In [48]:
Out[4
Bob 7
Steve -5
Jeff 3
Ryan dtype: int64
5.1.2 DataFrame
DataFrame 表示一个矩形数据表,并包含有序的、命名的列集合,每个列可以是不同的值类型(数字、字符串、布尔值等)。DataFrame 同时具有行索引和列索引;它可以被认为是共享相同索引的 Series 的字典。
虽然 DataFrame 物理上是二维的,但您可以使用分层索引以表格格式表示更高维的数据,我们将在 Ch 8: Data Wrangling: Join, Combine, and Reshape 中讨论该主题,也是某些 pandas 中更高级的数据处理功能。
构造 DataFrame 的方法有很多,最常见的方法之一是使用等长列表或 NumPy 数组的字典:
= {"state": ["Ohio", "Ohio", "Ohio", "Nevada", "Nevada", "Nevada"],
data "year": [2000, 2001, 2002, 2001, 2002, 2003],
"pop": [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
= pd.DataFrame(data) frame
与 Series 一样,生成的 DataFrame 将自动分配其索引,并且列根据数据中键的顺序放置(这取决于它们在字典中的插入顺序):
50]: frame
In [50]:
Out[
state year pop0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2
如果您使用的是 Jupyter notebook,pandas DataFrame 对象将显示为对浏览器更友好的 HTML 表格。有关示例,请参见 Figure 5.1。
对于大型 DataFrame,head
方法仅选择前五行:
51]: frame.head()
In [51]:
Out[
state year pop0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
同样,tail
返回最后五行:
52]: frame.tail()
In [52]:
Out[
state year pop1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2
如果指定列序列,DataFrame 的列将按该顺序排列:
53]: pd.DataFrame(data, columns=["year", "state", "pop"])
In [53]:
Out[
year state pop0 2000 Ohio 1.5
1 2001 Ohio 1.7
2 2002 Ohio 3.6
3 2001 Nevada 2.4
4 2002 Nevada 2.9
5 2003 Nevada 3.2
如果您传递字典中未包含的列,则结果中将显示缺失值:
54]: frame2 = pd.DataFrame(data, columns=["year", "state", "pop", "debt"])
In [
55]: frame2
In [55]:
Out[
year state pop debt0 2000 Ohio 1.5 NaN
1 2001 Ohio 1.7 NaN
2 2002 Ohio 3.6 NaN
3 2001 Nevada 2.4 NaN
4 2002 Nevada 2.9 NaN
5 2003 Nevada 3.2 NaN
56]: frame2.columns
In [56]: Index(['year', 'state', 'pop', 'debt'], dtype='object') Out[
DataFrame 中的列可以通过类似字典的表示法或使用点属性表示法作为 Series 进行检索:
57]: frame2["state"]
In [57]:
Out[0 Ohio
1 Ohio
2 Ohio
3 Nevada
4 Nevada
5 Nevada
object
Name: state, dtype:
58]: frame2.year
In [58]:
Out[0 2000
1 2001
2 2002
3 2001
4 2002
5 2003
Name: year, dtype: int64
为了方便起见,IPython 中提供了类似属性的访问(例如,frame2.year
)和制表符完成列名称。
frame2[column]
适用于任何列名称,但 frame2.column
仅当列名称是有效的 Python 变量名称并且不与 DataFrame 中的任何方法名称冲突时才有效。例如,如果列的名称包含空格或下划线以外的符号,则无法使用点属性方法访问它。
请注意,返回的 Series 与 DataFrame 具有相同的索引,并且它们的 name
属性已适当设置。
还可以使用特殊的 iloc
和 loc
属性按位置或名称检索行(稍后将在 Selection on DataFrame with loc and iloc 中详细介绍):
59]: frame2.loc[1]
In [59]:
Out[2001
year
state Ohio1.7
pop
debt NaN1, dtype: object
Name:
60]: frame2.iloc[2]
In [60]:
Out[2002
year
state Ohio3.6
pop
debt NaN2, dtype: object Name:
可以通过分配来修改列。例如,可以为空 debt
列分配标量值或值数组:
61]: frame2["debt"] = 16.5
In [
62]: frame2
In [62]:
Out[
year state pop debt0 2000 Ohio 1.5 16.5
1 2001 Ohio 1.7 16.5
2 2002 Ohio 3.6 16.5
3 2001 Nevada 2.4 16.5
4 2002 Nevada 2.9 16.5
5 2003 Nevada 3.2 16.5
63]: frame2["debt"] = np.arange(6.)
In [
64]: frame2
In [64]:
Out[
year state pop debt0 2000 Ohio 1.5 0.0
1 2001 Ohio 1.7 1.0
2 2002 Ohio 3.6 2.0
3 2001 Nevada 2.4 3.0
4 2002 Nevada 2.9 4.0
5 2003 Nevada 3.2 5.0
将列表或数组分配给列时,值的长度必须与 DataFrame 的长度匹配。如果您分配一个 Series,它的标签将完全与 DataFrame 的索引重新对齐,在任何不存在的索引值中插入缺失值:
65]: val = pd.Series([-1.2, -1.5, -1.7], index=[2, 4, 5])
In [
66]: frame2["debt"] = val
In [
67]: frame2
In [67]:
Out[
year state pop debt0 2000 Ohio 1.5 NaN
1 2001 Ohio 1.7 NaN
2 2002 Ohio 3.6 -1.2
3 2001 Nevada 2.4 NaN
4 2002 Nevada 2.9 -1.5
5 2003 Nevada 3.2 -1.7
分配不存在的列将创建一个新列。
del
关键字将像字典一样删除列。作为示例,我首先添加一个新的布尔值列,其中 state
列等于 "Ohio"
:
68]: frame2["eastern"] = frame2["state"] == "Ohio"
In [
69]: frame2
In [69]:
Out[
year state pop debt eastern0 2000 Ohio 1.5 NaN True
1 2001 Ohio 1.7 NaN True
2 2002 Ohio 3.6 -1.2 True
3 2001 Nevada 2.4 NaN False
4 2002 Nevada 2.9 -1.5 False
5 2003 Nevada 3.2 -1.7 False
无法使用 frame2.eastern
点属性表示法创建新列。
然后可以使用 del
方法删除该列:
70]: del frame2["eastern"]
In [
71]: frame2.columns
In [71]: Index(['year', 'state', 'pop', 'debt'], dtype='object') Out[
对 DataFrame 进行索引返回的列是基础数据的视图,而不是副本。因此,对 Series 的任何就地修改都将反映在 DataFrame 中。可以使用 Series 的 copy
方法显式复制该列。
另一种常见的数据形式是字典的嵌套字典:
72]: populations = {"Ohio": {2000: 1.5, 2001: 1.7, 2002: 3.6},
In ["Nevada": {2001: 2.4, 2002: 2.9}} ....:
如果嵌套字典传递给 DataFrame,pandas 会将外部字典键解释为列,将内部键解释为行索引:
73]: frame3 = pd.DataFrame(populations)
In [
74]: frame3
In [74]:
Out[
Ohio Nevada2000 1.5 NaN
2001 1.7 2.4
2002 3.6 2.9
您可以使用与 NumPy 数组类似的语法转置 DataFrame(交换行和列):
75]: frame3.T
In [75]:
Out[2000 2001 2002
1.5 1.7 3.6
Ohio 2.4 2.9 Nevada NaN
请注意,如果列不全部具有相同的数据类型,转置会丢弃列数据类型,因此转置然后转回可能会丢失以前的类型信息。在这种情况下,列变成纯 Python 对象的数组。
内部字典中的键组合起来形成结果中的索引。如果指定了显式索引,则情况并非如此:
76]: pd.DataFrame(populations, index=[2001, 2002, 2003])
In [76]:
Out[
Ohio Nevada2001 1.7 2.4
2002 3.6 2.9
2003 NaN NaN
Series 字典的处理方式大致相同:
77]: pdata = {"Ohio": frame3["Ohio"][:-1],
In ["Nevada": frame3["Nevada"][:2]}
....:
78]: pd.DataFrame(pdata)
In [78]:
Out[
Ohio Nevada2000 1.5 NaN
2001 1.7 2.4
有关可以传递给 DataFrame 构造函数的许多内容的列表,请参阅 Table 5.1。
Type | Notes |
---|---|
2D ndarray | 数据矩阵,传递可选的行和列标签 |
Dictionary of arrays, lists, or tuples | 每个序列成为 DataFrame 中的一列;所有序列的长度必须相同 |
NumPy structured/record array | 被视为 “dictionary of arrays” 的情况 |
Dictionary of Series | 每个值成为一列;如果没有传递显式索引,则每个 Series 的索引将联合在一起形成结果的行索引 |
Dictionary of dictionaries | 每个内部字典成为一列;键被联合起来形成行索引,如 “dictionary of Series” 的情况 |
List of dictionaries or Series | 每个项目都成为 DataFrame 中的一行;字典键或 Series 索引的并集成为 DataFrame 的列标签 |
List of lists or tuples | 被视为 “2D ndarray” 情况 |
Another DataFrame | 除非传递不同的索引,否则将使用 DataFrame 的索引 |
NumPy MaskedArray | 与 “2D ndarray” 情况类似,只是 DataFrame 结果中缺少屏蔽值 |
如果 DataFrame 的 index
和 columns
设置了 name
属性,这些属性也会显示:
79]: frame3.index.name = "year"
In [
80]: frame3.columns.name = "state"
In [
81]: frame3
In [81]:
Out[
state Ohio Nevada
year 2000 1.5 NaN
2001 1.7 2.4
2002 3.6 2.9
与 Series 不同,DataFrame 没有 name
属性。DataFrame 的 to_numpy
方法将 DataFrame 中包含的数据作为二维 ndarray 返回:
82]: frame3.to_numpy()
In [82]:
Out[1.5, nan],
array([[1.7, 2.4],
[3.6, 2.9]]) [
如果 DataFrame 的列是不同的数据类型,则将选择返回数组的数据类型来容纳所有列:
83]: frame2.to_numpy()
In [83]:
Out[2000, 'Ohio', 1.5, nan],
array([[2001, 'Ohio', 1.7, nan],
[2002, 'Ohio', 3.6, -1.2],
[2001, 'Nevada', 2.4, nan],
[2002, 'Nevada', 2.9, -1.5],
[2003, 'Nevada', 3.2, -1.7]], dtype=object) [
5.1.3 Index Objects
pandas 的 Index 对象负责保存轴标签(包括 DataFrame 的列名称)和其他元数据(如轴名称或名称)。构建 Series 或 DataFrame 时使用的任何数组或其他标签序列都会在内部转换为 Index:
84]: obj = pd.Series(np.arange(3), index=["a", "b", "c"])
In [
85]: index = obj.index
In [
86]: index
In [86]: Index(['a', 'b', 'c'], dtype='object')
Out[
87]: index[1:]
In [87]: Index(['b', 'c'], dtype='object') Out[
Index 对象是不可变的,因此用户不能修改:
1] = "d" # TypeError index[
不变性使得在数据结构之间共享 Index 对象更加安全:
88]: labels = pd.Index(np.arange(3))
In [
89]: labels
In [89]: Index([0, 1, 2], dtype='int64')
Out[
90]: obj2 = pd.Series([1.5, -2.5, 0], index=labels)
In [
91]: obj2
In [91]:
Out[0 1.5
1 -2.5
2 0.0
dtype: float64
92]: obj2.index is labels
In [92]: True Out[
有些用户不会经常利用 Index 提供的功能,但由于某些操作会产生包含索引数据的结果,因此了解它们的工作原理非常重要。
除了类似于数组之外,索引的行为也类似于固定大小的集合:
93]: frame3
In [93]:
Out[
state Ohio Nevada
year 2000 1.5 NaN
2001 1.7 2.4
2002 3.6 2.9
94]: frame3.columns
In [94]: Index(['Ohio', 'Nevada'], dtype='object', name='state')
Out[
95]: "Ohio" in frame3.columns
In [95]: True
Out[
96]: 2003 in frame3.index
In [96]: False Out[
与 Python 集合不同,pandas 索引可以包含重复标签:
97]: pd.Index(["foo", "foo", "bar", "bar"])
In [97]: Index(['foo', 'foo', 'bar', 'bar'], dtype='object') Out[
具有重复标签的选择将选择该标签的所有出现位置。
每个索引都有许多用于集合逻辑的方法和属性,它们回答了有关其包含的数据的其他常见问题。Table 5.2 总结了一些有用的内容。
Method/Property | Description |
---|---|
append() |
与其他 Index 对象连接,生成新的 Index |
difference() |
将集合差异计算为 Index |
intersection() |
计算集合交集 |
union() |
计算集并集 |
isin() |
计算布尔数组,指示每个值是否包含在传递的集合中 |
delete() |
删除索引 i 处的元素计算新索引 |
drop() |
通过删除传递的值来计算新索引 |
insert() |
通过在索引 i 处插入元素来计算新索引 |
is_monotonic |
如果每个元素大于或等于前一个元素,则返回 True |
is_unique |
如果索引没有重复值,则返回 True |
unique() |
计算索引中唯一值的数组 |
5.2 Essential Functionality
本节将引导您了解与 Series 或 DataFrame 中包含的数据进行交互的基本机制。在接下来的章节中,我们将更深入地研究使用 pandas 进行数据分析和操作的主题。本书无意作为 pandas 库的详尽文档;相反,我们将专注于让您熟悉常用的功能,而将不太常见(即更深奥)的内容留给您通过阅读在线 pandas 文档来了解更多信息。
5.2.1 Reindexing
pandas 对象的一个重要方法是 reindex
,这意味着创建一个新对象,并重新排列值以与新索引对齐。考虑一个例子:
98]: obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=["d", "b", "a", "c"])
In [
99]: obj
In [99]:
Out[4.5
d 7.2
b -5.3
a 3.6
c dtype: float64
在此 Series 上调用 reindex
会根据新索引重新排列数据,如果尚不存在任何索引值,则会引入缺失值:
100]: obj2 = obj.reindex(["a", "b", "c", "d", "e"])
In [
101]: obj2
In [101]:
Out[-5.3
a 7.2
b 3.6
c 4.5
d
e NaN dtype: float64
对于时间序列等有序数据,您可能需要在重新索引时进行一些插值或填充值。 method
选项允许我们使用诸如 ffill
之类的方法来执行此操作,该方法向前填充值:
102]: obj3 = pd.Series(["blue", "purple", "yellow"], index=[0, 2, 4])
In [
103]: obj3
In [103]:
Out[0 blue
2 purple
4 yellow
object
dtype:
104]: obj3.reindex(np.arange(6), method="ffill")
In [104]:
Out[0 blue
1 blue
2 purple
3 purple
4 yellow
5 yellow
object dtype:
使用 DataFrame,reindex
可以更改(行)索引、列或两者。当仅传递一个序列时,它会重新索引结果中的行:
105]: frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
In [=["a", "c", "d"],
.....: index=["Ohio", "Texas", "California"])
.....: columns
106]: frame
In [106]:
Out[
Ohio Texas California0 1 2
a 3 4 5
c 6 7 8
d
107]: frame2 = frame.reindex(index=["a", "b", "c", "d"])
In [
108]: frame2
In [108]:
Out[
Ohio Texas California0.0 1.0 2.0
a
b NaN NaN NaN3.0 4.0 5.0
c 6.0 7.0 8.0 d
可以使用 columns
关键字对列重新索引:
109]: states = ["Texas", "Utah", "California"]
In [
110]: frame.reindex(columns=states)
In [110]:
Out[
Texas Utah California1 NaN 2
a 4 NaN 5
c 7 NaN 8 d
由于 "Ohio"
不在 states
内,因此该列的数据将从结果中删除。
重新索引特定轴的另一种方法是将新轴标签作为位置参数传递,然后使用 axis
关键字指定要重新索引的轴:
111]: frame.reindex(states, axis="columns")
In [111]:
Out[
Texas Utah California1 NaN 2
a 4 NaN 5
c 7 NaN 8 d
有关 reindex
参数的更多信息,请参阅 Table 5.3。
Argument | Description |
---|---|
labels |
用作索引的新序列。可以是 Index 实例或任何其他类似序列的 Python 数据结构。索引将按原样使用,无需任何复制。 |
index |
使用传递的序列作为新的索引标签。 |
columns |
使用传递的序列作为新的列标签。 |
axis |
要重新索引的轴,无论是 "index" (行)还是 "columns" 。默认值为 "index" 。您可以选择执行 reindex(index=new_labels) 或 reindex(columns=new_labels) 。 |
method |
插值(填充)法;"ffill" 向前填充,而 "bfill" 向后填充。 |
fill_value |
通过重新索引引入缺失数据时要使用的替代值。当您希望结果中缺少的标签具有空值时,请使用 fill_value="missing" (默认行为)。 |
limit |
前向填充或回填时,要填充的最大间隙(以元素数量为单位)。 |
tolerance |
向前填充或回填时,用于填充不精确匹配的最大尺寸间隙(以绝对数字距离表示)。 |
level |
在 MultiIndex 级别上匹配简单索引;否则选择子集。 |
copy |
如果为 True ,则始终复制基础数据,即使新索引与旧索引等效;如果为 False ,则当索引相等时不复制数据。 |
正如我们稍后将在 Selection on DataFrame with loc and iloc 中探讨的那样,您还可以使用 loc
运算符重新索引,并且许多用户更喜欢始终这样做。仅当所有新索引标签已存在于 DataFrame 中时,此方法才有效(而 reindex
将为新标签插入缺失的数据):
112]: frame.loc[["a", "d", "c"], ["California", "Texas"]]
In [112]:
Out[
California Texas2 1
a 8 7
d 5 4 c
5.2.2 Dropping Entries from an Axis
如果您已经有一个没有这些条目的索引数组或列表,则从轴中删除一个或多个条目很简单,因为您可以使用 reindex
方法或基于 .loc
的索引。由于这可能需要一些修改和设置逻辑,因此 drop
方法将返回一个新对象,其中包含从轴删除的一个或多个指示值:
113]: obj = pd.Series(np.arange(5.), index=["a", "b", "c", "d", "e"])
In [
114]: obj
In [114]:
Out[0.0
a 1.0
b 2.0
c 3.0
d 4.0
e
dtype: float64
115]: new_obj = obj.drop("c")
In [
116]: new_obj
In [116]:
Out[0.0
a 1.0
b 3.0
d 4.0
e
dtype: float64
117]: obj.drop(["d", "c"])
In [117]:
Out[0.0
a 1.0
b 4.0
e dtype: float64
使用 DataFrame,可以从任一轴删除索引值。为了说明这一点,我们首先创建一个示例 DataFrame:
118]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
In [=["Ohio", "Colorado", "Utah", "New York"],
.....: index=["one", "two", "three", "four"])
.....: columns
119]: data
In [119]:
Out[
one two three four0 1 2 3
Ohio 4 5 6 7
Colorado 8 9 10 11
Utah 12 13 14 15 New York
使用一系列标签调用 drop
将从行标签(axis 0)中删除值:
120]: data.drop(index=["Colorado", "Ohio"])
In [120]:
Out[
one two three four8 9 10 11
Utah 12 13 14 15 New York
要从列中删除标签,请使用 columns
关键字:
121]: data.drop(columns=["two"])
In [121]:
Out[
one three four0 2 3
Ohio 4 6 7
Colorado 8 10 11
Utah 12 14 15 New York
您还可以通过传递 axis=1
(类似于 NumPy)或 axis="columns"
来删除列中的值:
122]: data.drop("two", axis=1)
In [122]:
Out[
one three four0 2 3
Ohio 4 6 7
Colorado 8 10 11
Utah 12 14 15
New York
123]: data.drop(["two", "four"], axis="columns")
In [123]:
Out[
one three0 2
Ohio 4 6
Colorado 8 10
Utah 12 14 New York
5.2.3 Indexing, Selection, and Filtering
Series 索引 (obj[...]
) 的工作方式与 NumPy 数组索引类似,只不过您可以使用 Series 的索引值而不仅仅是整数。以下是一些例子:
124]: obj = pd.Series(np.arange(4.), index=["a", "b", "c", "d"])
In [
125]: obj
In [125]:
Out[0.0
a 1.0
b 2.0
c 3.0
d
dtype: float64
126]: obj["b"]
In [126]: 1.0
Out[
127]: obj[1]
In [127]: 1.0
Out[
128]: obj[2:4]
In [128]:
Out[2.0
c 3.0
d
dtype: float64
129]: obj[["b", "a", "d"]]
In [129]:
Out[1.0
b 0.0
a 3.0
d
dtype: float64
130]: obj[[1, 3]]
In [130]:
Out[1.0
b 3.0
d
dtype: float64
131]: obj[obj < 2]
In [131]:
Out[0.0
a 1.0
b dtype: float64
虽然您可以通过这种方式通过标签选择数据,但选择索引值的首选方法是使用特殊的 loc
运算符:
132]: obj.loc[["b", "a", "d"]]
In [132]:
Out[1.0
b 0.0
a 3.0
d dtype: float64
更喜欢 loc
的原因是因为用 []
索引时对整数的处理不同。如果索引包含整数,则基于常规 []
的索引会将整数视为标签,因此行为会根据索引的数据类型而有所不同。例如:
133]: obj1 = pd.Series([1, 2, 3], index=[2, 0, 1])
In [
134]: obj2 = pd.Series([1, 2, 3], index=["a", "b", "c"])
In [
135]: obj1
In [135]:
Out[2 1
0 2
1 3
dtype: int64
136]: obj2
In [136]:
Out[1
a 2
b 3
c
dtype: int64
137]: obj1[[0, 1, 2]]
In [137]:
Out[0 2
1 3
2 1
dtype: int64
138]: obj2[[0, 1, 2]]
In [138]:
Out[1
a 2
b 3
c dtype: int64
使用 loc
时,当索引不包含整数时,表达式 obj.loc[[0, 1, 2]]
将失败:
134]: obj2.loc[[0, 1]]
In [---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/tmp/ipykernel_804589/4185657903.py in <module>
----> 1 obj2.loc[[0, 1]]
^ LONG EXCEPTION ABBREVIATED ^
KeyError: "None of [Int64Index([0, 1], dtype="int64")] are in the [index]"
由于 loc
运算符仅使用标签进行索引,因此还有一个 iloc
运算符仅使用整数进行索引,无论索引是否包含整数,都可以一致地工作:
139]: obj1.iloc[[0, 1, 2]]
In [139]:
Out[2 1
0 2
1 3
dtype: int64
140]: obj2.iloc[[0, 1, 2]]
In [140]:
Out[1
a 2
b 3
c dtype: int64
您还可以使用标签进行切片,但它的工作方式与普通 Python 切片不同,因为端点是包含的:
141]: obj2.loc["b":"c"]
In [141]:
Out[2
b 3
c dtype: int64
使用这些方法赋值会修改 Series 的相应部分:
142]: obj2.loc["b":"c"] = 5
In [
143]: obj2
In [143]:
Out[1
a 5
b 5
c dtype: int64
尝试调用类似 loc
或 iloc
的函数而不是用方括号“索引”它们可能是一个常见的新手错误。方括号表示法用于启用切片操作并允许使用 DataFrame 对象在多个轴上进行索引。
对 DataFrame 进行索引可检索具有单个值或序列的一列或多列:
144]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
In [=["Ohio", "Colorado", "Utah", "New York"],
.....: index=["one", "two", "three", "four"])
.....: columns
145]: data
In [145]:
Out[
one two three four0 1 2 3
Ohio 4 5 6 7
Colorado 8 9 10 11
Utah 12 13 14 15
New York
146]: data["two"]
In [146]:
Out[1
Ohio 5
Colorado 9
Utah 13
New York
Name: two, dtype: int64
147]: data[["three", "one"]]
In [147]:
Out[
three one2 0
Ohio 6 4
Colorado 10 8
Utah 14 12 New York
像这样的索引有一些特殊情况。第一个是使用布尔数组切片或选择数据:
148]: data[:2]
In [148]:
Out[
one two three four0 1 2 3
Ohio 4 5 6 7
Colorado
149]: data[data["three"] > 5]
In [149]:
Out[
one two three four4 5 6 7
Colorado 8 9 10 11
Utah 12 13 14 15 New York
提供行选择语法 data[:2]
是为了方便起见。将单个元素或列表传递给 []
运算符会选择列。
另一个用例是使用布尔 DataFrame 进行索引,例如通过标量比较生成的索引。考虑一个 DataFrame,其中所有布尔值都是通过与标量值进行比较而生成的:
150]: data < 5
In [150]:
Out[
one two three fourTrue True True True
Ohio True False False False
Colorado False False False False
Utah False False False False New York
我们可以使用此 DataFrame 将值 0 分配给值为 True
的每个位置,如下所示:
151]: data[data < 5] = 0
In [
152]: data
In [152]:
Out[
one two three four0 0 0 0
Ohio 0 5 6 7
Colorado 8 9 10 11
Utah 12 13 14 15 New York
Selection on DataFrame with loc and iloc
与 Series 一样,DataFrame 具有特殊属性 loc
和 iloc
,分别用于基于标签和基于整数的索引。由于 DataFrame 是二维的,因此您可以使用轴标签 (loc
) 或整数 (iloc
) 以类似 NumPy 的表示法选择行和列的子集。
作为第一个示例,让我们按标签选择一行:
153]: data
In [153]:
Out[
one two three four0 0 0 0
Ohio 0 5 6 7
Colorado 8 9 10 11
Utah 12 13 14 15
New York
154]: data.loc["Colorado"]
In [154]:
Out[0
one 5
two 6
three 7
four Name: Colorado, dtype: int64
选择单行的结果是一个 Series,其索引包含 DataFrame 的列标签。要选择多个角色,创建一个新的 DataFrame,传递一系列标签:
155]: data.loc[["Colorado", "New York"]]
In [155]:
Out[
one two three four0 5 6 7
Colorado 12 13 14 15 New York
您可以通过用逗号分隔选择来组合 loc
中的行和列选择:
156]: data.loc["Colorado", ["two", "three"]]
In [156]:
Out[5
two 6
three Name: Colorado, dtype: int64
然后,我们将使用 iloc
对整数执行一些类似的选择:
157]: data.iloc[2]
In [157]:
Out[8
one 9
two 10
three 11
four
Name: Utah, dtype: int64
158]: data.iloc[[2, 1]]
In [158]:
Out[
one two three four8 9 10 11
Utah 0 5 6 7
Colorado
159]: data.iloc[2, [3, 0, 1]]
In [159]:
Out[11
four 8
one 9
two
Name: Utah, dtype: int64
160]: data.iloc[[1, 2], [3, 0, 1]]
In [160]:
Out[
four one two7 0 5
Colorado 11 8 9 Utah
除了单个标签或标签列表之外,这两个索引函数还可以使用切片:
161]: data.loc[:"Utah", "two"]
In [161]:
Out[0
Ohio 5
Colorado 9
Utah
Name: two, dtype: int64
162]: data.iloc[:, :3][data.three > 5]
In [162]:
Out[
one two three0 5 6
Colorado 8 9 10
Utah 12 13 14 New York
布尔数组可以与 loc
一起使用,但不能与 iloc
一起使用:
163]: data.loc[data.three >= 2]
In [163]:
Out[
one two three four0 5 6 7
Colorado 8 9 10 11
Utah 12 13 14 15 New York
有多种方法可以选择和重新排列 pandas 对象中包含的数据。对于 DataFrame,Table 5.4 提供了其中许多内容的简短摘要。正如您稍后将看到的,还有许多用于处理分层索引的附加选项。
Type | Notes |
---|---|
df[column] |
从 DataFrame 中选择单列或列序列;特殊情况的便利:布尔数组(过滤行)、切片(切片行)或布尔 DataFrame(根据某些标准设置值) |
df.loc[rows] |
按标签从 DataFrame 中选择单行或行子集 |
df.loc[:, cols] |
按标签选择单列或列的子集 |
df.loc[rows, cols] |
通过标签选择行和列 |
df.iloc[rows] |
按整数位置从 DataFrame 中选择单行或行子集 |
df.iloc[:, cols] |
按整数位置选择单列或列子集 |
df.iloc[rows, cols] |
按整数位置选择行和列 |
df.iat[row, col] |
按行和列位置(整数)选择单个标量值 |
reindex |
通过标签选择行或列 |
Integer indexing pitfalls
使用由整数索引的 pandas 对象可能会成为新用户的绊脚石,因为它们的工作方式与内置的 Python 数据结构(如列表和元组)不同。例如,您可能不希望以下代码生成错误:
164]: ser = pd.Series(np.arange(3.))
In [
165]: ser
In [165]:
Out[0 0.0
1 1.0
2 2.0
dtype: float64
166]: ser[-1]
In [---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/indexes/ra
in get_loc(self, key)
nge.py 344 try:
--> 345 return self._range.index(new_key)
346 except ValueError as err:
ValueError: -1 is not in range
The above exception was the direct cause of the following exception:KeyError Traceback (most recent call last)
<ipython-input-166-44969a759c20> in <module>
----> 1 ser[-1]
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/series.py
in __getitem__(self, key)
1010
1011 elif key_is_scalar:
-> 1012 return self._get_value(key)
1013
1014 if is_hashable(key):
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/series.py
in _get_value(self, label, takeable)
1119
1120 # Similar to Index.get_value, but we do not fall back to position
al-> 1121 loc = self.index.get_loc(label)
1122
1123 if is_integer(loc):
~/miniforge-x86/envs/book-env/lib/python3.10/site-packages/pandas/core/indexes/ra
in get_loc(self, key)
nge.py 345 return self._range.index(new_key)
346 except ValueError as err:
--> 347 raise KeyError(key) from err
348 self._check_indexing_error(key)
349 raise KeyError(key)
KeyError: -1
在这种情况下,pandas 可以“回退(fall back)”整数索引,但通常很难在不向用户代码中引入细微错误的情况下做到这一点。这里我们有一个包含 0
、1
和 2
的索引,但 pandas 不想猜测用户想要什么(基于标签的索引或基于位置的索引):
167]: ser
In [167]:
Out[0 0.0
1 1.0
2 2.0
dtype: float64
另一方面,对于非整数索引,就不存在这样的歧义:
168]: ser2 = pd.Series(np.arange(3.), index=["a", "b", "c"])
In [
169]: ser2[-1]
In [169]: 2.0 Out[
如果您的轴索引包含整数,则数据选择将始终面向标签。正如我上面所说,如果您使用 loc
(用于标签)或 iloc
(用于整数),您将得到您想要的:
170]: ser.iloc[-1]
In [170]: 2.0 Out[
另一方面,整数切片始终是面向整数的:
171]: ser[:2]
In [171]:
Out[0 0.0
1 1.0
dtype: float64
由于这些陷阱,最好始终首选使用 loc
和 iloc
进行索引以避免歧义。
Pitfalls with chained indexing
在上一节中,我们了解了如何使用 loc
和 iloc
在 DataFrame 上进行灵活的选择。这些索引属性也可用于就地修改 DataFrame 对象,但这样做需要小心。
例如,在上面的示例 DataFrame 中,我们可以通过标签或整数位置分配给列或行:
172]: data.loc[:, "one"] = 1
In [
173]: data
In [173]:
Out[
one two three four1 0 0 0
Ohio 1 5 6 7
Colorado 1 9 10 11
Utah 1 13 14 15
New York
174]: data.iloc[2] = 5
In [
175]: data
In [175]:
Out[
one two three four1 0 0 0
Ohio 1 5 6 7
Colorado 5 5 5 5
Utah 1 13 14 15
New York
176]: data.loc[data["four"] > 5] = 3
In [
177]: data
In [177]:
Out[
one two three four1 0 0 0
Ohio 3 3 3 3
Colorado 5 5 5 5
Utah 3 3 3 3 New York
对于新的 pandas 用户来说,一个常见的问题是在分配时链接选择,如下所示:
177]: data.loc[data.three == 5]["three"] = 6
In [<ipython-input-11-0ed1cf2155d5>:1: SettingWithCopyWarning:
is trying to be set on a copy of a slice from a DataFrame.
A value = value instead Try using .loc[row_indexer,col_indexer]
根据数据内容,这可能会打印一个特殊的 SettingWithCopyWarning
,它警告您正在尝试修改临时值(data.loc[data. Three == 5]
的非空结果)而不是原始 DataFrame 数据,这可能就是你想要的。这里,data
未修改:
179]: data
In [179]:
Out[
one two three four1 0 0 0
Ohio 3 3 3 3
Colorado 5 5 5 5
Utah 3 3 3 3 New York
在这些情况下,修复方法是重写链式赋值以使用单个 loc
操作:
180]: data.loc[data.three == 5, "three"] = 6
In [
181]: data
In [181]:
Out[
one two three four1 0 0 0
Ohio 3 3 3 3
Colorado 5 5 6 5
Utah 3 3 3 3 New York
一个好的经验法则是在进行分配时避免链式索引。在其他情况下,pandas 会生成与链式索引有关的 SettingWithCopyWarning
。我建议您参考在线 pandas 文档中的这个主题。
5.2.4 Arithmetic and Data Alignment
pandas 可以使处理具有不同索引的对象变得更加简单。例如,当您添加对象时,如果任何索引对不相同,则结果中的相应索引将是索引对的并集。让我们看一个例子:
182]: s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=["a", "c", "d", "e"])
In [
183]: s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
In [=["a", "c", "e", "f", "g"])
.....: index
184]: s1
In [184]:
Out[7.3
a -2.5
c 3.4
d 1.5
e
dtype: float64
185]: s2
In [185]:
Out[-2.1
a 3.6
c -1.5
e 4.0
f 3.1
g dtype: float64
相加:
186]: s1 + s2
In [186]:
Out[5.2
a 1.1
c
d NaN0.0
e
f NaN
g NaN dtype: float64
内部数据对齐在不重叠的标签位置引入了缺失值。缺失值将在进一步的算术计算中传播。
对于 DataFrame,对齐是在行和列上执行的:
187]: df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list("bcd"),
In [=["Ohio", "Texas", "Colorado"])
.....: index
188]: df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list("bde"),
In [=["Utah", "Ohio", "Texas", "Oregon"])
.....: index
189]: df1
In [189]:
Out[
b c d0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Colorado
190]: df2
In [190]:
Out[
b d e0.0 1.0 2.0
Utah 3.0 4.0 5.0
Ohio 6.0 7.0 8.0
Texas 9.0 10.0 11.0 Oregon
添加这些会返回一个带有索引和列的 DataFrame,这些索引和列是每个 DataFrame 中的索引和列的并集:
191]: df1 + df2
In [191]:
Out[
b c d e
Colorado NaN NaN NaN NaN3.0 NaN 6.0 NaN
Ohio
Oregon NaN NaN NaN NaN9.0 NaN 12.0 NaN
Texas Utah NaN NaN NaN NaN
由于在两个 DataFrame 对象中均未找到 "c"
和 "e"
列,因此它们在结果中显示为缺失。这同样适用于带有两个对象不常见的标签的行。
如果添加没有共同列或行标签的 DataFrame 对象,结果将包含所有空值:
192]: df1 = pd.DataFrame({"A": [1, 2]})
In [
193]: df2 = pd.DataFrame({"B": [3, 4]})
In [
194]: df1
In [194]:
Out[
A0 1
1 2
195]: df2
In [195]:
Out[
B0 3
1 4
196]: df1 + df2
In [196]:
Out[
A B0 NaN NaN
1 NaN NaN
Arithmetic methods with fill values
在不同索引对象之间的算术运算中,当在一个对象中找到轴标签但在另一个对象中未找到时,您可能需要填充特殊值,例如 0。下面是一个示例,我们通过将 np.nan
分配给它来将特定值设置为 NA (null):
197]: df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
In [=list("abcd"))
.....: columns
198]: df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
In [=list("abcde"))
.....: columns
199]: df2.loc[1, "b"] = np.nan
In [
200]: df1
In [200]:
Out[
a b c d0 0.0 1.0 2.0 3.0
1 4.0 5.0 6.0 7.0
2 8.0 9.0 10.0 11.0
201]: df2
In [201]:
Out[
a b c d e0 0.0 1.0 2.0 3.0 4.0
1 5.0 NaN 7.0 8.0 9.0
2 10.0 11.0 12.0 13.0 14.0
3 15.0 16.0 17.0 18.0 19.0
相加导致不重叠位置的缺失值:
202]: df1 + df2
In [202]:
Out[
a b c d e0 0.0 2.0 4.0 6.0 NaN
1 9.0 NaN 13.0 15.0 NaN
2 18.0 20.0 22.0 24.0 NaN
3 NaN NaN NaN NaN NaN
使用 df1
上的 add
方法,我将 df2
和一个参数传递给 fill_value
,这将用传递的值替换操作中的任何缺失值:
203]: df1.add(df2, fill_value=0)
In [203]:
Out[
a b c d e0 0.0 2.0 4.0 6.0 4.0
1 9.0 5.0 13.0 15.0 9.0
2 18.0 20.0 22.0 24.0 14.0
3 15.0 16.0 17.0 18.0 19.0
请参阅 Table 5.5,了解用于算术的 Series 和 DataFrame 方法的列表。每个都有一个对应的,以字母 r
开头,其参数相反。所以这两个语句是等价的:
204]: 1 / df1
In [204]:
Out[
a b c d0 inf 1.000000 0.500000 0.333333
1 0.250 0.200000 0.166667 0.142857
2 0.125 0.111111 0.100000 0.090909
205]: df1.rdiv(1)
In [205]:
Out[
a b c d0 inf 1.000000 0.500000 0.333333
1 0.250 0.200000 0.166667 0.142857
2 0.125 0.111111 0.100000 0.090909
相关地,在重新索引 Series 或 DataFrame 时,您还可以指定不同的填充值:
206]: df1.reindex(columns=df2.columns, fill_value=0)
In [206]:
Out[
a b c d e0 0.0 1.0 2.0 3.0 0
1 4.0 5.0 6.0 7.0 0
2 8.0 9.0 10.0 11.0 0
Method | Description |
---|---|
add, radd |
Methods for addition (+) |
sub, rsub |
Methods for subtraction (-) |
div, rdiv |
Methods for division (/) |
floordiv, rfloordiv |
Methods for floor division (//) |
mul, rmul |
Methods for multiplication (*) |
pow, rpow |
Methods for exponentiation (**) |
Operations between DataFrame and Series
与不同维度的 NumPy 数组一样,DataFrame 和 Series 之间的算术也被定义。首先,作为一个激励示例,考虑二维数组与其行之一之间的差异:
207]: arr = np.arange(12.).reshape((3, 4))
In [
208]: arr
In [208]:
Out[0., 1., 2., 3.],
array([[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
[
209]: arr[0]
In [209]: array([0., 1., 2., 3.])
Out[
210]: arr - arr[0]
In [210]:
Out[0., 0., 0., 0.],
array([[4., 4., 4., 4.],
[8., 8., 8., 8.]]) [
当我们从 arr
中减去 arr[0]
时,每行执行一次减法。这称为广播(broadcasting),并在 Appendix A: Advanced NumPy 中更详细地解释了它与通用 NumPy 数组的关系。DataFrame 和 Series 之间的操作类似:
211]: frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
In [=list("bde"),
.....: columns=["Utah", "Ohio", "Texas", "Oregon"])
.....: index
212]: series = frame.iloc[0]
In [
213]: frame
In [213]:
Out[
b d e0.0 1.0 2.0
Utah 3.0 4.0 5.0
Ohio 6.0 7.0 8.0
Texas 9.0 10.0 11.0
Oregon
214]: series
In [214]:
Out[0.0
b 1.0
d 2.0
e Name: Utah, dtype: float64
默认情况下,DataFrame 和 Series 之间的算术与 DataFrame 列上 Series 的索引相匹配,并向下广播行:
215]: frame - series
In [215]:
Out[
b d e0.0 0.0 0.0
Utah 3.0 3.0 3.0
Ohio 6.0 6.0 6.0
Texas 9.0 9.0 9.0 Oregon
如果在 DataFrame 的列或 Series 的索引中都找不到索引值,则对象将被重新索引以形成并集:
216]: series2 = pd.Series(np.arange(3), index=["b", "e", "f"])
In [
217]: series2
In [217]:
Out[0
b 1
e 2
f
dtype: int64
218]: frame + series2
In [218]:
Out[
b d e f0.0 NaN 3.0 NaN
Utah 3.0 NaN 6.0 NaN
Ohio 6.0 NaN 9.0 NaN
Texas 9.0 NaN 12.0 NaN Oregon
如果您想在列上进行广播,在行上进行匹配,则必须使用其中一种算术方法并指定在索引上进行匹配。例如:
219]: series3 = frame["d"]
In [
220]: frame
In [220]:
Out[
b d e0.0 1.0 2.0
Utah 3.0 4.0 5.0
Ohio 6.0 7.0 8.0
Texas 9.0 10.0 11.0
Oregon
221]: series3
In [221]:
Out[1.0
Utah 4.0
Ohio 7.0
Texas 10.0
Oregon
Name: d, dtype: float64
222]: frame.sub(series3, axis="index")
In [222]:
Out[
b d e-1.0 0.0 1.0
Utah -1.0 0.0 1.0
Ohio -1.0 0.0 1.0
Texas -1.0 0.0 1.0 Oregon
您传递的轴是要匹配的轴。在本例中,我们的意思是匹配 DataFrame 的行索引 (axis="index"
) 并跨列广播。
5.2.5 Function Application and Mapping
NumPy ufuncs(逐元素数组方法)也适用于 pandas 对象:
223]: frame = pd.DataFrame(np.random.standard_normal((4, 3)),
In [=list("bde"),
.....: columns=["Utah", "Ohio", "Texas", "Oregon"])
.....: index
224]: frame
In [224]:
Out[
b d e-0.204708 0.478943 -0.519439
Utah -0.555730 1.965781 1.393406
Ohio 0.092908 0.281746 0.769023
Texas 1.246435 1.007189 -1.296221
Oregon
225]: np.abs(frame)
In [225]:
Out[
b d e0.204708 0.478943 0.519439
Utah 0.555730 1.965781 1.393406
Ohio 0.092908 0.281746 0.769023
Texas 1.246435 1.007189 1.296221 Oregon
另一种常见的操作是将一维数组上的函数应用于每一列或行。DataFrame 的 apply
方法正是这样做的:
226]: def f1(x):
In [return x.max() - x.min()
.....:
227]: frame.apply(f1)
In [227]:
Out[1.802165
b 1.684034
d 2.689627
e dtype: float64
这里,函数 f
计算 Series 的最大值和最小值之间的差,在 frame
中的每一列上调用一次。结果是一个以 frame
列作为索引的系列。
如果您传递 axis="columns"
给 apply
,则该函数将每行调用一次。思考这个问题的一个有用方法是“跨列应用”:
228]: frame.apply(f1, axis="columns")
In [228]:
Out[0.998382
Utah 2.521511
Ohio 0.676115
Texas 2.542656
Oregon dtype: float64
许多最常见的数组统计数据(例如 sum
和 Mean
)都是 DataFrame 方法,因此没有必要使用 apply
。
传递给 apply
的函数不需要返回标量值;它还可以返回具有多个值的 Series:
229]: def f2(x):
In [return pd.Series([x.min(), x.max()], index=["min", "max"])
.....:
230]: frame.apply(f2)
In [230]:
Out[
b d emin -0.555730 0.281746 -1.296221
max 1.246435 1.965781 1.393406
也可以使用逐元素 Python 函数。假设您想根据 frame
中的每个浮点值计算格式化字符串。您可以使用 applymap
执行此操作:
231]: def my_format(x):
In [return f"{x:.2f}"
.....:
232]: frame.applymap(my_format)
In [232]:
Out[
b d e-0.20 0.48 -0.52
Utah -0.56 1.97 1.39
Ohio 0.09 0.28 0.77
Texas 1.25 1.01 -1.30 Oregon
名称 applymap
的原因是 Series 有一个用于应用逐元素函数的 map
方法:
233]: frame["e"].map(my_format)
In [233]:
Out[-0.52
Utah 1.39
Ohio 0.77
Texas -1.30
Oregon object Name: e, dtype:
5.2.6 Sorting and Ranking
按某种标准对数据集进行排序是另一个重要的内置操作。要按行或列标签按字典顺序排序,请使用 sort_index
方法,该方法返回一个新的排序对象:
234]: obj = pd.Series(np.arange(4), index=["d", "a", "b", "c"])
In [
235]: obj
In [235]:
Out[0
d 1
a 2
b 3
c
dtype: int64
236]: obj.sort_index()
In [236]:
Out[1
a 2
b 3
c 0
d dtype: int64
使用 DataFrame,您可以按任一轴上的索引进行排序:
237]: frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
In [=["three", "one"],
.....: index=["d", "a", "b", "c"])
.....: columns
238]: frame
In [238]:
Out[
d a b c0 1 2 3
three 4 5 6 7
one
239]: frame.sort_index()
In [239]:
Out[
d a b c4 5 6 7
one 0 1 2 3
three
240]: frame.sort_index(axis="columns")
In [240]:
Out[
a b c d1 2 3 0
three 5 6 7 4 one
数据默认按升序排序,但也可以按降序排序:
241]: frame.sort_index(axis="columns", ascending=False)
In [241]:
Out[
d c b a0 3 2 1
three 4 7 6 5 one
要按值对 Series 进行排序,请使用其 sort_values
方法:
242]: obj = pd.Series([4, 7, -3, 2])
In [
243]: obj.sort_values()
In [243]:
Out[2 -3
3 2
0 4
1 7
dtype: int64
默认情况下,所有缺失值都会排序到 Series 末尾:
244]: obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
In [
245]: obj.sort_values()
In [245]:
Out[4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
可以使用 na_position
选项将缺失值排序到开头:
246]: obj.sort_values(na_position="first")
In [246]:
Out[1 NaN
3 NaN
4 -3.0
5 2.0
0 4.0
2 7.0
dtype: float64
对 DataFrame 进行排序时,可以使用一列或多列中的数据作为排序键。为此,请将一个或多个列名称传递给 sort_values
:
247]: frame = pd.DataFrame({"b": [4, 7, -3, 2], "a": [0, 1, 0, 1]})
In [
248]: frame
In [248]:
Out[
b a0 4 0
1 7 1
2 -3 0
3 2 1
249]: frame.sort_values("b")
In [249]:
Out[
b a2 -3 0
3 2 1
0 4 0
1 7 1
要按多列排序,请传递名称列表:
250]: frame.sort_values(["a", "b"])
In [250]:
Out[
b a2 -3 0
0 4 0
3 2 1
1 7 1
排名从最低值开始,从 1 到数组中有效数据点的数量分配排名。Series 和 DataFrame 的 rank
方法是值得关注的地方;默认情况下,rank
通过为每个组分配平均排名来打破平局:
251]: obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
In [
252]: obj.rank()
In [252]:
Out[0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
还可以根据数据中观察到的顺序来分配排名:
253]: obj.rank(method="first")
In [253]:
Out[0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
dtype: float64
此处,条目 0 和 2 没有使用平均排名 6.5,而是将它们设置为 6 和 7,因为数据中标签 0 位于标签 2 之前。
您也可以按降序排列:
254]: obj.rank(ascending=False)
In [254]:
Out[0 1.5
1 7.0
2 1.5
3 3.5
4 5.0
5 6.0
6 3.5
dtype: float64
请参阅 Table 5.6,了解可用的打破平局方法的列表。
DataFrame 可以计算行或列的排名:
255]: frame = pd.DataFrame({"b": [4.3, 7, -3, 2], "a": [0, 1, 0, 1],
In ["c": [-2, 5, 8, -2.5]})
.....:
256]: frame
In [256]:
Out[
b a c0 4.3 0 -2.0
1 7.0 1 5.0
2 -3.0 0 8.0
3 2.0 1 -2.5
257]: frame.rank(axis="columns")
In [257]:
Out[
b a c0 3.0 2.0 1.0
1 3.0 1.0 2.0
2 1.0 2.0 3.0
3 3.0 2.0 1.0
Method | Description | ||
---|---|---|---|
"average" |
默认值:为同等组中的每个条目分配平均排名 | ||
"min" |
使用整个组的最低排名 | ||
"max" |
使用整个组的最大排名 | ||
"first" |
按照值在数据中出现的顺序分配排名 | ||
"dense" |
与 method="min" 类似,但组之间的排名总是增加 1,而不是组中相等元素的数量 |
5.2.7 Axis Indexes with Duplicate Labels
到目前为止,我们看过的几乎所有示例都有唯一的轴标签(索引值)。虽然许多 pandas 函数(如 reindex
)要求标签是唯一的,但这不是强制性的。让我们考虑一个具有重复索引的小 Series:
258]: obj = pd.Series(np.arange(5), index=["a", "a", "b", "b", "c"])
In [
259]: obj
In [259]:
Out[0
a 1
a 2
b 3
b 4
c dtype: int64
索引的 is_unique
属性可以告诉你它的标签是否唯一:
260]: obj.index.is_unique
In [260]: False Out[
数据选择是与重复项表现不同的主要因素之一。对具有多个条目的标签进行索引会返回一个 Series,而单个条目则返回一个标量值:
261]: obj["a"]
In [261]:
Out[0
a 1
a
dtype: int64
262]: obj["c"]
In [262]: 4 Out[
这可能会使您的代码更加复杂,因为索引的输出类型可能会根据标签是否重复而有所不同。
相同的逻辑扩展到 DataFrame 中的索引行(或列):
263]: df = pd.DataFrame(np.random.standard_normal((5, 3)),
In [=["a", "a", "b", "b", "c"])
.....: index
264]: df
In [264]:
Out[0 1 2
0.274992 0.228913 1.352917
a 0.886429 -2.001637 -0.371843
a 1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228
b -0.577087 0.124121 0.302614
c
265]: df.loc["b"]
In [265]:
Out[0 1 2
1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228
b
266]: df.loc["c"]
In [266]:
Out[0 -0.577087
1 0.124121
2 0.302614
Name: c, dtype: float64
5.3 Summarizing and Computing Descriptive Statistics
pandas 对象配备了一套常见的数学和统计方法。其中大多数属于归约或汇总统计的类别,从 Series 中提取单个值(如总和或平均值)的方法,或从 DataFrame 的行或列中提取一系列值的方法。与 NumPy 数组上的类似方法相比,它们具有针对丢失数据的内置处理。考虑一个小的 DataFrame:
267]: df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
In [0.75, -1.3]],
.....: [np.nan, np.nan], [=["a", "b", "c", "d"],
.....: index=["one", "two"])
.....: columns
268]: df
In [268]:
Out[
one two1.40 NaN
a 7.10 -4.5
b
c NaN NaN0.75 -1.3 d
调用 DataFrame 的 sum
方法会返回一个包含列总和的 Series:
269]: df.sum()
In [269]:
Out[9.25
one -5.80
two dtype: float64
而是传递 axis="columns"
或 axis=1
跨列求和:
270]: df.sum(axis="columns")
In [270]:
Out[1.40
a 2.60
b 0.00
c -0.55
d dtype: float64
当整行或整列包含所有 NA 值时,总和为 0,而如果任何值不为 NA,则结果为 NA。可以使用 skipna
选项禁用此功能,在这种情况下,行或列中的任何 NA 值都会命名相应的结果 NA:
271]: df.sum(axis="index", skipna=False)
In [271]:
Out[
one NaN
two NaN
dtype: float64
272]: df.sum(axis="columns", skipna=False)
In [272]:
Out[
a NaN2.60
b
c NaN-0.55
d dtype: float64
一些聚合(例如 mean
)需要至少一个非 NA 值才能产生值结果,因此这里我们有:
273]: df.mean(axis="columns")
In [273]:
Out[1.400
a 1.300
b
c NaN-0.275
d dtype: float64
请参阅 Table 5.7,了解每种缩减方法的常用选项列表。
Method | Description |
---|---|
axis |
轴减少过度;DataFrame 的行的“index”和列的“columns” |
skipna |
排除缺失值;默认为 True |
level |
如果轴是分层索引的(MultiIndex),则减少按级别分组 |
某些方法(例如 idxmin
和 idxmax
)返回间接统计信息,例如达到最小值或最大值的索引值:
274]: df.idxmax()
In [274]:
Out[
one b
two dobject dtype:
其他方法都是累加:
275]: df.cumsum()
In [275]:
Out[
one two1.40 NaN
a 8.50 -4.5
b
c NaN NaN9.25 -5.8 d
有些方法既不是减法,也不是累加。describe
就是这样一个例子,一次性生成多个汇总统计信息:
276]: df.describe()
In [276]:
Out[
one two3.000000 2.000000
count 3.083333 -2.900000
mean 3.493685 2.262742
std min 0.750000 -4.500000
25% 1.075000 -3.700000
50% 1.400000 -2.900000
75% 4.250000 -2.100000
max 7.100000 -1.300000
对于非数字数据,describe
会生成替代的汇总统计数据:
277]: obj = pd.Series(["a", "a", "b", "c"] * 4)
In [
278]: obj.describe()
In [278]:
Out[16
count 3
unique
top a8
freq object dtype:
有关汇总统计数据和相关方法的完整列表,请参阅 Table 5.8。
Method | Description |
---|---|
count |
Number of non-NA values |
describe |
Compute set of summary statistics |
min, max |
Compute minimum and maximum values |
argmin, argmax |
Compute index locations (integers) at which minimum or maximum value is obtained, respectively; not available on DataFrame objects |
idxmin, idxmax |
Compute index labels at which minimum or maximum value is obtained, respectively |
quantile |
Compute sample quantile ranging from 0 to 1 (default: 0.5) |
sum |
Sum of values |
mean |
Mean of values |
median |
Arithmetic median (50% quantile) of values |
mad |
Mean absolute deviation from mean value |
prod |
Product of all values |
var |
Sample variance of values |
std |
Sample standard deviation of values |
skew |
Sample skewness (third moment) of values |
kurt |
Sample kurtosis (fourth moment) of values |
cumsum |
Cumulative sum of values |
cummin, cummax |
Cumulative minimum or maximum of values, respectively |
cumprod |
Cumulative product of values |
diff |
Compute first arithmetic difference (useful for time series) |
pct_change |
Compute percent changes |
5.3.1 Correlation and Covariance
一些汇总统计数据(例如相关性和协方差)是根据参数对计算的。让我们考虑一些最初从 Yahoo! 获得的股票价格和交易量的 DataFrames。财务并以二进制 Python pickle 文件形式提供,您可以在本书随附的数据集中找到:
279]: price = pd.read_pickle("examples/yahoo_price.pkl")
In [
280]: volume = pd.read_pickle("examples/yahoo_volume.pkl") In [
我现在计算价格的百分比变化,这是一种时间序列运算,将在 Ch 11: Time Series 中进一步探讨:
281]: returns = price.pct_change()
In [
282]: returns.tail()
In [282]:
Out[
AAPL GOOG IBM MSFT
Date 2016-10-17 -0.000680 0.001837 0.002072 -0.003483
2016-10-18 -0.000681 0.019616 -0.026168 0.007690
2016-10-19 -0.002979 0.007846 0.003583 -0.002255
2016-10-20 -0.000512 -0.005652 0.001719 -0.004867
2016-10-21 -0.003930 0.003011 -0.012474 0.042096
Series 的 corr
方法计算两个 Series 中重叠的、非 NA 的、按索引对齐的值的相关性。相关地,cov
计算协方差:
283]: returns["MSFT"].corr(returns["IBM"])
In [283]: 0.49976361144151166
Out[
284]: returns["MSFT"].cov(returns["IBM"])
In [284]: 8.870655479703549e-05 Out[
另一方面,DataFrame 的 corr
和 cov
方法分别返回完整的相关或协方差矩阵作为 DataFrame:
285]: returns.corr()
In [285]:
Out[
AAPL GOOG IBM MSFT1.000000 0.407919 0.386817 0.389695
AAPL 0.407919 1.000000 0.405099 0.465919
GOOG 0.386817 0.405099 1.000000 0.499764
IBM 0.389695 0.465919 0.499764 1.000000
MSFT
286]: returns.cov()
In [286]:
Out[
AAPL GOOG IBM MSFT0.000277 0.000107 0.000078 0.000095
AAPL 0.000107 0.000251 0.000078 0.000108
GOOG 0.000078 0.000078 0.000146 0.000089
IBM 0.000095 0.000108 0.000089 0.000215 MSFT
使用 DataFrame 的 corrwith
方法,您可以计算 DataFrame 的列或行与另一个 Series 或 DataFrame 之间的成对相关性。传递 Series 返回一个 Series,其中包含为每列计算的相关值:
287]: returns.corrwith(returns["IBM"])
In [287]:
Out[0.386817
AAPL 0.405099
GOOG 1.000000
IBM 0.499764
MSFT dtype: float64
传递 DataFrame 会计算匹配列名称的相关性。在这里,我计算百分比变化与交易量的相关性:
288]: returns.corrwith(volume)
In [288]:
Out[-0.075565
AAPL -0.007067
GOOG -0.204849
IBM -0.092950
MSFT dtype: float64
传递 axis="columns"
会逐行执行操作。在所有情况下,在计算相关性之前,数据点都会按标签对齐。
5.3.2 Unique Values, Value Counts, and Membership
另一类相关方法提取有关一维 Series 中包含的值的信息。为了说明这些,请考虑以下示例:
289]: obj = pd.Series(["c", "a", "d", "a", "a", "b", "b", "c", "c"]) In [
第一个函数是 unique
,它为您提供 Series 中唯一值的数组:
290]: uniques = obj.unique()
In [
291]: uniques
In [291]: array(['c', 'a', 'd', 'b'], dtype=object) Out[
唯一值不一定按它们首次出现的顺序返回,也不是按排序顺序返回,但如果需要,可以在事后对它们进行排序 (uniques.sort()
)。相关地,value_counts
计算包含值频率的 Series:
292]: obj.value_counts()
In [292]:
Out[3
c 3
a 2
b 1
d Name: count, dtype: int64
为了方便起见,该 Series 按值降序排列。value_counts
也可用作顶级 pandas 方法,可与 NumPy 数组或其他 Python 序列一起使用:
293]: pd.value_counts(obj.to_numpy(), sort=False)
In [293]:
Out[3
c 3
a 1
d 2
b Name: count, dtype: int64
isin
执行矢量化集成员资格检查,可用于将数据集过滤为 DataFrame 中的 Series 或列中的值的子集:
294]: obj
In [294]:
Out[0 c
1 a
2 d
3 a
4 a
5 b
6 b
7 c
8 c
object
dtype:
295]: mask = obj.isin(["b", "c"])
In [
296]: mask
In [296]:
Out[0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
bool
dtype:
297]: obj[mask]
In [297]:
Out[0 c
5 b
6 b
7 c
8 c
object dtype:
与 isin
相关的是 Index.get_indexer
方法,它为您提供一个索引数组,从一个可能非不同值的数组到另一个不同值的数组:
298]: to_match = pd.Series(["c", "a", "b", "b", "c", "a"])
In [
299]: unique_vals = pd.Series(["c", "b", "a"])
In [
300]: indices = pd.Index(unique_vals).get_indexer(to_match)
In [
301]: indices
In [301]: array([0, 2, 1, 1, 0, 2]) Out[
有关这些方法的参考,请参阅 Table 5.9。
Method | Description |
---|---|
isin |
计算一个布尔数组,指示每个 Series 或 DataFrame 值是否包含在传递的值序列中 |
get_indexer |
将数组中每个值的整数索引计算到另一个不同值的数组中;有助于数据对齐和连接类型操作 |
unique |
计算 Series 中唯一值的数组,按观察到的顺序返回 |
value_counts |
返回一个包含唯一值作为其索引和频率作为其值的 Series,按降序排列计数 |
在某些情况下,您可能需要计算 DataFrame 中多个相关列的直方图。这是一个例子:
302]: data = pd.DataFrame({"Qu1": [1, 3, 4, 3, 4],
In ["Qu2": [2, 3, 1, 2, 3],
.....: "Qu3": [1, 5, 2, 4, 4]})
.....:
303]: data
In [303]:
Out[
Qu1 Qu2 Qu30 1 2 1
1 3 3 5
2 4 1 2
3 3 2 4
4 4 3 4
我们可以计算单个列的值计数,如下所示:
304]: data["Qu1"].value_counts().sort_index()
In [304]:
Out[
Qu11 1
3 2
4 2
Name: count, dtype: int64
要计算所有列的值,请将 pandas.value_counts
传递给 DataFrame 的 apply
方法:
305]: result = data.apply(pd.value_counts).fillna(0)
In [
306]: result
In [306]:
Out[
Qu1 Qu2 Qu31 1.0 1.0 1.0
2 0.0 2.0 1.0
3 2.0 2.0 0.0
4 2.0 0.0 2.0
5 0.0 0.0 1.0
在这里,结果中的行标签是所有列中出现的不同值。这些值是每列中这些值的相应计数。
还有一个 DataFrame.value_counts
方法,但它将 DataFrame 的每一行视为一个元组来计算计数,以确定每个不同行的出现次数:
307]: data = pd.DataFrame({"a": [1, 1, 1, 2, 2], "b": [0, 0, 1, 0, 0]})
In [
308]: data
In [308]:
Out[
a b0 1 0
1 1 0
2 1 1
3 2 0
4 2 0
309]: data.value_counts()
In [309]:
Out[
a b1 0 2
2 0 2
1 1 1
Name: count, dtype: int64
在这种情况下,结果有一个索引,将不同的行表示为分层索引,我们将在 Ch 8: Data Wrangling: Join, Combine, and Reshape 中更详细地探讨该主题。
5.4 Conclusion
在下一章中,我们将讨论使用 pandas 读取(或加载)和写入数据集的工具。之后,我们将更深入地研究使用 pandas 的数据清理、整理、分析和可视化工具。