controlla se un array numpy ha 0 su tutti i suoi bordi [chiuso]


13

Quale sarebbe il modo più veloce per verificare se un array numpy multidimensionale ha 0 su tutti i lati.

Quindi, per un semplice esempio 2D, ho:

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

Mentre questo va bene per i casi 2D a destra, scrivere per dimensioni più alte è un po 'noioso e mi chiedevo se ci sia qualche trucco intelligente e insipido che posso usare qui per renderlo efficiente e anche più mantenibile.


8
Non np.all (x[:, 0] == 0)sarebbe più sicuro della somma? Il test di somma è corretto solo se tutti i numeri sono positivi.
— Demi-Lune,


1
@ Demi-Lume Ha senso. Nel mio caso, tutto sarà> = 0 ma il tuo commento è apprezzato :)
— Luca

1
In un caso 3D, intendi le facce (ce ne sono sei) o i bordi (ce ne sono 12) del cubo?
— Riccardo Bucco,

@RiccardoBucco Sì, 6 facce. ma il mio problema è che può andare oltre la dimensione 3.
— Luca

Risposte:


7

Ecco come puoi farlo:

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

np.take fa la stessa cosa dell'indicizzazione "fantasia".


1
@Luca: la documentazione non chiarisce, ma ne numpy.takefa una copia. Ciò potrebbe causare prestazioni peggiori del codice in base a una vista. (Il tempismo sarebbe necessario per essere sicuri - l'efficienza della visualizzazione NumPy a volte è strana.)
— user2357112 supporta Monica

1
@RiccardoBucco: len(x.shape)può essere scritto più semplicemente come x.ndim.
— user2357112 supporta Monica

1
@utente2357112supportsMonica grazie, l'ho risolto :)
— Riccardo Bucco il

5
Inoltre, l'uso della comprensione di un elenco impedisce il allcorto circuito. È possibile rimuovere le parentesi quadre per utilizzare un'espressione del generatore, consentendo alldi tornare non appena viene restituita una singola numpy.allchiamata False.
— user2357112 supporta Monica

1
@ user2357112supportsMonica True !!
— Riccardo Bucco,

5

Ecco una risposta che esamina effettivamente le parti dell'array che ti interessano e non perde tempo a costruire una maschera delle dimensioni dell'intero array. C'è un loop a livello di Python, ma è breve, con iterazioni proporzionali al numero di dimensioni anziché alle dimensioni dell'array.

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

Ci sono circostanze in cui not (view[0] == 0).all()non è equivalente a view[0].any()?
— Paul Panzer,

@PaulPanzer: suppongo view[0].any()che funzionerebbe anche. Non sono del tutto sicuro delle implicazioni sull'efficienza del casting e del buffering coinvolti nelle due opzioni: view[0].any()teoricamente potrebbero essere implementate più rapidamente, ma ho già visto strani risultati prima e non capisco completamente il buffering in questione.
— user2357112 supporta Monica

Suppongo che view[0].view(bool).any()sarebbe la soluzione ad alta velocità.
— Paul Panzer,


(Inoltre, se argmaxo any, utilizzare una vista booleana significa gestire lo zero negativo come diverso da zero normale.)
— user2357112 supporta Monica

2

Ho rimodellato l'array e poi ho iterato. Sfortunatamente, la mia risposta presuppone che tu abbia almeno tre dimensioni e sbaglierà per le matrici normali, dovresti aggiungere una clausola speciale per le matrici a forma 1 e 2 dimensionale. Inoltre, questo sarà lento, quindi ci sono probabilmente soluzioni migliori.

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

Che produrrà

>>> False
>>> True

Fondamentalmente impilo tutte le dimensioni una sopra l'altra e poi guardo attraverso di esse per controllare i loro bordi.


Questo esamina le parti sbagliate dell'array. Per un array tridimensionale, vogliamo esaminare le facce dell'intero array, non i bordi di ciascun subarray bidimensionale.
— user2357112 supporta Monica

Ah, ha più senso. Ho frainteso
— lwileczek il

1

forse l'operatore con i puntini di sospensione è quello che stai cercando, che funzionerà per molte dimensioni:

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)

Questo non colora tutte le facce. Ad esempio, provalo con un cubo (4, 4, 4).
— Luca

Non sono sicuro di cosa intendi per colorare i volti, ma funziona se fai x (4, 4, 4)
— daveg

1

È possibile utilizzare slicee mascheramento booleano per completare il lavoro:

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

Questa funzione modella prima il "nucleo" dell'array nella tupla s, quindi crea una maschera che mostra Truesolo per i punti confinanti. L'indicizzazione booleana fornisce quindi i punti di confine.

Esempio funzionante:

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

Quindi, np.all(borders==0)ti darà le informazioni desiderate.


Nota: questo si interrompe per gli array unidimensionali, anche se li considero un caso limite. Probabilmente stai meglio solo controllando i due punti in questione lì


Ciò richiede un tempo proporzionale al numero totale di elementi nell'array, anziché solo al bordo. Inoltre, le matrici unidimensionali non sono un caso limite irrilevante.
— user2357112 supporta Monica

1
Inoltre, np.arange(15)non include 15.
— user2357112 supporta Monica

Sono d'accordo sul fatto che "irrilevante" sia una formulazione forte, sebbene ritenga che tu stia meglio controllando i due punti relativi a un array 1d. Il 15 è un refuso, buona cattura
— Lukas Thaler il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.