为啥 .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 df1
和 df2
:
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
假设我们有一个基于标签的任务要执行:我们想从df1
和df2
中获取b
和c
之间的行,并且我们希望对两者使用相同的代码数据帧。因为b
和c
在两个DataFrame 中的位置不同,所以简单的位置切片无法解决问题。所以我们转向基于标签的切片。
如果.loc
是排他的,要获得b
和c
之间的行,我们不仅需要知道所需结束行的标签,还需要知道之后的下一行的标签。按照构造,下一个标签在每个 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 对切片具有包容性行为?的主要内容,如果未能解决你的问题,请参考以下文章