我们知道设置单个单元格的标准方法是使用at
或iat
。但是,我注意到一些有趣的行为,我想知道是否有人可以合理化。
在解决this question时,我遇到了loc
的一些奇怪行为。
# Setup.
pd.__version__
# '0.24.0rc1'
df = pd.DataFrame({'A': [12, 23], 'B': [['a', 'b'], ['c', 'd']]})
df
A B
0 12 [a, b]
1 23 [c, d]
要设置单元格(1,'B'),只需使用df.at[1, 'B'] = ...
这样的at即可。但是使用loc时,我最初尝试了此方法,但没有成功:
df.loc[1, 'B'] = ['m', 'n', 'o', 'p']
# ValueError: Must have equal len keys and value when setting with an iterable
所以,我尝试了(也失败了)
df.loc[1, 'B'] = [['m', 'n', 'o', 'p']]
# ValueError: Must have equal len keys and value when setting with an ndarray
我认为loc
还能以某种方式在此处获取嵌套列表。在一件奇怪的事件中,这个代码起作用了:
df.loc[1, 'B'] = [['m'], ['n'], ['o'], ['p']]
df
A B
0 12 [a, b]
1 23 [m, n, o, p]
loc
为什么这样工作?此外,如果您将任何其他元素添加到任何列表中,则会失败:
df.loc[1, 'B'] = [['m'], ['n'], ['o'], ['p', 'q']]
# ValueError: Must have equal len keys and value when setting with an iterable
空列表也不起作用。将每个元素嵌套在自己的列表中似乎没有意义。
为什么loc
会这样做?是记录下来的行为还是错误?
答案 0 :(得分:2)
之所以会这样,是因为loc
进行了bunch的检查,以检查它支持的所有用例。 (请注意:历史记录是loc
和iloc
的创建是为了消除ix
的歧义,可追溯到2013年v0.11,但即使到今天,{{ 1}}。)
在这种情况下,loc
可以返回:
此外:df.loc[1, 'B']
在这种情况下会遇到相同的问题,尽管总是总是第一种情况,但这可能是因为loc和iloc共享此分配代码。 < / p>
所以熊猫需要支持所有这些情况以进行分配!
赋值逻辑的早期部分将列表中的列表转换为numpy数组:
iloc
因此,您不能仅传递列表列表并期望获得正确的数组。相反,您可以显式设置为对象数组:
In [11]: np.array(['m', 'n', 'o', 'p']).shape
Out[11]: (4,)
In [12]: np.array([['m', 'n', 'o', 'p']]).shape
Out[12]: (1, 4)
现在您可以在作业中使用它:
In [13]: a = np.empty(1, dtype=object)
In [14]: a[0] = ['m', 'n', 'o', 'p']
In [15]: a
Out[15]: array([list(['m', 'n', 'o', 'p'])], dtype=object)
这仍然不是理想的,但要重申In [16]: df.loc[0, 'B'] = a
In [17]: df
Out[17]:
A B
0 12 [m, n, o, p]
1 23 [c, d]
和loc
中有很多极端情况,解决方案是尽可能明确地避免它们(使用iloc
这里)。而且,正如您所知,更普遍的是,避免在DataFrame中使用列表!