python cara pad array numpy dengan nol

97

Saya ingin tahu bagaimana saya dapat mengisi array numpy 2D dengan nol menggunakan python 2.6.6 dengan numpy versi 1.5.0. Maaf! Tetapi ini adalah batasan saya. Oleh karena itu saya tidak dapat menggunakan np.pad. Misalnya, saya ingin mengisi aangka nol sehingga bentuknya cocok b. Alasan mengapa saya ingin melakukan ini adalah agar saya dapat melakukan:

b-a

seperti yang

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

Satu-satunya cara yang dapat saya pikirkan untuk melakukan ini adalah menambahkan, namun ini tampaknya sangat jelek. apakah ada solusi yang lebih bersih yang mungkin digunakan b.shape?

Edit, Terima kasih atas jawaban MSeiferts. Saya harus membersihkannya sedikit, dan inilah yang saya dapatkan:

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
pengguna2015487
sumber

Jawaban:

155

Sangat sederhana, Anda membuat array yang berisi nol menggunakan bentuk referensi:

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

dan kemudian masukkan larik di mana Anda membutuhkannya:

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

dan voila Anda telah memasukkannya:

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

Anda juga dapat membuatnya sedikit lebih umum jika Anda menentukan di mana elemen kiri atas Anda harus dimasukkan

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

tetapi berhati-hatilah agar Anda tidak memiliki offset yang lebih besar dari yang diizinkan. Untuk x_offset = 2untuk contoh ini akan gagal.


Jika Anda memiliki jumlah dimensi yang ditentukan, Anda dapat menentukan daftar irisan untuk memasukkan larik asli. Saya merasa menarik untuk bermain-main sedikit dan membuat fungsi padding yang dapat mengisi (dengan offset) array berbentuk arbiter selama array dan referensi memiliki jumlah dimensi yang sama dan offset tidak terlalu besar.

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

Dan beberapa kasus uji:

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
sumber
Hanya untuk meringkas kasus yang saya butuhkan: jika memasukkan pada asalnya, dimensi sewenang-wenang:padded = np.zeros(b.shape) padded[tuple(slice(0,n) for n in a.shape)] = a
shaneb
163

NumPy 1.7.0 (saat numpy.padditambahkan) sudah cukup tua sekarang (dirilis pada 2013) jadi meskipun pertanyaan menanyakan cara tanpa menggunakan fungsi itu, saya pikir akan berguna untuk mengetahui bagaimana itu bisa dicapai dengan menggunakan numpy.pad.

Sebenarnya cukup sederhana:

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

Dalam hal ini yang saya gunakan 0adalah nilai default untuk mode='constant'. Tapi itu juga bisa ditentukan dengan meneruskannya secara eksplisit:

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

Untuk berjaga-jaga jika argumen kedua ( [(0, 1), (0, 1)]) tampak membingungkan: Setiap item daftar (dalam hal ini tupel) sesuai dengan dimensi dan item di dalamnya mewakili padding sebelum (elemen pertama) dan sesudah (elemen kedua). Begitu:

[(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.

Dalam hal ini padding untuk sumbu pertama dan kedua adalah identik, jadi seseorang juga bisa melewatkan 2-tuple:

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

Jika padding sebelum dan sesudah identik, seseorang bahkan dapat menghilangkan tuple (tidak berlaku dalam kasus ini):

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

Atau jika padding sebelum dan sesudah identik tetapi berbeda untuk sumbu, Anda juga dapat menghilangkan argumen kedua di tupel dalam:

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

Namun saya cenderung lebih suka untuk selalu menggunakan yang eksplisit, karena mudah untuk membuat kesalahan (ketika harapan NumPys berbeda dari niat Anda):

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

Di sini NumPy berpikir Anda ingin mengisi semua sumbu dengan 1 elemen sebelum dan 2 elemen setelah setiap sumbu! Bahkan jika Anda menginginkannya untuk diisi dengan 1 elemen dalam sumbu 1 dan 2 elemen untuk sumbu 2.

Saya menggunakan daftar tupel untuk padding, perhatikan bahwa ini hanya "konvensi saya", Anda juga dapat menggunakan daftar daftar atau tupel tupel, atau bahkan tupel array. NumPy hanya memeriksa panjang argumen (atau jika tidak memiliki panjang) dan panjang setiap item (atau jika memiliki panjang)!

MSeifert
sumber
4
Itu dijelaskan dengan sangat baik. Jauh lebih baik dari dokumentasi aslinya. Terima kasih.
M.Innat
mode='constant'adalah default yang masuk akal, jadi padding dengan nol dapat dicapai tanpa perlu kata kunci opsional apa pun, yang mengarah ke kode yang sedikit lebih mudah dibaca.
divenex
bagaimana cara menambahkan padding hanya ke dimensi ketiga dari array numpy 3D?
Ramsha Siddiqui
@RamshaSiddiqui Anda dapat menggunakan 0s untuk dimensi yang tidak boleh diisi.
MSeifert
9

Saya mengerti bahwa masalah utama Anda adalah Anda perlu menghitung d=b-atetapi array Anda memiliki ukuran yang berbeda. Tidak perlu bantalan perantarac

Anda dapat menyelesaikan ini tanpa padding:

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

Keluaran:

[[ 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
sumber
Benar, untuk kasus spesifiknya, dia tidak perlu pad tetapi itu salah satu dari sedikit operasi aritmatika di mana padding dan pendekatan Anda setara. Namun jawaban yang bagus!
MSeifert
1
Tidak hanya itu. Ini juga bisa lebih hemat memori daripada zero-padding.
norok2
0

Jika Anda perlu menambahkan pagar 1 ke 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
sumber
0

Saya tahu saya agak terlambat untuk ini, tetapi jika Anda ingin melakukan padding relatif (alias padding tepi), inilah cara Anda dapat menerapkannya. Perhatikan bahwa contoh pertama dari tugas menghasilkan bantalan-nol, sehingga Anda dapat menggunakan ini untuk bantalan-nol dan bantalan relatif (ini adalah tempat Anda menyalin nilai tepi dari larik asli ke dalam larik yang diisi).

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

Analisis Kompleksitas:

Solusi optimal untuk ini adalah metode bantalan numpy. Setelah rata-rata untuk 5 kali berjalan, np.pad dengan bantalan relatif hanya 8%lebih baik daripada fungsi yang ditentukan di atas. Ini menunjukkan bahwa ini adalah metode yang cukup optimal untuk bantalan relatif dan bantalan nol.


#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
sumber