为啥 .loc 对切片具有包容性行为?

Posted

技术标签:

【中文标题】为啥 .loc 对切片具有包容性行为?【英文标题】:Why does .loc have inclusive behavior for slices?为什么 .loc 对切片具有包容性行为? 【发布时间】:2018-10-02 09:32:15 【问题描述】:

由于某种原因,以下 2 次对 iloc / loc 的调用会产生不同的行为:

>>> import pandas as pd
>>> df = pd.DataFrame(dict(A=range(3), B=range(3)))
>>> df.iloc[:1]
   A  B
0  0  0
>>> df.loc[:1]
   A  B
0  0  0
1  1  1

我知道loc 考虑行标签,而iloc 考虑行的基于整数的索引。但是为什么loc 调用的上限被认为是包容的,而iloc 的上限被认为是独占的?

【问题讨论】:

想想list(range(1)),只会返回0,.iloc会选择多于(而不是少) 他们还在documentation 中明确指定了此行为。在.loc 文档下方:“◦带有标签'a':'f'的切片对象,(请注意,与通常的python切片相反,开始和停止都包括在内!)” 真正的“答案”应该是loc 应该是一个允许指定数字或名称的函数。我试图发布一个用例,说明为什么这是一个大问题(简而言之,我们经常希望使用数字行和列名),但该帖子立即被删除,因为这是一个“重复”。这是熊猫的大问题!它会导致许多错误和丑陋的代码。 嘿@MikeWilliamson - 如果它们被合并,您希望它们的切片是端独占还是端端包含? @ASGM 我觉得它应该与所有其他 Python 样式匹配:它是包含开头和结尾的。例如,list(range(2,9))[2, 3, 4, 5, 6, 7, 8]。我觉得所有 Python 库都应该遵循 Python 的标准,这将导致对 Principle of Least Astonishment 的最佳适应。 【参考方案1】:

快速回答:

在使用标签时进行包含末端的切片通常更有意义,因为它需要较少了解 DataFrame 中的其他行。

当您关心标签而不是位置时,结束独占标签切片会以一种不方便的方式引入位置依赖性。


更长的答案:

任何函数的行为都是一种权衡:您偏爱某些用例而不是其他用例。最终,.iloc 的操作是 Pandas 开发人员的主观设计决定(正如@ALlollz 的评论所示,这种行为is intentional)。但要了解他们为什么会这样设计,请考虑标签切片与位置切片的不同之处。

假设我们有两个 DataFrames df1df2

df1 = pd.DataFrame(dict(X=range(4)), index=['a','b','c','d'])
df2 = pd.DataFrame(dict(X=range(4)), index=['b','c','z'])

df1 包含:

   X
Y
a  0
b  1
c  2
d  3

df2 包含:

   X
Y
b  0
c  1
z  2

假设我们有一个基于标签的任务要执行:我们想从df1df2 中获取bc 之间的行,并且我们希望对两者使用相同的代码数据帧。因为bc 在两个DataFrame 中的位置不同,所以简单的位置切片无法解决问题。所以我们转向基于标签的切片。

如果.loc 是排他的,要获得bc 之间的行,我们不仅需要知道所需结束行的标签,还需要知道之后的下一行的标签。按照构造,下一个标签在每个 DataFrame 中都会有所不同。

在这种情况下,我们有两个选择:

为每个 DataFrame 使用单独的代码:df1.loc['b':'d']df2.loc['b':'z']。这很不方便,因为这意味着我们需要知道超出我们想要的行之外的额外信息。 先获取位置索引,加1,然后使用位置切片:df.loc[df.index.get_loc('b'):df.index.get_loc('c')+1]。这只是罗嗦。

但由于 .loc 是末端包容的,我们可以只说 .loc['b':'c']。简单多了!

当您关心标签而不是位置时,并且您尝试编写与位置无关的代码时,包含末端的标签切片会以一种不方便的方式重新引入位置依赖性

也就是说,也许在某些用例中您确实需要基于标签的最终排他切片。如果是这样,您可以使用@Willz's answer in this question:

df.loc[start:end].iloc[:-1]

【讨论】:

不应该是df.loc[start:end].iloc[:, :-1]?现在它只是排除最后一行而不是最后一列 @Suhas 不确定我是否理解您的评论:OP 的示例是关于行的,因此排除最后一行是预期的行为。我错过了什么吗? 我理解这种行为是故意的......但它仍然是“错误的”。我觉得有人做出了类似的决定,“来自 R 的人会更喜欢包容性,他们会更频繁地使用名称”。但我们将花费更多的时间在 Python 中编程,而不是学习 Python(或任何语言、库或框架)。因此,虽然学习曲线可能略微更高,但 API 一致性更为重要。这只是一个糟糕的设计决策,会理所当然地导致用户在重构时意外地犯错。 :( 有人说,我喜欢pandas,我认为这很棒。我用它所有的时间。我只是希望这些“错误”能够被公开讨论和修正。我试图为此提交一个错误报告,以及另一个类似的“选择错误”,但几分钟内我就被赶出了讨论。 虽然我认为现有行为很好(出于可学习性以外的原因,如我的回答中所述),但我认为这是一个有效的讨论主题。抱歉,如果您已经尝试过此操作,但提交 Feature Request 而不是 Bug Report 可能会获得更多关注?

以上是关于为啥 .loc 对切片具有包容性行为?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 .loc 对 DF 进行切片,列表中可能包含在索引/列中找不到的元素

熊猫切片不包括末端

Go 在数组子切片上的内置范围具有不一致的行为

通过python中的索引和数组对数据框进行切片

python--pandas切片

为啥索引超出范围的子字符串切片有效?