python como preencher array numpy com zeros

99

Eu quero saber como posso preencher um array numpy 2D com zeros usando python 2.6.6 com numpy versão 1.5.0. Desculpa! Mas essas são minhas limitações. Portanto, não posso usar np.pad. Por exemplo, desejo preencher acom zeros de forma que sua forma corresponda b. O motivo pelo qual quero fazer isso é:

b-a

de tal modo 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]])

A única maneira que consigo pensar em fazer isso é anexando, no entanto, parece muito feio. existe uma solução mais limpa possivelmente usando b.shape?

Editar, obrigado à resposta de MSeiferts. Tive que limpar um pouco e foi isso que consegui:

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
user2015487
fonte

Respostas:

161

Muito simples, você cria uma matriz contendo zeros usando a forma de referência:

result = np.zeros(b.shape)
# actually you can also use result = np.zeros_like(b) 
# but that also copies the dtype not only the shape

e insira a matriz onde você precisa:

result[:a.shape[0],:a.shape[1]] = a

e voila você o acolchoou:

print(result)
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.]])

Você também pode torná-lo um pouco mais geral se definir onde o elemento superior esquerdo deve ser inserido

result = np.zeros_like(b)
x_offset = 1  # 0 would be what you wanted
y_offset = 1  # 0 in your case
result[x_offset:a.shape[0]+x_offset,y_offset:a.shape[1]+y_offset] = a
result

array([[ 0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  1.,  1.,  1.,  1.,  1.],
       [ 0.,  1.,  1.,  1.,  1.,  1.]])

mas tenha cuidado para não ter deslocamentos maiores do que o permitido. Por x_offset = 2exemplo, isso irá falhar.


Se você tiver um número arbitrário de dimensões, poderá definir uma lista de fatias para inserir a matriz original. Achei interessante brincar um pouco e criei uma função de preenchimento que pode preencher (com deslocamento) uma matriz de forma arbitrária, desde que a matriz e a referência tenham o mesmo número de dimensões e os deslocamentos não sejam muito grandes.

def pad(array, reference, offsets):
    """
    array: Array to be padded
    reference: Reference array with the desired shape
    offsets: list of offsets (number of elements must be equal to the dimension of the array)
    """
    # 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(offset[dim], offset[dim] + array.shape[dim]) for dim in range(a.ndim)]
    # Insert the array in the result at the specified offsets
    result[insertHere] = a
    return result

E alguns casos de teste:

import numpy as np

# 1 Dimension
a = np.ones(2)
b = np.ones(5)
offset = [3]
pad(a, b, offset)

# 3 Dimensions

a = np.ones((3,3,3))
b = np.ones((5,4,3))
offset = [1,0,0]
pad(a, b, offset)
MSeifert
fonte
Apenas para resumir o caso, eu precisava: se inserir na origem, dimensões arbitrárias:padded = np.zeros(b.shape) padded[tuple(slice(0,n) for n in a.shape)] = a
Shaneb
169

O NumPy 1.7.0 (quando numpy.padfoi adicionado) é muito antigo (foi lançado em 2013), então, embora a pergunta fosse sobre uma maneira de não usar essa função, pensei que seria útil saber como isso poderia ser feito usando numpy.pad.

Na verdade, é muito simples:

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

Neste caso, usei esse 0é o valor padrão para mode='constant'. Mas também pode ser especificado passando-o explicitamente:

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

Apenas no caso do segundo argumento ( [(0, 1), (0, 1)]) parecer confuso: Cada item da lista (neste caso tupla) corresponde a uma dimensão e o item nele representa o preenchimento antes (primeiro elemento) e depois (segundo elemento). Então:

[(0, 1), (0, 1)]
         ^^^^^^------ padding for second dimension
 ^^^^^^-------------- padding for first dimension

  ^------------------ no padding at the beginning of the first axis
     ^--------------- pad with one "value" at the end of the first axis.

Neste caso, o preenchimento para o primeiro e o segundo eixos são idênticos, portanto, também é possível apenas passar a 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.]])

No caso de o preenchimento antes e depois ser idêntico, pode-se até omitir a tupla (não aplicável neste 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.]])

Ou se o preenchimento antes e depois for idêntico, mas diferente para o eixo, você também pode omitir o segundo argumento nas 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.]])

No entanto, tendo a preferir sempre usar o explícito, porque é muito fácil cometer erros (quando as expectativas do NumPys diferem das suas intenções):

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

Aqui, NumPy pensa que você queria preencher todos os eixos com 1 elemento antes e 2 elementos depois de cada eixo! Mesmo que você pretenda preencher com 1 elemento no eixo 1 e 2 elementos no eixo 2.

Usei listas de tuplas para o preenchimento, note que isso é apenas "minha convenção", você também pode usar listas de listas ou tuplas de tuplas, ou mesmo tuplas de arrays. NumPy apenas verifica o comprimento do argumento (ou se não tem comprimento) e o comprimento de cada item (ou se tem comprimento)!

MSeifert
fonte
5
Isso está muito bem explicado. Muito melhor do que a documentação original. Obrigado.
M.Innat
mode='constant'é o padrão sensato, portanto, o preenchimento com zeros pode ser obtido sem a necessidade de qualquer palavra-chave opcional, levando a um código um pouco mais legível.
divenex de
como posso adicionar preenchimento apenas para a terceira dimensão de uma matriz numpy 3D?
Ramsha Siddiqui
@RamshaSiddiqui você pode usar 0s para as dimensões que não devem ser preenchidas.
MSeifert de
9

Eu entendo que seu principal problema é que você precisa calcular, d=b-amas seus arrays têm tamanhos diferentes. Não há necessidade de um preenchimento intermediárioc

Você pode resolver isso sem preenchimento:

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

Resultado:

[[ 2.  2.  2.  2.  2.  3.]
 [ 2.  2.  2.  2.  2.  3.]
 [ 2.  2.  2.  2.  2.  3.]
 [ 3.  3.  3.  3.  3.  3.]]
Juan Leni
fonte
É verdade que, para seu caso específico, ele não precisa necessariamente de preenchimento, mas essa é uma das poucas operações aritméticas em que o preenchimento e sua abordagem são equivalentes. No entanto, boa resposta!
MSeifert
1
Não apenas isso. Isso também pode ser mais eficiente em termos de memória do que o preenchimento de zero.
norok2
0

Caso você precise adicionar uma cerca de 1s a um array:

>>> 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]])
MSeifert
fonte
0

Eu sei que estou um pouco atrasado para isso, mas no caso de você querer executar preenchimento relativo (também conhecido como preenchimento de borda), veja como você pode implementá-lo. Observe que a primeira instância de atribuição resulta em preenchimento de zero, portanto, você pode usar isso para preenchimento de zero e preenchimento relativo (aqui é onde você copia os valores de borda da matriz original para a matriz preenchida).

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álise de complexidade:

A solução ideal para isso é o método pad numpy. Após calcular a média de 5 execuções, np.pad com preenchimento relativo só é 8%melhor do que a função definida acima. Isso mostra que este é um método ideal para preenchimento relativo e zero-padding.


#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%
Qasim Wani
fonte
0

O Tensorflow também implementou funções para redimensionar / preencher imagens tf.image.pad tf.pad .

padded_image = tf.image.pad_to_bounding_box(image, top_padding, left_padding, target_height, target_width)

padded_image = tf.pad(image, paddings, "CONSTANT")

Essas funções funcionam como outros recursos de pipeline de entrada do tensorflow e funcionarão muito melhor para aplicativos de aprendizado de máquina.

krenerd
fonte