Как я заметил в другом ответе , приведение типов в numpy довольно сложно, и это root причина поведения, которое вы видите. Документы, связанные в этом ответе, ясно дают понять, что скаляры (/ 0d-массивы) и 1d-массивы отличаются в преобразованиях типов, поскольку последние не рассматриваются как значение по значению.
Первая половина проблемы, которую вы уже знаете : проблема в том, что преобразование типов происходит по-разному для ваших двух случаев:
>>> (a + b).dtype
dtype('float64')
>>> (a + b[0]).dtype
dtype('float32')
>>> (a[0] + b[0]).dtype
dtype('float64')
Я полагаю, что мы можем понять, что происходит в вашем примере, если мы рассмотрим таблицы преобразования типов:
>> 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 <b>d</b> 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 <b>f</b> 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
[...]
Вышесказанное является частью текущих рекламных таблиц для продвижения на основе стоимости. Он обозначает, как различные типы влияют на тип результата при сопряжении двух numpy объектов данного вида (см. Первый столбец и первую строку для указанных c типов). Типы следует понимать в соответствии с односимвольными спецификациями dtype (ниже "Односимвольные строки"), в частности, np.dtype('f')
соответствует np.float32
(f для C -плава типа) и np.dtype('d')
(d для C в стиле double) до np.float64
(см. также np.typename('f')
и то же самое для 'd'
).
Я отметил два пункта, выделенных жирным шрифтом в приведенных выше таблицах :
скаляр f + скаляр d -> d
массив f + скаляр d -> f
Теперь давайте рассмотрим ваши случаи. Предполагается, что у вас есть 'f'
массив a
и 'd'
массив b
. Тот факт, что a
имеет только один элемент, не имеет значения: это массив 1d длиной 1, а не массив 0d.
Когда вы делаете a > b
, вы сравниваете два массива, это не обозначено в приведенных выше таблицах. Я не уверен, что поведение здесь; я предполагаю, что a
передается в форме b
, а затем его тип приводится к 'd'
. Я думаю, что причина в том, что np.can_cast(a, np.float64)
- это True
, а np.can_cast(b, np.float32)
- False
. Но это всего лишь предположение, что многие из этих механизмов в numpy не интуитивны для меня.
Когда вы делаете a > b[0]
, вы сравниваете массив 'f'
с 'd'
скаляр, так что согласно приведенному выше вы получите массив 'f'
. Вот что (a + b[0]).dtype
сказал нам. (Когда вы используете a > b[0]
, вы не видите шаг преобразования, потому что результат всегда является логическим.)
Когда вы делаете a[0] > b[0]
, вы сравниваете 'f'
от скаляра до 'd'
скаляра, поэтому в соответствии с вышеизложенным вы получите 'd'
скаляр. Вот что (a[0] + b[0]).dtype
сказал нам.
Так что я считаю, что все это соответствует причудам преобразования типов в numpy. Хотя это может показаться неудачным угловым случаем со значением 0.4
в двойной и одинарной точности, эта функция идет глубже, и проблема служит большим красным предупреждением о том, что вы должны быть очень осторожны при смешивании разных dtypes.
Самый безопасный способ действий - преобразовать ваши типы самостоятельно, чтобы контролировать, что происходит в вашем коде. Тем более, что обсуждается вопрос о пересмотре некоторых аспектов продвижения типов.