¿Detectar si una matriz NumPy contiene al menos un valor no numérico?

103

Necesito escribir una función que detecte si la entrada contiene al menos un valor que no sea numérico. Si se encuentra un valor no numérico, generaré un error (porque el cálculo solo debe devolver un valor numérico). El número de dimensiones de la matriz de entrada no se conoce de antemano; la función debe dar el valor correcto independientemente de ndim. Como complicación adicional, la entrada podría ser un solo flotador numpy.float64o incluso algo extraño como una matriz de dimensión cero.

La forma obvia de resolver esto es escribir una función recursiva que itera sobre cada objeto iterable en la matriz hasta que encuentra un no iterabe. Aplicará la numpy.isnan()función sobre cada objeto no iterable. Si se encuentra al menos un valor no numérico, la función devolverá False inmediatamente. De lo contrario, si todos los valores del iterable son numéricos, eventualmente devolverá True.

Eso funciona bien, pero es bastante lento y espero que NumPy tenga una forma mucho mejor de hacerlo. ¿Cuál es una alternativa que es más rápida y numpyish?

Aquí está mi maqueta:

def contains_nan( myarray ):
    """
    @param myarray : An n-dimensional array or a single float
    @type myarray : numpy.ndarray, numpy.array, float
    @returns: bool
    Returns true if myarray is numeric or only contains numeric values.
    Returns false if at least one non-numeric value exists
    Not-A-Number is given by the numpy.isnan() function.
    """
    return True
Salim Fadhley
fuente
3
Su descripción de contains_nanparece sospechosa: "Devuelve falso si existe al menos un valor no numérico". Hubiera esperado contains_nanregresar Truesi la matriz contiene NaN.
Samuel Tardieu
¿Qué pasa con las entradas como array(['None', 'None'], dtype=object)? ¿Debería tal entrada generar una excepción?
Finn Årup Nielsen
NO lo use float('nan') in x. No funciona.
Charlie Parker

Respuestas:

182

Esto debería ser más rápido que iterar y funcionará independientemente de la forma.

numpy.isnan(myarray).any()

Editar: 30 veces más rápido:

import timeit
s = 'import numpy;a = numpy.arange(10000.).reshape((100,100));a[10,10]=numpy.nan'
ms = [
    'numpy.isnan(a).any()',
    'any(numpy.isnan(x) for x in a.flatten())']
for m in ms:
    print "  %.2f s" % timeit.Timer(m, s).timeit(1000), m

Resultados:

  0.11 s numpy.isnan(a).any()
  3.75 s any(numpy.isnan(x) for x in a.flatten())

Bono: funciona bien para tipos NumPy que no son de matriz:

>>> a = numpy.float64(42.)
>>> numpy.isnan(a).any()
False
>>> a = numpy.float64(numpy.nan)
>>> numpy.isnan(a).any()
True
Pablo
fuente
1
con numpy 1.7, la versión flatten () es solo dos veces más rápida que la primera
Christian Geier
¿Por qué algo como float('nan') in xno funciona? Lo probé y Python regresa Falsedonde x = [1,2,3,float('nan')].
Charlie Parker
1
@CharlieParker la misma razón por la que float ('nan') == float ('nan') devolverá False. NaN no es igual a NaN. Aquí más información: stackoverflow.com/questions/10034149/…
Muppet
1
@mab: Eso es porque llamar numpy.anya genexp solo devuelve genexp; en realidad no está haciendo el cálculo que cree que está haciendo. Nunca llame numpy.anya un genexp.
user2357112 apoya a Monica
En un escenario de depuración real, también recomendaría mirar en np.isfinitelugar de np.isnandetectar desbordamientos numéricos, inestabilidad, etc.
Ben Usman
18

Si infinito es un valor posible, usaría numpy.isfinite

numpy.isfinite(myarray).all()

Si lo anterior se evalúa como True, entonces myarrayno contiene valores numpy.nan, numpy.info -numpy.inf.

numpy.nanestará bien con los numpy.infvalores, por ejemplo:

In [11]: import numpy as np

In [12]: b = np.array([[4, np.inf],[np.nan, -np.inf]])

In [13]: np.isnan(b)
Out[13]: 
array([[False, False],
       [ True, False]], dtype=bool)

In [14]: np.isfinite(b)
Out[14]: 
array([[ True, False],
       [False, False]], dtype=bool)
Akavall
fuente
¿Por qué algo como float('nan') in xno funciona? Lo probé y Python regresa Falsedonde x = [1,2,3,float('nan')].
Charlie Parker
1
@CharlieParker porque dos nans no se consideran iguales entre sí. Prueba float('nan') == float('nan').
Akavall
interesante. ¿Por qué no se consideran iguales?
Charlie Parker
1
@CharlieParker, no creo que pueda dar una muy buena respuesta aquí. Quizás esto es lo que está buscando: stackoverflow.com/questions/1565164/…
Akavall
4

¡No! Microsegundos! Nunca resuelva un problema en microsegundos que pueda resolverse en nanosegundos.

Tenga en cuenta que la respuesta aceptada:

  • itera sobre todos los datos, independientemente de si se encuentra un nan
  • crea una matriz temporal de tamaño N, que es redundante.

Una mejor solución es devolver True inmediatamente cuando se encuentra NAN:

import numba
import numpy as np

NAN = float("nan")

@numba.njit(nogil=True)
def _any_nans(a):
    for x in a:
        if np.isnan(x): return True
    return False

@numba.jit
def any_nans(a):
    if not a.dtype.kind=='f': return False
    return _any_nans(a.flat)

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 573us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 774ns  (!nanoseconds)

y funciona para n dimensiones:

array1M_nd = array1M.reshape((len(array1M)/2, 2))
assert any_nans(array1M_nd)==True
%timeit any_nans(array1M_nd)  # 774ns

Compare esto con la solución nativa numpy:

def any_nans(a):
    if not a.dtype.kind=='f': return False
    return np.isnan(a).any()

array1M = np.random.rand(1000000)
assert any_nans(array1M)==False
%timeit any_nans(array1M)  # 456us

array1M[0] = NAN
assert any_nans(array1M)==True
%timeit any_nans(array1M)  # 470us

%timeit np.isnan(array1M).any()  # 532us

El método de salida anticipada es de 3 órdenes o aceleración de magnitud (en algunos casos). No está mal para una simple anotación.

user48956
fuente
3

Con numpy 1.3 o svn puedes hacer esto

In [1]: a = arange(10000.).reshape(100,100)

In [3]: isnan(a.max())
Out[3]: False

In [4]: a[50,50] = nan

In [5]: isnan(a.max())
Out[5]: True

In [6]: timeit isnan(a.max())
10000 loops, best of 3: 66.3 µs per loop

El tratamiento de los nans en las comparaciones no fue consistente en versiones anteriores.


fuente
¿Por qué algo como float('nan') in xno funciona? Lo probé y Python regresa Falsedonde x = [1,2,3,float('nan')].
Charlie Parker
@CharlieParker ... porque la comparación con NAN no hace lo que esperas. NAN se trata como un NULL lógico (= no lo sé). float("nan")==float("nan")dar False(aunque es factible que probablemente debería devolver NAN o None). De manera similar, la rareza con NAN y boolen NULL es cierta en muchos idiomas, incluido SQL (donde NULL = NULL nunca es cierto).
user48956
2

(np.where(np.isnan(A)))[0].shape[0]será mayor que 0si Acontiene al menos un elemento de nan, Apodría ser una n x mmatriz.

Ejemplo:

import numpy as np

A = np.array([1,2,4,np.nan])

if (np.where(np.isnan(A)))[0].shape[0]: 
    print "A contains nan"
else:
    print "A does not contain nan"
Ting en Chan
fuente