这给我带来了很多麻烦,我对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数组时会被提升为整数,并产生切片数组。
这是什么原因?
答案 0 :(得分:3)
花式索引
正如numpy目前的情况,numpy中的花式索引如下:
如果括号之间的东西是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
。
如果括号中的内容完全一个ndarray
,而不是子类或其他类似数组,并且具有布尔类型,则它将作为掩码应用。这就是x[deltamask.values]
给出预期结果的原因(自deltamask
全部为False
以来的空数组。
如果括号之间的东西是任何类似数组的,无论是Series
之类的子类还是仅仅list
,还是其他东西,它都会转换为np.intp
数组(如果可能的话)并用作整数索引。所以x[deltamask]
等同于x[[False] * 7]
或仅x[[0] * 7]
。在这种情况下,len(deltamask)==7
和x[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讨论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中找到对所有这些的更简洁的解释。