Tengo una matriz MxN de valores tomados de un experimento. Algunos de estos valores no son válidos y se establecen en 0 para indicarlo. Puedo construir una máscara de valores válidos / inválidos usando

mask = (mat1 == 0) & (mat2 == 0)

Que produce una matriz MxN de bool. Debe tenerse en cuenta que las ubicaciones enmascaradas no siguen perfectamente las columnas o filas de la matriz, por lo que simplemente recortar la matriz no es una opción.

Ahora, quiero tomar la media a lo largo de un eje de mi matriz (por ejemplo, E.G termina con una matriz 1xN) mientras excluyo esos valores no válidos en el cálculo de la media. Intuitivamente pensé

 np.mean(mat1[mask],axis=1)

Debería hacerlo, pero la operación mat1[mask] produce una matriz 1D que parece ser solo los elementos donde mask es verdadero, lo que no ayuda cuando solo quiero una media en una dimensión de la matriz.

¿Hay alguna forma 'python-esque' o numpy para hacer esto? Supongo que podría usar la máscara para establecer elementos enmascarados en NaN y usar np.nanmean, pero eso todavía se siente un poco torpe. ¿Hay alguna manera de hacer esto 'limpiamente'?

4
fergu 16 oct. 2018 a las 22:55

2 respuestas

La mejor respuesta

Creo que la mejor manera de hacer esto sería algo como:

masked = np.ma.masked_where(mat1 == 0 && mat2 == 0, array_to_mask)

Luego toma la media con

masked.mean(axis=1)
1
lsterzinger 16 oct. 2018 a las 20:27

Una manera igualmente torpe pero eficiente es multiplicar su matriz con la máscara, estableciendo los valores enmascarados en cero. Entonces, por supuesto, tendrá que dividir entre la cantidad de valores no enmascarados manualmente. De ahí la torpeza. Pero esto funcionará con matrices de valores enteros, algo que no se puede decir sobre el caso nan. También parece ser más rápido para matrices pequeñas y grandes (incluida la solución de matriz enmascarada en otra respuesta):

import numpy as np

def nanny(mat, mask):
    mat = mat.astype(float).copy() # don't mutate the original
    mat[~mask] = np.nan            # mask values
    return np.nanmean(mat, axis=0) # compute mean

def manual(mat, mask):
    # zero masked values, divide by number of nonzeros
    return (mat*mask).sum(axis=0)/mask.sum(axis=0)

# set up dummy data for testing
N,M = 400,400
mat1 = np.random.randint(0,N,(N,M))
mask = np.random.randint(0,2,(N,M)).astype(bool)

print(np.array_equal(nanny(mat1, mask), manual(mat1, mask))) # True
1
Andras Deak 16 oct. 2018 a las 20:35