Quiero saber cómo puedo rellenar una matriz numpy 2D con ceros usando python 2.6.6 con la versión numpy 1.5.0. ¡Lo siento! Pero estas son mis limitaciones. Por lo tanto, no puedo usar np.pad
. Por ejemplo, quiero rellenar a
con ceros para que su forma coincida b
. La razón por la que quiero hacer esto es para poder hacer:
b-a
tal que
>>> a
array([[ 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1.],
[ 1., 1., 1., 1., 1.]])
>>> b
array([[ 3., 3., 3., 3., 3., 3.],
[ 3., 3., 3., 3., 3., 3.],
[ 3., 3., 3., 3., 3., 3.],
[ 3., 3., 3., 3., 3., 3.]])
>>> c
array([[1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0]])
La única forma en que puedo pensar en hacer esto es agregando, sin embargo, esto parece bastante feo. ¿Existe una solución más limpia que posiblemente esté usando b.shape
?
Editar, gracias a la respuesta de MSeiferts. Tuve que limpiarlo un poco, y esto es lo que obtuve:
def pad(array, reference_shape, offsets):
"""
array: Array to be padded
reference_shape: tuple of size of ndarray to create
offsets: list of offsets (number of elements must be equal to the dimension of the array)
will throw a ValueError if offsets is too big and the reference_shape cannot handle the offsets
"""
# Create an array of zeros with the reference shape
result = np.zeros(reference_shape)
# Create a list of slices from offset to offset + shape in each dimension
insertHere = [slice(offsets[dim], offsets[dim] + array.shape[dim]) for dim in range(array.ndim)]
# Insert the array in the result at the specified offsets
result[insertHere] = array
return result
padded = np.zeros(b.shape)
padded[tuple(slice(0,n) for n in a.shape)] = a
NumPy 1.7.0 (cuando
numpy.pad
se agregó) es bastante antiguo ahora (se lanzó en 2013), así que aunque la pregunta pedía una forma sin usar esa función, pensé que podría ser útil saber cómo se podría lograr usandonumpy.pad
.En realidad, es bastante simple:
>>> import numpy as np >>> a = np.array([[ 1., 1., 1., 1., 1.], ... [ 1., 1., 1., 1., 1.], ... [ 1., 1., 1., 1., 1.]]) >>> np.pad(a, [(0, 1), (0, 1)], mode='constant') array([[ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0.]])
En este caso utilicé que
0
es el valor predeterminado paramode='constant'
. Pero también podría especificarse pasándolo explícitamente:>>> np.pad(a, [(0, 1), (0, 1)], mode='constant', constant_values=0) array([[ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0.]])
En caso de que el segundo argumento (
[(0, 1), (0, 1)]
) parezca confuso: cada elemento de la lista (en este caso tupla) corresponde a una dimensión y el elemento en el mismo representa el relleno antes (primer elemento) y después (segundo elemento). Entonces:En este caso, el relleno para el primer y segundo eje es idéntico, por lo que también se podría pasar en la 2-tupla:
>>> np.pad(a, (0, 1), mode='constant') array([[ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0.]])
En caso de que el relleno antes y después sea idéntico, se podría incluso omitir la tupla (aunque no es aplicable en este caso):
>>> np.pad(a, 1, mode='constant') array([[ 0., 0., 0., 0., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0.], [ 0., 1., 1., 1., 1., 1., 0.], [ 0., 1., 1., 1., 1., 1., 0.], [ 0., 0., 0., 0., 0., 0., 0.]])
O si el relleno antes y después es idéntico pero diferente para el eje, también puede omitir el segundo argumento en las tuplas internas:
>>> np.pad(a, [(1, ), (2, )], mode='constant') array([[ 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
Sin embargo, tiendo a preferir siempre usar el explícito, porque es demasiado fácil cometer errores (cuando las expectativas de NumPys difieren de sus intenciones):
>>> np.pad(a, [1, 2], mode='constant') array([[ 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 1., 1., 1., 1., 1., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0.]])
¡Aquí NumPy cree que querías rellenar todos los ejes con 1 elemento antes y 2 elementos después de cada eje! Incluso si tenía la intención de rellenar con 1 elemento en el eje 1 y 2 elementos para el eje 2.
Usé listas de tuplas para el relleno, tenga en cuenta que esto es solo "mi convención", también podría usar listas de listas o tuplas de tuplas, o incluso tuplas de matrices. NumPy solo verifica la longitud del argumento (o si no tiene una longitud) y la longitud de cada elemento (o si tiene una longitud).
fuente
mode='constant'
es el valor predeterminado sensato, por lo que el relleno con ceros se puede lograr sin la necesidad de ninguna palabra clave opcional, lo que lleva a un código un poco más legible.Entiendo que su principal problema es que necesita calcular,
d=b-a
pero sus matrices tienen diferentes tamaños. No es necesario un acolchado intermedioc
Puede resolver esto sin relleno:
import numpy as np a = np.array([[ 1., 1., 1., 1., 1.], [ 1., 1., 1., 1., 1.], [ 1., 1., 1., 1., 1.]]) b = np.array([[ 3., 3., 3., 3., 3., 3.], [ 3., 3., 3., 3., 3., 3.], [ 3., 3., 3., 3., 3., 3.], [ 3., 3., 3., 3., 3., 3.]]) d = b.copy() d[:a.shape[0],:a.shape[1]] -= a print d
Salida:
[[ 2. 2. 2. 2. 2. 3.] [ 2. 2. 2. 2. 2. 3.] [ 2. 2. 2. 2. 2. 3.] [ 3. 3. 3. 3. 3. 3.]]
fuente
En caso de que necesite agregar una cerca de 1 a una matriz:
>>> mat = np.zeros((4,4), np.int32) >>> mat array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]) >>> mat[0,:] = mat[:,0] = mat[:,-1] = mat[-1,:] = 1 >>> mat array([[1, 1, 1, 1], [1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 1, 1]])
fuente
Sé que llego un poco tarde a esto, pero en caso de que desee realizar un relleno relativo (también conocido como relleno de borde), así es como puede implementarlo. Tenga en cuenta que la primera instancia de asignación da como resultado un relleno de ceros, por lo que puede usar esto tanto para el relleno de ceros como para el relleno relativo (aquí es donde copia los valores de los bordes de la matriz original en la matriz de relleno).
def replicate_padding(arr): """Perform replicate padding on a numpy array.""" new_pad_shape = tuple(np.array(arr.shape) + 2) # 2 indicates the width + height to change, a (512, 512) image --> (514, 514) padded image. padded_array = np.zeros(new_pad_shape) #create an array of zeros with new dimensions # perform replication padded_array[1:-1,1:-1] = arr # result will be zero-pad padded_array[0,1:-1] = arr[0] # perform edge pad for top row padded_array[-1, 1:-1] = arr[-1] # edge pad for bottom row padded_array.T[0, 1:-1] = arr.T[0] # edge pad for first column padded_array.T[-1, 1:-1] = arr.T[-1] # edge pad for last column #at this point, all values except for the 4 corners should have been replicated padded_array[0][0] = arr[0][0] # top left corner padded_array[-1][0] = arr[-1][0] # bottom left corner padded_array[0][-1] = arr[0][-1] # top right corner padded_array[-1][-1] = arr[-1][-1] # bottom right corner return padded_array
Análisis de complejidad:
La solución óptima para esto es el método de almohadilla de numpy. Después de promediar 5 ejecuciones, np.pad con relleno relativo solo es
8%
mejor que la función definida anteriormente. Esto muestra que este es un método bastante óptimo para el relleno relativo y de relleno de ceros.#My method, replicate_padding start = time.time() padded = replicate_padding(input_image) end = time.time() delta0 = end - start #np.pad with edge padding start = time.time() padded = np.pad(input_image, 1, mode='edge') end = time.time() delta = end - start print(delta0) # np Output: 0.0008790493011474609 print(delta) # My Output: 0.0008130073547363281 print(100*((delta0-delta)/delta)) # Percent difference: 8.12316715542522%
fuente