2012-09-20 18 views
12

Un semplice esempio di indicizzazione NumPy:Numpy indicizzazione: Ritorna il resto

In: a = numpy.arange(10) 
In: sel_id = numpy.arange(5) 
In: a[sel_id] 
Out: array([0,1,2,3,4]) 

Come faccio a restituire il resto della matrice che non vengono indicizzati da sel_id? Quello che posso pensare è:

In: numpy.array([x for x in a if x not in a[id]]) 
out: array([5,6,7,8,9]) 

C'è un modo più semplice?

+0

È un'operazione una volta sola? O riutilizzerai 'sel_id' (ed è la negazione) lungo la strada? Inoltre, ti interessa il caso multidimensionale o solo il caso 1D? – mgilson

+0

Nella mia applicazione, sarà operato su un array multi-dimensionale, e sì, riutilizzerò sel_id. – CJLam

+0

Ho appena realizzato che la mia soluzione sopra è SBAGLIATA. Se è una matrice di dieci 1, allora il codice dato darà una matrice None invece di una matrice di cinque 1. – CJLam

risposta

10

Per questo semplice caso 1D, mi piacerebbe davvero utilizzare una maschera booleano:

a = numpy.arange(10) 
include_index = numpy.arange(4) 
include_idx = set(include_index) #Set is more efficient, but doesn't reorder your elements if that is desireable 
mask = numpy.array([(i in include_idx) for i in xrange(len(a))]) 

Ora è possibile ottenere i vostri valori:

included = a[mask] # array([0, 1, 2, 3]) 
excluded = a[~mask] # array([4, 5, 6, 7, 8, 9]) 

Nota che a[mask] non necessariamente produrrà gli stessi cosa come a[include_index] poiché l'ordine di include_index è importante per l'output in tale scenario (dovrebbe essere approssimativamente equivalente a a[sorted(include_index)]). Tuttavia, poiché l'ordine degli elementi esclusi non è ben definito, questo dovrebbe funzionare Ok.


EDIT

Un modo migliore per creare la maschera è:

mask = np.zeros(a.shape,dtype=bool) 
mask[include_idx] = True 

(grazie a seberg).

+0

Sono felice di risolvere eventuali problemi che questa risposta potrebbe avere se si sceglie di lasciare un commento su ciò che è sbagliato con esso. – mgilson

+0

@BiRico - sbagliato. Ho convertito 'include_index' in un' set' (chiamato 'include_idx') che ha un metodo' __contains__' che va in O (1). Questa soluzione ha la complessità 'O (N)'. – mgilson

+0

+1, questo è quasi esattamente quello che stavo per suggerire, ma ho dovuto allontanarmi dal computer. L'uso di una maschera booleana è utile per operazioni come queste perché non devi fare alcun lavoro extra per calcolare il relativo complemento. Solo fyi, usando 'fromiter' su un generatore invece di' array' su una list list, si ottiene un piccolo aumento di velocità in base ai miei test. – senderle

-1

numpy.setdiff1d(a, a[sel_id]) dovrebbe fare il trucco. Non so se c'è qualcosa di più bello di questo.

+0

Questo non funzionerà se ci sono valori ripetuti nell'array. – reptilicus

2

è più simile a:

a = numpy.array([1, 2, 3, 4, 5, 6, 7, 4]) 
exclude_index = numpy.arange(5) 
include_index = numpy.setdiff1d(numpy.arange(len(a)), exclude_index) 
a[include_index] 
# array([6, 7, 4]) 

# Notice this is a little different from 
numpy.setdiff1d(a, a[exclude_index]) 
# array([6, 7] 
0

Inoltre, se sono contigui uso della [N:] sintassi per selezionare il resto. Ad esempio, arr [5:] seleziona il quinto all'ultimo elemento dell'array.

+0

E se non lo sono? –

4

È possibile farlo bene con le maschere booleani:

a = numpy.arange(10) 

mask = np.ones(len(a), dtype=bool) # all elements included/True. 
mask[[7,2,8]] = False    # Set unwanted elements to False 

print a[mask] 
# Gives (removing entries 7, 2 and 8): 
[0 1 3 4 5 6 9] 

Addition (tratto da @mgilson). La maschera binaria creata può essere utilizzata in modo appropriato per ripristinare le porzioni originali con a[~mask] tuttavia questo è lo stesso se gli indici originali erano ordinati.


EDIT: spostato verso il basso, come ho dovuto capire che vorrei prendere in considerazione np.delete buggy in questo momento (settembre 2012).

È anche possibile utilizzare np.delete, anche se le maschere sono più potenti (e in futuro penso che dovrebbe essere un'opzione OK). Al momento però è più lento di quanto sopra, e creerà risultati imprevisti con indici negativi (o passi quando viene data una fetta).

print np.delete(a, [7,2,8]) 
+1

Sì - il secondo approccio è il migliore finora, e l'unico approccio lineare puro-insensato ... sembra ovvio in retrospettiva! (Si noti che dietro le quinte, 'numpy.delete' usa solo [' setdiff1d'] (https://github.com/numpy/numpy/blob/master/numpy/lib/function_base.py#L3380) che a sua volta usa ['in1d'] (https://github.com/numpy/numpy/blob/master/numpy/lib/arraysetops.py#L384). Quindi è anche n log n.) Vorrei +1 ma hai già il mio! – senderle

+0

@senderle sul serio! Quello è divertente, forse il 'np.delete' potrebbe usare una modifica per quel percorso di esecuzione ... – seberg

+1

-1 per l'uso di 'np.delete'. ** Mai ** una buona idea. –

-1

Supponendo che a è un array 1D, si può solo pop gli elementi che non si desidera dalla lista degli indici:

accept = [i for i in range(a.size) if i not in avoid_list] 
a[accept] 

Si potrebbe anche provare ad usare qualcosa come

accept = sorted(set(range(a.size)) - set(indices_to_discard)) 
a[accept] 

L'idea è di utilizzare l'indicizzazione di fantasia sul complementare dell'insieme di indici che non si desidera.

1

Lo farei con una maschera booleana ma leggermente diversa. Che ha il vantaggio di lavorare in N-dimensioni, con indici continui o non. L'utilizzo della memoria dipenderà dal fatto che una vista o una copia sia fatta per l'array mascherato e non sono sicuro.

import numpy 
a = numpy.arange(10) 
sel_id = numpy.arange(5) 
mask = numpy.ma.make_mask_none(a.shape) 
mask[sel_id] = True 
answer = numpy.ma.masked_array(a, mask).compressed() 
print answer 
# [5 6 7 8 9] 
+0

Gli array mascherati possono essere un'opzione davvero piacevole. Anche se '.compressed()' in qualche modo sconfigge l'oggetto IMO mascherato, in quanto crea una normale copia dell'array. – seberg

+0

D'accordo, ma la domanda era di ottenere il resto dell'array ... –

Problemi correlati