使用布尔数组屏蔽系列

时间:2016-08-25 12:49:25

标签: python pandas numpy

这给我带来了很多麻烦,我对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

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

但是,如果你这样做

x[deltamask]
y[deltamask]

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

这样的操作
x[deltamask]*y[deltamask]

导致错误:

print type(x-y)
print type(x[deltamask]), len(x[deltamask])
print type(y[deltamask]),  len(y[deltamask])

更令人困惑的是,我注意到操作员<对待方式不同。例如

print type(2*x < x*y)
print type(2 <  x*y) 

将分别为您提供pd.series和np.array。

此外,

5 < x - y

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

这是什么原因?

1 个答案:

答案 0 :(得分:3)

花式索引

正如numpy目前的情况,numpy中的花式索引如下:

  1. 如果括号之间的东西是tuple(无论是否有明确的parens),元组的元素是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. 如果括号中的内容完全一个ndarray,而不是子类或其他类似数组,并且具有布尔类型,则它将作为掩码应用。这就是x[deltamask.values]给出预期结果的原因(自deltamask全部为False以来的空数组。

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

  4. 此行为违反直觉,并且它生成的FutureWarning: in the future, boolean array-likes will be handled as a boolean array index表示修复工作正在进行中。当我发现/对numpy进行任何更改时,我会更新这个答案。

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

    关系运算符

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

    当你5 < x - y时,会发生类似的事情。虽然ndarray不是int的子类,但比较int.__lt__(ndarray)会返回NotImplemented,因此Python最终会调用(x - y).__gt__(5),这当然是定义并且有效的很好。

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