2009-05-26 10 views
48

Necesito escribir una función que detectará si la entrada contiene al menos un valor que no es numérico. Si se encuentra un valor no numérico, generaré un error (porque el cálculo solo debería 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 una complicación adicional, la entrada podría ser un flotador simple o numpy.float64 o incluso algo extraño como una matriz de dimensión cero.¿Detecta si una matriz NumPy contiene al menos un valor no numérico?

La forma obvia de resolver esto es escribir una función recursiva que itere sobre cada objeto iterable en la matriz hasta que encuentre un no-iterabe. Aplicará la función numpy.isnan() 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 en 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 más numpyish?

Aquí es 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 
+3

Su descripción de 'contains_nan' parece sospechoso: "Devuelve false si existe al menos un valor no numérico". Hubiera esperado que 'contains_nan' devolviera' True' si la matriz contiene NaN. –

+0

¿Qué pasa con las entradas tales como 'array (['None', 'None'], dtype = object)'? ¿Debería tal entrada solo generar una excepción? –

+0

NO use 'float ('nan') en x'. No funciona. –

Respuesta

78

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

numpy.isnan(myarray).any() 

Editar: 30x 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 muy bien para no-array tipos NumPy:

>>> a = numpy.float64(42.) 
>>> numpy.isnan(a).any() 
False 
>>> a = numpy.float64(numpy.nan) 
>>> numpy.isnan(a).any() 
True 
+0

con numpy 1.7 la versión flatten() es solo dos veces más rápida que la primera –

+0

¿Por qué no funciona algo como 'float ('nan') en x'? Lo intenté y Python devuelve 'False' donde' x = [1,2,3, float ('nan')] '. –

+1

@CharlieParker la misma razón por la cual float ('nan') == float ('nan') devolverá False. NaN no es igual a NaN. Aquí más información: http://stackoverflow.com/questions/10034149/why-is-nan-not-equal-to-nan – Muppet

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 nans en las comparaciones no fue consistente en las versiones anteriores.

+0

¿Por qué no funciona algo como 'float ('nan') en x'? Lo intenté y Python devuelve 'False' donde' x = [1,2,3, float ('nan')] '. –

+0

@CharlieParker ... porque la comparación con NAN no hace lo que esperas. NAN se trata como un NULL lógico (= no sé). 'float (" nan ") == float (" nan ")' da 'False' (aunque probablemente sea NAN o None). De manera similar, la rareza con NAN y Boole NULL es verdadera en muchos idiomas, incluido SQL (donde NULL = NULL nunca es verdadero). – user48956

9

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

numpy.isfinite(myarray).all() 

Si los evalúa anteriores a True, entonces myarray no contiene, numpy.nan, numpy.inf o -numpy.inf valores.

numpy.nan va a estar bien con numpy.inf valores, 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) 
+0

¿Por qué no funciona algo como 'float ('nan') en x'? Lo intenté y Python devuelve 'False' donde' x = [1,2,3, float ('nan')] '. –

+1

@CharlieParker porque dos 'nan's no se consideran iguales entre sí. Pruebe 'float ('nan') == float ('nan')'. – Akavall

+0

interesante. ¿Por qué no son considerados iguales? –

2

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

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" 
Cuestiones relacionadas