# Pandas 很有趣! 什么是 Pandas? 在之前的章节中,我们已经讨论过 NumPy。 现在让我们继续学习 pandas,这是一个经过精心设计的软件包,用于在 Python 中存储,管理和处理数据。 我们将从讨论什么是 Pandas 以及人们为什么使用 Pandas 开始本章。 接下来,我们将讨论 Pandas 提供的两个最重要的对象:序列和数据帧。 然后,我们将介绍如何子集您的数据。 在本章中,我们将简要概述什么是 Pandas 以及其受欢迎的原因。 # Pandas 做什么? pandas 向 Python 引入了两个关键对象,序列和数据帧,后者可能是最有用的,但是 pandas DataFrames 可以认为是绑定在一起的系列。 系列是一系列数据,例如基本 Python 中的列表或一维 NumPy 数组。 而且,与 NumPy 数组一样,序列具有单个数据类型,但是用序列进行索引是不同的。 使用 NumPy 时,对行和列索引的控制不多; 但是对于一个系列,该系列中的每个元素都必须具有唯一的索引,名称,键,但是您需要考虑一下。 该索引可以由字符串组成,例如一个国家中的城市,而序列中的相应元素表示一些统计值,例如城市人口; 或日期,例如股票系列的交易日。 可以将数据帧视为具有公共索引的多个系列的公共长度,它们在单个表格对象中绑定在一起。 该对象类似于 NumPy 2D `ndarray`,但不是同一件事。 并非所有列都必须具有相同的数据类型。 回到城市示例,我们可以有一个包含人口的列,另一个包含该城市所在州或省的信息,还有一个包含布尔值的列,用于标识城市是州还是省的首都,仅使用 NumPy 来完成是一个棘手的壮举。 这些列中的每一个可能都有一个唯一的名称,一个字符串来标识它们包含的信息。 也许可以将其视为变量。 有了这个对象,我们可以轻松,有效地存储,访问和操纵我们的数据。 在下面的笔记本中,我们将预览可以使用序列和数据帧进行的操作: ![](img/1e8c0c05-3a69-4331-8685-aa11ae55feee.png) 我们将同时加载 NumPy 和 pandas,我们将研究读取 NumPy 和 pandas 的 CSV 文件。 实际上,我们可以在 NumPy 中加载 CSV 文件,并且它们可以具有不同类型的数据,但是为了管理此类文件,您需要创建自定义`dtype`以类似于此类数据。 因此,这里有一个 CSV 文件`iris.csv`,其中包含鸢尾花数据集。 现在,如果我们希望加载该数据,则需要考虑以下事实:每一行的数据不一定都是同一类型的。 特别是最后一列是针对物种的,它不是数字,而是字符串。 因此,我们需要创建一个自定义`dtype`,在此处执行此操作,以调用此新的`dtype`模式: ![](img/d449f161-4900-4564-8b7a-5e2a7bf0edf7.png) 我们可以使用 NumPy 函数`loadtxt`加载此数据集,将`dtype`设置为`schema`对象,并将定界符设置为逗号以指示它是 CSV 文件。 实际上,我们可以在以下位置读取此数据集: ![](img/fb5ffbd9-5c83-4152-b50c-09c72d0f5704.png) 请注意,此数据集必须在您的工作目录中。 如果我们看一下这个数据集,这将是我们注意到的: ![](img/b2367c9c-d37f-4737-8685-8f7c8a433fd4.png) 此输出屏幕截图为,仅表示,实际输出包含更多行。 此数据集的每一行都是此一维 NumPy 数组中的新条目。 实际上,这是一个 NumPy 数组: ![](img/7662a321-9cfa-465c-872a-90e359ee2026.png) 我们使用以下命令选择前五行: ![](img/5262fbb7-d8e0-4e99-b468-91dba378371a.png) 我们可以选择前五行,并指定我们只希望使用隔片长度,它们是每行的第一元素: ![](img/83e4c85b-b624-4086-9051-d2016421d13d.png) 我们甚至可以选择花瓣的长度和种类: ![](img/0d275bf1-2f8a-4b4f-841e-5120cf7579c4.png) 但是有一种更好的方法可以对付 Pandas。 在 Pandas 中,我们将使用`read_csv`函数,该函数将自动正确解析 CSV 文件: ![](img/08a48a18-eac9-4788-b8f1-5aae1022d307.png) 查看此数据集,请注意,使用 Jupyter 笔记本电脑,它的显示方式更加可读。 实际上,这是一个`pandas` DataFrame: ![](img/87068bb2-f619-4e2e-8b37-3ff5c460c85c.png) 使用`head`函数可以看到前五行: ![](img/47f9f523-1847-4fa6-b05c-b518d001c7df.png) 通过将其指定为好像是此数据帧的一个属性,我们还可以看到其间隔长度: ![](img/7da227f0-f566-4636-8681-f891a2bb2b7a.png) 我们得到的实际上是一个系列。 我们可以选择此数据帧的一个子集,再次返回前五行,然后选择`petal_length`和`species`列: ![](img/abf8e8f5-aa1b-4c69-907d-cfe54d25b340.png) 话虽如此,Pandas 的核心是建立在 NumPy 之上。 实际上,我们可以看到 pandas 用于描述其内容的 NumPy 对象: ![](img/c65cf239-06de-4aaf-b417-7a5b18ee2682.png) 实际上,我们之前创建的 NumPy 对象可用于构造 Pandas DataFrame: ![](img/e88f7759-0724-4736-bac7-de4ab7fb85ce.png) 现在是时候仔细看一下 pandas 系列和数据帧了。 # 探索系列和数据帧对象 我们将开始研究 pandas 系列和数据帧对象。 在本节中,我们将通过研究 Pandas 系列和数据帧的创建方式来开始熟悉它们。 我们将从系列开始,因为它们是数据帧的构建块。 系列是包含单一类型数据的一维数组状对象。 仅凭这一事实,您就可以正确地得出结论,它们与一维 NumPy 数组非常相似,但是与 NumPy 数组相比,序列具有不同的方法,这使它们更适合管理数据。 可以使用索引创建索引,该索引是标识系列内容的元数据。 系列可以处理丢失的数据; 他们通过用 NumPy 的 NaN 表示丢失的数据来做到这一点。 # 创建系列 我们可以从类似数组的对象创建序列; 其中包括列表,元组和 NumPy `ndarray`对象。 我们还可以根据 Python 字典创建系列。 向系列添加索引的另一种方法是通过将唯一哈希值的索引或类似数组的对象传递给系列的 create 方法的 index 参数来创建索引。 我们也可以单独创建索引。 创建索引与创建序列很像,但是我们要求所有值都必须唯一。 每个系列都有一个索引。 如果我们不分配索引,则将从 0 开始的简单数字序列用作索引。 我们可以通过将字符串传递给该系列的 create 方法的 name 参数来为该系列命名。 我们这样做是为了,如果我们要使用该系列创建一个数据帧,我们可以自动为该系列分配列名或行名,这样我们就可以知道该系列描述的日期。 换句话说,该名称提供了有用的元数据,我建议在合理范围内尽可能设置此参数。 让我们看一个可行的例子。 请注意,我们直接将序列和数据帧对象导入名称空间: ![](img/b2516bf8-dccd-41be-bf29-9dbbe26a3c22.png) 我们经常这样做,因为这些对象已被详尽地使用。 在这里,我们创建两个系列,一个由数字`1`,`2`,`3`,`4`组成,另一个由字母`a`,`b`和`c`组成: ![](img/ecf99f93-73a1-4895-ac0f-63bda88dbc28.png) 请注意,索引已自动分配给这两个系列。 让我们创建一个索引; 该索引包含美国城市的名称: ![](img/24b34554-6143-4b05-85f8-3592e934bea6.png) 我们将创建一个由`pops`组成的新系列,并将该索引分配给我们创建的系列。 这些城市的人口成千上万。 我从维基百科获得了这些数据。 我们还为该系列分配了名称`Population`。 结果如下: ![](img/7d137c03-9a76-4029-a9f0-b06f8af92009.png) 注意,我插入了一个缺失值; 这是`Phoenix`的人口,我们确实知道,但是我想添加一些额外的内容来进行演示。 我们也可以使用字典创建系列。 在这种情况下,字典的键将成为结果序列的索引,而值将是结果序列的值。 因此,在这里,我们添加`state`名称: ![](img/0f40bfee-28c6-419a-ac7b-6f5f42b37b51.png) 我还使用字典创建了一个系列,并在这些城市中填充了相应的区域: ![](img/25fd070e-296e-455d-a38c-0325591d2165.png) 现在,我想提请您注意以下事实:这些系列的长度不相等,而且它们也不都包含相同的键。 它们并非全部或都包含相同的索引。 我们稍后将使用这些系列,因此请记住这一点。 # 创建数据框 系列很有趣,主要是因为它们用于构建 pandas DataFrame。 我们可以将 pandas DataFrame 视为将系列组合在一起以形成表格对象,其中行和列为系列。 我们可以通过多种方式创建数据帧,我们将在此处进行演示。 我们可以给数据帧一个索引。 我们还可以通过设置 columns 参数来手动指定列名。 选择列名遵循与选择索引名相同的规则。 让我们看看一些创建数据帧的方法。 我们要做的第一件事是创建数据帧,我们不会太在意它们的索引。 我们可以从 NumPy 数组创建一个数据帧: ![](img/299f7640-bffc-4ad5-b4f4-4ae28afa3667.png) 在这里,我们有一个三维 NumPy 数组,其中填充了数字。 我们可以简单地通过将该对象作为第一个参数传递给数据帧创建函数从该对象创建一个数据帧: ![](img/119801fe-cb4e-4579-9d7c-7b6e88c0b1ee.png) 如果需要,可以向此`DataFrame`添加索引和列名: ![](img/7d5fa02d-ae75-4803-adf4-c00b47e4e973.png) 我们从元组列表创建数据帧: ![](img/18107d9f-204a-405e-b397-287510801717.png) 我们也可以从`dict`创建数据帧: ![](img/aa282d53-2377-44e2-9fa3-e87784e108db.png) 现在,假设我们要创建一个数据帧并将一个 dict 传递给它,但是该 dict 不由长度相同的列表组成。 这将产生一个错误: ![](img/34b2c99c-803f-4f06-8ec9-9e87c2ae3681.png) 原因是需要将索引分配给这些值,但是函数不知道如何分配丢失的信息。 它不知道如何对齐这些列表中的数据。 但是,如果我们要传递一个字典(并且字典的值是不等长的序列,但是这些序列具有索引),则不会产生错误: ![](img/0bb83419-123b-489f-9b80-dd70ce1e6dd2.png) 取而代之的是,由于它知道如何排列不同系列中的元素,因此它将这样做,并用 NaN 填充任何缺少信息的位置。 现在,让我们创建一个包含有关系列信息的数据帧,您可能还记得这些系列的长度不同。 此外,它们并非都包含相同的索引值,但是我们仍然能够从它们创建一个数据帧: ![](img/1b83c6ff-184f-45ae-a02a-24d0d4e10930.png) 但是,在这种情况下,这不是我们想要的数据帧。 这是错误的方向; 行是我们将解释为变量的内容,列是我们将解释为键的内容。 因此,我们可以在实际需要的方法中使用字典创建数据帧: ![](img/bd1d3af6-ccfe-4d06-9da9-c2bb9d06c40c.png) 或者我们可以像 NumPy 数组一样使用转置方法`T`方法来使数据帧处于正确的方向: ![](img/96d001d2-c8b3-4668-8aed-7a384d1d5afe.png) # 新增资料 创建系列或数据帧之后,我们可以使用`concat`函数或`append`方法向其中添加更多数据。 我们将一个对象传递给包含将添加到现有对象中的数据的方法。 如果我们正在使用数据帧,则可以附加新行或新列。 我们可以使用`concat`函数添加新列,并使用`dict`,系列或数据帧进行串联。 让我们看看如何将新信息添加到系列或数据帧中。 例如,让我们在`pops`系列中添加两个新城市,分别是`Seattle`和`Denver`。 结果如下: ![](img/4d9bb80b-ddac-4e0d-9db1-dc6987b9425b.png) 请注意,这尚未完成。 也就是说,返回了一个新系列,而不是更改现有系列。 我将通过使用所需数据创建一个数据帧来向该数据帧添加新行: ![](img/696a8c03-b0af-4b8c-8390-98e16a8a1f34.png) 我还可以通过有效地创建多个数据帧将新列添加到此数据帧。 我有一个列表,在此列表中,我有两个数据帧。 我有`df`,并且我有新的数据帧包含要添加的列。 这不会更改现有的数据帧,而是创建一个全新的数据帧,然后我们需要将其分配给变量: ![](img/9d8d6515-f3c5-4035-964f-2da367628601.png) # 保存数据框 假设我们有一个数据帧; 称它为`df`。 我们可以轻松保存数据帧的数据。 我们可以使用`to_pickle`方法对数据帧进行腌制(将其保存为 Python 常用的格式),并将文件名作为第一个参数传递。 我们可以使用`to_csv`保存 CSV 文件,使用`to_json`保存 JSON 文件或使用`to_html`保存 HTML 表。 还有许多其他格式可用; 例如,我们可以将数据保存在 Excel 电子表格,Stata,DAT 文件,HDF5 格式和 SQL 命令中,以将其插入数据库,甚至复制到剪贴板中。 稍后我们可能会讨论其他方法以及如何加载不同格式的数据。 在此示例中,我将数据帧中的数据保存到 CSV 文件中: ![](img/5283f0c0-1ce8-487b-a37b-1cf556a6a691.png) 希望到目前为止,您对什么系列和数据帧更加熟悉。 接下来,我们将讨论在数据帧中设置数据子集,以便您可以快速轻松地获取所需的信息。 # 子集数据 现在我们可以制作 pandas 系列和数据帧,让我们处理它们包含的数据。 在本节中,我们将看到如何获取和处理我们存储在 Pandas 系列或数据帧中的数据。 自然,这是一个重要的话题。 这些对象否则将毫无用处。 您不应该惊讶于如何对数据帧进行子集化有很多变体。 我们不会在这里涵盖所有特质; 请参考文档进行详尽的讨论。 但是,我们将讨论每个 Pandas 用户应该意识到的最重要的功能。 # 子系列化 让我们首先看一下系列。 由于它们与数据帧相似,因此有一些适用的关键课程。 子集序列的最简单方法是用方括号括起来,我们可以这样做,就像我们将列表或 NumPy 数组子集化一样。 冒号运算符确实在这里工作,但我们还有更多工作要做。 我们可以根据序列的索引选择元素,而不是仅根据序列中元素的位置,遵循许多相同的规则,就好像我们使用指示序列中元素位置的整数一样。 冒号运算符也可以正常工作,并且在很大程度上符合预期。 选择两个索引之间的所有元素: ![](img/89f8aa74-cb85-404d-a300-98786db9f47c.png) 但是与整数位置不同,冒号运算符确实包含端点。 一个特别有趣的情况是使用布尔值建立索引时。 我将展示这种用法可能看起来像什么。 这样可以方便地获取特定范围内的数据。 如果我们可以得到类似数组的对象(例如列表,NumPy 数组或其他序列)来生成布尔值,则可以将该对象用于索引。 这是一些示例代码,展示了对一系列索引的索引: ![](img/cacfc4fa-9a1a-44bc-ad71-14ad18d22094.png) 到目前为止,整数索引以及布尔值索引的行为均符合预期: ![](img/e83b3582-4a47-40ce-a080-00ee3a1a74b4.png) 唯一真正有趣的示例是当我们将冒号运算符与索引一起使用时; 请注意,所有 beta 和 delta 都包括在内,尤其是 delta。 这与我们通常与冒号运算符关联的行为不同。 这是一个有趣的示例: ![](img/c6327805-7b1d-444f-a460-dbb14c237112.png) 我们有一个序列,并且该序列具有`index`的整数,该整数的顺序不为 0 到 4。 现在,订单混合了。 考虑我们要求的索引。 会发生什么? 一方面,我们可以说最后一个命令将基于索引进行选择。 因此它将选择元素 2 和 4; 他们之间什么都没有。 但另一方面,它可能会使用整数位置来选择系列的第三和第四元素。 换句话说,当我们从 0 开始计数时,它是位置 2 和位置 3,就像您希望将`srs2`视为列表一样。 哪种行为会占上风? 还不是很清楚。 # 索引方法 Pandas 提供的方法可以使我们清楚地说明我们要如何编制索引。 我们还可以区分基于系列索引值的索引和基于对象在系列中的位置的索引,就像处理列表一样。 我们将关注的两种方法是`loc`和`iloc`。`loc`专注于根据序列的索引进行选择,如果我们尝试选择不存在的关键元素,则会出现错误。`iloc`就像我们在处理 Python 列表一样建立索引; 也就是说,它基于整数位置进行索引。 因此,如果我们尝试在`iloc`中使用非整数进行索引,或者尝试选择有效整数范围之外的元素,则会产生错误。 有一种`hybrid`方法`ix`,其作用类似于`loc`,但是如果传递的输入无法针对索引进行解释,则它的作用将类似于`iloc`。 由于`ix`的行为模棱两可,因此我建议大多数时候坚持使用`loc`或`iloc`。 让我们回到我们的例子。 事实证明,在这种情况下,方括号的索引类似于`iloc`; 也就是说,它们基于整数位置进行索引,就好像`srs2`是一个列表一样。 如果我们想基于`srs2`的索引进行索引,则可以使用`loc`进行索引,以获得其他可能的结果。 再次注意,在这种情况下,两个端点都包括在内。 这与我们通常与冒号运算符关联的行为不同: ![](img/fbd66516-62ee-411e-a047-fc38a67c14c5.png) # 切片数据框 在讨论切片系列之后,让我们谈谈切片数据帧。 好消息是,在谈论系列切片时,许多艰苦的工作已经完成。 我们介绍了`loc`和`iloc`作为串联方法,但它们也是数据帧方法。 毕竟,您应该考虑将数据帧视为多个列粘合在一起的系列。 现在,我们需要考虑从系列中学到的知识如何转换为二维设置。 如果我们使用括号表示法,它将仅适用于数据帧的列。 我们将需要使用`loc`和`iloc`来对数据帧的行进行子集化。 实际上,这些方法可以接受两个位置参数。 根据我们前面描述的规则,第一个位置参数确定要选择的行,第二个位置参数确定要选择的列。 可以发出第二个参数来选择所有列,并将选择规则仅应用于行。 这意味着我们应该将第一个参数作为冒号,以便在我们选择的列中更加挑剔。 `loc`和`iloc`将在它们的两个参数上加上基于索引的索引或基于整数位置的索引,而`ix`可能允许混合使用此行为。 我不建议这样做。 对于后来的读者来说,结果太含糊了。 如果要混合`loc`和`iloc`的行为,建议使用方法链接。 也就是说,如果要基于索引选择行,而要基于整数位置选择列,请首先使用`loc`方法选择行,然后使用`iloc`方法选择列。 执行此操作时,如何选择数据帧的元素没有任何歧义。 如果您只想选择一列怎么办? 结果如下: ![](img/7a04ad57-6627-41aa-b93d-89bc0d95ef54.png) 这样做很简捷; 只需将特定的列视为数据帧的属性,作为对象,使用点表示法有效地选择它即可。 这可以很方便: ![](img/f049093d-84e8-473b-b2d4-765c08aa2744.png) 请记住,Pandas 是从 NumPy 构建的,在数据帧的后面是 NumPy 数组。 因此,知道了您现在对 NumPy 数组所了解的知识后,以下事实对您来说就不足为奇了。 将数据帧的切片操作的结果分配给变量时,变量承载的不是数据的副本,而是原始数据帧中数据的视图: ![](img/279074fb-d4e4-44a0-8124-01b6a2813a02.png) ![](img/1884861d-d8ae-4d16-ac15-c87f0b32b6c4.png) 如果要制作此数据的独立副本,则需要使用数据帧的`copy`方法。 系列也是如此。 现在来看一个例子。 H 是,我们创建一个 DataFrame `df`,它具有有趣的索引和列名: ![](img/1735a032-d079-4cfb-a7f1-af13d15f6d01.png) 通过将第一列的名称视为`df`的属性,我可以轻松地获得一个表示第一列中数据的系列。 接下来,我们看到`loc`和`iloc`的行为。`loc`根据它们的索引选择行和列,但是`iloc`像选择列表一样选择它们。 也就是说,它使用整数位置: ![](img/57648920-799d-4aa6-a581-faca79154bff.png) 在这里,我们看到了方法链接。 对于输入 10,您可能会注意到它的开始类似于上一张幻灯片中的输入 9,但随后我在结果视图上调用了`loc`,以进一步细分数据。 我将此方法链接的结果保存在`df2`中。 我还用`df2`更改了第二列的内容,并用一系列新的自定义数据替换了它们: ![](img/b2e51dc4-3b34-4a0e-b65a-714e5be57710.png) 由于`df2`是`df`的独立副本,因此请注意,在创建`df2`时必须使用复制方法; 原始数据不受影响。 这使我们到达了重要的地步。序列和数据帧不是不可变的对象。 您可以更改其内容。 这类似于更改 NumPy 数组中的内容。 但是,在跨列进行更改时要小心; 它们可能不是同一数据类型,从而导致不可预测的结果。有时 : ![](img/683559b6-4034-4715-86ab-2024b20970c5.png) 我们在这里看到什么分配: ![](img/27c5c14f-fdfb-49e4-9bd2-843ad03d966e.png) 这种行为与您在 NumPy 中看到的行为非常相似,因此我将不做过多讨论。 关于子集,还有很多要说的,特别是当索引实际上是`MultiIndex`时,但这是以后使用的。 # 摘要 在本章中,我们介绍了 Pandas 并研究了它的作用。 我们探索了 Pandas 系列数据帧并创建了它们。 我们还研究了如何将数据添加到序列和数据帧中。 最后,我们介绍了保存数据帧。 在下一章中,我们将讨论算术,函数应用程序和函数映射。