为什么 .loc 的行为会因值是打印还是分配而有所不同?
Why does .loc behave differently depending on whether values are printed or assigned?
我对以下行为感到困惑。当我有这样的数据框时:
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 indexers [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
的文档字符串时,我预计这总是会导致上述错误(尤其是粗体部分):
Allowed inputs are:
- A single label, e.g.
5
or 'a'
, (note that 5
is interpreted as a label of the index, and **never as an integer
position along the index**).
- A list or array of labels, e.g.
['a', 'b', 'c']
.
- A slice object with labels, e.g.
'a':'f'
(note that contrary to usual python slices, both the start and the stop are
included!).
- A boolean array.
- A
callable
function with one argument (the calling Series, DataFrame or Panel) and that returns valid output for indexing (one
of the above)
.loc
will raise a KeyError
when the items are not found.
任何解释;我在这里错过了什么?
编辑
在 this question 中,类似的行为被认为是一个错误,已在 0.13 中修复。我使用 0.19.1.
编辑 2
在@EdChum 的 post 基础上,可以执行以下操作:
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
所以这些值不会添加到一个 - 或者至少我 - 希望添加它们的地方(使用的是位置而不是标签)。
我不认为这是一个错误,而是未记录的语义,例如,对于行标签不存在的简单情况,允许进行放大设置:
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
您链接的 post 错误指的是没有赋值的选择,其中传递了 non-existent 标签,这应该引发 KeyError
,此处不同[=22] =]
如果我们看__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')
如果我们查看文档字符串:
Signature: df.index._convert_slice_indexer(key, kind=None) Docstring:
convert a slice indexer. disallow floats in the start/stop/step
Parameters
---------- key : label of the slice bound kind : {'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')
所以我们看到,即使你传递了看似non-existent的标签,整数切片对象根据不同的规则被转换为与df兼容的序数切片
我对以下行为感到困惑。当我有这样的数据框时:
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 indexers [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
的文档字符串时,我预计这总是会导致上述错误(尤其是粗体部分):
Allowed inputs are:
- A single label, e.g.
5
or'a'
, (note that5
is interpreted as a label of the index, and **never as an integer position along the index**).- A list or array of labels, e.g.
['a', 'b', 'c']
.- A slice object with labels, e.g.
'a':'f'
(note that contrary to usual python slices, both the start and the stop are included!).- A boolean array.
- A
callable
function with one argument (the calling Series, DataFrame or Panel) and that returns valid output for indexing (one of the above)
.loc
will raise aKeyError
when the items are not found.
任何解释;我在这里错过了什么?
编辑
在 this question 中,类似的行为被认为是一个错误,已在 0.13 中修复。我使用 0.19.1.
编辑 2 在@EdChum 的 post 基础上,可以执行以下操作:
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
所以这些值不会添加到一个 - 或者至少我 - 希望添加它们的地方(使用的是位置而不是标签)。
我不认为这是一个错误,而是未记录的语义,例如,对于行标签不存在的简单情况,允许进行放大设置:
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
您链接的 post 错误指的是没有赋值的选择,其中传递了 non-existent 标签,这应该引发 KeyError
,此处不同[=22] =]
如果我们看__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')
如果我们查看文档字符串:
Signature: df.index._convert_slice_indexer(key, kind=None) Docstring: convert a slice indexer. disallow floats in the start/stop/step
Parameters ---------- key : label of the slice bound kind : {'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')
所以我们看到,即使你传递了看似non-existent的标签,整数切片对象根据不同的规则被转换为与df兼容的序数切片