我们知道设置单个单元格的标准方法是使用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'),只需使用 at 即可,例如 df.at[1, 'B'] = ...
。但是对于 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
这样做?这是记录在案的行为,还是错误?
最佳答案
这是因为 loc
做了一个 bunch检查它支持的所有许多用例。 (注意:历史是 loc
和 iloc
是为了消除 ix
的歧义而创建的,早在 2013 年 v0.11,但即使在今天loc
中仍然有很多歧义。)
在这种情况下,df.loc[1, 'B']
可以返回:
- 单个元素(在本例中,当 1/'B' 有一个唯一的索引/列时)。
- 一个系列(如果 1/'B' 之一多次出现在索引/列中)。
- 一个 DataFrame(如果 BOTH 1/'B' 多次出现在索引/列中)。
旁白:iloc
在这种情况下遇到了同样的问题,尽管它始终是第一种情况,但这可能是因为 loc 和 iloc 共享此分配代码。
因此 pandas 需要支持所有这些案例以进行分配!
赋值逻辑的早期部分将(列表的)列表转换为 numpy 数组:
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
中有太多边缘情况,解决方案是尽可能明确地避免它们(在此处使用 at
)。更一般地说,如您所知,避免在 DataFrame 中使用列表!
关于python - 将列表插入单元格 - 为什么 loc 实际上在这里工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54400137/