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).