NumPy数据类型比较

6
我在比较两个不同数组的数据类型,以选择适合组合这两个数组的一个。我很高兴地发现我可以执行比较操作,但在此过程中发现了以下奇怪的行为:
In [1]: numpy.int16 > numpy.float32
Out[1]: True

In [2]: numpy.dtype('int16') > numpy.dtype('float32')
Out[2]: False

有人能解释一下这是怎么回事吗?这是 NumPy 1.8.2。


顺便提一下,我最终发现并使用了np.find_common_type,但我仍然对这里发生的事情感兴趣。谢谢! - farenorth
我正在Windows上使用numpy 1.9.2,两个比较都返回“False”。正如下面的答案所述,在Python 3中这是毫无意义的,并已被删除。 - MattDMo
2个回答

6
第一个比较没有意义,第二个是有意义的。
使用 numpy.int16 > numpy.float32 进行比较时,我们比较了两个 type 对象:
>>> type(numpy.int16)
type
>>> numpy.int16 > numpy.float32 # I'm using Python 3
TypeError: unorderable types: type() > type()

在Python 3中,由于没有为type实例定义排序,因此此比较会立即失败。在Python 2中,会返回布尔值,但不能保证一致性(它会回退到比较内存地址或其他实现级别的东西)。第二个比较在Python 3中确实有效,并且它是一致的(在Python 2中也是相同的)。这是因为我们现在正在比较dtype实例:
>>> type(numpy.dtype('int16'))
numpy.dtype
>>> numpy.dtype('int16') > numpy.dtype('float32')
False
>>> numpy.dtype('int32') < numpy.dtype('|S10')
False
>>> numpy.dtype('int32') < numpy.dtype('|S11')
True

这个排序的逻辑是什么?
根据一个数据类型是否可以(安全地)转换为另一个数据类型,dtype实例会被排序。如果一个类型可以被安全地转换为另一个类型,则该类型小于另一个类型。
要查看比较运算符的实现,请参阅descriptor.c;特别是arraydescr_richcompare函数。
下面是<运算符的映射:
switch (cmp_op) {
 case Py_LT:
        if (!PyArray_EquivTypes(self, new) && PyArray_CanCastTo(self, new)) {
            result = Py_True;
        }
        else {
            result = Py_False;
        }
        break;

基本上,NumPy只检查这两种类型不相等,并且第一种类型可以转换为第二种类型。
此功能也在NumPy API中公开,称为 np.can_cast
>>> np.can_cast('int32', '|S10')
False
>>> np.can_cast('int32', '|S11')
True

3

这不是什么有趣的事情。Python 2试图为那些无法定义如何相互比较的对象提供一致但毫无意义的比较结果。开发者们认为这是一个错误,因此在Python 3中,这些比较将会引发TypeError异常。


第二个比较在Python 3中不会失败,暗示它是无意义的是不正确的(就像第一个比较一样)。它与一个类型是否可以转换为另一个类型有关。(我已经添加了一个解释这个问题的答案。) - Alex Riley
@ajcr:哦,我必须承认我实际上没有检查过文档,但现在我已经查看了,我没有看到任何关于dtypes的比较记录。源代码与您描述的相符,但这种行为是否可以依赖,或者是可能会在没有通知的情况下更改的内容?它有被记录在任何地方吗? - user2357112
我也找不到任何描述比较运算符使用的文档,甚至没有在相关邮件列表中找到任何交流。看起来这只是库中尚未记录的那些角落之一。我不确定开发人员是否会在未来的版本中更改行为,但我以前确实没有看到过它被广泛使用。像can_cast这样的函数似乎是比较dtype更清晰和更灵活的方式。 - Alex Riley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接