Cómo evaluar la sum de valores dentro de bloques de matriz

Tengo matriz de datos, con forma 100×100. Quiero dividirlo en bloques de 5×5, y cada bloque tiene cuadrículas de 20×20. El valor de cada bloque que quiero es la sum de todos los valores en él.

¿Hay una manera más elegante de lograrlo?

x = np.arange(100) y = np.arange(100) X, Y = np.meshgrid(x, y) Z = np.cos(X)*np.sin(Y) Z_new = np.zeros((5, 5)) for i in range(5): for j in range(5): Z_new[i, j] = np.sum(Z[i*20:20+i*20, j*20:20+j*20]) 

Esto se basa en el índice, ¿cómo si se basa en x?

 x = np.linspace(0, 1, 100) y = np.linspace(0, 1, 100) X, Y = np.meshgrid(x, y) Z = np.cos(X)*np.sin(Y) x_new = np.linspace(0, 1, 15) y_new = np.linspace(0, 1, 15) 

Z_new?

Simplemente reshape dividiendo cada uno de esos dos ejes en dos cada uno con forma (5,20) para formar una matriz 4D y luego sum la sum a lo largo de los ejes que tienen las longitudes 20 , como así:

 Z_new = Z.reshape(5,20,5,20).sum(axis=(1,3)) 

Funcionalmente la misma opción, pero potencialmente más rápida con np.einsum

 Z_new = np.einsum('ijkl->ik',Z.reshape(5,20,5,20)) 

Tamaño de bloque genérico

Extendiéndose a un caso genérico

 H,W = 5,5 # block-size m,n = Z.shape Z_new = Z.reshape(H,m//H,W,n//W).sum(axis=(1,3)) 

Con einsum que se convierte en …

 Z_new = np.einsum('ijkl->ik',Z.reshape(H,m//H,W,n//W)) 

Para calcular el promedio / promedio entre bloques, use el método de la mean lugar de la sum .

Operación de reducción y tamaño de bloque genérico.

Extendiéndose para usar operaciones de reduction que tienen ufuncs admiten el parámetro de axes múltiples con axis para reducciones, sería –

 def blockwise_reduction(a, height, width, reduction_func=np.sum): m,n = a.shape a4D = a.reshape(height,m//height,width,n//width) return reduction_func(a4D,axis=(1,3)) 

Así, para resolver nuestro caso específico, sería:

 blockwise_reduction(Z, height=5, width=5) 

y para una computación promedio en bloques, sería –

 blockwise_reduction(Z, height=5, width=5, reduction_func=np.mean) 

Puedes hacer lo siguiente.

 t = np.eye(5).repeat(20, axis=1) Z_new = t.dot(Z).dot(tT) 

Esto es correcto porque Z_new[i, j] = t[i, k] * Z[k, l] * t[j, l]

También esto parece más rápido que la solución de Divakar.

Tal problema es un muy buen candidato para una función como scipy.ndimage.measurements.sum, ya que permite los términos de “agrupación” y “etiquetado”. Tendrás lo que quieras con algo como:

 labels = [[20*(y//5) + x//5 for x in range(100)] for y in range(100)] s = scipy.ndimage.measurements.sum(Z, labels, range(400)) 

(No probado, pero esa es la idea).