Numpy dove funzione più condizioni


132

Ho una serie di distanze chiamate dist. Voglio selezionare le dist tra due valori. Ho scritto la seguente riga di codice per farlo:

 dists[(np.where(dists >= r)) and (np.where(dists <= r + dr))]

Tuttavia, questo seleziona solo per la condizione

 (np.where(dists <= r + dr))

Se eseguo i comandi in sequenza utilizzando una variabile temporanea, funziona perfettamente. Perché il codice sopra riportato non funziona e come posso farlo funzionare?

Saluti

Risposte:


203

Il modo migliore nel tuo caso particolare sarebbe solo quello di cambiare i tuoi due criteri in un criterio:

dists[abs(dists - r - dr/2.) <= dr/2.]

Crea solo un array booleano, e secondo me è più facile da leggere perché dice, è distall'interno di un dro r? (Anche se vorrei ridefinire rdi essere il centro della tua regione di interesse anziché l'inizio, quindi r = r + dr/2.) Ma questo non risponde alla tua domanda.


La risposta alla tua domanda: in
realtà non ti serve wherese stai solo cercando di filtrare gli elementi distsche non corrispondono ai tuoi criteri:

dists[(dists >= r) & (dists <= r+dr)]

Perché &ti darà un elementally and(le parentesi sono necessarie).

Oppure, se si desidera utilizzare whereper qualche motivo, è possibile fare:

 dists[(np.where((dists >= r) & (dists <= r + dr)))]

Perché:
il motivo per cui non funziona è perché np.whererestituisce un elenco di indici, non un array booleano. Stai cercando di passare andtra due elenchi di numeri, che ovviamente non hanno i valori True/ Falseche ti aspetti. Se ae bsono entrambi Truevalori, quindi a and brestituisce b. Quindi dire qualcosa del genere [0,1,2] and [2,3,4]ti darà [2,3,4]. Eccolo in azione:

In [230]: dists = np.arange(0,10,.5)
In [231]: r = 5
In [232]: dr = 1

In [233]: np.where(dists >= r)
Out[233]: (array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),)

In [234]: np.where(dists <= r+dr)
Out[234]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

In [235]: np.where(dists >= r) and np.where(dists <= r+dr)
Out[235]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

Ciò che ti aspettavi di confrontare era semplicemente l'array booleano, per esempio

In [236]: dists >= r
Out[236]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True], dtype=bool)

In [237]: dists <= r + dr
Out[237]: 
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

In [238]: (dists >= r) & (dists <= r + dr)
Out[238]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

Ora puoi chiamare np.wherel'array booleano combinato:

In [239]: np.where((dists >= r) & (dists <= r + dr))
Out[239]: (array([10, 11, 12]),)

In [240]: dists[np.where((dists >= r) & (dists <= r + dr))]
Out[240]: array([ 5. ,  5.5,  6. ])

O semplicemente indicizza l'array originale con l'array booleano usando l' indicizzazione di fantasia

In [241]: dists[(dists >= r) & (dists <= r + dr)]
Out[241]: array([ 5. ,  5.5,  6. ])

61

La risposta accettata ha spiegato il problema abbastanza bene. Tuttavia, l'approccio più Numpythonic per l'applicazione di più condizioni è l'uso di funzioni logiche intorpidite . In questo esempio puoi usare np.logical_and:

np.where(np.logical_and(np.greater_equal(dists,r),np.greater_equal(dists,r + dr)))

11

Una cosa interessante da sottolineare qui; il solito modo di usare OR e AND funzionerà anche in questo caso, ma con una piccola modifica. Invece di "e" e invece di "o", usa invece la e commerciale (&) e l' operatore di tubi (|) e funzionerà.

Quando utilizziamo "e" :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) and (ar<6), 'yo', ar)

Output:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Quando utilizziamo la e commerciale (&) :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) & (ar<6), 'yo', ar)

Output:
array(['3', 'yo', 'yo', '14', '2', 'yo', '3', '7'], dtype='<U11')

E questo è lo stesso nel caso in cui stiamo provando ad applicare più filtri in caso di Panda Dataframe. Ora il ragionamento alla base di questo deve fare qualcosa con gli operatori logici e gli operatori bit a bit e per una maggiore comprensione dello stesso, suggerirei di passare attraverso questa risposta o una domanda / risposta simile nello stackoverflow.

AGGIORNARE

Un utente ha chiesto, perché c'è bisogno di dare (ar> 3) e (ar <6) all'interno della parentesi. Bene, ecco la cosa. Prima di iniziare a parlare di ciò che sta accadendo qui, è necessario conoscere la precedenza dell'operatore in Python.

Simile a ciò che riguarda BODMAS, python dà anche la precedenza a ciò che dovrebbe essere eseguito per primo. Gli elementi tra parentesi vengono eseguiti prima e poi l'operatore bit a bit si mette al lavoro. Mostrerò di seguito cosa succede in entrambi i casi quando si utilizza e non si utilizza "(", ")".

Caso 1:

np.where( ar>3 & ar<6, 'yo', ar)
np.where( np.array([3,4,5,14,2,4,3,7])>3 & np.array([3,4,5,14,2,4,3,7])<6, 'yo', ar)

Dato che qui non ci sono parentesi quadre, l'operatore bit per bit ( &) viene confuso qui che cosa stai chiedendo persino di ottenere AND logico, perché nella tabella di precedenza degli operatori, se vedi, &viene data la precedenza <o gli >operatori. Ecco la tabella dalla precedenza più bassa alla precedenza più alta.

inserisci qui la descrizione dell'immagine

Non è nemmeno eseguire l' <e >funzionamento e viene chiesto di eseguire un'operazione logica AND. Ecco perché dà quell'errore.

È possibile consultare il seguente link per ulteriori informazioni su: precedenza dell'operatore

Ora al caso 2:

Se usi la parentesi, vedi chiaramente cosa succede.

np.where( (ar>3) & (ar<6), 'yo', ar)
np.where( (array([False,  True,  True,  True, False,  True, False,  True])) & (array([ True,  True,  True, False,  True,  True,  True, False])), 'yo', ar)

Due matrici di vero e falso. E puoi facilmente eseguire operazioni logiche AND su di essi. Che ti dà:

np.where( array([False,  True,  True, False, False,  True, False, False]),  'yo', ar)

E resto lo sai, np.where, per determinati casi, ovunque True, assegna il primo valore (cioè qui 'yo') e se False, l'altro (cioè qui, mantenendo l'originale).

È tutto. Spero di aver spiegato bene la domanda.


1
Perché devi metterti in ()giro (ar>3)e (ar>6)?
RTrain3k,

Questa è davvero una bella domanda. È una domanda così buona che ho dovuto pensare a me stesso che cosa ne abbia bisogno. Così ho fatto, ho chiesto anche a un collega e abbiamo discusso e ora ho una soluzione per te. Inserendolo nella risposta come AGGIORNAMENTO. È davvero semplice ma difficile da capire davvero.
Amit Amola,

Guarda l'aggiornamento RTrain3k, ho risposto alla tua domanda.
Amit Amola,

5

Mi piace usare np.vectorizeper tali compiti. Considera quanto segue:

>>> # function which returns True when constraints are satisfied.
>>> func = lambda d: d >= r and d<= (r+dr) 
>>>
>>> # Apply constraints element-wise to the dists array.
>>> result = np.vectorize(func)(dists) 
>>>
>>> result = np.where(result) # Get output.

È inoltre possibile utilizzare np.argwhereinvece che np.whereper l'output chiaro. Ma questa è la tua chiamata :)

Spero che sia d'aiuto.


2

Provare:

np.intersect1d(np.where(dists >= r)[0],np.where(dists <= r + dr)[0])

2

Questo dovrebbe funzionare:

dists[((dists >= r) & (dists <= r+dr))]

Il modo più elegante ~~


2

Provare:

import numpy as np
dist = np.array([1,2,3,4,5])
r = 2
dr = 3
np.where(np.logical_and(dist> r, dist<=r+dr))

Output: (array ([2, 3]),)

Puoi vedere le funzioni logiche per maggiori dettagli.


0

Ho elaborato questo semplice esempio

import numpy as np

ar = np.array([3,4,5,14,2,4,3,7])

print [X for X in list(ar) if (X >= 3 and X <= 6)]

>>> 
[3, 4, 5, 4, 3]

6
Non è necessario ripetere l'iterazione in questo caso. NumPy ha l'indicizzazione booleana.
M456,
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.