我想使用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;Python3.6.3
发布于 2018-08-31 15:28:57
源代码说明了原因。__getitem__
方法( []
是语法糖)专门检查是否通过dataframe进行索引:
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
发布于 2018-08-31 16:34:58
将True
的行索引写在mask_np
中:行0
、行0
、行2
、行3
。选择在df
中具有相同索引的行,并将它们连接起来。这就是df[mask_np]
的生成方式。
这可能是Pandas的错误,因为在源代码中假定用于索引的数组是一维的。
查看源代码(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
中使用)是True
's在mask_df
中的“行”索引。
第一个数组用于沿索引进行take
,因此您可以得到行0, 0, 2, 3
of df
作为回报。
https://stackoverflow.com/questions/52123388
复制