如何重置 pandas DataFrame 索引
如何重置 pandas DataFrame 索引
在本教程中,您将学习如何重置 pandas DataFrame 索引、可能需要这样做的原因以及如果不这样做可能出现的问题。
在开始学习之旅之前,你应该熟悉如何创建pandas DataFrame 。了解DataFrame和pandas Series之间的区别也会对你有帮助。
此外,您可能希望在完成本教程中的示例时使用数据分析工具Jupyter Notebook 。或者, JupyterLab将为您提供增强的笔记本体验,但您可以随意使用您想要的任何 Python 环境。
下表描述了您开始时需要用到的数据:
列名称 | PyArrow 数据类型 | 描述 |
---|---|---|
first_name | string | 会员名字 |
last_name | string | 会员姓氏 |
instrument | string | 主要演奏的乐器 |
date_of_birth | string | 会员出生日期 |
如您所见,数据包含摇滚乐队The Beach Boys成员的详细信息。每行都包含有关其过去和现在的各个成员的信息。
注意:如果您从未听说过海滩男孩,他们是一支于 20 世纪 60 年代初成立的美国摇滚乐队。
在本教程中,您将使用pandas库来处理 DataFrames,以及较新的PyArrow库。PyArrow 库为 pandas 提供了其自己的优化数据类型,这些数据类型比 pandas 默认使用的传统NumPy 类型更快且占用的内存更少。
如果您在命令行上工作,则可以使用单个命令 python -m pip install pandas pyarrow
同时安装pandas
和pyarrow。 如果您在 Jupyter Notebook 中工作,则应使用!python -m pip install pandas pyarrow
。 无论如何,您都应该在虚拟环境中执行此操作,以避免与您在全局环境中使用的库发生冲突。
一旦你有了这些库,就可以将数据读入 DataFrame 了:
>>> import pandas as pd
>>> beach_boys = pd.read_csv(
... "band_members.csv"
... ).convert_dtypes(dtype_backend="pyarrow")
首先,您曾经import pandas
在代码中提供该库。为了构造 DataFrame 并将其读入beach_boys
变量,您使用了 pandasread_csv()
函数,将band_members.csv
其作为要读取的文件传递,band_members.csv的具体内容往下看,您可以自己创建该文件
。最后,通过传递dtype_backend="pyarrow"
给.convert_dtypes()
您将所有列转换为pyarrow
类型。
如果你想验证pyarrow
数据类型确实被使用,那么beach_boys.dtypes
将满足你的好奇心:
>>> beach_boys.dtypes
first_name string[pyarrow]
last_name string[pyarrow]
instrument string[pyarrow]
date_of_birth string[pyarrow]
dtype: object
如您所见,每种数据类型都包含[pyarrow]
其名称。
如果您想要彻底分析日期信息,那么您需要解析该date_of_birth
列以确保日期被读取为合适的pyarrow
日期类型。这样您就可以按特定的日期、月份或年份等进行分析,就像数据透视表中常见的那样。
date_of_birth
本教程不分析该列,因此string
将分析其被读取的数据类型。稍后,您将有机会通过一些练习来磨练您的技能。如果您想了解如何完成,解决方案包括日期解析代码。
现在文件已被加载到 DataFrame 中,您可能需要查看它,您可以根据下面内容自己创建数据文件band_members.csv
:
>>> beach_boys
first_name last_name instrument date_of_birth
0 Brian Wilson Bass 20-Jun-1942
1 Mike Love Saxophone 15-Mar-1941
2 Al Jardine Guitar 03-Sep-1942
3 Bruce Johnston Bass 27-Jun-1942
4 Carl Wilson Guitar 21-Dec-1946
5 Dennis Wilson Drums 04-Dec-1944
6 David Marks Guitar 22-Aug-1948
7 Ricky Fataar Drums 05-Sep-1952
8 Blondie Chaplin Guitar 07-Jul-1951
DataFrames 是类似于电子表格或数据库表的二维数据结构。Pandas DataFrame 可以视为一组列,每列都是一个 Pandas Series。每列还有一个标题,这是name
Series 的属性,每行都有一个标签,称为其关联索引对象的元素。
DataFrame 的索引显示在 DataFrame 的左侧。它不是原始band_members.csv
源文件的一部分,而是作为 DataFrame 创建过程的一部分添加的。这就是您要学习重置的索引对象。
DataFrame 的索引是一列额外的标签,可帮助您识别行。与列标题结合使用时,它允许您访问 DataFrame 内的特定数据。默认索引标签是整数序列,但您可以使用字符串使它们更有意义。您实际上可以使用任何可哈希类型作为索引,但整数、字符串和时间戳是最常见的。
注意:虽然索引在 Pandas 中确实很有用,但 Pandas 的另一种选择是新的高性能Polars库,它消除了索引,转而使用行号。这可能令人惊讶,但除了用于选择行或列之外,在分析 DataFrames 时通常不会使用索引。此外,在 Polars DataFrame 中添加或删除行时,行号始终保持连续。Pandas 中的索引并非如此。
完成这些准备工作后,是时候开着您的Little Deuce Coupe去海滩、做一次弹出式活动并进行一些学习了。现在,您将研究重新索引 DataFrame 的主要方法。您可以将对象Index直接应用于 DataFrame 的.index属性,也可以使用 DataFrame 的.set_axis()方法。首先,您将使用 DataFrame 的.reset_index()方法。
如何使用以下方法重置 pandas DataFrame 索引.reset_index()
该DataFrame.reset_index()方法,顾名思义,用于将 pandas DataFrame 的索引重置为默认值。您还可以使用此方法重置 DataFrameMultiIndex上的索引(如果有)。稍后您将详细了解如何重置多索引。
假设你对 DataFrame 进行了一些数据清理,并决定按字母顺序对其进行排序first_name
。你可以使用以下方法执行此操作.sort_values()
:
>>> beach_boys.sort_values(by="first_name")
first_name last_name instrument date_of_birth
2 Al Jardine Guitar 03-Sep-1942
8 Blondie Chaplin Guitar 07-Jul-1951
0 Brian Wilson Bass 20-Jun-1942
3 Bruce Johnston Bass 27-Jun-1942
4 Carl Wilson Guitar 21-Dec-1946
6 David Marks Guitar 22-Aug-1948
5 Dennis Wilson Drums 04-Dec-1944
1 Mike Love Saxophone 15-Mar-1941
7 Ricky Fataar Drums 05-Sep-1952
虽然你对排序工作完美感到高兴,但你对索引的当前状态并不满意。索引不再与更新后的行顺序有任何相似之处。
为了解决这个问题,您的第一个想法可能是使用.reset_index()
其默认参数,但结果可能不是您想要的:
>>> beach_boys.sort_values(by="first_name").reset_index()
index first_name last_name instrument date_of_birth
0 2 Al Jardine Guitar 03-Sep-1942
1 8 Blondie Chaplin Guitar 07-Jul-1951
2 0 Brian Wilson Bass 20-Jun-1942
3 3 Bruce Johnston Bass 27-Jun-1942
4 4 Carl Wilson Guitar 21-Dec-1946
5 6 David Marks Guitar 22-Aug-1948
6 5 Dennis Wilson Drums 04-Dec-1944
7 1 Mike Love Saxophone 15-Mar-1941
8 7 Ricky Fataar Drums 05-Sep-1952
现在,您有了一个新的默认索引,它是从零开始的一系列连续数字,可以清楚地定义行顺序。但是,原始索引仍然保留,并已移动到一个容易混淆的新列中index
。如果您将来需要将 DataFrame 恢复为其原始排序顺序,这可能是可以接受的,但通常情况下,包含它只会浪费内存。您通常希望摆脱它。
幸运的是,这很容易做到。默认情况下,.reset_index()
复制原始 DataFrame,将默认索引应用于第二个副本,然后将第二个副本返回给您。这意味着原始 DataFrame 上的索引保持不变,因此没有对其造成任何损害。
您可以通过将其参数.reset_index()
设置为 来指示完全删除原始索引并将其替换为新的默认索引。 您还可以将原始引用重新分配给新的 DataFrame。 更新后的版本将是从此时起唯一存在的版本:drop
True
>>> beach_boys = (
... beach_boys.sort_values(by="first_name")
... .reset_index(drop=True)
... )
>>> beach_boys
first_name last_name instrument date_of_birth
0 Al Jardine Guitar 03-Sep-1942
1 Blondie Chaplin Guitar 07-Jul-1951
2 Brian Wilson Bass 20-Jun-1942
3 Bruce Johnston Bass 27-Jun-1942
4 Carl Wilson Guitar 21-Dec-1946
5 David Marks Guitar 22-Aug-1948
6 Dennis Wilson Drums 04-Dec-1944
7 Mike Love Saxophone 15-Mar-1941
8 Ricky Fataar Drums 05-Sep-1952
现在,您已按自己想要的方式整理了 DataFrame。您还将原始引用重新分配给了新的 DataFrame,这意味着那个烦人的index
列已消失得无影无踪。
注意:
原始 DataFrame 保持不变.reset_index(),因为 默认返回原始 DataFrame 的副本inplace。您可以将参数设置为True来更改原始 DataFrame。
随着时间的推移,pandas 的开发人员将大多数方法设计为默认返回 DataFrame 副本,以便可以轻松正确地重做任何错误的分析。如果每个方法都更新了 DataFrame,则任何错误都需要重新运行所有先前的处理。
一个常见的误解是,通过设置inplace=True可以节省处理时间。这通常是不正确的。在 Python 内部,通常会创建并处理 DataFrame 副本,但重新索引的副本会分配给原始 DataFrame 的变量。
建议不要使用此参数,因为它计划在未来的 pandas 版本中弃用。弃用的原因是,设置inplace为会True更改原始 DataFrame 中的底层数据。程序中从其他地方引用原始 DataFrame 的任何代码都将引用更改后的数据,这可能会引入难以发现的错误。
设置inplace=True还使方法链无法使用。方法链是指您应用连续方法来更改原始 DataFrame,而不是创建大量中间变异 DataFrame。但只有当链中使用的方法返回 DataFrame 时,链才有效。
reset_index()如果将 DataFrame 方法应用于已inplace设置为的结果True,则不会返回任何 DataFrame 来应用它,并且您的代码将会中断。
直接重置索引.index
使用 DataFrames 时,可以使用.loc[]
或 来识别行.iloc[]
。每个都有其用例,尽管它们都可以在任何 DataFrame 上使用。
DataFrame 的索引是使用其.index
属性引用的。要用您自己的索引替换当前索引,您可以分配.index
一个包含新索引标签的可迭代对象。这让您有很大空间来自定义 DataFrame 索引,而不仅仅是默认的递增数字。
在探索.loc[]
和之前.iloc[]
,您需要向 DataFrame 添加一个自定义索引,其中包含海滩男孩乐队成员的姓名首字母。首先,您将从 CSV 文件中读取乐队成员的数据并将其转换为pyarrow
数据类型:
>>> beach_boys = pd.read_csv(
... "band_members.csv"
... ).convert_dtypes(dtype_backend="pyarrow")
>>> initials = ["BW", "ML", "AJ", "BJ", "CW", "DW", "DM", "RF", "BC"] >>> beach_boys.index = initials >>> beach_boys
first_name last_name instrument date_of_birth
BW Brian Wilson Bass 20-Jun-1942
ML Mike Love Saxophone 15-Mar-1941
AJ Al Jardine Guitar 03-Sep-1942
BJ Bruce Johnston Bass 27-Jun-1942
CW Carl Wilson Guitar 21-Dec-1946
DW Dennis Wilson Drums 04-Dec-1944
DM David Marks Guitar 22-Aug-1948
RF Ricky Fataar Drums 05-Sep-1952
BC Blondie Chaplin Guitar 07-Jul-1951
您将在短时间内了解重置索引的另一种方法,但首先,您将深入了解行选择。
.loc[]
使用和选择行.iloc[]
该.loc[]
属性允许您按索引标签对行进行索引。当底层索引标签在 DataFrame 中具有内在含义(例如用户名或时间戳)时,此功能特别有用。
如果使用 进行切片.loc[]
,它将使用闭区间,这意味着您指定的起始和终止索引值都将出现在输出中。因此,通过按用户名选择行切片,您的输出将显示第一行、最后一行以及介于两者之间的所有内容。
该.iloc[]
属性允许您根据行的索引位置查找行,而不管索引标签的值是什么。使用 时.iloc[]
,您使用从零开始的索引。换句话说,第一行位于位置0
,这1
可能与您预期的不同。.iloc[]
使用开
区间,这意味着虽然您指定的起始索引值将出现在输出中,但结束索引值不会出现。
如果您曾经对字符串或列表进行过切片,那么这些概念对您来说会非常熟悉,但需要注意的是,.iloc[] 看似选择的最终项目实际上并没有被选中。
.iloc[]
当然,使用默认索引或处理默认索引并不重要.loc[]
,因为默认索引也使用从零开始的序列,因此它们的标签和位置是匹配的。但是,如果索引包含字符串或非默认数字序列,则传递给的参数.iloc[]
将与您正在访问的行的实际索引值不相似,从而使您的代码更难阅读。
注意:虽然你可以同时使用.iloc[]和.loc[]来选择行,.loc[]但如果你的索引具有明确的含义,通常最好使用。否则,你可能会更愿意使用,.iloc[]因为它与 Python 的其他领域相似。如果你使用默认索引,那么选择哪个都无所谓。
记住它们之间区别的一个好方法是将 的首字母l与.loc[]单词label联系起来。这会提醒您.loc[]使用索引标签值。
现在您的 DataFrame 已使用新索引进行了更新,您可以使用它来使用.loc[]
和选择一些行.iloc[]
:
1>>> beach_boys = pd.read_csv(
2... "band_members.csv"
3... ).convert_dtypes(dtype_backend="pyarrow")
4
5>>> initials = ["BW", "ML", "AJ", "BJ", "CW", "DW", "DM", "RF", "BC"]
6>>> beach_boys.index = initials
7
8>>> beach_boys.loc[["BW"]] 9 first_name last_name instrument date_of_birth
10BW Brian Wilson Bass 20-Jun-1942
11
12>>> beach_boys.iloc[[1]] 13 first_name last_name instrument date_of_birth
14ML Mike Love Saxophone 15-Mar-1941
15
16>>> beach_boys.loc["BW":"BJ"] 17 first_name last_name instrument date_of_birth
18BW Brian Wilson Bass 20-Jun-1942
19ML Mike Love Saxophone 15-Mar-1941
20AJ Al Jardine Guitar 03-Sep-1942
21BJ Bruce Johnston Bass 27-Jun-1942
22
23>>> beach_boys.iloc[1:4] 24 first_name last_name instrument date_of_birth
25ML Mike Love Saxophone 15-Mar-1941
26AJ Al Jardine Guitar 03-Sep-1942
27BJ Bruce Johnston Bass 27-Jun-1942
BW
在第 8 行中,您通过将索引值作为字符串传递给 来访问其索引值为 的第一行.loc[]
。通过将其作为列表传递给.loc[["BW"]]
,您的输出将变为 DataFrame。您可以直接将其传入,但这会产生行为不同的 pandas Series。需要注意的要点是,通过将索引值传递给.loc[]
,您将返回索引标签为该值的行。
将其与第 12 行之后的输出进行比较。代码访问行号1
,这次使用.iloc[[1]]
。请注意输出的不同之处。使用.iloc[[1]]
意味着您已选择DataFrame 的第二.iloc[]
行。请记住,将 DataFrame 的第一行视为0
,而不管实际索引标签是什么。
在第 16 行中,您通过将第一行和第四行的标签传递到 来选择前四行.loc[]
。
注意:此切片将返回BW
和记录以及其间的所有内容,无论字母顺序如何。如果需要按字母顺序选择行,BJ有时最好在切片之前使用 DataFrame 的方法对索引进行排序。.sort_index()
最后,当您在第 23 行将切片1:4
传入时.iloc[]
,您选择了从索引位置 1 开始的行(换句话说,第二行,但以索引位置 3 结束)。同样,这是由于行的从零开始的编号效果,并且因为位置的最后一个切片参数4
被排除。记住.iloc[]
假设一个开区间。
直接重置索引.set_axis()
重置索引的第三种方法是使用 DataFrame 的.set_axis()
方法。此方法允许您为 DataFrame 分配一个新RangeIndex
对象,还允许您更改列标签。
要更改 DataFrame 的现有索引,可以使用内置构造函数传递.set_axis()
一个对象。这将为索引分配一个从零开始的升序整数间隔:range()
>>> beach_boys = pd.read_csv(
... "band_members.csv"
... ).convert_dtypes(dtype_backend="pyarrow")
>>> beach_boys.set_axis(range(len(beach_boys)))
first_name last_name instrument date_of_birth
0 Brian Wilson Bass 20-Jun-1942
1 Mike Love Saxophone 15-Mar-1941
2 Al Jardine Guitar 03-Sep-1942
3 Bruce Johnston Bass 27-Jun-1942
4 Carl Wilson Guitar 21-Dec-1946
5 Dennis Wilson Drums 04-Dec-1944
6 David Marks Guitar 22-Aug-1948
7 Ricky Fataar Drums 05-Sep-1952
8 Blondie Chaplin Guitar 07-Jul-1951
在这里,您用来.set_axis()
将索引重置回其默认值。为此,您传递了.set_axis()
一个range
长度等于原始beach_boys
DataFrame 长度的 。使用len()
可确保 DataFrame 中每行都有正确数量的数字。生成的数字将从零开始,刚好足以覆盖每一行。如您所见,索引现在已重置为其默认值。
注意:到目前为止,您已经了解了三种重置 DataFrame 索引的方法。您可能不知道应该使用哪一种。如果您只想将索引重置为默认值,那么您可以使用timeit模块在IPython或 Jupyter Notebook中计时小段代码:
In [1]: import pandas as pd In [2]: beach_boys = pd.read_csv( ...: "band_members.csv" ...: ).convert_dtypes(dtype_backend="pyarrow") In [3]: %timeit -n 1000 beach_boys.reset_index(drop=True) 22.5 µs ± 1.71 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each) In [4]: %timeit -n 1000 beach_boys.index = pd.RangeIndex(len(beach_boys.index)) 3.75 µs ± 307 ns per loop (mean ± std. dev. of 7 runs, 1,000 loops each) In [5]: %timeit -n 1000 beach_boys.set_axis(range(len(beach_boys))) 28.1 µs ± 1.89 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
从这三个时间可以看出,如果您只需要将索引重置为默认值,则将对象RangeIndex直接分配给 DataFrame 的 .index属性是三种方法中最快的。这也是重置原始 DataFrame 上的索引的唯一方法。其他两种方法会创建一个副本,您可以将其分配回变量beach_boys。但是,.reset_index()虽然速度慢,但确实提供了更多的配置选项。
请注意,您的确切时间数字将与此处显示的数字不同,但它们应该显示相同的趋势。
现在您已经知道如何重置 DataFrame 的索引,接下来您将了解为什么要这样做。现在还不是放松的时候。
恢复顺序索引
在分析之前清理数据时,您通常需要从 DataFrame 中删除某些行。例如,您可能需要删除重复或其他不需要的行。删除这些行时,行的索引值也会被删除。这可能会导致索引中出现间隙。通过重置索引,您可以修复此问题。
稍后,在有关对齐多个 DataFrames 索引的部分中,您将了解 DataFrame 索引中缺失的值在您需要分析其数据时会造成多大破坏。现在,您将弄乱您可爱的beach_boys
DataFrame 的索引:
1>>> beach_boys = pd.read_csv(
2... "band_members.csv"
3... ).convert_dtypes(dtype_backend="pyarrow")
4
5>>> beach_boys
6 first_name last_name instrument date_of_birth
70 Brian Wilson Bass 20-Jun-1942
81 Mike Love Saxophone 15-Mar-1941
92 Al Jardine Guitar 03-Sep-1942
103 Bruce Johnston Bass 27-Jun-1942
114 Carl Wilson Guitar 21-Dec-1946
125 Dennis Wilson Drums 04-Dec-1944
136 David Marks Guitar 22-Aug-1948
147 Ricky Fataar Drums 05-Sep-1952
158 Blondie Chaplin Guitar 07-Jul-1951
16
17>>> beach_boys.drop(labels=[3, 5]) 18 first_name last_name instrument date_of_birth
190 Brian Wilson Bass 20-Jun-1942
201 Mike Love Saxophone 15-Mar-1941
212 Al Jardine Guitar 03-Sep-1942
224 Carl Wilson Guitar 21-Dec-1946
236 David Marks Guitar 22-Aug-1948
247 Ricky Fataar Drums 05-Sep-1952
258 Blondie Chaplin Guitar 07-Jul-1951
如您所见,DataFrame 包含具有默认索引的原始九行乐队成员。要删除一些行,您使用了 DataFrame 的.drop()
方法。要删除的行在提供给其参数的 Python 列表中定义labels
。在本例中,删除了索引标签为3
或 的行5
。仔细查看第 17 行下面生成的输出中的索引,您会发现索引不再是连续的。
要解决此问题,您可以使用迄今为止学到的任何技术。在这里,您将使用.reset_index()
,因为它不会更改底层 DataFrame,这意味着您可以在后面的示例中重用原始的干净索引 DataFrame:
>>> (
... beach_boys
... .drop(labels=[3, 5])
... .reset_index()
... )
index first_name last_name instrument date_of_birth
0 0 Brian Wilson Bass 20-Jun-1942
1 1 Mike Love Saxophone 15-Mar-1941
2 2 Al Jardine Guitar 03-Sep-1942
3 4 Carl Wilson Guitar 21-Dec-1946
4 6 David Marks Guitar 22-Aug-1948
5 7 Ricky Fataar Drums 05-Sep-1952
6 8 Blondie Chaplin Guitar 07-Jul-1951
如前所述,.reset_index()
已将新的默认索引添加到 DataFrame。但是,您还不满意,因为您的原始索引仍然存在,尽管是在新列中。再次,您需要调整drop
的参数.reset_index()
来完成工作:
>>> (
... beach_boys
... .drop(labels=[3, 5])
... .reset_index(drop=True) ... )
first_name last_name instrument date_of_birth
0 Brian Wilson Bass 20-Jun-1942
1 Mike Love Saxophone 15-Mar-1941
2 Al Jardine Guitar 03-Sep-1942
3 Carl Wilson Guitar 21-Dec-1946
4 David Marks Guitar 22-Aug-1948
5 Ricky Fataar Drums 05-Sep-1952
6 Blondie Chaplin Guitar 07-Jul-1951
正如您所见,通过设置drop=True
,原始索引现在无处可见,但您闪亮的新索引看起来又新又闪亮。
删除重复的索引值
您可能会惊讶地发现索引有时会有重复值。它们不一定需要是唯一标识符。但是,您通常要避免重复,因为它们可能会导致问题。幸运的是,.reset_index()
可以为您处理这个问题。
两个 DataFrame 合并时,经常会出现重复的索引值。重复可能会导致行选择、切片和筛选不正确的问题。在看到这些问题之前,您首先需要将一些重复值应用于索引:
1>>> beach_boys = pd.read_csv(
2... "band_members.csv"
3... ).convert_dtypes(dtype_backend="pyarrow")
4
5>>> guitar_players = beach_boys.query(
6... "instrument == 'Guitar'"
7... ).reset_index(drop=True)
8
9>>> guitar_players 10 first_name last_name instrument date_of_birth
110 Al Jardine Guitar 03-Sep-1942
121 Carl Wilson Guitar 21-Dec-1946
132 David Marks Guitar 22-Aug-1948
143 Blondie Chaplin Guitar 07-Jul-1951
15
16>>> others = beach_boys.query(
17... "instrument != 'Guitar'"
18... ).reset_index(drop=True)
19
20>>> others 21 first_name last_name instrument date_of_birth
220 Brian Wilson Bass 20-Jun-1942
231 Mike Love Saxophone 15-Mar-1941
242 Bruce Johnston Bass 27-Jun-1942
253 Dennis Wilson Drums 04-Dec-1944
264 Ricky Fataar Drums 05-Sep-1952
在这里,您将原始 DataFrame 拆分为两个新的 DataFrame。guitar_players
第 9 行下方显示的 DataFrame 包含弹吉他的组员的记录。others
第 20 行下方显示的 DataFrame 包含其余成员。
为了选择吉他手,您需要将"instrument == 'Guitar'"
查询字符串传递到第 5 行到第 7 行,以提取列值匹配.query()的所有行。instrument"Guitar"
第 16 行至第 18 行使用类似的代码创建了第二个 DataFrame,其中包含其他行。在本例中,未标记为吉他手的音乐家。
在这两种情况下,.reset_index()
都用于确保两个新 DataFrame 中的索引都是连续的。这确保了两个 DataFrame 中都出现了一些相同的索引值。当您将这两个 DataFrame 合并在一起时,您可能会认为您会回到原始 DataFrame,但事实并非如此:
>>> all_beach_boys = pd.concat([guitar_players, others])
>>> all_beach_boys
first_name last_name instrument date_of_birth
0 Al Jardine Guitar 03-Sep-1942
1 Carl Wilson Guitar 21-Dec-1946
2 David Marks Guitar 22-Aug-1948
3 Blondie Chaplin Guitar 07-Jul-1951
0 Brian Wilson Bass 20-Jun-1942
1 Mike Love Saxophone 15-Mar-1941
2 Bruce Johnston Bass 27-Jun-1942
3 Dennis Wilson Drums 04-Dec-1944
4 Ricky Fataar Drums 05-Sep-1952
您已使用 创建了一个新的 DataFrame concat()
。通过同时传递guitar_players
和others
,您的新all_beach_boys
DataFrame 会再次显示原来的九名乐队成员,但索引包含重复项。现在您有一个具有重复索引的 DataFrame,您将调查这可能导致的问题。
假设您要选择第四行(索引位置为 的行)3
。您不能使用.loc[]
来执行此操作,因为重复的索引会导致问题。运行以下代码,您将看到问题:
>>> all_beach_boys.loc[3] first_name last_name instrument date_of_birth
3 Blondie Chaplin Guitar 07-Jul-1951
3 Dennis Wilson Drums 04-Dec-1944
>>> all_beach_boys.iloc[[3]] first_name last_name instrument date_of_birth
3 Blondie Chaplin Guitar 07-Jul-1951
看一下突出显示的行。如您所见,由于.loc[]
选择了索引标签为 的行3
,因此返回了两条记录。要解决此问题,您需要使用.iloc[]
来选择所需的单行。
当您尝试使用切片选择连续行时,重复的索引值也会造成严重破坏。假设您想查看索引位置三和四处的行。您的第一次尝试可能是尝试.loc[]
:
>>> all_beach_boys.loc[3:4] Traceback (most recent call last):
...
KeyError: 'Cannot get left slice bound for non-unique label: 3'
>>> all_beach_boys.iloc[3:5]
first_name last_name instrument date_of_birth
3 Blondie Chaplin Guitar 07-Jul-1951
0 Brian Wilson Bass 20-Jun-1942
如您所见,当您尝试将所需的索引位置传递到 时.loc[]
,它会引发KeyError异常,因为您的标签不唯一。要解决此问题,您需要改用使用.iloc[]
。
注意:当涉及重复索引时,可以使用 进行切片.loc[]
。执行此操作之前,请确保先使用 对索引进行排序.sort_index()
。否则,您将KeyError
因重复值而引发异常:
>>> all_beach_boys.sort_index().loc[3:4] first_name last_name instrument date_of_birth 3 Blondie Chaplin Guitar 07-Jul-1951 3 Dennis Wilson Drums 04-Dec-1944 4 Ricky Fataar Drums 05-Sep-1952
如您所见,已返回具有索引标签3
和的行。4
假设你现在想查看带有索引标签1
和的元素3
。这次,你使用 DataFrame 的.filter()
方法:
>>> all_beach_boys.filter(items=[1, 3], axis="index")
Traceback (most recent call last):
...
ValueError: cannot reindex on an axis with duplicate labels
您尝试使用索引标签过滤 DataFrame 1
,3
并将它们作为列表传递给items
的参数.filter()
。您还将axis
参数设置为 以"index"
将过滤器应用于索引。尽管您的代码在技术上是正确的,但ValueError
由于标签重复,结果出现异常。
使用现有列作为索引
虽然默认的顺序数字索引为 DataFrame 中的行提供了唯一的访问器,但它不太可能具有任何固有含义。例如,到目前为止,分配给您使用的 DataFrame 的每一行的数字相对于它们索引的数据没有任何意义。
在从 读入的 DataFrame 中band_members.csv
,乐队成员Brian Wilson
的索引值为 ,0
仅仅是因为他在文件中排在第一位。这本身并无意义,尽管如果你是乐队粉丝,认为他应该是第 一位,这可能会冒犯你1
。
如果您使用过关系数据库中的键,那么使用不相关顺序索引的概念对您来说很熟悉,但您可能希望在 DataFrames 中使用一些更有意义的东西。
如果您想要更方便用户的索引标签,您可以使用现有列并将其提升为索引。为此,您可以使用.set_index()
方法。虽然您可以将任何现有列提升为索引,但请记住,除非您想要的列包含唯一值,否则您仍然会遇到与之前看到的重复相同的问题。
假设您想要重置索引以包含标签first_name
。您可以按如下所示执行此操作:
>>> beach_boys = pd.read_csv(
... "band_members.csv"
... ).convert_dtypes(dtype_backend="pyarrow")
>>> (
... beach_boys
... .set_index("first_name")
... .loc[["Brian", "Carl"]]
... )
last_name instrument date_of_birth
first_name
Brian Wilson Bass 20-Jun-1942
Carl Wilson Guitar 21-Dec-1946
当您调用.set_index()
并beach_boys
传入时"first_name"
,此列将提升为索引。然后,您可以.loc[]
使用 您感兴趣的音乐家的名字来选择一行或多行。
在某些情况下,您可能希望重置索引,以便其现有值对于索引的数据更有意义。虽然您无法使用.reset_index()
此功能,但您可以将更合适的内容应用于 DataFrame 的.index
属性。
一个常见的例子是使用员工标识符而不是普通的顺序整数。以下代码使用更多上下文值更新现有的顺序索引:
>>> beach_boys.index = [f"Employee_{x + 1}" for x in range(len(beach_boys))]
>>> beach_boys
first_name last_name instrument date_of_birth
Employee_1 Brian Wilson Bass 20-Jun-1942
Employee_2 Mike Love Saxophone 15-Mar-1941
Employee_3 Al Jardine Guitar 03-Sep-1942
Employee_4 Bruce Johnston Bass 27-Jun-1942
Employee_5 Carl Wilson Guitar 21-Dec-1946
Employee_6 Dennis Wilson Drums 04-Dec-1944
Employee_7 David Marks Guitar 22-Aug-1948
Employee_8 Ricky Fataar Drums 05-Sep-1952
Employee_9 Blondie Chaplin Guitar 07-Jul-1951
在此代码中,您使用列表推导式创建了一个包含 的字符串列表["Employee_1", "Employee_2", ...]
。然后将其分配给.index
的属性beach_boys
。
现在您有了更有意义的索引,您可以按照使用默认数字索引的方式使用它。例如,您可以根据新Employee_
值选择行:
>>> beach_boys.loc[["Employee_4"]]
first_name last_name instrument date_of_birth
Employee_4 Bruce Johnston Bass 27-Jun-1942
在上面的代码中,您用来.loc[]
选择的记录Employee_4
。
接下来,您将获得一些双人冲浪的经验。现在是时候一起使用两个 DataFrame 了,而不是一个。
对齐多个 DataFrame 的索引
使用 pandas DataFrames 的一大优点是,您可以使用基本算术运算符将它们的数据相加。不幸的是,只有当它们的索引对齐时,您才能享受这种便利。否则,您会遇到问题。
假设您正在分析唱片店的每周唱片销售情况。两周的销售数据存储在两个名为week1_record_sales.csv
和 的CSV 文件中week2_record_sales.csv
。为了演示目的,两个文件包含相同的销售数据,但它们的索引不同:
>>> week1_sales = pd.read_csv(
... "week1_record_sales.csv"
... ).set_index("index")
>>> week2_sales = pd.read_csv(
... "week2_record_sales.csv"
... ).set_index("index")
>>> week1_sales
day sales
index
0 Mon 100
1 Tue 150
2 Wed 200
3 Thu 250
4 Fri 300
>>> week2_sales
day sales
index
1 Mon 100
2 Tue 150
3 Wed 200
4 Thu 250
5 Fri 300
每个文件被读入一个 DataFrame 中,包含每日销售信息。每行都由其列标识index
,该列已设置为 DataFrame 的索引.set_index()
。
假设您现在想找出两周的总销售额。这只需进行简单的算术运算即可实现:
>>> week1_sales["sales"] + week2_sales["sales"]
index
0 NaN
1 250.0
2 350.0
3 450.0
4 550.0
5 NaN
Name: sales, dtype: float64
如您所见,出现了问题。由于两个 DataFrame 包含相同的数据,因此您期望答案是其原始值的两倍。相反,第一个和最后一个答案是NaN
,这意味着由于缺少值而无法执行算术计算。此外,其余结果也是不正确的。
这两个问题都是由索引不匹配引起的。NaN
出现这些值是因为您的两个 DataFrame 中都没有出现 index0
和 index 5
。计算是错误的,因为例如,星期三的销售额数字200
是按照2
第一个 DataFrame 中的索引编制的,而 index2
是指第二个 DataFrame 中的星期二的销售额150
。当您将这些相加时,结果毫无意义。
您还可以像合并关系数据库表一样合并两个 DataFrame。这样您就可以在同一位置查看两个 DataFrame 中的匹配数据。同样,如果您按索引连接,则每个索引值都必须引用两个 DataFrame 中的相关数据。
例如,假设您想查看两个销售周的所有数据。为此,您可以使用 DataFrame 的.merge()
方法:
>>> week1_sales.merge(week2_sales, left_index=True, right_index=True)
day_x sales_x day_y sales_y
index
1 Tue 150 Mon 100
2 Wed 200 Tue 150
3 Thu 250 Wed 200
4 Fri 300 Thu 250
您马上就会发现一些问题。带索引的记录0
和索引都5
不见了。每日数据也不一致。
在这里,您对两个 DataFrame执行了内部连接,这意味着只有索引值出现在两个 DataFrame 中的记录才会被合并。由于索引值和5
不出现在两者中,因此它们不包含在合并中。日期不匹配,因为相同的索引在每个 DataFrame 中引用不同的日期。
要解决这两个问题,您需要确保两个 DataFrames 都使用相同的索引。一种方法是将索引重置week2_sales
为其默认值。这将与 使用的索引相匹配week1_sales
,但仅仅是因为两个 DataFrames 的每日数据已经处于相同的顺序:
>>> week2_sales = week2_sales.reset_index(drop=True)
与之前一样,要将索引重置为默认值,请使用.reset_index()
并传递True
给其drop
参数以删除有问题的原始索引。现在,当您运行前面的两段代码时,结果就更加令人满意了:
>>> week1_sales["sales"] + week2_sales["sales"]
index
0 200
1 300
2 400
3 500
4 600
Name: sales, dtype: int64
>>> week1_sales.merge(week2_sales, left_index=True, right_index=True)
day_x sales_x day_y sales_y
index
0 Mon 100 Mon 100
1 Tue 150 Tue 150
2 Wed 200 Wed 200
3 Thu 250 Thu 250
4 Fri 300 Fri 300
如您所见,现在所有内容都匹配,没有遗漏任何内容。对齐索引一举解决了这两个问题。但是,此示例之所以有效,是因为 DataFrames 中的行从一开始就处于正确的顺序。情况并非总是如此。
重置多索引
到目前为止,您使用的每个 DataFrames 都由单列Index
对象组成。DataFrames 还支持MultiIndex对象,这些对象为您的 DataFrames 提供分层或多级索引。
在本节中,您将从加州海滩男孩的梦中醒来,并决定吃早餐。您将使用该cereals.csv文件来帮助您决定吃哪种谷物。此文件包含来自各个制造商的各种流行早餐谷物的数据。原始数据来自Kaggle ,可根据Creative Commons 许可证免费提供。在这里,您使用的是它的精简版本。
您需要做的第一件事是将谷物数据读入 DataFrame:
>>> cereals = pd.read_csv("cereals.csv").convert_dtypes(
... dtype_backend="pyarrow"
... )
>>> cereals.head()
name manufacturer type fiber
0 100% Bran Nabisco Cold 10.0
1 100% Natural Bran Quaker Oats Cold 2.0
2 All-Bran Kelloggs Cold 9.0
3 All-Bran with Extra Fiber Kelloggs Cold 14.0
4 Almond Delight Ralston Purina Cold 1.0
如您所见,该文件包含不同早餐谷物的详细信息。当您调用 DataFrame 的.head()
方法时,您会看到前五条记录,这些记录显示了谷物的name
和manufacturer
。您还可以看到它的type
,它告诉您谷物是热吃还是冷吃,以及它的fiber
内容。
毫不奇怪,这个 DataFrame 有一个简单的索引。创建索引的一种快速方法是从中MultiIndex
创建一个数据透视表:
>>> cereals.pivot_table(
... values="fiber",
... index=["manufacturer", "type"],
... aggfunc="mean",
... )
fiber
manufacturer type
American Home Food Products Hot 0.0
General Mills Cold 1.272727
Kelloggs Cold 2.73913
Nabisco Cold 4.6
Hot 1.0
Post Cold 2.777778
Quaker Oats Cold 1.142857
Hot 2.7
Ralston Purina Cold 1.875
此数据透视表实际上是另一个 DataFrame,它通过计算每个制造商每种谷物的平均纤维含量来分析原始数据。此 DataFrame 的索引与您过去看到的索引略有不同:
>>> cereals.pivot_table(
... values="fiber",
... index=["manufacturer", "type"],
... aggfunc="mean",
... ).index
MultiIndex([('American Home Food Products', 'Hot'),
( 'General Mills', 'Cold'),
( 'Kelloggs', 'Cold'),
( 'Nabisco', 'Cold'),
( 'Nabisco', 'Hot'),
( 'Post', 'Cold'),
( 'Quaker Oats', 'Cold'),
( 'Quaker Oats', 'Hot'),
( 'Ralston Purina', 'Cold')],
names=['manufacturer', 'type'])
此pivot_tableMultiIndex
中的由manufacturer
和type
列组成。您现在看到的不再是简单的单列,而是更复杂的多层结构。
在此示例中,您创建了一个由两个级别组成的。这些级别通过将和 传递给的参数MultiIndex
来定义。"manufacturer"
"type"
index
.pivot_table()
在MultiIndex
对象中,manufacturer
称为级别 0,而type
称为级别 1。如果您需要重置索引,这些级别编号很重要,因为它们允许您在特定级别执行此操作,甚至完全重置它。
假设您只想重置第 1 级,对应于type
,这会将其降级到单独的列,然后将其完全删除:
>>> cereals = pd.read_csv("cereals.csv").convert_dtypes(dtype_backend="pyarrow")
>>> cereals.pivot_table(
... values="fiber",
... index=["manufacturer", "type"],
... aggfunc="mean"
... ).reset_index(level=1, drop=True)
>>> cereals
fiber
manufacturer
American Home Food Products 0.0
General Mills 1.272727
Kelloggs 2.73913
Nabisco 4.6
Nabisco 1.0
Post 2.777778
Quaker Oats 1.142857
Quaker Oats 2.7
Ralston Purina 1.875
通过将level=1
和 drop=True
传递给.reset_index()
,您可以删除type
详细信息,仅保留manufacturer
简单的信息Index
。
执行此操作时要小心,因为您现在已经创建了重复的索引值,其中包含了之前看到的所有问题。此外,您的数据现在已经失去了一些意义。例如,Nabisco
和Quaker Oats
标签所显示的内容令人困惑。您不再知道哪个指的是热麦片,哪个指的是冷麦片。
请注意,重置部分MultiIndex
可能会产生并不总是明显的不良副作用。
有时,最好重置 a 的所有级别MultiIndex
但保留所有数据。当您使用其默认参数应用于.reset_index()
a时MultiIndex
,您将用一个简单的默认版本替换完整索引,并从 DataFrame 中的原始索引创建其他列:
>>> cereals.pivot_table(
... values="fiber",
... index=["manufacturer", "type"],
... aggfunc="mean",
... ).reset_index()
manufacturer type fiber
0 American Home Food Products Hot 0.0
1 General Mills Cold 1.272727
2 Kelloggs Cold 2.73913
3 Nabisco Cold 4.6
4 Nabisco Hot 1.0
5 Post Cold 2.777778
6 Quaker Oats Cold 1.142857
7 Quaker Oats Hot 2.7
8 Ralston Purina Cold 1.875
这次,您的 DataFrame 已应用默认索引,但它还具有新manufacturer
列type
以及聚合fiber
列的数据。最重要的是,数据没有失去任何意义。
最后的挑战将测试您现在使用 DataFrame 索引的能力的良好感觉。
最后
在本教程中,您了解到,虽然该.reset_index()
方法是重置索引最可自定义的方法,但它并不是最快的选项。不过,当您处理MultiIndex
重置时,它很有用。
您还了解到,直接将索引应用于 DataFrame 的.index
属性是重置索引的最快方法,并且它会在此过程中更改原始 DataFrame。此外,您还了解了该.set_axis()
方法如何允许您根据需要重置和重新标记索引。
原文地址:https://blog.csdn.net/xt14327/article/details/143808686
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!