【问题标题】:Why does .loc behave differently depending on whether values are printed or assigned?为什么 .loc 的行为会根据是否打印或分配值而有所不同?
【发布时间】:2017-06-11 01:27:51
【问题描述】:

我对以下行为感到困惑。当我有这样的数据框时:

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randn(6, 4), columns=list('ABCD'), index=list('bcdefg'))

如下所示:

          A         B         C         D
b -0.907325  0.211740  0.150066 -0.240011
c -0.307543  0.691359 -0.179995 -0.334836
d  1.280978  0.469956 -0.912541  0.487357
e  1.447153 -0.087224 -0.176256  1.319822
f  0.660994 -0.289151  0.956900 -1.063623
g -1.880520  1.099098 -0.759683 -0.657774

我收到预期错误

TypeError: cannot do slice indexing on with these indexer [3] of type 'int'

当我使用.loc 尝试以下切片时:

print df.loc[3:, ['C', 'D']]

这是预期的,因为我传递了一个整数作为索引,而不是 index 中包含的字母之一。

但是,如果我现在尝试

df.loc[3:, ['C', 'D']] = 10

它工作正常并给我输出:

          A         B          C          D
b -0.907325  0.211740   0.150066  -0.240011
c -0.307543  0.691359  -0.179995  -0.334836
d  1.280978  0.469956  -0.912541   0.487357
e  1.447153 -0.087224  10.000000  10.000000
f  0.660994 -0.289151  10.000000  10.000000
g -1.880520  1.099098  10.000000  10.000000

我的问题是为什么在打印某些内容时相同的命令会失败,以及为什么在分配值时它会起作用。当我检查.loc 的文档字符串时,我原以为这总是会导致上面提到的错误(尤其是粗体部分):

允许的输入是:

  • 单个标签,例如5'a',(注意5 被解释为索引的标签,而**never 被解释为整数 沿索引的位置**)。
  • 标签列表或数组,例如['a', 'b', 'c']
  • 带有标签的切片对象,例如'a':'f'(请注意,与通常的 python 切片相反,开始和停止都是 包括在内!)。
  • 布尔数组。
  • callable 函数有一个参数(调用 Series、DataFrame 或 Panel)并返回有效的索引输出(一个 以上)

.loc 将在找不到项目时引发KeyError

对此的任何解释;我在这里错过了什么?

编辑

this question 中,类似的行为被认为是在 0.13 中修复的错误。我用的是 0.19.1。

编辑 2 以@EdChum 的帖子为基础,可以执行以下操作:

df.loc[2] = 20
df.loc[3] = 30
df.loc[4] = 40

产生

           A          B          C          D
b   0.083326  -1.047032   0.830499  -0.729662
c   0.942744  -0.535013   0.809251   1.132983
d  -0.074918   1.123331  -2.205294  -0.497468
e   0.213349   0.694366  -0.816550   0.496324
f   0.021347   0.917340  -0.595254  -0.392177
g  -1.149890   0.965645   0.172672  -0.043652
2  20.000000  20.000000  20.000000  20.000000
3  30.000000  30.000000  30.000000  30.000000
4  40.000000  40.000000  40.000000  40.000000

然而,这仍然让我感到困惑,因为虽然

print df.loc['d':'f', ['C', 'D']]

工作正常,命令

print df.loc[2:4, ['C', 'D']]

给出上面提到的索引错误。

此外,当人们现在分配这样的值时

df.loc[2:4, ['C', 'D']] = 100

数据框如下所示:

           A          B           C           D
b   0.083326  -1.047032    0.830499   -0.729662
c   0.942744  -0.535013    0.809251    1.132983
d  -0.074918   1.123331  100.000000  100.000000
e   0.213349   0.694366  100.000000  100.000000
f   0.021347   0.917340   -0.595254   -0.392177
g  -1.149890   0.965645    0.172672   -0.043652
2  20.000000  20.000000   20.000000   20.000000
3  30.000000  30.000000   30.000000   30.000000
4  40.000000  40.000000   40.000000   40.000000

因此,值不会被添加到一个人 - 或者至少我 - 期望它们被添加的地方(使用位置而不是标签)。

【问题讨论】:

  • 根据pandas-docs.github.io/pandas-docs-travis/…,第一个案例调用“getitem”,而赋值案例调用“setitem”方法。我假设第二种方法旨在处理整数切片
  • 这里的关键是“.loc 主要是基于标签的,但也可以与布尔数组一起使用。.loc 将在找不到项目时引发 KeyError”所以当你尝试使用索引时键 'e' 而不是 3,它对数据进行切片。 df.loc['e':, ['C', 'D']]
  • @Prateek:我认为这是完全相同的方法。但一次是print df.loc[3:, ['C', 'D']],另一次是df.loc[3:, ['C', 'D']] = 10。所以在这两种情况下,都使用了.loc,没有链式索引(我认为)。
  • @VaishaliGarg:是的,但我不传递布尔数组,而是传递表示索引的整数。据我了解文档字符串,这应该总是失败。但是,分配有效,而打印失败。
  • @Cleb 在同一个链接中提到 dfmi.loc[:,('one','second')] 将 (slice(None),('one','second')) 的嵌套元组传递给对 __getitem__ 的单个调用,而dfmi.loc[:,('one','second')] = value 变为 dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)。您的示例中的第一个示例 print 是通过 getitem 方法执行的。这似乎也是一个合理的解释,因为查看 pandas 代码库中的 indexing.py 类表明__setitem____getitem__ 具有更多检测/扩展索引的条件和方法。

标签: python pandas slice


【解决方案1】:

我不认为这是一个错误,而是未记录的语义,例如对于不存在行标签的简单情况,允许设置放大:

In [22]:
df.loc[3] = 10
df

Out[22]:
           A          B          C          D
b  -0.907325   0.211740   0.150066  -0.240011
c  -0.307543   0.691359  -0.179995  -0.334836
d   1.280978   0.469956  -0.912541   0.487357
e   1.447153  -0.087224  -0.176256   1.319822
f   0.660994  -0.289151   0.956900  -1.063623
g  -1.880520   1.099098  -0.759683  -0.657774
3  10.000000  10.000000  10.000000  10.000000

如果我们传递一个切片,则在切片中找不到标签,但由于它是一个整数切片,它会被转换为一个序数切片:

In [24]:
df.loc[3:5] = 9
df

Out[24]:
           A          B          C          D
b  -0.907325   0.211740   0.150066  -0.240011
c  -0.307543   0.691359  -0.179995  -0.334836
d   1.280978   0.469956  -0.912541   0.487357
e   9.000000   9.000000   9.000000   9.000000
f   9.000000   9.000000   9.000000   9.000000
g  -1.880520   1.099098  -0.759683  -0.657774
3  10.000000  10.000000  10.000000  10.000000

您链接的帖子和错误指的是没有赋值的选择,其中传递了一个不存在的标签,这应该引发KeyError,这在此处有所不同

如果我们查看__setitem__

def __setitem__(self, key, value):
        key = com._apply_if_callable(key, self)

        # see if we can slice the rows
        indexer = convert_to_index_sliceable(self, key))

这里会尝试转换切片调用convert_to_index_sliceable:

def convert_to_index_sliceable(obj, key):
    """if we are index sliceable, then return my slicer, otherwise return None
    """
    idx = obj.index
    if isinstance(key, slice):
        return idx._convert_slice_indexer(key, kind='getitem')

如果我们查看文档字符串:

签名:df.index._convert_slice_indexer(key, kind=None) 文档字符串: 转换切片索引器。禁止在开始/停止/步骤中浮动

参数 ---------- key : 切片绑定类型的标签 : {'ix', 'loc', 'getitem', 'iloc'} or None

然后运行这个:

In [29]:
df.index._convert_slice_indexer(slice(3,5),'loc')

Out[29]:
slice(3, 5, None)

然后使用它对索引进行切片:

In [28]:
df.index[df.index._convert_slice_indexer(slice(3,5),'loc')]

Out[28]:
Index(['e', 'f'], dtype='object')

所以我们看到,即使您传递了看似不存在的标签,整数切片对象仍根据不同的规则转换为与 df 兼容的序数切片

【讨论】:

  • 感谢您的详细回复。然而,这会导致更多的混乱(见我的编辑 2)。
猜你喜欢
  • 2018-04-05
  • 1970-01-01
  • 2022-07-22
  • 2015-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-17
相关资源
最近更新 更多