перевірте, чи має нумерований масив 0 на всіх його межах [закрито]


13

Що було б найшвидшим способом перевірити, чи має багатовимірний нумеровий масив 0 з усіх боків.

Отже, для простого двовимірного прикладу я маю:

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

Хоча це нормально, щоб 2D випадки вправо писати для більш високих розмірів - трохи нудно, і мені було цікаво, чи є якась хитра хитра хитрість, яку я можу використати тут, щоб зробити її ефективнішою і також більш доцільною.


8
Не np.all (x[:, 0] == 0)було б безпечніше суми? Тестовий підсумок правильний, лише якщо всі числа є позитивними.
Демі-Луна


1
@ Demi-Lume Має сенс. У моєму випадку все буде> = 0, але ваш коментар цінується :)
Лука

1
Ви маєте на увазі в 3D-випадку грані (їх шість) або ребра (їх 12) куба?
Ріккардо Букко

@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робить копію. Це може призвести до того, що воно буде працювати гірше, ніж код на основі перегляду. (Timing було б необхідно , щоб бути впевненим - NumPy ефективність зору іноді дивно.)
user2357112 підтримує Monica

1
@RiccardoBucco: len(x.shape)можна записати простіше як x.ndim.
user2357112 підтримує Моніку

1
@ user2357112supportsMonica спасибі, я це виправив :)
Ріккардо Букко

5
Також використання розуміння списку запобігає allкороткому замиканню. Ви можете зняти дужки, щоб використовувати вираз генератора, що дозволяє allповернутися, як тільки один numpy.allвиклик повернеться False.
user2357112 підтримує Моніку

1
@ user2357112підтримкаMonica True !!
Ріккардо Букко

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()?
Пол Панцер

@PaulPanzer: Я вважаю, що view[0].any()це теж працюватиме. Я не зовсім впевнений у ефективності наслідків лиття та буферизації, що стосуються двох варіантів - view[0].any()теоретично це можна було б реалізувати швидше, але я раніше бачив дивні результати, і я не повністю розумію, що це буферизація.
user2357112 підтримує Monica

Я думаю, це view[0].view(bool).any()було б швидкісним рішенням.
Пол Панцер


(Крім того , незалежно від того , argmaxчи any, використовуючи логічні засоби обробки виду негативний нуля , як нерівномірні до звичайного нулю.)
user2357112 підтримує Моніку

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 підтримує Моніку

Ах, це має більше сенсу. Я неправильно зрозумів
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) куб.
Лука

Я не впевнений, що ви маєте на увазі під фарбуванням облич, але це спрацює, якщо ви робите х (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 підтримує Моніку

1
Також np.arange(15)не включає 15.
user2357112 підтримує Моніку

Я погоджуюся, що "неважливо" - це чітка формулювання, хоча я відчуваю, що вам краще просто перевірити дві точки, що стосуються 1d масиву. 15 - друкарня, хороший улов
Лукас Талер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.