functions - python numpy doc
gotchas donde Numpy se diferencia de python recta? (15)
Gente
¿Existe una colección de errores en la que Numpy se diferencie de python, puntos que hayan desconcertado y hayan costado tiempo?
"¡El horror de ese momento que nunca olvidaré!"
"Lo harás, sin embargo", dijo la reina, "si no haces un memorándum de eso".
Por ejemplo, los NaN siempre son problemas, en cualquier lugar. Si puedes explicar esto sin ejecutarlo, date un punto:
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] )
(No estoy dando muchas vueltas, hay mucho buen trabajo allí, solo creo que sería útil una FAQ o Wiki de errores).
Edit: esperaba coleccionar media docena de errores (sorpresas para las personas que aprenden Numpy).
Luego, si hay errores comunes o, mejor, explicaciones comunes, podríamos hablar sobre agregarlos a una comunidad Wiki (¿dónde?) No parece que tengamos suficiente hasta ahora.
(Relacionado, pero un número NumPy vs. SciPy gotcha, en lugar de NumPy vs Python)
Cortar más allá del tamaño real de una matriz funciona de manera diferente:
>>> 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
Por lo tanto, al scipy.sparse
matrices scipy.sparse
, debe asegurarse manualmente de que sus límites de scipy.sparse
estén dentro del rango. Esto difiere de cómo funcionan tanto NumPy como Python simple.
Cortar crea vistas, no copias.
>>> 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])
Creo que este es gracioso:
>>> 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]])
Para las listas de Python, por otro lado, esto funciona como se pretende:
>>> b = [[1,2],[3,4]]
>>> b[1], b[0] = b[0], b[1]
>>> b
[[3, 4], [1, 2]]
Nota graciosa: el mismo numpy tenía un error en la función de shuffle
, porque usaba esa notación :-) (ver here ).
La razón es que en el primer caso estamos tratando con vistas de la matriz, por lo que los valores se sobrescriben en el lugar.
Debido a que __eq__
no devuelve un bool, el uso de matrices numpy en cualquier tipo de contenedores evita las pruebas de igualdad sin una solución específica del contenedor.
Ejemplo:
>>> 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()
Este es un problema horrible. Por ejemplo, no puede escribir TestCase.assertEqual()
para contenedores que usan TestCase.assertEqual()
y, en cambio, debe escribir funciones de comparación personalizadas. Supongamos que escribimos una función de work-around special_eq_for_numpy_and_tuples
. Ahora podemos hacer esto en una prueba de unidad:
x = (array1, ''deserialized'')
y = (array2, ''deserialized'')
self.failUnless( special_eq_for_numpy_and_tuples(x, y) )
Ahora debemos hacer esto para cada tipo de contenedor que podamos usar para almacenar matrices numpy. Además, __eq__
podría devolver un bool en lugar de una matriz de bools:
>>> a = numpy.array(range(3))
>>> b = numpy.array(range(5))
>>> a == b
False
Ahora cada una de nuestras funciones de comparación de igualdad de contenedores específicos también debe manejar ese caso especial.
Tal vez podamos parchear esta verruga con una subclase?
>>> 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
Eso parece hacer lo correcto. La clase también debe exportar explícitamente la comparación elemental, ya que a menudo es útil.
El valor de verdad de una matriz de Numpy difiere del de un tipo de secuencia de python, donde cualquier secuencia no vacía es verdadera.
>>> 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()
>>>
Los tipos numéricos son verdaderos cuando no son cero y como una colección de números, la matriz Nupy hereda esta definición. Pero con una colección de números, la verdad podría significar razonablemente que "todos los elementos son distintos de cero" o "al menos un elemento es distinto de cero". Numpy se niega a adivinar qué definición significa y plantea la excepción anterior. El uso de los métodos .any () y .all () permite especificar el significado de verdadero.
>>> if a.any(): print "Im true"
...
Im true
>>> if a.all(): print "Im true"
...
>>>
Encontré el hecho de que multiplicar las listas de elementos simplemente crea una vista de los elementos que me sorprendieron.
>>> a=[0]*5
>>>a
[0,0,0,0,0]
>>>a[2] = 1
>>>a
[0,0,1,0,0]
>>>b = [np.ones(3)]*5
>>>b
[array([ 1., 1., 1.]), array([ 1., 1., 1.]), array([ 1., 1., 1.]), array([ 1., 1., 1.]), array([ 1., 1., 1.])]
>>>b[2][1] = 2
>>>b
[array([ 1., 2., 1.]), array([ 1., 2., 1.]), array([ 1., 2., 1.]), array([ 1., 2., 1.]), array([ 1., 2., 1.])]
Entonces, si creas una lista de elementos como este y pretendes realizar diferentes operaciones en ellos, estás sorprendido ...
Una solución sencilla es crear de forma iterativa cada uno de los arreglos (usando un ''bucle for'' o una comprensión de lista) o usar un arreglo dimensional más alto (donde, por ejemplo, cada uno de estos arreglos 1D es una fila en su arreglo 2D, que generalmente es más rápido).
Lo que más me gustó fue que casi todos los operadores estándar están sobrecargados para distribuir en todo el arreglo.
Definir una lista y una matriz.
>>> 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])
La multiplicación duplica la lista de python, pero se distribuye sobre la matriz 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])
La adición y la división no están definidas en las listas de 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 sobrecargas para tratar listas como matrices a veces
>>> 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])
Nadie parece haber mencionado esto hasta ahora:
>>> 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 es any
y no juega bien con los generadores, y no genera ningún error que le advierta que no lo hacen.
No es un gran problema: con el corte booleano, a veces desearía poder hacerlo
x[ 3 <= y < 7 ]
Como la doble comparación de Python. En cambio, tengo que escribir
x[ np.logical_and(3<=y, y<7) ]
(A menos que sepas algo mejor?)
Además, np.logical_and y np.logical_or solo toman dos argumentos cada uno, me gustaría que tomen un número variable, o una lista, para poder incluir más de dos cláusulas lógicas.
(Numpy 1.3, tal vez todo esto haya cambiado en versiones posteriores).
Una matriz 0-d de Ninguno se parece a Ninguna pero no es lo mismo:
In [1]: print None
None
In [2]: import numpy
In [3]: print numpy.array(None)
None
In [4]: numpy.array(None) is None
Out[4]: False
In [5]: numpy.array(None) == None
Out[5]: False
In [6]: print repr(numpy.array(None))
array(None, dtype=object)
Una sorpresa con la asignación *=
en combinación con numpy.array
:
>>> from numpy import array
>>> a = array([1, 2, 3])
>>> a *= 1.1
>>> print(a)
[1 2 3] # not quite what we expect or would like to see
>>> print(a.dtype)
int64 # and this is why
>>> a = 1.1 * a # here, a new array is created
>>> print(a, a.dtype)
[ 1.1 2.2 3.3] float64 # with the expected outcome
Sorprendente, molesta, pero comprensible. El operador *=
no cambiará el tipo de datos de la array
, por lo que la multiplicación de una array
int
por un float
fallará en el significado convencional de esta multiplicación. La versión de Python a = 1; a *= 1.1
a = 1; a *= 1.1
en la otra parte funciona como se espera.
de Neil Martinsen-Burrell en una numpy-discussion 7 de septiembre -
El tipo ndarray disponible en Numpy no es conceptualmente una extensión de los iterables de Python. Si desea ayudar a otros usuarios de Numpy con este problema, puede editar la documentación en el editor de documentación en línea en numpy-docs
NaN
no es un singleton como None
, por lo que realmente no se puede usar el cheque. Lo que lo hace un poco complicado es que NaN == NaN
es False
como lo requiere IEEE-754. Es por eso que necesita usar la función numpy.isnan()
para verificar si un flotador no es un número. O la biblioteca estándar math.isnan()
si está usando Python 2.6+.
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
Así que no pruebe el vacío de una matriz comprobando su valor de verdad. Utilice size(array())
.
Y no uses len(array())
, tampoco:
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
print pynan is pynan, pynan is NaN, NaN is NaN
Esto prueba la identidad, es decir, si es el mismo objeto. El resultado debería ser obviamente verdadero, falso, verdadero, porque cuando haces flotar (lo que sea) estás creando un nuevo objeto flotante.
a = (0, pynan)
print a, a[1] is pynan, any([aa is pynan for aa in a])
No sé qué es lo que encuentras sorprendente con esto.
a = array(( 0, NaN ))
print a, a[1] is NaN, isnan( a[1] )
Esto tuve que correr. :-) Cuando se pega NaN en una matriz, se convierte en un objeto numpy.float64, por lo que un [1] es NaN falla.
Todo esto me parece bastante sorprendente. Pero entonces realmente no sé mucho acerca de NumPy. :-)