2015-06-06 14 views
5

Qualcuno può fornirmi un modo migliore (più semplice, più leggibile, più Pythonic, più efficiente, ecc) per rimuovere più valori da una matrice di quello che segue:Come rimuovere più valori da un array in una volta

import numpy as np 

# The array. 
x = np.linspace(0, 360, 37) 

# The values to be removed. 
a = 0 
b = 180 
c = 360 

new_array = np.delete(x, np.where(np.logical_or(np.logical_or(x == a, 
                   x == b), 
               x == c))) 

Una buona risposta a questa domanda produrrebbe lo stesso risultato del codice precedente (ovvero, new_array), ma potrebbe fare un lavoro migliore occupandosi dell'uguaglianza tra float rispetto al codice precedente.

BONUS

Qualcuno può spiegarmi perché questo produce il risultato che non va?

In [5]: np.delete(x, x == a) 
/usr/lib/python2.7/dist-packages/numpy/lib/function_base.py:3254: FutureWarning: in the future insert will treat boolean arrays and array-likes as boolean index instead of casting it to integer 
    "of casting it to integer", FutureWarning) 
Out[5]: 
array([ 20., 30., 40., 50., 60., 70., 80., 90., 100., 
     110., 120., 130., 140., 150., 160., 170., 180., 190., 
     200., 210., 220., 230., 240., 250., 260., 270., 280., 
     290., 300., 310., 320., 330., 340., 350., 360.]) 

I valori 0 e 10 sono entrambi stati rimossi, piuttosto che 0 (a).

nota, x == a è come previsto (quindi il problema è all'interno np.delete):

In [6]: x == a 
Out[6]: 
array([ True, False, False, False, False, False, False, False, False, 
     False, False, False, False, False, False, False, False, False, 
     False, False, False, False, False, False, False, False, False, 
     False, False, False, False, False, False, False, False, False, False], dtype=bool) 

Nota anche che np.delete(x, np.where(x == a)) produce il risultato corretto. Pertanto, mi sembra che np.delete non possa gestire indici booleani.

+1

Se si dispone di indici booleani, non è necessario utilizzare 'delete'. Specifica che 'obj: slice, int o array of ints' (non booleano). – hpaulj

risposta

4

il codice fa sembrare un po 'complessa. Mi chiedevo se avessi preso in considerazione l'indicizzazione vettoriale booleana di Numpy.

Dopo lo stesso setup come ti ho cronometrato il tuo codice:

In [175]: %%timeit 
    .....: np.delete(x, np.where(np.logical_or(np.logical_or(x == a, x == b), x == c))) 
    .....: 
10000 loops, best of 3: 32.9 µs per loop 

Poi ho cronometrato due applicazioni separate di indicizzazione booleana.

In [176]: %%timeit 
    .....: x1 = x[x != a] 
    .....: x2 = x1[x1 != b] 
    .....: new_array = x2[x2 != c] 
    .....: 
100000 loops, best of 3: 6.56 µs per loop 

Infine, per comodità di programmazione e di estendere la tecnica per un numero arbitrario di valori esclusi ho riscritto lo stesso codice come un ciclo. Questo sarà un po 'più lento, a causa della necessità di fare una copia prima, ma è ancora abbastanza rispettabile.

In [177]: %%timeit 
    .....: new_array = x.copy() 
    .....: for val in (a, b, c): 
    .....:  new_array = new_array[new_array != val] 
    .....: 
100000 loops, best of 3: 7.61 µs per loop 

Penso che il vero vantaggio sia comunque nella chiarezza della programmazione. Alla fine ho pensato che fosse meglio per verificare che i tre algoritmi erano dare gli stessi risultati ...

In [179]: new_array1 = np.delete(x, 
    .....:     np.where(np.logical_or(np.logical_or(x == a, x == b), x == c))) 

In [180]: x1 = x[x != a] 

In [181]: x2 = x1[x1 != b] 

In [182]: new_array2 = x2[x2 != c] 

In [183]: new_array3 = x.copy() 

In [184]: for val in (a, b, c): 
    .....:   new_array3 = new_array3[new_array3 != val] 
    .....: 

In [185]: all(new_array1 == new_array2) 
Out[185]: True 

In [186]: all(new_array1 == new_array3) 
Out[186]: True 

per gestire il problema dei confronti in virgola mobile è necessario utilizzare la funzione di NumPy isclose(). Come previsto, questo invia i tempi per l'inferno:

In [188]: %%timeit 
    .....: new_array = x.copy() 
    .....: for val in (a, b, c): 
    .....:  new_array = new_array[~np.isclose(new_array, val)] 
    .....: 
10000 loops, best of 3: 126 µs per loop 

La risposta alla tua bonus è contenuto all'interno del avvertimento, ma l'avviso non è molto utile se non si sa che False e True confrontare numericamente uguale a zero e uno rispettivamente. Quindi il codice è equivalente a

np.delete(1, 1) 

Come l'avvertimento chiarisce, il team NumPy fine intende che il risultato utilizzando gli argomenti booleani per np.delete() è destinata a cambiare, ma al momento ci vogliono solo argomenti di indice.

+0

yikes, la correzione comparativa a virgola mobile ha un grande costo di velocità. – dbliss

+0

re: l'avvertimento, grazie per averlo spiegato. l'avvertimento dice "inserisci" quando significa "cancella" - che mi ha gettato. – dbliss

+1

Sì, i confronti F-P costano molto perché invece di 'x == y' la funzione deve calcolare' x-delta <= y <= x + delta', un calcolo significativamente più complesso. Ho segnalato il problema relativo al messaggio di errore - questo è il [bug report] (https://github.com/numpy/numpy/issues/5945). – holdenweb

1

Si potrebbe prendere in prestito np.allclose approccio per testare se i galleggianti sono uguali:

def float_equal(x,y,rtol=1.e-5, atol=1.e-8): 
    return np.less_equal(abs(x-y), atol + rtol * abs(y)) 

np.delete(x,np.where(np.logical_or.reduce([float_equal(x,y) for y in [0,180,360]]))) 

Ci where parte produce:

(array([ 0, 18, 36]),) 

float_equal potrebbe probabilmente essere cambiato per trasmettere x contro y, eliminando la lista di comprensione .

Ho usato il fatto che logical_or è un ufunc e ha un metodo reduce.

Non è necessario il where; basta utilizzare il risultato della logical_or come indice booleana:

I = np.logical_or.reduce([float_equal(x,y) for y in [0,180,360]]) 
x[~I] 

(con questo piccolo esempio, l'uso diretto del booleano è 2 volte più veloce rispetto all'approccio np.delete(np.where(...)).)


Con questa x , == produce la stessa cosa:

np.where(np.logical_or.reduce([x==y for y in [0,180,360]])) 
# (array([ 0, 18, 36]),) 

così fa questo approccio vettorizzati:

0.123.
abc = np.array([0,180,360]) 
np.where(np.sum(x==abc[:,None],axis=0)) 
# (array([ 0, 18, 36]),) 

x==abc[:,None] è (3,37) matrice booleana; np.sum si comporta come un logico o.

mio float_equal funziona anche in questo modo:

float_equal(x,abc[:,None]).sum(axis=0) 
+0

grazie. Stavo ottenendo il risultato sbagliato quando ho usato gli indici booleani, ma non sono sicuro se fosse specifico per i valori che stavo usando. aggiornerò la domanda con quel codice e il risultato sbagliato che ho ottenuto. – dbliss

+0

OK, aggiornato. puoi confermare che il tuo metodo produce il risultato corretto? – dbliss

+0

Pensavo che avessi intenzione di riferire se il 'dove 'è davvero necessario o meno - cioè, se la risposta è misteriosamente errata senza il' dove'. – dbliss

3

È inoltre possibile utilizzare np.ravel per ottenere l'indice di values e poi rimuoverli utilizzando np.delete

In [32]: r = [a,b,c] 

In [33]: indx = np.ravel([np.where(x == i) for i in r]) 

In [34]: indx 
Out[34]: array([ 0, 18, 36]) 

In [35]: np.delete(x, indx) 
Out[35]: 
array([ 10., 20., 30., 40., 50., 60., 70., 80., 90., 
     100., 110., 120., 130., 140., 150., 160., 170., 190., 
     200., 210., 220., 230., 240., 250., 260., 270., 280., 
     290., 300., 310., 320., 330., 340., 350.]) 
+0

questo vince per la leggibilità. Immagino che aspetterò un po 'per vedere se arrivano più risposte e poi fare un test di velocità. – dbliss

+1

ah, cavolo, qualcun altro può fare il test della velocità. per ora, sei il vincitore. – dbliss

+1

Sembra essere anche abbastanza veloce. Bella risposta! – holdenweb

Problemi correlati