sprawdź, czy tablica numpy ma 0 na wszystkich swoich granicach [zamknięte]

13

Jaki byłby najszybszy sposób sprawdzenia, czy wielowymiarowa tablica liczbowa ma 0 ze wszystkich stron.

Dla prostego przykładu 2D mam:

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

Chociaż jest to w porządku, aby przypadki 2D miały rację, pisanie dla wyższych wymiarów jest nieco nudne i zastanawiałem się, czy jest jakaś sprytna sztuczka numeryczna, której mogę użyć tutaj, aby była wydajna i łatwiejsza w utrzymaniu.

Luca
źródło
8
Czy nie np.all (x[:, 0] == 0)byłoby bezpieczniej niż suma? Test sumowy jest poprawny tylko wtedy, gdy wszystkie liczby są dodatnie.
Demi-Lune
1
@ Demi-Lume Ma sens. W moim przypadku wszystko będzie> = 0, ale twój komentarz jest mile widziany :)
Luca
1
Czy w przypadku 3D masz na myśli twarze (jest ich sześć) lub krawędzie (jest ich 12) sześcianu?
Riccardo Bucco
@RiccardoBucco Tak, 6 twarzy. ale moim problemem jest to, że może osiągnąć wyższy wymiar niż 3.
Luca

Odpowiedzi:

7

Oto jak możesz to zrobić:

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

np.take robi to samo, co „fantazyjne” indeksowanie.

Riccardo Bucco
źródło
1
@Luca: Dokumentacja nie wyjaśnia, ale numpy.takerobi kopię. Może to spowodować, że będzie gorzej niż kod oparty na widoku. (Konieczne byłoby
określenie czasu
1
@RiccardoBucco: len(x.shape)można napisać prościej jako x.ndim.
user2357112 obsługuje Monikę
1
@ user2357112supportsMonica dzięki, naprawiłem to :)
Riccardo Bucco
5
Ponadto użycie funkcji listowania zapobiega allzwarciom. Możesz usunąć nawiasy klamrowe, aby użyć wyrażenia generatora, co pozwala allna powrót, gdy tylko jedno numpy.allpołączenie powróci False.
user2357112 obsługuje Monikę
1
@ user2357112supports Monica True !!
Riccardo Bucco
5

Oto odpowiedź, która faktycznie bada części tablicy, którą jesteś zainteresowany i nie marnuje czasu na tworzenie maski wielkości całej tablicy. Istnieje pętla na poziomie Pythona, ale jest krótka, z iteracjami proporcjonalnymi do liczby wymiarów zamiast wielkości tablicy.

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
user2357112 obsługuje Monikę
źródło
Czy są jakieś okoliczności, w których not (view[0] == 0).all()nie jest to równoważne view[0].any()?
Paul Panzer
@PaulPanzer: Przypuszczam, view[0].any()że też będzie działać. Nie jestem całkowicie pewien wpływu na rzutowanie i buforowanie w obu opcjach - view[0].any()może teoretycznie zostać wdrożony szybciej, ale wcześniej widziałem dziwne wyniki i nie do końca rozumiem związane z tym buforowanie.
user2357112 obsługuje Monikę
Przypuszczam view[0].view(bool).any(), że byłoby to szybkie rozwiązanie.
Paul Panzer
@PaulPanzer: argmaxmoże faktycznie anyprzewyższyć widok logiczny . Te rzeczy stają się dziwne.
user2357112 obsługuje Monikę
(Ponadto, czy argmaxlub anyużycie widoku logicznego oznacza obsługę ujemnego zera jako nierównego do zwykłego zera.)
user2357112 obsługuje Monikę
2

Przekształciłem tablicę, a następnie wykonałem iterację. Niestety, moja odpowiedź zakłada, że ​​masz co najmniej trzy wymiary i popełnisz błąd w przypadku normalnych matryc, musisz dodać specjalną klauzulę dla tablic o kształcie 1 i 2 wymiarowym. Ponadto będzie to powolne, więc prawdopodobnie są lepsze rozwiązania.

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

Który wyprodukuje

>>> False
>>> True

Zasadniczo układam wszystkie wymiary jeden na drugim, a następnie przeglądam je, aby sprawdzić ich krawędzie.

lwileczek
źródło
To sprawdza niewłaściwe części tablicy. W przypadku trójwymiarowej tablicy chcemy zbadać powierzchnie całej tablicy, a nie krawędzie każdej 2-wymiarowej podtablicy.
user2357112 obsługuje Monikę
Ach, to ma więcej sensu. Nie zrozumiałem
lwileczek
1

może operator elipsy jest tym, czego szukasz, który będzie działał dla wielu wymiarów:

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)
David
źródło
To nie pokoloruje wszystkich twarzy. Na przykład spróbuj z kostką (4, 4, 4).
Luca
Nie jestem pewien, co masz na myśli przez kolorowanie twarzy, ale działa, jeśli zrobisz x (4, 4, 4)
David
1

Możesz skorzystać z slicemaskowania logicznego, aby wykonać zadanie:

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

Ta funkcja najpierw kształtuje „rdzeń” tablicy w krotkę s, a następnie buduje pokazaną maskęTrue tylko punkty graniczne. Indeksowanie boolowskie dostarcza punktów granicznych.

Przykład roboczy:

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

Następnie np.all(borders==0)poda pożądane informacje.


Uwaga: to łamie się dla tablic jednowymiarowych, chociaż uważam je za przypadek krawędzi. Prawdopodobnie lepiej jest po prostu sprawdzić tam dwa punkty, o których mowa

Lukas Thaler
źródło
Zajmuje to czas proporcjonalny do całkowitej liczby elementów w tablicy zamiast samej granicy. Również tablice jednowymiarowe nie są nieistotnym przypadkiem na krawędzi.
user2357112 obsługuje Monikę
1
Ponadto np.arange(15)nie obejmuje 15.
użytkownik2357112 obsługuje Monikę
Zgadzam się, że „nieistotny” to mocne sformułowanie, choć uważam, że lepiej jest po prostu sprawdzić dwa punkty dotyczące tablicy 1d. 15 to literówka, dobry haczyk
Lukas Thaler