Почему 0d массивы в Numpy считаются скалярными? - PullRequest
1 голос
/ 28 мая 2019

В некотором смысле, это уже имеет отличный ответ :

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

Психическое здоровье и долголетие, конечно, хороши, но как насчет гордости этого человека, которая получила еще один удар, пытаясь быть умным и жестоко отрицаемым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 здесь ведет себя как скаляр?

1 Ответ

2 голосов
/ 28 мая 2019

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 не может "upcast". Но список ведет себя как массив 1d и выполняет «upcast»

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({}).

...