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.


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.


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

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


(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.


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)

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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.