python, як заповнити масив numpy нулями


97

Я хочу знати, як я можу заповнити двовимірний масив numpy нулями за допомогою python 2.6.6 з numpy версії 1.5.0. Вибачте! Але це мої обмеження. Тому я не можу користуватися np.pad. Наприклад, я хочу заповнити aнулями так, щоб його форма збігалася b. Причина, по якій я хочу це зробити, полягає в тому, що я можу зробити:

b-a

такий, що

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

Єдиний спосіб, яким я можу подумати, що це зробити - це додавання, проте це здається досить потворним. чи існує чистіше рішення, можливо, використовуючи b.shape?

Редагувати, дякую відповіді MSeiferts. Мені довелося його трохи прибрати, і ось що я отримав:

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

Відповіді:


155

Дуже просто, ви створюєте масив, що містить нулі, використовуючи посилальну форму:

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

а потім вставте масив туди, де вам це потрібно:

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

і вуаля ви його добили:

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

Ви також можете зробити це трохи загальнішим, якщо визначите, куди слід вставити ваш верхній лівий елемент

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

але тоді будьте обережні, щоб у вас не було зсувів більших, ніж дозволено. Для x_offset = 2, наприклад , це буде провал.


Якщо у вас є довільна кількість розмірів, ви можете визначити список фрагментів, щоб вставити вихідний масив. Мені було цікаво трохи погратись і створив функцію заповнення, яка може заповнювати (зі зміщенням) масив довільної форми, якщо масив і посилання мають однакову кількість розмірів, а зміщення не надто великі.

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

І кілька тестових випадків:

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)

Просто, щоб підсумувати випадок, який мені потрібен: якщо вставляти в початковій точці, довільні розміри:padded = np.zeros(b.shape) padded[tuple(slice(0,n) for n in a.shape)] = a
shaneb

163

NumPy 1.7.0 (коли numpy.padбув доданий) зараз досить старий (він був випущений в 2013 році), тому, незважаючи на те, що питання задавав спосіб без використання цієї функції, я вважав, що було б корисно знати, як цього можна досягти за допомогою numpy.pad.

Насправді це досить просто:

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

У цьому випадку я використав 0це значення за замовчуванням для mode='constant'. Але це також можна вказати, передавши його явно:

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

На всякий випадок, якщо другий аргумент ( [(0, 1), (0, 1)]) здається заплутаним: кожному елементу списку (в даному випадку кортеж) відповідає вимір, а елемент у ньому представляє відступ перед (першим елементом) і після (другим елементом). Тому:

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

У цьому випадку відступ для першої та другої осі ідентичний, тому можна також просто передати 2-кортеж:

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

У випадку, якщо відступ до і після ідентичний, можна навіть опустити кортеж (не застосовується в цьому випадку):

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

Або якщо заповнення до і після ідентичне, але різне для осі, ви також можете опустити другий аргумент у внутрішніх кортежах:

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

Однак я, як правило, вважаю за краще завжди використовувати явний, оскільки просто зробити помилки легко (коли очікування NumPys відрізняються від ваших намірів):

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

Тут NumPy вважає, що ви хотіли заповнити всю вісь 1 елементом до і 2 елементами після кожної осі! Навіть якщо ви передбачали, щоб він наклав 1 елемент на вісь 1 та 2 елементи на вісь 2.

Я використовував списки кортежів для заповнення, зауважте, що це просто "моя умова", ви також можете використовувати списки списків або кортежів кортежів, або навіть кортежів масивів. NumPy просто перевіряє довжину аргументу (або якщо він не має довжини) і довжину кожного елемента (або якщо він має довжину)!


4
Це дійсно добре пояснено. Набагато краще, ніж оригінальна документація. Дякую.
M.Innat

mode='constant'є розумним за замовчуванням, тому заповнення нулями може бути досягнуте без необхідності будь-якого додаткового ключового слова, що призведе до дещо більш читабельного коду.
divenex

як я можу додати відступ лише до третього виміру тривимірного масиву numpy?
Рамша Сіддікі,

@RamshaSiddiqui ви можете використовувати 0 для розмірів, які не слід заповнювати.
MSeifert,

9

Я розумію, що ваша основна проблема полягає в тому, що вам потрібно обчислити, d=b-aале ваші масиви мають різні розміри. Немає необхідності в проміжних прокладкахc

Ви можете вирішити це без заповнення:

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

Вихід:

[[ 2.  2.  2.  2.  2.  3.]
 [ 2.  2.  2.  2.  2.  3.]
 [ 2.  2.  2.  2.  2.  3.]
 [ 3.  3.  3.  3.  3.  3.]]

Правда, для його конкретного випадку йому не обов’язково потрібно вкладати вкладиші, але це одна з небагатьох арифметичних операцій, де відступ та ваш підхід рівнозначні. Тим не менше приємна відповідь!
MSeifert 02.03.16

1
Не лише це. Це також може бути більш ефективним, ніж заповнення нулем.
norok2

0

Якщо вам потрібно додати до масиву паркан 1s:

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

0

Я знаю, що я трохи запізнився з цим, але якщо ви хочете виконати відносне заповнення (відоме як заповнення краю), ось як ви можете це реалізувати. Зверніть увагу, що найперший примірник призначення призводить до нульового заповнення, тому ви можете використовувати це як для нульового, так і для відносного заповнення (саме тут ви копіюєте крайові значення вихідного масиву в заповнений масив).

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

Аналіз складності:

Оптимальним рішенням для цього є метод numpy's pad. Після усереднення за 5 запусків np.pad із відносним заповненням є лише 8%кращим за функцію, визначену вище. Це показує, що це досить оптимальний метод відносного та нульового заповнення.


#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%
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.