python làm thế nào để đệm mảng numpy với số không


97

Tôi muốn biết làm thế nào tôi có thể đệm một mảng số 2D với các số không bằng cách sử dụng python 2.6.6 với phiên bản numpy 1.5.0. Lấy làm tiếc! Nhưng đây là những hạn chế của tôi. Do đó tôi không thể sử dụng np.pad. Ví dụ, tôi muốn đệm abằng các số không sao cho hình dạng của nó khớp b. Lý do tại sao tôi muốn làm điều này là vì vậy tôi có thể làm:

b-a

như vậy mà

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

Cách duy nhất tôi có thể nghĩ đến để làm điều này là chữa khỏi, tuy nhiên điều này có vẻ khá xấu. có một giải pháp sạch hơn có thể sử dụng b.shape?

Chỉnh sửa, Cảm ơn câu trả lời của MSeiferts. Tôi đã phải dọn dẹp nó một chút, và đây là những gì tôi nhận được:

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

Câu trả lời:


155

Rất đơn giản, bạn tạo một mảng chứa các số không bằng cách sử dụng hình dạng tham chiếu:

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

và sau đó chèn mảng vào nơi bạn cần:

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

và thì bạn đã độn nó:

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

Bạn cũng có thể làm cho nó tổng quát hơn một chút nếu bạn xác định nơi phần tử phía trên bên trái của bạn sẽ được chèn

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

nhưng sau đó hãy cẩn thận rằng bạn không có hiệu số lớn hơn mức cho phép. Ví x_offset = 2dụ, điều này sẽ thất bại.


Nếu bạn có một số kích thước tùy ý, bạn có thể xác định danh sách các lát cắt để chèn mảng ban đầu. Tôi thấy thú vị khi chơi xung quanh một chút và tạo ra một hàm đệm có thể đệm (có bù đắp) một mảng hình trọng tài miễn là mảng và tham chiếu có cùng số kích thước và hiệu số không quá lớn.

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

Và một số trường hợp thử nghiệm:

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)

Chỉ cần để tóm tắt trường hợp tôi cần thiết: nếu chèn vào nguồn gốc, kích thước tùy ý:padded = np.zeros(b.shape) padded[tuple(slice(0,n) for n in a.shape)] = a
shaneb

163

NumPy 1.7.0 (khi numpy.padđược thêm vào) bây giờ đã khá cũ (nó được phát hành vào năm 2013) vì vậy mặc dù câu hỏi đặt ra cho một cách mà không sử dụng chức năng đó, tôi nghĩ rằng nó có thể hữu ích để biết cách có thể đạt được bằng cách sử dụng numpy.pad.

Nó thực sự khá đơn giản:

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

Trong trường hợp này, tôi đã sử dụng đó 0là giá trị mặc định cho mode='constant'. Nhưng nó cũng có thể được chỉ định bằng cách chuyển nó một cách rõ ràng:

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

Đề phòng trường hợp đối số thứ hai ( [(0, 1), (0, 1)]) có vẻ khó hiểu: Mỗi mục danh sách (trong trường hợp này là tuple) tương ứng với một thứ nguyên và mục trong đó đại diện cho phần đệm trước (phần tử đầu tiên) và sau (phần tử thứ hai). Vì thế:

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

Trong trường hợp này, phần đệm cho trục thứ nhất và trục thứ hai giống hệt nhau, vì vậy người ta cũng có thể chuyển qua bộ 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.]])

Trong trường hợp phần đệm trước và sau giống hệt nhau, người ta thậm chí có thể bỏ qua phần đệm (mặc dù không áp dụng trong trường hợp này):

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

Hoặc nếu phần đệm trước và sau giống hệt nhau nhưng khác với trục, bạn cũng có thể bỏ qua đối số thứ hai trong các bộ giá trị bên trong:

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

Tuy nhiên, tôi có xu hướng thích luôn sử dụng một cách rõ ràng, vì nó chỉ dễ mắc sai lầm (khi kỳ vọng của NumPys khác với ý định của bạn):

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

Ở đây NumPy nghĩ rằng bạn muốn đệm tất cả các trục bằng 1 phần tử trước và 2 phần tử sau mỗi trục! Ngay cả khi bạn dự định đệm với 1 phần tử trong trục 1 và 2 phần tử cho trục 2.

Tôi đã sử dụng danh sách các bộ giá trị cho phần đệm, lưu ý rằng đây chỉ là "quy ước của tôi", bạn cũng có thể sử dụng danh sách các danh sách hoặc bộ giá trị của bộ giá trị, hoặc thậm chí bộ giá trị của mảng. NumPy chỉ kiểm tra độ dài của đối số (hoặc nếu nó không có độ dài) và độ dài của từng mục (hoặc nếu nó có độ dài)!


4
Điều đó thực sự được giải thích rõ ràng. Tốt hơn nhiều so với tài liệu gốc. Cảm ơn.
M.Innat

mode='constant'là mặc định hợp lý, vì vậy có thể đạt được phần đệm bằng số 0 mà không cần bất kỳ từ khóa tùy chọn nào, dẫn đến mã dễ đọc hơn một chút.
phụ bản

làm cách nào để chỉ thêm phần đệm vào chiều thứ ba của mảng 3D numpy?
Ramsha Siddiqui

@RamshaSiddiqui, bạn có thể sử dụng số 0 cho các kích thước không nên độn.
MSeifert

9

Tôi hiểu rằng vấn đề chính của bạn là bạn cần tính toán d=b-anhưng các mảng của bạn có kích thước khác nhau. Không cần có đệm trung gianc

Bạn có thể giải quyết vấn đề này mà không cần đệm:

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

Đầu ra:

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

Đúng, đối với trường hợp cụ thể của anh ấy, anh ấy không nhất thiết phải pad nhưng đó là một trong số rất ít phép toán số học mà padding và cách tiếp cận của bạn là tương đương nhau. Tuy nhiên, câu trả lời hay!
MSeifert

1
Không chỉ thế. Điều này cũng có thể hiệu quả hơn bộ nhớ so với không đệm.
norok2

0

Trong trường hợp bạn cần thêm hàng rào 1s vào một mảng:

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

Tôi biết tôi hơi muộn với điều này, nhưng trong trường hợp bạn muốn thực hiện đệm tương đối (hay còn gọi là đệm cạnh), đây là cách bạn có thể thực hiện. Lưu ý rằng trường hợp đầu tiên của phép gán kết quả là không đệm, vì vậy bạn có thể sử dụng điều này cho cả đệm không và đệm tương đối (đây là nơi bạn sao chép các giá trị cạnh của mảng ban đầu vào mảng đệm).

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

Phân tích độ phức tạp:

Giải pháp tối ưu cho việc này là phương pháp độn của numpy. Sau khi tính trung bình cho 5 lần chạy, np.pad với phần đệm tương đối chỉ 8%tốt hơn chức năng được xác định ở trên. Điều này cho thấy đây là một phương pháp khá tối ưu cho những trường hợp đệm lót tương đối và bằng không.


#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%
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.