2012-08-23 27 views
8

Me gustaría agregar miles de matrices 4D en cuanto a elementos y contabilidad para nans. Un ejemplo sencillo utilizando matrices 1D sería:fast numpy addnan

X = array([4,7,89,nan,89,65, nan]) 
Y = array([0,5,4, 9, 8, 100,nan]) 
z = X+Y 
print z = array([4,12,93,9,97,165,nan]) 

He escrito un simple bucle alrededor de esto, pero se necesita siempre - no es una solución inteligente. Otra solución podría ser crear una matriz más grande y usar el cuello de botella nansum, pero esto llevaría demasiada memoria para mi computadora portátil. Necesito una suma corriente de más de 11000 casos.

¿Alguien tiene una manera inteligente y rápida de hacer esto?

Respuesta

10

Aquí es una posibilidad:

>>> x = np.array([1, 2, np.nan, 3, np.nan, 4]) 
... y = np.array([1, np.nan, 2, 5, np.nan, 8]) 
>>> x = np.ma.masked_array(np.nan_to_num(x), mask=np.isnan(x) & np.isnan(y)) 
>>> y = np.ma.masked_array(np.nan_to_num(y), mask=x.mask) 
>>> (x+y).filled(np.nan) 
array([ 2., 2., 2., 8., nan, 12.]) 

La verdadera dificultad es que parece que quieres nan a ser interpretado como cero a menos que todos los valores en una posición particular son nan. Esto significa que debe mirar tanto x como y para determinar qué nans reemplazar. Si está de acuerdo con reemplazar todos los valores de nan, entonces simplemente puede hacer np.nan_to_num(x) + np.nan_to_num(y).

+0

Las matrices enmascaradas son el camino a seguir aquí si su implementación numpy es lo suficientemente nueva como para admitirla (la mía no lo es, tal vez sea el momento de una actualización) (+1). – mgilson

+0

@mgilson: ¡Heh, probablemente sea el momento! Creo que los arreglos enmascarados han estado en numpy desde hace unos años. – BrenBarn

+0

Bueno, mi computadora tiene algunos años; ^) – mgilson

1

No estoy seguro de cómo esto podría llevar a cabo, pero vale la pena un tiro :)

def nan_to_zero(array): 
    new_arr = array.copy() 
    new_arr[np.isnan(array)] = 0. 
    return new_arr 

sum(nan_to_zero(arr) for arr in array_generator) 

Esto no se traduce en un NaN en el último lugar de la matriz sin embargo. El resultado es un 0 ...

+2

Numpy ya lo proporciona en la función 'nan_to_num'. – BrenBarn

+0

@mgilson: una lista de comprensión después de eliminar los nans. Nunca pensé en la parte de comprensión de la lista. Pero sospecho que esto supone una matriz 1D. No veo cómo podría codificar este método para una matriz 4D. – Shejo284

+1

@ Shejo284 - En realidad es una expresión de generador, pero funciona de manera similar. Sin embargo, no veo ninguna razón por la que esto no se pueda usar con arrays 4D. Realmente, las matrices 4D son solo matrices 1D en la memoria de todos modos (a menos que realmente tenga objetos de vista, pero igual debería funcionar con ellos) – mgilson

3

Se podría hacer algo como:

arr1 = np.array([1.0, 1.0, np.nan, 1.0, 1.0, np.nan]) 
arr2 = np.array([1.0, 1.0, 1.0, 1.0, 1.0, np.nan]) 
flags = np.isnan(arr1) & np.isnan(arr2) 
copy1 = arr1.copy() 
copy2 = arr2.copy() 
copy1[np.isnan(copy1)] = 0.0 
copy2[np.isnan(copy2)] = 0.0 
out = copy1 + copy2 
out[flags] = np.NaN 
print out 
array([ 2., 2., 1., 2., 2., NaN]) 

para encontrar las ubicaciones en las matrices cuando las dos tienen un NaN en ese índice. A continuación, haga básicamente lo que sugirió @mgilson, como hacer copias y reemplazar el NaN con 0.0, agregar los dos arreglos juntos y luego reemplazar los índices marcados arriba con np.NaN.

+0

@mgilson: Estoy tratando de escribir una expresión de generador, ya que consume menos memoria, pero estoy un poco confundido sobre cómo funciona esto cuando se trata de números muy grandes y leer un archivo netcdf, cortar por segmento: para i en casos: array = np.array (netcdfvar [i]) # A continuación, suma estas rebanadas que representan nan no estoy seguro de cómo se vería este generador. – Shejo284

+0

@ Shejo284 - Creo que publicaste esto en la respuesta incorrecta ;-). De todos modos, no estoy familiarizado con la lectura de segmentos de un archivo netcdf, pero puedes intentar lo siguiente: 'sum (nan_to_zero (np.array (netcdfvar [i])) para i en casos)', o como señala BrenBarn : 'sum (np.nan_to_num (netcdfvar [i]) para i en los casos)' – mgilson

+0

@mgilson: sí, tienes razón. Todavía estoy aprendiendo a usar este sitio. Gracias. He estado probando varias variaciones con éxito variable. Su solución es un poco contra intuitiva. Lo probaré. – Shejo284

1

Veo varias soluciones más simples:

  • (editado) Uso de np.ma

    mX = np.ma.masked_array(X, mask=np.isnan(X)) 
    mY = np.ma.masked_array(Y, mask=np.isnan(Y)) 
    mZ = np.ma.masked_array(mX.filled(0) + mY.filled(0), 
             mask=mX.mask * mY.mask) 
    Z = mZ.filled(np.nan) 
    
  • (editado) evitando el uso np.ma

    mx = np.isnan(x) 
    my = np.isnan(y) 
    z = np.where(mx,0,x) + np.where(my,0,y) 
    z[mx&my] = np.nan 
    
+1

Estas soluciones no producen el resultado deseado. Él quiere que se agreguen los términos que no sean nan, con lo que nan aparece en el resultado solo si * todos * los valores en una posición particular son nan. Sus soluciones producen nans adicionales en posiciones donde solo uno de los dos vectores de entrada tiene un nan. – BrenBarn

+0

Correcto, reparado. Gracias por mantenerme alerta –

+0

También tenga en cuenta que su última solución es algo que el OP dijo explícitamente que no quería hacer (crear una matriz más grande que contenga ambos). La segunda solución se ve bien, sin embargo. – BrenBarn

3
import numpy as np 
z=np.nansum([X,Y],axis=0) 
+1

Esto casi funciona. El problema es que esta solución no produce el resultado deseado. La salida debe incluir NaN donde * ambos * vectores de entrada tienen NaN en las mismas posiciones. Podemos volver a poner los NaNs con la adición de una tercera línea a esta solución: 'z [np.isnan (x) & np.isnan (y)] = np.NaN' –