课程
学习 Pandas 时,我们都会头疼的一件事,就是弄清 .loc 和 .iloc 的区别。
让我们来彻底澄清这两个方法的差异。我会给出大量示例,希望读完本文后,这两者的区别会清晰许多。
Pandas 中的 .loc 和 .iloc 是什么?
.loc 和 .iloc 都是 Pandas DataFrame 的关键属性,用于选择特定的数据子集。它们的目的都是访问并操作 DataFrame 的某一部分,而不是整个 DataFrame。
|
特性 |
.loc |
.iloc |
|
语法 |
df.loc[row_indexer, column_indexer] |
df.iloc[row_indexer, column_indexer] |
|
索引方式 |
基于标签的索引 |
基于位置的索引 |
|
参考依据 |
行与列的标签(名称) |
行与列的数值索引(从 0 开始) |
正如我们从表中所见,语法非常相似。不同之处在于我们如何使用 row_indexer 和 column_indexer 参数。这是因为二者提供了不同的索引方式:.loc 基于标签名进行索引,而 .iloc 接收行列的数值位置索引作为参数。
下面分别详细看看这两种方法,先从 .loc 开始。
使用 .loc:按标签选择
为便于说明,我们来看一个假想的客户数据库,用名为 df 的 DataFrame 表示,其中 Customer ID 作为行索引:
|
Customer ID |
Name |
Country |
Region |
Age |
|
C123 |
John Doe |
United States |
North America |
67 |
|
C234 |
Petra Müller |
Germany |
Europe |
51 |
|
C345 |
Ali Khan |
Pakistan |
Asia |
19 |
|
C456 |
Maria Gonzalez |
Mexico |
North America |
26 |
|
C567 |
David Lee |
China |
Asia |
40 |
使用 .loc 选择行主要有四种方式:
- 选择单行
- 选择多行
- 选择行切片
- 按条件选择行
使用 .loc 选择单行
要选择单行,我们使用目标行的标签作为 row_indexer。相应语法为:df.loc['row_label']。例如,显示客户 Ali Khan 的全部信息:
df.loc['C345']
|
C345 |
|
|
Name |
Ali Khan |
|
Country |
Pakistan |
|
Region |
Asia |
|
Age |
19 |
使用 .loc 选择多行
如果要选择若干不一定相邻的行,需要将它们的行标签列表作为 row_indexer 参数传入。也就是说,需要用到两层方括号:一层是常规的 .loc 语法,一层是标签列表。
代码 df.loc[['row_label_1', 'row_label_2']] 会返回列表中指定的两行。比如我们想查看 Ali Khan 和 David Lee 的信息:
df.loc[['C345', 'C567']]
|
Customer ID |
Name |
Country |
Region |
Age |
|
C345 |
Ali Khan |
Pakistan |
Asia |
19 |
|
C567 |
David Lee |
China |
Asia |
40 |
使用 .loc 选择行切片
要选择一段连续的行,可用起止行标签并在中间放置冒号:df.loc['row_label_start':'row_label_end']。例如展示我们 DataFrame 的前四行:
df.loc['C123' : 'C456']
|
Customer ID |
Name |
Country |
Region |
Signup Date |
|
C123 |
John Doe |
United States |
North America |
67 |
|
C234 |
Petra Müller |
Germany |
Europe |
51 |
|
C345 |
Ali Khan |
Pakistan |
Asia |
19 |
|
C456 |
Maria Gonzalez |
Mexico |
North America |
26 |
这里需要注意两点:
- 输出包含
row_label_end指定的最后一行。.iloc的行为不同,我们稍后会介绍。 - 尽管我们要取多行,但只使用一层方括号。我们不是用列表来指定多行,因此使用两层方括号会报
SyntaxError。
使用 .loc 按条件选择行
我们也可以基于条件表达式返回行。即按某个条件对所有行进行筛选,只显示满足条件的行。
相应语法为 df.loc[conditional_expression],其中 conditional_expression 是针对某一列允许取值的判断语句。
对于非数值数据的列(如 Name 或 Country),由于值之间没有序关系,只能使用等于或不等于运算符。例如返回所有不来自 Asia 的客户行:
df.loc[df['Region'] != 'Asia']
|
Customer ID |
Name |
Country |
Region |
Age |
|
C123 |
John Doe |
United States |
North America |
67 |
|
C234 |
Petra Müller |
Germany |
Europe |
51 |
|
C456 |
Maria Gonzalez |
Mexico |
North America |
26 |
使用 .loc 选择单列
选择列时,需要指定位于 row_indexer 之后的 column_indexer 参数。如果只想指定 column_indexer,就需要以某种方式表明要返回所有行,仅在列上进行过滤。我们来看看如何实现!
选择单列时,将该列的标签作为 column_indexer 指定。为检索所有行,我们用一个简单的冒号作为 row_indexer。语法如下:df.loc[:, 'column_name']。
下面显示每位客户的 Name:
df.loc[:, 'Name']
|
Customer ID |
Name |
|
C123 |
John Doe |
|
C234 |
Petra Müller |
|
C345 |
Ali Khan |
|
C456 |
Maria Gonzalez |
|
C567 |
David Lee |
使用 .loc 选择多列
与选择多行类似,如果要返回若干不一定相邻的列,需要传入列标签列表:df.loc[:, [col_label_1, 'col_label_2']]。
如果我们想在上一个输出中加上所有客户的 Age,可以这样做:
df.loc[:, ['Name', 'Age']]
|
Customer ID |
Name |
Age |
|
C123 |
John Doe |
67 |
|
C234 |
Petra Müller |
51 |
|
C345 |
Ali Khan |
19 |
|
C456 |
Maria Gonzalez |
26 |
|
C567 |
David Lee |
40 |
使用 .loc 选择列切片
在两个列标签之间使用冒号,可选择介于两者之间范围内的所有列,且包含结束列。标准语法如下:df.loc[:, 'col_start':'col_end']。
如果我们关心客户的 Name、Country 和 Region,代码可以是:
df.loc[:, 'Name':'Region']
|
Customer ID |
Name |
Country |
Region |
|
C123 |
John Doe |
United States |
North America |
|
C234 |
Petra Müller |
Germany |
Europe |
|
C345 |
Ali Khan |
Pakistan |
Asia |
|
C456 |
Maria Gonzalez |
Mexico |
North America |
|
C567 |
David Lee |
China |
Asia |
使用 .loc 组合选择行与列
也可以同时指定 row_indexer 和 column_indexer。这可用于检索单个信息,即 DataFrame 的某个单元格。做法是用语法 df.loc['row_label', 'column_name'] 指定一行一列。
更常见也更有用的场景是返回一个子 DataFrame,恰好包含我们关心的那些行和列。可以将两个 indexer 都用列表(方括号)或切片(冒号)指定,甚至还能把行选择与条件表达式结合。
下面这个示例返回所有 Age 大于 30 的客户的 Name、Country 和 Region:
df.loc[df['Age'] > 30, 'Name':'Region']
|
Customer ID |
Name |
Country |
Region |
|
C123 |
John Doe |
United States |
North America |
|
C234 |
Petra Müller |
Germany |
Europe |
|
C567 |
David Lee |
China |
Asia |
使用 .iloc:按整数位置选择
.iloc 是按位置而非标签进行选择。其标准语法为:df.iloc[row_indexer, column_indexer]。需要特别注意两点:
- 从 0 开始计数:第一行和第一列的索引是 0,第二个是 1,依此类推。
- 切片的右端不包含:使用切片时,冒号后指定的行或列不会包含在选择结果中。
使用 .iloc 选择单行
选择单行时,使用表示行索引号的整数作为 row_indexer。由于输入的是整数而非 .loc 中的标签字符串,因此不需要引号。要返回名为 df 的 DataFrame 的第一行,输入 df.iloc[0]。
在我们的示例 DataFrame 中,这行代码会返回 John Doe 的信息:
df.iloc[0]
|
C123 |
|
|
Name |
John Doe |
|
Country |
United States |
|
Region |
North America |
|
Age |
67 |
使用 .iloc 选择多行
在 .iloc 中,选择多行与 .loc 类似——将行索引整数放入列表中(方括号)。语法如:df.iloc[[0, 3, 4]]。
在我们的客户表中,相应输出如下:
df.iloc[[0, 3, 4]]
|
Customer ID |
Name |
Country |
Region |
Age |
|
C123 |
John Doe |
United States |
North America |
67 |
|
C456 |
Maria Gonzalez |
Mexico |
North America |
26 |
|
C567 |
David Lee |
China |
Asia |
40 |
使用 .iloc 选择行切片
选择一段行时,在两个行索引整数之间使用冒号。此时要注意前面提到的不包含右端点规则。
以 df.iloc[1:4] 为例说明。索引 1 表示第二行,因此切片从第二行开始。索引 4 表示第五行——但由于 .iloc 的切片不包含右端点,输出只包含直到该行之前的所有行。因此会返回第二、第三和第四行。
我们来验证一下这行代码的效果:
df.iloc[1:4]
|
Customer ID |
Name |
Country |
Region |
Age |
|
C234 |
Petra Müller |
Germany |
Europe |
51 |
|
C345 |
Ali Khan |
Pakistan |
Asia |
19 |
|
C456 |
Maria Gonzalez |
Mexico |
North America |
26 |
使用 .iloc 选择单列
使用 .iloc 选择列的逻辑与前文一致。我们分别看看单列、多列和列切片的用法。
与 .loc 一样,必须先指定 row_indexer,再指定 column_indexer。要为每一行取 df 的第三列,输入 df.iloc[:, 2]。
由于 Region 是我们 DataFrame 的第三列,因此这行代码会将其取出:
df.iloc[:, 2]
|
Customer ID |
Region |
|
C123 |
North America |
|
C234 |
Europe |
|
C345 |
Asia |
|
C456 |
North America |
|
C567 |
Asia |
使用 .iloc 选择多列
要选择若干不一定相邻的列,同样可以将整数列表作为 column_indexer 传入。df.iloc[:, [0, 3]] 会返回第一列和第四列。
在我们的例子中,显示的是每位客户的 Name 和 Age:
df.iloc[:, [0, 3]]
|
Customer ID |
Name |
Age |
|
C123 |
John Doe |
67 |
|
C234 |
Petra Müller |
51 |
|
C345 |
Ali Khan |
19 |
|
C456 |
Maria Gonzalez |
26 |
|
C567 |
David Lee |
40 |
使用 .iloc 选择列切片
在 .iloc 中,列切片的 column_indexer 遵循与 row_indexer 相同的逻辑:冒号后的列不会包含在输出中。要检索第二和第三列,代码应为:df.iloc[:, 1:3]。
下面这行会返回我们对客户掌握的全部地理信息:
df.iloc[:, 1:3]
|
Customer ID |
Country |
Region |
|
C123 |
United States |
North America |
|
C234 |
Germany |
Europe |
|
C345 |
Pakistan |
Asia |
|
C456 |
Mexico |
North America |
|
C567 |
China |
Asia |
使用 .iloc 组合选择行与列
我们可以将 .iloc 的用法组合起来进行行列联合选择。同样,可以返回单个单元格或一个子 DataFrame。要返回第 3 行第 4 列交叉处的单元格,输入 df.iloc[2, 3]。
与 .loc 一样,可以将两个 indexer 都指定为列表(方括号)或切片(冒号)。如果想用条件表达式选择行,从技术上讲也可以结合 .iloc 实现,但并不推荐。通常使用标签名配合 .loc 更直观,也更不易出错。
最后这个示例展示 DataFrame 中第一、第二和第五行的 Country、Region 与 Age:
df.iloc[[0,1,4], 1:4]
|
Customer ID |
Country |
Region |
Age |
|
C123 |
United States |
North America |
67 |
|
C234 |
Germany |
Europe |
51 |
|
C567 |
China |
Asia |
40 |
.iloc 与 .loc:何时用哪一个
通常有一个简单的经验法则,取决于您对 DataFrame 的了解:
- 当您知道行/列的标签(名称)时,使用
.loc。 - 当您知道行/列的整数位置时,使用
.iloc。
某些场景天然更适合 .loc 或 .iloc。例如,按位置遍历行或列时,用整数比用标签更容易、更直观。而正如我们已提及的,根据列值条件过滤行时,使用列标签名通常更不易出错。
|
适合使用 .loc 的场景 |
适合使用 .iloc 的场景 |
|
您的 DataFrame 具有有意义的索引/列名。 |
您按位置遍历行/列。 |
|
您需要基于列值条件进行过滤。 |
索引/列名与您的任务无关。 |
使用 .loc 与 .iloc 时的 KeyError、NameError 和 IndexError
来看一些常见问题。使用 .loc 时,常见陷阱是遇到 KeyError。当尝试访问 DataFrame 中不存在的行或列标签时,就会出现该错误。为避免此问题,应确保使用的标签准确无误,与 DataFrame 中已有标签一致,并仔细检查是否有拼写错误。
此外,使用 .loc 指定标签时务必加引号。忘记加引号会导致 NameError。
使用 .iloc 时,如果给出的整数位置超出 DataFrame 索引的有效范围,可能会出现 IndexError。当要访问的索引不存在(超过行或列数量,或为负值)时就会发生。为避免该错误,请检查 DataFrame 的维度,并在有效范围内使用合适的索引值。
总结
希望这篇文章对您有所帮助,现在您应该已经清楚 .loc 与 .iloc 的区别了。若想继续学习,以下是一些不错的下一步资源:
