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

Несомненно, массив 0d является скалярным, но Numpy, похоже, не так думает... я что-то упустил или просто не понимаю концепцию?

>>> foo = numpy.array(1.11111111111, numpy.float64)
>>> numpy.ndim(foo)
0
>>> numpy.isscalar(foo)
False
>>> foo.item()
1.11111111111

Ответ 1

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

Любопытная ситуация с скалярными типами Numpy была обусловлена ​​отсутствием грациозного и последовательного способа деградации матрицы 1x1 для скалярных типов. Хотя математически они одно и то же, они обрабатываются совсем другим кодом.

Если вы делали какой-либо научный код, в конечном итоге вы хотели бы, чтобы такие вещи, как max(a), работали над матрицами всех размеров, даже с скалярами. Математически это вполне разумная вещь. Однако для программистов это означает, что все, что представляет собой скаляр в Numpy, должно иметь .shape и .ndim attirbute, поэтому, по крайней мере, ufuncs не должны выполнять явную проверку типов на своем входе для 21 возможных скалярных типов в Numpy.

С другой стороны, они также должны работать с существующими библиотеками Python, которые выполняют явные проверки типов по скалярному типу. Это дилемма, поскольку Numpy ndarray должен индивидуально изменять свой тип, когда они были сведены к скаляру, и нет никакого способа узнать, произошло ли это, если у него нет проверок на весь доступ. Фактически, этот маршрут, вероятно, сделает смехотворно медленным работу со стандартами скалярного типа.

Решение разработчика Numpy должно наследовать как от скаляров ndarray, так и от Python для собственного скалярного типа, так что все скаляры также имеют .shape,.ndim,.T и т.д. и т.д. Матрица 1x1 все равно будет присутствовать, но ее использование будет обескуражено, если вы знаете, что будете иметь дело со скаляром. В то время как это должно работать нормально в теории, иногда вы все еще можете видеть некоторые места, где они пропустили с помощью роликового ролика, а уродливые внутренности открыты для всех, чтобы увидеть:

>>> from numpy import *
>>> a = array(1)
>>> b = int_(1)
>>> a.ndim
0
>>> b.ndim
0
>>> a[...]
array(1)
>>> a[()]
1
>>> b[...]
array(1)
>>> b[()]
1

Нет причин, по которым a[...] и a[()] должны возвращать разные вещи, но это так. Есть предложения, чтобы изменить это, но похоже, что они забыли закончить работу для массивов 1x1.

Потенциально большая и, возможно, неразрешимая проблема, заключается в том, что сканеры Numpy неизменяемы. Поэтому "распыление" скаляра в ndarray, математически сопряженная операция свертывания массива в скаляр, представляет собой PITA для реализации. Вы не можете на самом деле вырастить скаляр Numpy, он не может быть по определению включен в ndarray, хотя newaxis таинственно работает над ним:

>>> b[0,1,2,3] = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'numpy.int32' object does not support item assignment
>>> b[newaxis]
array([1])

В Matlab рост размера скаляра является вполне приемлемой и безмозглой операцией. В Numpy вам придется вставлять jarring a = array(a) везде, где вы думаете, что у вас будет возможность начать со скаляра и в итоге получить массив. Я понимаю, почему Numpy должен быть таким образом, чтобы хорошо играть с Python, но это не меняет того факта, что многие новые переключатели глубоко смущены этим. У некоторых есть явная память о борьбе с этим поведением и, в конечном счете, настойчивом, в то время как другие, которые слишком далеко ушли, как правило, остаются с глубоким бесформенным умственным шрамом, который часто преследует их самые невинные мечты. Это ужасная ситуация для всех.

Ответ 2

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

>>> x = numpy.float64(1.111)
>>> x
1.111
>>> numpy.isscalar(x)
True
>>> numpy.ndim(x)
0

Похоже, что скаляры в numpy могут быть немного другой концепцией, из которой вы можете использовать с чисто математической точки зрения. Я предполагаю, что вы думаете о скалярных матрицах?