python วิธีการจัดเรียงอาร์เรย์ numpy ด้วยศูนย์


96

ฉันต้องการทราบว่าฉันสามารถวางอาร์เรย์ตัวเลข 2D ด้วยศูนย์โดยใช้ 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

และ voila ที่คุณใส่ไว้:

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 องค์ประกอบในแกน 1 และ 2 องค์ประกอบสำหรับแกน 2

ฉันใช้รายการสิ่งที่เพิ่มขึ้นสำหรับช่องว่างภายในโปรดทราบว่านี่เป็นเพียง "การประชุมของฉัน" คุณยังสามารถใช้รายการของรายการหรือสิ่งที่เพิ่มขึ้นของสิ่งที่เพิ่มขึ้นหรือแม้แต่สิ่งที่เพิ่มขึ้นของอาร์เรย์ NumPy เพียงตรวจสอบความยาวของอาร์กิวเมนต์ (หรือถ้าไม่มีความยาว) และความยาวของแต่ละรายการ (หรือถ้ามีความยาว)!


4
นั่นอธิบายได้ดีจริงๆ ดีกว่าเอกสารต้นฉบับมาก ขอบคุณ.
นาฎ

mode='constant'เป็นค่าเริ่มต้นที่เหมาะสมดังนั้นการเว้นช่องว่างด้วยศูนย์จึงสามารถทำได้โดยไม่ต้องใช้คีย์เวิร์ดที่เป็นตัวเลือกใด ๆ ซึ่งจะนำไปสู่โค้ดที่อ่านได้ง่ายขึ้นเล็กน้อย
divenex

ฉันจะเพิ่มช่องว่างภายในเฉพาะมิติที่สามของอาร์เรย์ตัวเลข 3 มิติได้อย่างไร
Ramsha Siddiqui

@RamshaSiddiqui คุณสามารถใช้ 0s สำหรับขนาดที่ไม่ควรเบาะ
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

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

ฉันรู้ว่าฉันมาช้าไปหน่อย แต่ในกรณีที่คุณต้องการใช้ช่องว่างภายในแบบสัมพัทธ์ (aka edge padding) นี่คือวิธีที่คุณสามารถนำไปใช้ได้ โปรดทราบว่าอินสแตนซ์แรกของการกำหนดส่งผลให้เกิดการเว้นช่องว่างเป็นศูนย์ดังนั้นคุณสามารถใช้สิ่งนี้สำหรับทั้งการเว้นช่องว่างแบบศูนย์และช่องว่างแบบสัมพัทธ์ (นี่คือที่ที่คุณคัดลอกค่าขอบของอาร์เรย์เดิมลงในอาร์เรย์ที่มีเบาะ)

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 หลังจากค่าเฉลี่ยสำหรับการรัน 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%
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.