kiểm tra xem một mảng numpy có 0 trên tất cả các viền của nó không [đóng]


13

Điều gì sẽ là cách nhanh nhất để kiểm tra nếu một mảng numpy đa chiều có 0 ở tất cả các phía.

Vì vậy, đối với một ví dụ 2D đơn giản, tôi có:

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

Mặc dù điều này là ổn đối với các trường hợp 2D ở bên phải, nhưng viết cho kích thước cao hơn thì hơi tẻ nhạt và tôi đã tự hỏi liệu có một mẹo khéo léo thông minh nào tôi có thể sử dụng ở đây để làm cho nó hiệu quả và cũng dễ bảo trì hơn.


8
Sẽ không np.all (x[:, 0] == 0)an toàn hơn tổng? Kiểm tra tổng chỉ đúng nếu tất cả các số đều dương.
Demi-Lune


1
@ Demi-Lume Làm cho ý nghĩa. Trong trường hợp của tôi, mọi thứ sẽ> = 0 nhưng nhận xét của bạn được đánh giá cao :)
Luca

1
Trong trường hợp 3D, bạn có nghĩa là khuôn mặt (có sáu trong số chúng) hoặc các cạnh (có 12 trong số chúng) của khối lập phương?
Riccardo Bucco

@RiccardoBucco Vâng, 6 khuôn mặt. nhưng vấn đề của tôi là nó có thể tăng kích thước cao hơn 3.
Luca

Câu trả lời:


7

Đây là cách bạn có thể làm điều đó:

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

np.take thực hiện điều tương tự như lập chỉ mục "ưa thích".


1
@Luca: Tài liệu không làm cho nó rõ ràng, nhưng numpy.taketạo một bản sao. Điều này có thể khiến nó hoạt động kém hơn mã dựa trên chế độ xem. (Thời gian sẽ là cần thiết để đảm bảo - Hiệu quả của chế độ xem NumPy đôi khi rất kỳ lạ.)
user2357112 hỗ trợ Monica

1
@RiccardoBucco: len(x.shape)có thể được viết đơn giản hơn như x.ndim.
user2357112 hỗ trợ Monica

1
@ user2357112supportsMonica cảm ơn, tôi đã sửa nó :)
Riccardo Bucco

5
Ngoài ra, việc sử dụng một sự hiểu biết danh sách ngăn ngừa allngắn mạch. Bạn có thể xóa dấu ngoặc để sử dụng biểu thức trình tạo, cho phép alltrả về ngay khi một numpy.allcuộc gọi trở lại False.
user2357112 hỗ trợ Monica

1
@ user2357112supportsMonica Đúng !!
Riccardo Bucco

5

Đây là một câu trả lời thực sự kiểm tra các phần của mảng mà bạn quan tâm và không lãng phí thời gian để xây dựng một mặt nạ kích thước của toàn bộ mảng. Có một vòng lặp cấp Python, nhưng nó ngắn, với số lần lặp tỷ lệ với số lượng kích thước thay vì kích thước của mảng.

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

Có trường hợp nào not (view[0] == 0).all()không tương đương view[0].any()?
Paul Panzer

@PaulPanzer: Tôi cho rằng view[0].any()cũng sẽ hoạt động. Tôi không hoàn toàn chắc chắn về ý nghĩa hiệu quả của việc truyền và đệm liên quan đến hai tùy chọn - view[0].any()về mặt lý thuyết có thể được thực hiện nhanh hơn, nhưng tôi đã thấy kết quả kỳ lạ trước đây và tôi không hiểu hết về bộ đệm liên quan.
user2357112 hỗ trợ Monica

Tôi cho rằng view[0].view(bool).any()sẽ là giải pháp tốc độ cao.
Paul Panzer


(Ngoài ra, cho dù argmaxhay any, sử dụng chế độ xem boolean có nghĩa là xử lý số 0 âm là không bằng với số không thông thường.)
user2357112 hỗ trợ Monica

2

Tôi đã định hình lại mảng và sau đó lặp lại qua nó. Thật không may, câu trả lời của tôi cho rằng bạn có ít nhất ba chiều và sẽ lỗi với các ma trận thông thường, bạn sẽ phải thêm một mệnh đề đặc biệt cho các mảng hình 1 & 2 chiều. Ngoài ra, điều này sẽ chậm nên có khả năng có giải pháp tốt hơn.

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

Cái nào sẽ sản xuất

>>> False
>>> True

Về cơ bản, tôi xếp tất cả các kích thước lên nhau và sau đó nhìn qua chúng để kiểm tra các cạnh của chúng.


Điều này kiểm tra các phần sai của mảng. Đối với mảng 3 chiều, chúng tôi muốn kiểm tra các mặt của toàn bộ mảng, chứ không phải các cạnh của mỗi phân đoạn 2 chiều.
user2357112 hỗ trợ Monica

Ah, điều đó có ý nghĩa hơn. Tôi đã hiểu lầm
lwileczek

1

có thể toán tử dấu chấm lửng là thứ bạn đang tìm kiếm, nó sẽ hoạt động cho nhiều chiều:

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)

Điều này sẽ không tô màu tất cả các khuôn mặt. Ví dụ: thử với khối lập phương (4, 4, 4).
Luca

Tôi không chắc ý của bạn là gì khi tô màu khuôn mặt, nhưng nó hoạt động nếu bạn tạo x (4, 4, 4)
daveg

1

Bạn có thể sử dụng slicevà mặt nạ boolean để hoàn thành công việc:

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

Hàm này trước tiên định hình "lõi" của mảng thành bộ dữ liệu s, sau đó xây dựng mặt nạ Truechỉ hiển thị cho các điểm giáp. Lập chỉ mục Boolean sau đó cung cấp các điểm biên giới.

Ví dụ làm việc:

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

Sau đó, np.all(borders==0)sẽ cung cấp cho bạn thông tin mong muốn.


Lưu ý: điều này phá vỡ cho các mảng một chiều, mặc dù tôi coi đó là một trường hợp cạnh. Có lẽ bạn nên kiểm tra hai điểm trong câu hỏi đó


Điều này mất thời gian tỷ lệ thuận với tổng số phần tử trong mảng, thay vì chỉ đường viền. Ngoài ra, mảng một chiều không phải là trường hợp cạnh không liên quan.
user2357112 hỗ trợ Monica

1
Ngoài ra, np.arange(15)không bao gồm 15.
user2357112 hỗ trợ Monica

Tôi đồng ý rằng "không liên quan" là một từ ngữ mạnh mẽ, mặc dù tôi cảm thấy tốt hơn hết là chỉ kiểm tra hai điểm liên quan cho mảng 1d. Số 15 là một lỗi đánh máy, bắt tốt
Lukas Thaler
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.