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


96

Я хочу знать, как можно заполнить двумерный массив 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?

Edit, спасибо за ответ 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

162

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 и двумя элементами для оси 2.

Я использовал списки кортежей для заполнения, обратите внимание, что это просто «мое соглашение», вы также можете использовать списки списков или кортежи кортежей или даже кортежи массивов. NumPy просто проверяет длину аргумента (или если она не имеет длины) и длину каждого элемента (или если она имеет длину)!


4
Это действительно хорошо объяснено. Намного лучше, чем оригинальная документация. Спасибо.
M.Innat

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

как я могу добавить заполнение только к третьему измерению массива 3D 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

1
Не только это. Это также может быть более эффективным с точки зрения памяти, чем заполнение нулями.
norok2

0

Если вам нужно добавить в массив забор из единиц:

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