ตรวจสอบว่าอาร์เรย์ numpy มี 0 ในทุกเส้นขอบ [ปิด]


13

สิ่งที่จะเป็นวิธีที่เร็วที่สุดในการตรวจสอบว่าอาร์เรย์ที่มีหลายมิติมี 0 ในทุกด้าน

ดังนั้นสำหรับตัวอย่างง่ายๆ 2 มิติฉันมี:

x = np.random.rand(5, 5)
assert np.sum(x[0:,  0]) == 0
assert np.sum(x[0,  0:]) == 0
assert np.sum(x[0:, -1]) == 0
assert np.sum(x[-1, 0:]) == 0

แม้ว่านี่จะโอเคสำหรับกรณี 2 มิติไปทางขวาการเขียนสำหรับมิติที่สูงกว่านั้นค่อนข้างน่าเบื่อและฉันก็สงสัยว่ามีเคล็ดลับที่ฉลาดพอสมควรที่ฉันสามารถใช้ที่นี่เพื่อทำให้มันมีประสิทธิภาพและบำรุงรักษาได้มากกว่า


8
จะnp.all (x[:, 0] == 0)ปลอดภัยกว่ายอดรวมหรือไม่ การทดสอบผลรวมนั้นถูกต้องเฉพาะในกรณีที่จำนวนทั้งหมดเป็นบวก
Demi-Lune


1
@ Demi-Lume ทำให้รู้สึก ในกรณีของฉันทุกอย่างจะ> = 0 แต่ความคิดเห็นของคุณจะชื่นชม :)
Luca

1
ในกรณี 3 มิติคุณหมายถึงใบหน้า (มีหกหน้า) หรือขอบ (มี 12 ภาพ) ของลูกบาศก์หรือไม่
Riccardo Bucco

@RiccardoBucco ใช่แล้ว 6 หน้า แต่ปัญหาของฉันคือมันสามารถไปในมิติที่สูงกว่า 3
ลูกา

คำตอบ:


7

นี่คือวิธีที่คุณสามารถทำได้:

assert(all(np.all(np.take(x, index, axis=axis) == 0)
           for axis in range(x.ndim)
           for index in (0, -1)))

np.take ทำสิ่งเดียวกันกับการจัดทำดัชนี "แฟนซี"


1
@Luca: เอกสารไม่ชัดเจน แต่numpy.takeทำสำเนา สิ่งนี้อาจทำให้มันทำงานได้แย่กว่าโค้ดตามมุมมอง (การกำหนดเวลาเป็นสิ่งจำเป็นเพื่อให้แน่ใจว่า - ประสิทธิภาพการดู NumPy เป็นบางครั้งที่แปลก)
23,771,122 สนับสนุนโมนิก้า

1
@RiccardoBucco: สามารถเขียนได้มากขึ้นก็เป็นlen(x.shape) x.ndim
user2357112 รองรับ Monica

1
@ user2357112supportsMonica ขอบคุณผมคงมัน :)
คาร์โด้ Bucco

5
นอกจากนี้การใช้ความเข้าใจในรายการจะป้องกันไม่ให้เกิดallการลัดวงจร คุณสามารถลบวงเล็บใช้นิพจน์เครื่องกำเนิดไฟฟ้าที่ช่วยให้allจะกลับมาเร็วที่สุดเท่าที่เป็นหนึ่งเดียวโทรส่งกลับnumpy.all False
user2357112 รองรับ Monica

1
@ user2357112supportsMonica True !!
Riccardo Bucco

5

ต่อไปนี้เป็นคำตอบที่ตรวจสอบส่วนต่างๆของอาร์เรย์ที่คุณสนใจและไม่ต้องเสียเวลาในการสร้างรูปแบบขนาดของอาร์เรย์ทั้งหมด มีการวนรอบระดับ Python แต่สั้นโดยมีการวนซ้ำตามสัดส่วนของขนาดแทนที่จะเป็นขนาดของอาร์เรย์

def all_borders_zero(array):
    if not array.ndim:
        raise ValueError("0-dimensional arrays not supported")
    for dim in range(array.ndim):
        view = numpy.moveaxis(array, dim, 0)
        if not (view[0] == 0).all():
            return False
        if not (view[-1] == 0).all():
            return False
    return True

จะมีสถานการณ์ใด ๆ ที่not (view[0] == 0).all()จะไม่เทียบเท่ากับview[0].any()?
Paul Panzer

@PaulPanzer: ฉันคิดว่าview[0].any()จะทำงานด้วย ฉันไม่แน่ใจทั้งหมดเกี่ยวกับประสิทธิภาพของการหล่อและการบัฟเฟอร์ที่เกี่ยวข้องกับตัวเลือกสองตัว - ในview[0].any()ทางทฤษฎีสามารถใช้งานได้เร็วขึ้น แต่ฉันเคยเห็นผลลัพธ์แปลก ๆ มาก่อนและฉันไม่เข้าใจการบัฟเฟอร์อย่างเต็มที่
user2357112 รองรับ Monica

ฉันคิดว่าview[0].view(bool).any()น่าจะเป็นทางออกความเร็วสูง
Paul Panzer

@PaulPanzer: argmaxจริงๆแล้วอาจจะชนะanyมากกว่ามุมมองแบบบูล สิ่งนี้ดูแปลก ๆ
user2357112 รองรับ Monica

( argmaxหรือไม่หรือการanyใช้มุมมองบูลีนหมายถึงการจัดการศูนย์ลบว่าไม่เท่ากับศูนย์ปกติ)
user2357112 สนับสนุน Monica

2

ฉันเปลี่ยนรูปร่างอาร์เรย์แล้วทำซ้ำผ่านมัน น่าเสียดายที่คำตอบของฉันถือว่าคุณมีสามมิติอย่างน้อยและจะผิดพลาดสำหรับเมทริกซ์ปกติคุณจะต้องเพิ่มประโยคพิเศษสำหรับ 1 & 2 อาร์เรย์ที่มีรูปร่างมิติ นอกจากนี้สิ่งนี้จะช้าดังนั้นจึงน่าจะมีทางออกที่ดีกว่า

x = np.array(
        [
            [
                [0 , 1, 1, 0],
                [0 , 2, 3, 0],
                [0 , 4, 5, 0]
            ],
            [
                [0 , 6, 7, 0],
                [0 , 7, 8, 0],
                [0 , 9, 5, 0]
            ]
        ])

xx = np.array(
        [
            [
                [0 , 0, 0, 0],
                [0 , 2, 3, 0],
                [0 , 0, 0, 0]
            ],
            [
                [0 , 0, 0, 0],
                [0 , 7, 8, 0],
                [0 , 0, 0, 0]
            ]
        ])

def check_edges(x):

    idx = x.shape
    chunk = np.prod(idx[:-2])
    x = x.reshape((chunk*idx[-2], idx[-1]))
    for block in range(chunk):
        z = x[block*idx[-2]:(block+1)*idx[-2], :]
        if not np.all(z[:, 0] == 0):
            return False
        if not np.all(z[:, -1] == 0):
            return False
        if not np.all(z[0, :] == 0):
            return False
        if not np.all(z[-1, :] == 0):
            return False

    return True

ซึ่งจะผลิต

>>> False
>>> True

โดยทั่วไปฉันสแต็คมิติทั้งหมดที่ด้านบนของกันและกันแล้วมองผ่านพวกเขาเพื่อตรวจสอบขอบของพวกเขา


สิ่งนี้จะตรวจสอบส่วนที่ไม่ถูกต้องของอาร์เรย์ สำหรับอาเรย์สามมิตินั้นเราต้องการตรวจสอบใบหน้าของอาเรย์ทั้งหมดไม่ใช่ที่ขอบของแต่ละซับเรย์แบบสองมิติ
user2357112 รองรับ Monica

อ้านั่นเหมาะสมกว่า ฉันเข้าใจผิด
lwileczek

1

บางทีโอเปอเรเตอร์จุดไข่ปลาคือสิ่งที่คุณกำลังมองหาซึ่งจะใช้ได้กับหลายมิติ:

import numpy as np

# data
x = np.random.rand(2, 5, 5)
x[..., 0:, 0] = 0
x[..., 0, 0:] = 0
x[..., 0:, -1] = 0
x[..., -1, 0:] = 0

test = np.all(
    [
        np.all(x[..., 0:, 0] == 0),
        np.all(x[..., 0, 0:] == 0),
        np.all(x[..., 0:, -1] == 0),
        np.all(x[..., -1, 0:] == 0),
    ]
)

print(test)

สิ่งนี้จะไม่ทำให้สีใบหน้าทั้งหมด ตัวอย่างเช่นลองกับลูกบาศก์ (4, 4, 4)
Luca

ฉันไม่แน่ใจว่าคุณหมายถึงอะไรด้วยการระบายสีใบหน้า แต่มันจะใช้ได้ผลถ้าคุณสร้าง x (4, 4, 4)
daveg

1

คุณสามารถใช้sliceและกำบังแบบบูลเพื่อให้งานสำเร็จ:

def get_borders(arr):
    s=tuple(slice(1,i-1) for i in a.shape)
    mask = np.ones(arr.shape, dtype=bool)
    mask[s] = False
    return(arr[mask])

ฟังก์ชั่นนี้จะสร้าง "แกนกลาง" ของอาร์เรย์เป็นอันดับsแรกจากนั้นสร้างหน้ากากที่แสดงTrueเฉพาะจุดที่มีขอบ การจัดทำดัชนีบูลีนจะส่งมอบจุดขอบ

ตัวอย่างการทำงาน:

a = np.arange(16).reshape((4,4))

print(a)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

borders = get_borders(a)
print(borders)
array([ 0,  1,  2,  3,  4,  7,  8, 11, 12, 13, 14, 15])

จากนั้นnp.all(borders==0)จะให้ข้อมูลที่ต้องการ


หมายเหตุ:ตัวแบ่งสำหรับอาร์เรย์หนึ่งมิติ แต่ฉันพิจารณากรณีขอบเหล่านั้น คุณน่าจะดีกว่าแค่ตรวจสอบจุดสองจุดที่ตรงนั้น


การทำเช่นนี้จะใช้เวลาเป็นสัดส่วนกับจำนวนองค์ประกอบทั้งหมดในอาร์เรย์แทนที่จะเป็นแค่เส้นขอบ นอกจากนี้อาร์เรย์หนึ่งมิติไม่ใช่กรณีขอบที่ไม่เกี่ยวข้อง
user2357112 รองรับ Monica

1
นอกจากนี้np.arange(15)ไม่รวม 15.
user2357112 รองรับ Monica

ฉันยอมรับว่า "ไม่เกี่ยวข้อง" เป็นถ้อยคำที่แข็งแกร่ง แต่ฉันรู้สึกว่าคุณดีกว่าแค่ตรวจสอบทั้งสองประเด็นเกี่ยวกับอาร์เรย์ 1d 15 คือการพิมพ์ผิดจับที่ดี
Lukas Thaler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.