Почему массивы 0d в Numpy считаются скалярными?

В каком-то смысле здесь уже есть отличный ответ:

Не стоит слишком много думать об этом. В конечном итоге это лучше для психического здоровья и долголетия человека.

Психическое здоровье и долголетие, конечно, хороши, но как насчет гордости этого человека, который получил еще один удар, пытаясь быть умным, и жестоко отвергнутый придурком:

Рассмотрим следующее, где мы начинаем с некоторых байтовых данных:

a = np.linspace(0,255,6, dtype=np.uint8)
a
# array([  0,  51, 102, 153, 204, 255], dtype=uint8)

Предположим, мы хотим что-то добавить и продвинуть тип, чтобы он не зацикливался. Со скаляром это не работает:

b = np.uint16(1)

a + b
# array([  1,  52, 103, 154, 205,   0], dtype=uint8)

Но с массивом это делает:

c = np.ones(1, np.uint16)

a + c
# array([  1,  52, 103, 154, 205, 256], dtype=uint16)

Я подумал, давайте сделаем массив.

b[...]
# array(1, dtype=uint16)
np.isscalar(b[...])
# False

Но увы:

a + b[...]
# array([  1,  52, 103, 154, 205,   0], dtype=uint8)

Почему этот массив 0d ведет себя здесь как скаляр?


person Paul Panzer    schedule 28.05.2019    source источник
comment
Фактически np.isscalar(b) и b=np.uint(16) вернули мне True. Какую версию ты используешь? Numpy 1.15 здесь.   -  person knh190    schedule 28.05.2019
comment
А как насчет np.isscalar(b[...]) (не пропустите многоточие)?   -  person Paul Panzer    schedule 28.05.2019
comment
Это _1 _...! Но док сказал, что вы должны использовать ndim почти везде.   -  person knh190    schedule 28.05.2019
comment
np.isscalar(b[...]) является ложным на Numpy 1.16.3, поскольку b [...] действительно является массивом numpy. Думаю, этот ответ (stackoverflow.com/a/42191121/10640534) поможет.   -  person Patol75    schedule 28.05.2019
comment
Поскольку 0-мерный массив - это экземпляр ndarray, содержащий ровно один скаляр массива   -  person Chrispresso    schedule 28.05.2019
comment
isscalar - это просто набор тестов type и isinstance. Я не знаю, есть ли хорошее применение np.uint8(1) стилю строительства. Мне это кажется новичком. np.array(1, np.uint8) выглядит лучше с немного более полным набором методов.   -  person hpaulj    schedule 28.05.2019
comment
Похоже, основной вопрос заключается в том, почему np.array([1], np.uint16) производит продвижение dtype, а np.array(1, np.uint16) нет (т.е. 1d против 0d).   -  person hpaulj    schedule 28.05.2019
comment
@hpaulj любой массив, кроме 0d, даже пустой a + np.ones((0,1),uint16)   -  person Paul Panzer    schedule 28.05.2019


Ответы (1)


https://docs.scipy.org/doc/numpy/reference/ufuncs.html#casting-rules

последний абзац:

Операции со смешанными скалярными массивами используют другой набор правил приведения, которые гарантируют, что скаляр не может «преобразовать» массив, если скаляр не относится к принципиально другому типу данных (т. Е. В другой иерархии в иерархии типов данных), чем скаляр множество. Это правило позволяет вам использовать скалярные константы в вашем коде (которые, как типы Python, соответственно интерпретируются в ufuncs), не беспокоясь о том, приведет ли точность скалярной константы к повышению вашего большого (малой точности) массива.

Я понимаю это так, что следующие выражения имеют такой же эффект:

In [56]: np.add(a,1)                                                                 
Out[56]: array([  1,  52, 103, 154, 205,   0], dtype=uint8)
In [57]: np.add(a,np.array(1))                                                       
Out[57]: array([  1,  52, 103, 154, 205,   0], dtype=uint8)

Чтобы это было правдой, 0d не может "повышать". Но список ведет себя как 1d-массив и выполняет "восходящее преобразование".

In [60]: np.add(a,[1])                                                               
Out[60]: array([  1,  52, 103, 154, 205, 256])
In [61]: np.add(a,np.array([1]))                                                     
Out[61]: array([  1,  52, 103, 154, 205, 256])

https://docs.scipy.org/doc/numpy/reference/arrays.scalars.html

Скаляры массива включают np.uint8(1) и т. Д.

Скалярные объекты массива имеют приоритет массива NPY_SCALAR_PRIORITY (-1,000,000.0).

In [67]: np.uint8(1).__array_priority__                                              
Out[67]: -1000000.0
In [68]: np.array(1,'uint8').__array_priority__                                      
Out[68]: 0.0

Скаляры массивов имеют те же методы, что и массивы. По умолчанию эти методы выполняют внутреннее преобразование скаляра в эквивалентный 0-мерный массив и вызывают соответствующий метод массива.

np.isscalar делает:

        (isinstance(num, generic)
        or type(num) in ScalarType
        or isinstance(num, numbers.Number))

np.isscalar рекомендует использовать np.ndim(x) == 0. Сначала проверяется атрибут .ndim (что было бы в случае массивов 0d), а если это не удается, выполняется попытка np.asarray(x).ndim. Таким образом, в этом смысле все, что может быть преобразовано в массив 0d, квалифицируется как «скаляр». Это может быть слишком широко, поскольку словарь считает: npdim({}).

person hpaulj    schedule 28.05.2019
comment
Я хочу сказать, что np.array(1) согласно собственному официальному тесту numpy, isscalar, не является скаляром, поэтому технически то, что вы цитируете из документации, не применимо. - person Paul Panzer; 28.05.2019
comment
np.array(1), массив 0d, не является array scalar в соответствии с тестом функции или array scalars документацией. Но в вычислительном отношении они похожи, потому что скаляр массива «продвигается» до массива 0d перед использованием. - person hpaulj; 28.05.2019