Почему numpy.ndarray позволяет использовать массив None? - PullRequest
0 голосов
/ 14 января 2019

Мне было интересно, что является обоснованием для следующих функций numpy.ndarray:

>>> a = None
>>> a = np.asarray(a)
array(None, dtype=object)

>>> type(a)
<class 'numpy.ndarray'>

>>> a == None
True

>>> a is None
False

Таким образом, в этом случае Python, кажется, фактически создает массив None (не массив Nones), который, по-видимому, обеспечивает тип над переменной a. Но в документации говорится, что позиционный аргумент должен быть «похожим на массив»:

a: array_like

Входные данные в любой форме, которые могут быть преобразованы в массив. Сюда входят списки, списки кортежей, кортежей, кортежей кортежей, кортежей списков и ndarrays.

Так почему None принимается как "массивоподобный", поскольку он не является ни одним из перечисленных выше?

По аналогии, list(None) вернет ошибку, потому что None не является итеративным согласно документации.

Кроме того, некоторые функции, похоже, на самом деле возвращают некорректные значения. Например, np.ndarray.argmax() или np.ndarray.argmin() фактически возвращают 0 для «None array», но приводят к ошибке для пустого массива, которая интуитивно выглядит как ожидаемое поведение.

>>> a
array(None, dtype=object)
>>> b
array([], dtype=object)
>>> a.argmax()
0
>>> b.argmax()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: attempt to get argmax of an empty sequence

Есть ли какое-то преимущество в наличии «массива None» (array(None, dtype=object)) по сравнению с пустым массивом (array([], dtype=object))?

Является ли это предполагаемой функциональностью или случайным следствием того, что Nones является фактическим объектом? Может кто-нибудь объяснить, что здесь происходит под капотом и почему?

Большое спасибо!

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Мне было интересно, каково обоснование следующей функциональности numpy.ndarray:

NumPy допускает 0-мерные массивы и допускает массивы object dtype. Вместе эти факты означают, что любой объект может быть интерпретирован как 0-мерный массив типа object dtype, и именно так numpy.array будет интерпретировать любой аргумент, который не может найти другой способ интерпретации. Вот что здесь происходит.

Имеется 0-мерный массив из 1 элемента, 1 элемент которого равен None.

In [12]: x = numpy.array(None)
In [13]: x.shape
Out[13]: ()
In [14]: x.size
Out[14]: 1
In [15]: print(x.item())
None

Так что в этом случае Python, похоже, фактически создает массив None (не массив Nones)

Нет, это массив Nones. Это массив ровно одного None. Вы можете получить доступ к None, предоставив кортеж без индексов, или вызвав метод item(), или несколькими другими способами.

In [15]: print(x.item())
None
In [16]: print(x[()])
None

Так почему же None принимается как "массивоподобный", поскольку он не является ни одним из перечисленных выше?

Список не является исчерпывающим.

Кроме того, некоторые функции, похоже, на самом деле возвращают некорректные значения. Например, np.ndarray.argmax () или np.ndarray.argmin () фактически возвращают 0 для «None array», но приводят к ошибке для пустого массива, который интуитивно выглядит как ожидаемое поведение.

Если вы не укажете аргумент axis, argmax и argmin по умолчанию будут работать над сглаженной формой ввода. 0 является индексом единственного элемента сплюснутой формы вашего 0-мерного массива.

In [23]: y = x.ravel()
In [24]: y
Out[24]: array([None], dtype=object)
In [25]: y.argmin()
Out[25]: 0
In [26]: y.argmax()
Out[26]: 0
In [27]: print(y[0])
None
0 голосов
/ 14 января 2019

То, что вы получаете с np.asarray(None) - это массив с формой (), который является скаляром , с dtype object. Вы получите нечто подобное, если вы сделаете np.asarray(2) или np.asarray('abc'). Скаляры нельзя повторять, но их можно сравнивать со значениями, отличными от NumPy. В то же время вы получаете с ними операции NumPy, поэтому вы можете выполнить:

list(np.asarray(None).reshape((1,)))

И это работает.

О таких функциях, как argmin или argmax. Обратите внимание, что скаляр не пустой. Массив с формой () имеет один элемент, но нулевые измерения, а массив с формой (0,) не имеет элементов, кроме одного измерения. Это может быть нелогичным, но это имеет смысл и заставляет вещи работать. Как задокументировано, argmin и argmax, когда значение axis не задано, работают с уплощенным массивом. Сглаженный массив для скаляра (например, np.asarray(None).ravel()) - это массив с формой (1,), и, поскольку вы запрашиваете индекс наименьшего или наибольшего значения, и он имеет только одно значение, ответом будет 0 в оба случая. Интересно, что если вы попробуете np.argmin(np.asarray([None, None])), то это не удастся, потому что теперь у вас есть два элемента, и вам нужно сравнить их, чтобы узнать, какой из них наименьший, но вы не можете сравнить значения None.

...