Гочась, где Numpy отличается от прямого питона? - PullRequest
26 голосов
/ 24 августа 2009

Люди,

есть ли коллекция ошибок, где Numpy отличается от python, Очки, которые озадачили и стоили времени?

"Ужас того момента я никогда не забудь! "
"Вы будете, однако," сказала Королева, "если вы этого не сделаете сделать меморандум об этом. "

Например, NaN всегда неприятны, где угодно. Если вы можете объяснить это, не выполняя, поставьте себе точку -

from numpy import array, NaN, isnan

pynan = float("nan")
print pynan is pynan, pynan is NaN, NaN is NaN
a = (0, pynan)
print a, a[1] is pynan, any([aa is pynan for aa in a])

a = array(( 0, NaN ))
print a, a[1] is NaN, isnan( a[1] )

(Я не сбиваю с ног, много хорошей работы там, просто думаю, что FAQ или Wiki о гучах будут полезны.)

Редактировать: Я надеялся собрать полдюжины ошибок (сюрпризов для людей, изучающих Numpy).
Тогда, если есть общие ошибки или, что лучше, общие объяснения, мы могли бы поговорить о добавлении их в сообщество вики (где?) Не похоже, что у нас достаточно пока.

Ответы [ 15 ]

24 голосов
/ 20 мая 2011

Поскольку __eq__ не возвращает логическое значение, использование пустых массивов в любых контейнерах предотвращает проверку на равенство без обхода конкретного контейнера.

Пример:

>>> import numpy
>>> a = numpy.array(range(3))
>>> b = numpy.array(range(3))
>>> a == b
array([ True,  True,  True], dtype=bool)
>>> x = (a, 'banana')
>>> y = (b, 'banana')
>>> x == y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Это ужасная проблема. Например, вы не можете написать юнит-тесты для контейнеров, которые используют TestCase.assertEqual() и должны вместо этого написать пользовательские функции сравнения. Предположим, мы пишем обходную функцию special_eq_for_numpy_and_tuples. Теперь мы можем сделать это в unittest:

x = (array1, 'deserialized')
y = (array2, 'deserialized')
self.failUnless( special_eq_for_numpy_and_tuples(x, y) )

Теперь мы должны сделать это для каждого типа контейнера, который мы можем использовать для хранения пустых массивов. Кроме того, __eq__ может возвращать bool, а не массив bools:

>>> a = numpy.array(range(3))
>>> b = numpy.array(range(5))
>>> a == b
False

Теперь каждая из наших функций сравнения равенства для конкретного контейнера также должна обрабатывать этот особый случай.

Может быть, мы сможем исправить эту бородавку с помощью подкласса?

>>> class SaneEqualityArray (numpy.ndarray):
...   def __eq__(self, other):
...     return isinstance(other, SaneEqualityArray) and self.shape == other.shape and (numpy.ndarray.__eq__(self, other)).all()
... 
>>> a = SaneEqualityArray( (2, 3) )
>>> a.fill(7)
>>> b = SaneEqualityArray( (2, 3) )
>>> b.fill(7)
>>> a == b
True
>>> x = (a, 'banana')
>>> y = (b, 'banana')
>>> x == y
True
>>> c = SaneEqualityArray( (7, 7) )
>>> c.fill(7)
>>> a == c
False

Кажется, это правильно. Класс также должен явно экспортировать поэлементное сравнение, поскольку это часто полезно.

22 голосов
/ 25 августа 2009

Самым большим недостатком для меня было то, что почти каждый стандартный оператор перегружен для распределения по массиву.

Определить список и массив

>>> l = range(10)
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> import numpy
>>> a = numpy.array(l)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

Умножение дублирует список Python, но распределяет по массиву NumPy

>>> l * 2
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> a * 2
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])

Добавление и деление не определены в списках Python

>>> l + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list
>>> a + 2
array([ 2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
>>> l / 2.0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'list' and 'float'
>>> a / 2.0
array([ 0. ,  0.5,  1. ,  1.5,  2. ,  2.5,  3. ,  3.5,  4. ,  4.5])

Перегрузка Numpy для обработки списков как массивов иногда

>>> a + a
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
>>> a + l
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
21 голосов
/ 24 августа 2009

Я думаю, что это смешно:

>>> import numpy as n
>>> a = n.array([[1,2],[3,4]])
>>> a[1], a[0] = a[0], a[1]
>>> a
array([[1, 2],
       [1, 2]])

Для списков Python, с другой стороны, это работает так, как задумано:

>>> b = [[1,2],[3,4]]
>>> b[1], b[0] = b[0], b[1]
>>> b
[[3, 4], [1, 2]]

Забавное замечание: у numpy была ошибкафункция shuffle, потому что она использовала эту запись :-) (см. здесь ).

Причина в том, что в первом случае мы имеем дело с views массива, поэтому значения перезаписываются на месте.

12 голосов
/ 24 августа 2009

NaN не является синглтоном, подобным None, поэтому вы не можете использовать его проверку. Немного сложнее то, что NaN == NaN - это False, как того требует IEEE-754. Вот почему вам нужно использовать функцию numpy.isnan(), чтобы проверить, не является ли число с плавающей точкой. Или стандартная библиотека math.isnan(), если вы используете Python 2.6 +.

7 голосов
/ 12 сентября 2009

Нарезка создает представления, а не копии.

>>> l = [1, 2, 3, 4]
>>> s = l[2:3]
>>> s[0] = 5
>>> l
[1, 2, 3, 4]

>>> a = array([1, 2, 3, 4])
>>> s = a[2:3]
>>> s[0] = 5
>>> a
array([1, 2, 5, 4])
6 голосов
/ 20 февраля 2013
In [1]: bool([])
Out[1]: False

In [2]: bool(array([]))
Out[2]: False

In [3]: bool([0])
Out[3]: True

In [4]: bool(array([0]))
Out[4]: False

Так что не проверяйте пустоту массива, проверяя его значение истинности. Используйте size(array()).

И не используйте len(array()), либо:

In [1]: size(array([]))
Out[1]: 0

In [2]: len(array([]))
Out[2]: 0

In [3]: size(array([0]))
Out[3]: 1

In [4]: len(array([0]))
Out[4]: 1

In [5]: size(array(0))
Out[5]: 1

In [6]: len(array(0))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-5b2872696128> in <module>()
----> 1 len(array(0))

TypeError: len() of unsized object
6 голосов
/ 12 сентября 2009

Значение истинности массива Numpy отличается от значения типа последовательности Python, где любая непустая последовательность является истинной.

>>> import numpy as np
>>> l = [0,1,2,3]
>>> a = np.arange(4)
>>> if l: print "Im true"
... 
Im true
>>> if a: print "Im true"
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use
a.any() or a.all()
>>>

Числовые типы имеют значение true, когда они не равны нулю, и как набор чисел массив Numpy наследует это определение. Но с набором чисел истина может разумно означать «все элементы отличны от нуля» или «хотя бы один элемент не равен нулю». Numpy отказывается угадывать, какое определение имеется в виду, и выдвигает вышеуказанное исключение. Использование методов .any() и .all() позволяет указать, какое значение имеет значение true.

>>> if a.any(): print "Im true"
... 
Im true
>>> if a.all(): print "Im true"
... 
>>>
5 голосов
/ 29 июля 2011

(Связано, но есть NumPy против SciPy, а не NumPy против Python)


Нарезка за пределы реального размера массива работает иначе:

>>> import numpy, scipy.sparse

>>> m = numpy.random.rand(2, 5) # create a 2x5 dense matrix
>>> print m[:3, :] # works like list slicing in Python: clips to real size
[[ 0.12245393  0.20642799  0.98128601  0.06102106  0.74091038]
[ 0.0527411   0.9131837   0.6475907   0.27900378  0.22396443]]

>>> s = scipy.sparse.lil_matrix(m) # same for csr_matrix and other sparse formats
>>> print s[:3, :] # doesn't clip!
IndexError: row index out of bounds

Таким образом, при нарезке массивов scipy.sparse необходимо вручную убедиться, что границы срезов находятся в пределах диапазона. Это отличается от того, как работают как NumPy, так и обычный Python.

3 голосов
/ 20 февраля 2013

Никто до сих пор не упомянул об этом:

>>> all(False for i in range(3))
False
>>> from numpy import all
>>> all(False for i in range(3))
True
>>> any(False for i in range(3))
False
>>> from numpy import any
>>> any(False for i in range(3))
True

numpy's any и all плохо работают с генераторами и не выдают никаких ошибок, предупреждающих вас об этом.

3 голосов
/ 24 августа 2009
print pynan is pynan, pynan is NaN, NaN is NaN

Это проверяет идентичность, то есть если это один и тот же объект. Поэтому результатом, очевидно, должно быть «Истина», «Ложь», «Истина», потому что когда вы выполняете float (что угодно), вы создаете новый объект float.

a = (0, pynan)
print a, a[1] is pynan, any([aa is pynan for aa in a])

Я не знаю, что вас удивляет в этом.

a = array(( 0, NaN ))
print a, a[1] is NaN, isnan( a[1] )

Это я должен был бежать. :-) Когда вы вставляете NaN в массив, он конвертируется в объект numpy.float64, поэтому [1] означает, что NaN завершается неудачей.

Это все кажется довольно неудивительным для меня. Но тогда я действительно ничего не знаю о NumPy. : -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...