使用NumPy数组与DataFrame遮罩Pandas DataFrame的区别

6
我希望使用一个二维布尔掩码来选择性地修改pandas DataFrame中的一些单元格。我注意到我无法成功地使用numpy数组作为掩码,但是我可以使用DataFrame。然而更令人沮丧的是,使用numpy方法时我并没有收到错误提示。
例如,
df = pd.DataFrame({'A':[1,2,3,4], 
                   'B':[10,20,30,40]})

mask_np = np.array([[True,True],
                    [False,False],
                    [True,False],
                    [False,True]])

mask_pd = pd.DataFrame(mask_np, columns=['A','B'])

我认为两个掩码都会返回在df中与掩码为True的值,但实际上,df[mask_np]得到的是:
   A   B
0  1  10
0  1  10
2  3  30
3  4  40

这并不是我所期望的,也无法解释。另一方面,df[mask_pd]会产生以下结果。

     A     B
0  1.0  10.0
1  NaN   NaN
2  3.0   NaN
3  NaN  40.0

这正是我期望并希望的。

为什么我不能使用 numpy 的掩码?我的互联网搜索没有找到相关信息。希望能够解释一下这种差异背后的原因!

[pandas 版本 0.20.3;Python 3.6.3]

2个回答

3
源代码给出了答案。其中,__getitem__方法([]的语法糖)专门检查通过数据框进行的索引:
elif isinstance(key, DataFrame):
    return self._getitem_frame(key)

_getitem_frame 方法会在数据框为布尔型时返回 pd.DataFrame.where

def _getitem_frame(self, key):
    if key.values.size and not is_bool_dtype(key.values):
        raise ValueError('Must pass DataFrame with boolean values only')
    return self.where(key)

NumPy数组的路线_getitem_array与常规不同,更加复杂。由于某种原因,代码被设计为对待NumPy/Pandas输入有所不同,而不是确保相同的数据类型的一致性。


使用Pandas数据框的常规布尔索引通常沿轴应用,即通过行/轴0通过df.loc[mask, :]或列/轴1通过df.loc[:, mask]

请注意,您可以并且可能应该直接访问pd.DataFrame.where以获得清晰度:

res = df.where(mask_np)

print(res)

     A     B
0  1.0  10.0
1  NaN   NaN
2  3.0   NaN
3  NaN  40.0

1
请写下您的mask_npTrue所在的行索引:第0行,第0行,第2行,第3行。选择在df中具有相同索引的行并连接它们。这就是如何生成df[mask_np]
这可能是Pandas的一个bug,因为在源代码中假定用于索引的数组是一维的。

查看源代码(Pandas 0.23.4版本),

df[mask_np]

等同于

df._getitem_bool_array(mask_np)

等同于

indexer = mask_np.nonzero()[0]
df._take(indexer, axis=0)

使用以下评估:
>>> mask_np.nonzero()
(array([0, 0, 2, 3]), array([0, 1, 0, 1]))

这个数组元组表示数组维度上非零元素的索引。在这种情况下,元组中第一个数组的元素(最终在df._take中使用)是mask_dfTrue的'行'索引。第一个数组用于沿着索引take,因此你将得到df的行0, 0, 2, 3

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