Tengo 2 matrices 2D. Estoy tratando de convolucionar a lo largo del eje 1. np.convolve no proporciona el argumento del axis . La respuesta aquí , convoluciona 1 matriz 2D con una matriz 1D usando np.apply_along_axis . Pero no se puede aplicar directamente a mi caso de uso. La pregunta aquí no tiene respuesta.
MWE es el siguiente.
import numpy as np a = np.random.randint(0, 5, (2, 5)) """ a= array([[4, 2, 0, 4, 3], [2, 2, 2, 3, 1]]) """ b = np.random.randint(0, 5, (2, 2)) """ b= array([[4, 3], [4, 0]]) """ # What I want c = np.convolve(a, b, axis=1) # axis is not supported as an argument """ c= array([[16, 20, 6, 16, 24, 9], [ 8, 8, 8, 12, 4, 0]]) """ Sé que puedo hacerlo usando np.fft.fft , pero parece un paso innecesario para hacer algo simple. ¿Hay una manera simple de hacer esto? Gracias.
¿Por qué no simplemente hacer una lista de comprensión con zip ?
>>> np.array([np.convolve(x, y) for x, y in zip(a, b)]) array([[16, 20, 6, 16, 24, 9], [ 8, 8, 8, 12, 4, 0]]) >>> O con scipy.signal.convolve2d :
>>> from scipy.signal import convolve2d >>> convolve2d(a, b)[[0, 2]] array([[16, 20, 6, 16, 24, 9], [ 8, 8, 8, 12, 4, 0]]) >>>Una posibilidad podría ser ir manualmente al espectro de Fourier y volver:
n = np.max([a.shape, b.shape]) + 1 np.abs(np.fft.ifft(np.fft.fft(a, n=n) * np.fft.fft(b, n=n))).astype(int) # array([[16, 20, 6, 16, 24, 9], # [ 8, 8, 8, 12, 4, 0]])¿Se consideraría demasiado feo recorrer la dimensión ortogonal? Eso no agregaría muchos gastos generales a menos que la dimensión principal sea muy corta. Crear la matriz de salida antes de tiempo garantiza que no sea necesario copiar la memoria.
def convolvesecond(a, b): N1, L1 = a.shape N2, L2 = b.shape if N1 != N2: raise ValueError("Not compatible") c = np.zeros((N1, L1 + L2 - 1), dtype=a.dtype) for n in range(N1): c[n,:] = np.convolve(a[n,:], b[n,:], 'full') return cPara el caso genérico (convolución a lo largo del eje k-ésimo de un par de matrices multidimensionales), recurriría a un par de funciones auxiliares que siempre tengo a mano para convertir problemas multidimensionales al caso básico 2d:
def semiflatten(x, d=0): '''SEMIFLATTEN - Permute and reshape an array to convenient matrix form y, s = SEMIFLATTEN(x, d) permutes and reshapes the arbitrary array X so that input dimension D (default: 0) becomes the second dimension of the output, and all other dimensions (if any) are combined into the first dimension of the output. The output is always 2-D, even if the input is only 1-D. If D<0, dimensions are counted from the end. Return value S can be used to invert the operation using SEMIUNFLATTEN. This is useful to facilitate looping over arrays with unknown shape.''' x = np.array(x) shp = x.shape ndims = x.ndim if d<0: d = ndims + d perm = list(range(ndims)) perm.pop(d) perm.append(d) y = np.transpose(x, perm) # Y has the original D-th axis last, preceded by the other axes, in order rest = np.array(shp, int)[perm[:-1]] y = np.reshape(y, [np.prod(rest), y.shape[-1]]) return y, (d, rest) def semiunflatten(y, s): '''SEMIUNFLATTEN - Reverse the operation of SEMIFLATTEN x = SEMIUNFLATTEN(y, s), where Y, S are as returned from SEMIFLATTEN, reverses the reshaping and permutation.''' d, rest = s x = np.reshape(y, np.append(rest, y.shape[-1])) perm = list(range(x.ndim)) perm.pop() perm.insert(d, x.ndim-1) x = np.transpose(x, perm) return x (Tenga en cuenta que reshape y transpose no crean copias, por lo que estas funciones son extremadamente rápidas).
Con esos, la forma genérica se puede escribir como:
def convolvealong(a, b, axis=-1): a, S1 = semiflatten(a, axis) b, S2 = semiflatten(b, axis) c = convolvesecond(a, b) return semiunflatten(c, S1)