使用布尔数组屏蔽系列

mic*_*hel 7 python numpy pandas

这给我带来了很多麻烦,我对numpy数组与pandas系列的不兼容性感到困惑.例如,当我使用一个系列创建一个布尔数组时

x = np.array([1,2,3,4,5,6,7])
y = pd.Series([1,2,3,4,5,6,7])
delta = np.percentile(x, 50)
deltamask =  x- y > delta
Run Code Online (Sandbox Code Playgroud)

delta掩码创建一个布尔熊猫系列.

但是,如果你这样做

x[deltamask]
y[deltamask]
Run Code Online (Sandbox Code Playgroud)

您发现该数组完全忽略了掩码.没有引发错误,但最终会得到两个不同长度的对象.这意味着一个操作就好

x[deltamask]*y[deltamask]
Run Code Online (Sandbox Code Playgroud)

导致错误:

print type(x-y)
print type(x[deltamask]), len(x[deltamask])
print type(y[deltamask]),  len(y[deltamask])
Run Code Online (Sandbox Code Playgroud)

更令人困惑的是,我注意到操作符<的处理方式不同.例如

print type(2*x < x*y)
print type(2 <  x*y) 
Run Code Online (Sandbox Code Playgroud)

会分别给你一个pd.series和np.array.

也,

5 < x - y
Run Code Online (Sandbox Code Playgroud)

结果是一系列,所以看起来系列优先,而系列掩码的布尔元素在传递给numpy数组时会被提升为整数并产生切片数组.

这是什么原因?

Mad*_*ist 5

花式索引

就 numpy 目前的情况而言,numpy 中的花哨索引的工作方式如下:

  1. 如果括号之间的东西是 a tuple(无论是否有显式括号),元组的元素是x. 例如,在这种情况下,x[(True, True)]x[True, True]都会提高IndexError: too many indices for array,因为x是 1D。但是,在异常发生之前,也会发出一个明显的警告:VisibleDeprecationWarning: using a boolean instead of an integer will result in an error in the future

  2. 如果括号之间的东西正好是an ndarray,而不是子类或其他类似数组的,并且具有布尔类型,则它将作为掩码应用。这就是为什么x[deltamask.values]给出预期结果(空数组,因为deltamask是 all False.

  3. 如果括号之间的东西是任何类似数组的东西,无论是像子类Series还是只是 alist或其他东西,它都会被转换为np.intp数组(如果可能)并用作整数索引。所以x[deltamask]yeilds 等价于x[[False] * 7]或只是x[[0] * 7]. 在这种情况下,len(deltamask)==7x[0]==1这样的结果是[1, 1, 1, 1, 1, 1, 1]

这种行为是违反直觉的,FutureWarning: in the future, boolean array-likes will be handled as a boolean array index它生成的结果表明修复程序正在进行中。当我发现/对 numpy 进行任何更改时,我将更新此答案。

这些信息可以在 Sebastian Berg 对我对 Numpy 讨论的初始查询的回复中找到

关系运算符

现在让我们解决关于比较如何工作的问题的第二部分。关系运算符 ( <, >, <=, >=) 通过在被比较的对象之一上调用相应的方法来工作。对于<这是__lt__. 然而,Python不只是调用x.__lt__(y)表达式,而是x < y实际检查被比较对象的类型。如果yx实现比较的子类型,则 Python 更喜欢调用y.__gt__(x),而不管您如何编写原始比较。x.__lt__(y)ify是其子类的唯一方法x是 ify.__gt__(x)返回NotImplemented以指示该方向不支持比较。

当你这样做时会发生类似的事情5 < x - y。虽然ndarray不是 的子类int,比较int.__lt__(ndarray)返回NotImplemented,所以 Python 实际上最终调用(x - y).__gt__(5),这当然是定义的并且工作得很好。

Python 文档中可以找到对所有这些的更简洁的解释。