我想使用2D布尔蒙版来选择性地更改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]
答案 0 :(得分:1)
源代码说明了原因。对于__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数据框的常规布尔索引沿一个轴,即通过df.loc[mask, :]
通过行/轴0或通过df.loc[:, mask]
通过列/轴1。
请注意,为清楚起见,您可以并且可能应该直接访问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 :(得分:1)
写下True
中mask_np
的行索引:行0
,行0
,行2
,行{{1 }}。在3
中选择具有相同索引的行并将其连接。 df
就是这样产生的。
这可能是熊猫的错误,因为在源代码中假定用于索引的数组是一维的。
查看源代码(熊猫0.23.4),
df[mask_np]
等效于
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
中True
的'行'索引。
第一个数组用于沿索引{{1}}沿索引,因此您将获得mask_df
的第take
行作为回报。