不同的切片对相同的元素给出不同的不等式

Ove*_*gon 5 python precision numpy

import numpy as np

a = np.array([.4], dtype='float32')
b = np.array([.4, .6])

print(a > b)
print(a > b[0], a > b[1])
print(a[0] > b[0], a[0] > b[1])
Run Code Online (Sandbox Code Playgroud)
[ True False]
[False] [False]
True False
Run Code Online (Sandbox Code Playgroud)

这是怎么回事?是的,b.dtype == 'float64'但是它的切片b[0]&b[1]a仍然是'float32'

注意:我问的是为什么会发生这种情况,而不是如何规避它,我知道(例如将两者都转换为'float64')。

And*_*eak 4

正如我在另一个答案中指出的,numpy 中的类型转换非常复杂,这是您所看到的行为的根本原因。该答案中链接的文档清楚地表明,标量(/0d 数组)和 1d 数组在类型转换方面有所不同,因为后者不被视为逐个值。

您已经知道问题的前半部分:问题是类型转换对于您的两种情况发生的方式不同:

>>> (a + b).dtype
dtype('float64')

>>> (a + b[0]).dtype
dtype('float32')

>>> (a[0] + b[0]).dtype
dtype('float64')
Run Code Online (Sandbox Code Playgroud)

还有一个名为的帮助程序numpy.result_type()可以告诉您相同的信息,而无需执行二进制运算:

>>> np.result_type(a, b)
dtype('float64')
>>> np.result_type(a, b[0])
dtype('float32')
>>> np.result_type(a[0], b[0])
dtype('float64')
Run Code Online (Sandbox Code Playgroud)

我相信,如果我们考虑类型转换表,我们就可以理解您的示例中发生的情况:

>>> from numpy.testing import print_coercion_tables
can cast
[...]

In these tables, ValueError is '!', OverflowError is '@', TypeError is '#'

scalar + scalar
+ ? b h i l q p B H I L Q P e f d g F D G S U V O M m 
? ? b h i l q l B H I L Q L e f d g F D G # # # O ! m 
b b b h i l q l h i l d d d e f d g F D G # # # O ! m 
h h h h i l q l h i l d d d f f d g F D G # # # O ! m 
i i i i i l q l i i l d d d d d d g D D G # # # O ! m 
l l l l l l q l l l l d d d d d d g D D G # # # O ! m 
q q q q q q q q q q q d d d d d d g D D G # # # O ! m 
p l l l l l q l l l l d d d d d d g D D G # # # O ! m 
B B h h i l q l B H I L Q L e f d g F D G # # # O ! m 
H H i i i l q l H H I L Q L f f d g F D G # # # O ! m 
I I l l l l q l I I I L Q L d d d g D D G # # # O ! m 
L L d d d d d d L L L L Q L d d d g D D G # # # O ! m 
Q Q d d d d d d Q Q Q Q Q Q d d d g D D G # # # O ! m 
P L d d d d d d L L L L Q L d d d g D D G # # # O ! m 
e e e f d d d d e f d d d d e f d g F D G # # # O ! # 
f f f f d d d d f f d d d d f f d g F D G # # # O ! # 
d d d d d d d d d d d d d d d d d g D D G # # # O ! # 
g g g g g g g g g g g g g g g g g g G G G # # # O ! # 
F F F F D D D D F F D D D D F F D G F D G # # # O ! # 
D D D D D D D D D D D D D D D D D G D D G # # # O ! # 
G G G G G G G G G G G G G G G G G G G G G # # # O ! # 
S # # # # # # # # # # # # # # # # # # # # # # # O ! # 
U # # # # # # # # # # # # # # # # # # # # # # # O ! # 
V # # # # # # # # # # # # # # # # # # # # # # # O ! # 
O O O O O O O O O O O O O O O O O O O O O O O O O ! # 
M ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 
m m m m m m m m m m m m m m # # # # # # # # # # # ! m 

scalar + neg scalar
[...]

array + scalar
+ ? b h i l q p B H I L Q P e f d g F D G S U V O M m 
? ? b h i l q l B H I L Q L e f d g F D G # # # O ! m 
b b b b b b b b b b b b b b e f d g F D G # # # O ! m 
h h h h h h h h h h h h h h f f d g F D G # # # O ! m 
i i i i i i i i i i i i i i d d d g D D G # # # O ! m 
l l l l l l l l l l l l l l d d d g D D G # # # O ! m 
q q q q q q q q q q q q q q d d d g D D G # # # O ! m 
p l l l l l l l l l l l l l d d d g D D G # # # O ! m 
B B B B B B B B B B B B B B e f d g F D G # # # O ! m 
H H H H H H H H H H H H H H f f d g F D G # # # O ! m 
I I I I I I I I I I I I I I d d d g D D G # # # O ! m 
L L L L L L L L L L L L L L d d d g D D G # # # O ! m 
Q Q Q Q Q Q Q Q Q Q Q Q Q Q d d d g D D G # # # O ! m 
P L L L L L L L L L L L L L d d d g D D G # # # O ! m 
e e e e e e e e e e e e e e e e e e F F F # # # O ! # 
f f f f f f f f f f f f f f f f f f F F F # # # O ! # 
d d d d d d d d d d d d d d d d d d D D D # # # O ! # 
g g g g g g g g g g g g g g g g g g G G G # # # O ! # 
F F F F F F F F F F F F F F F F F F F F F # # # O ! # 
D D D D D D D D D D D D D D D D D D D D D # # # O ! # 
G G G G G G G G G G G G G G G G G G G G G # # # O ! # 
S # # # # # # # # # # # # # # # # # # # # # # # O ! # 
U # # # # # # # # # # # # # # # # # # # # # # # O ! # 
V # # # # # # # # # # # # # # # # # # # # # # # O ! # 
O O O O O O O O O O O O O O O O O O O O O O O O O ! # 
M ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 
m m m m m m m m m m m m m m # # # # # # # # # # # ! m

[...]
Run Code Online (Sandbox Code Playgroud)

以上是目前基于价值的促销的促销表的一部分。它表示在配对给定类型的两个 numpy 对象时,不同类型如何影响结果类型(有关特定类型,请参阅第一列和第一行)。这些类型应根据单字符 dtype 规范(“单字符字符串”下方)来理解,特别np.dtype('f')是对应于np.float32(f 表示 C 样式 float)和np.dtype('d')(d 表示 C 样式 double)np.float64(另请参见np.typename('f')和与 ) 相同'd'

我在上表中以黑体字注明了两项:

标量 f + 标量 d --> d
数组 f + 标量 d --> f

现在让我们看看您的案例。前提是你有一个'f'数组a和一个'd'数组b。事实上,a只有一个元素并不重要:它是一个长度为 1 的 1d 数组,而不是 0d 数组。

  1. 当您a > b比较两个数组时,上表中没有指出这一点。我不确定这里的行为是什么;我的猜测是,它a被广播为bs 形状,然后其类型被转换为'd'. 我认为这是因为现在np.can_cast(a, np.float64)True现在np.can_cast(b, np.float32)都是False。但这只是一个猜测,numpy 中的很多机制对我来说并不直观。

  2. 当您这样做时,a > b[0]您正在将'f'数组与'd'标量进行比较,因此根据上面的内容您会得到一个'f'数组。这就是(a + b[0]).dtype告诉我们的。(当您使用时,a > b[0]您看不到转换步骤,因为结果始终是布尔值。)

  3. 当您这样做时,a[0] > b[0]您正在将'f'标量与'd'标量进行比较,因此根据上面的内容您会得到一个'd'标量。这就是(a[0] + b[0]).dtype告诉我们的。

所以我相信这与 numpy 中类型转换的怪癖是一致的。虽然它看起来像是一个不幸的极端情况,其值为0.4双精度和单精度,但此功能更深入,该问题充当一个大红色警告,表明您在混合不同的数据类型时应该非常小心。

最安全的做法是自己转换类型,以控制代码中发生的情况。特别是因为有关于重新考虑类型提升的某些方面的讨论。


作为旁注(目前),2021 年 5 月创建了一个正在进行中的NEP 50,它解释了当涉及标量时类型提升可能会多么令人困惑,并计划最终简化一些规则。由于这还涉及重大更改,因此它在 NumPy 中的实现不会一蹴而就。