为什么pandas DataFrame的[](__getitem__)有时选择列,有时选择行?

7

考虑这个数据框:

In [40]: df = pd.DataFrame({'A': [1, 1], 'B': [2, 2], 'C': [3, 3]})

In [41]: df
Out[41]:
   A  B  C
0  1  2  3
1  1  2  3

如果我将一个字符串列表传递给[],它将过滤列:
In [42]: df[['A', 'C']]
Out[42]:
   A  C
0  1  3
1  1  3

但如果我将一个布尔列表传递给[],它会过滤行:

In [45]: df[[True, False]]
Out[45]:
   A  B  C
0  1  2  3

有没有一种思考这种差异的方式,而不是“它就是这样子”的想法?

2
是的,问题的关键在于 df[...] 通常会尝试将你传递给 __getitem__ 的任何内容作为列索引器应用,除非你传递了一个布尔掩码。例如,尝试使用 df[[0, 1]],你就会明白。 - cs95
1
确实这是最佳实践,但如果你知道自己在做什么,它可以帮助你减少一些字符 ;) - cs95
1
pandas文档实际上建议在生产代码中使用.loc方法(或.iloc方法),也是为了优化原因。[]索引运算符基本上是一种“便利”方法:它旨在让您以最少的字符访问数据值,以满足一些最常见的用例。 - Xukrao
大部分情况下,我会使用.loc而不是[]。 - BENY
@AntonvBR 我不相信你可以在 iloc 中使用布尔掩码,这种情况下你必须使用另一种方法 :-) - cs95
显示剩余5条评论
2个回答

1
我的理解是,这个功能是为了模仿 R 语言的行为,使得迁移 R 脚本更加容易,并且它开始使用 ix,不过现在已经被弃用。以前有很多种方法来进行切片操作,但是现在我们只剩下以下几种:
  1. 获取单个元素,即获取一列数据。
  2. 获取多列数据,即获取一个子数据框。
  3. 通过布尔索引获取数据。
个人而言,我喜欢对所有的操作都使用 __getitem__ 方法:
In [11]: df[['A', 'C']]
Out[11]:
   A  C
0  1  3
1  1  3

In [12]: df['A']
Out[12]:
0    1
1    1
Name: A, dtype: int64

虽然可选方案更少歧义(loc(或iloc)过于冗长:

In [13]: df.loc[:, ['A', 'B']]
Out[13]:
   A  B
0  1  2
1  1  2

In [14]: df.loc[:, 'A']
Out[14]:
0    1
1    1
Name: A, dtype: int64

值得注意的是,布尔掩码通常不会产生歧义,除非您有一个奇特的示例,其中布尔列和输入长度与数据帧相匹配。
In [21]: df1 = pd.DataFrame({True: [1, 2], False: [3, 4]})

In [22]: df1
Out[22]:
   False  True
0      3      1
1      4      2

In [23]: df1[[True, False]]  # boolean slicing (not as column names)
Out[23]:
   False  True
0      3      1

历史上,ix 存在潜在的歧义问题(以及性能问题 - 有很多可能的路径可以选择)。因此,除了消除歧义之外,移动到 lociloc 还导致更快的代码(通常使用 iloc 如果可以的话它是最快的)。


R对于[]没有这种歧义,这就是我感到困惑的原因。例如,df[c('A', 'C')]df[c(TRUE, FALSE, TRUE)]都会在R中过滤列,保留列AC - Heisenberg
@Heisenberg 在 R 中,我认为应该是 df[, c(TRUE, FALSE, TRUE)]?逻辑索引与 pandas 中的相同,我认为... https://stats.idre.ucla.edu/r/modules/subsetting-data/ - Andy Hayden
在 R 中,就像你所说的,我们可以使用 df[row_index, col_index] 进行子集化。我们还可以使用 df[col_index] 来选择列,其中 col_index 可以是标签、整数或布尔值。在 R 中,df[col_index] 总是选择列,这就是我所说的“关于 [] 的歧义,在 R 中不存在”的意思。 - Heisenberg

1

索引和选择数据文档提到,[]运算符更多是为了方便而提供的。因此,它是针对行还是列进行操作似乎取决于什么被认为是常见操作。

Python和NumPy索引运算符[]和属性运算符.在各种用例中快速轻松地访问pandas数据结构。这使得交互式工作变得直观,如果您已经知道如何处理Python字典和NumPy数组,则几乎没有新东西需要学习。但是,由于要访问的数据类型事先不知道,直接使用标准运算符存在一些优化限制。对于生产代码,我们建议您利用本章介绍的优化pandas数据访问方法。

使用DataFrame,在[]内部进行切片将切片行。这主要是为了方便,因为它是如此常见的操作。

您可以使用与DataFrame索引长度相同的布尔向量从DataFrame中选择行。

由于使用[]进行索引必须处理许多情况(单标签访问、切片、布尔索引等),因此需要一些开销来确定您要请求的内容。
到目前为止,我发现以下内容:
选择行
- 布尔索引df[[True, False]] - 切片df[0:1] 选择列
- 单个标签df['A'] - 标签列表df[['A', 'C']] 虽然它们选择了看起来合理的行为,但“这就是它的方式”。此外,似乎没有关于[]索引运算符的清晰文档 - 至少会有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接