2015-06-18 18 views
20

Ho esaminato le documentazioni e anche altre domande qui, ma sembra che io non abbiate ancora il blocco di subsetting in array numpy.Compensazione di un array numpy 2D

Ho un array di NumPy, e per amor di discussione, lascia che sia definita come segue:

import numpy as np 
a = np.arange(100) 
a.shape = (10,10) 
# array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 
#  [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], 
#  [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 
#  [30, 31, 32, 33, 34, 35, 36, 37, 38, 39], 
#  [40, 41, 42, 43, 44, 45, 46, 47, 48, 49], 
#  [50, 51, 52, 53, 54, 55, 56, 57, 58, 59], 
#  [60, 61, 62, 63, 64, 65, 66, 67, 68, 69], 
#  [70, 71, 72, 73, 74, 75, 76, 77, 78, 79], 
#  [80, 81, 82, 83, 84, 85, 86, 87, 88, 89], 
#  [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]]) 

ora voglio scegliere righe e colonne di a specificati da vettori n1 e n2. Come esempio:

n1 = range(5) 
n2 = range(5) 

Ma quando uso:

b = a[n1,n2] 
# array([ 0, 11, 22, 33, 44]) 

Poi vengono scelti solo i primi elementi diagonali quinto, non l'intero blocco 5x5. La soluzione che ho trovato è quello di fare in questo modo:

b = a[n1,:] 
b = b[:,n2] 
# array([[ 0, 1, 2, 3, 4], 
#  [10, 11, 12, 13, 14], 
#  [20, 21, 22, 23, 24], 
#  [30, 31, 32, 33, 34], 
#  [40, 41, 42, 43, 44]]) 

Ma io sono sicuro che ci dovrebbe essere un modo per fare questo compito semplice in un solo comando.

risposta

17

Hai una manciata di buoni esempi di come fare ciò che vuoi. Tuttavia, è anche utile capire cosa sta succedendo e perché le cose funzionano come fanno. Ci sono alcune semplici regole che ti aiuteranno in futuro.

C'è una grande differenza tra l'indicizzazione "di fantasia" (cioè utilizzando una lista/sequenza) e l'indicizzazione "normale" (utilizzando una sezione). La ragione di fondo ha a che fare con la possibilità che l'array possa essere "regolarmente puntato", e quindi se una copia debba o meno essere fatta. Le sequenze arbitrarie devono pertanto essere trattate in modo diverso, se vogliamo essere in grado di creare "viste" senza fare copie.

Nel caso:

import numpy as np 

a = np.arange(100).reshape(10,10) 
n1, n2 = np.arange(5), np.arange(5) 

# Not what you want 
b = a[n1, n2] # array([ 0, 11, 22, 33, 44]) 

# What you want, but only for simple sequences 
# Note that no copy of *a* is made!! This is a view. 
b = a[:5, :5] 

# What you want, but probably confusing at first. (Also, makes a copy.) 
# np.meshgrid and np.ix_ are basically equivalent to this. 
b = a[n1[:,None], n2[None,:]] 

indicizzazione operata con sequenze 1D è sostanzialmente equivalente a lampo insieme e indicizzazione del risultato.

print "Fancy Indexing:" 
print a[n1, n2] 

print "Manual indexing:" 
for i, j in zip(n1, n2): 
    print a[i, j] 

Tuttavia, se le sequenze Cerchi indicizzazione con partita la dimensionalità della matrice si sta indicizzare (2D, in questo caso), L'indicizzazione è trattato diversamente. Invece di "zippare i due insieme", numpy usa gli indici come una maschera.

In altre parole, a[[[1, 2, 3]], [[1],[2],[3]]] viene trattato in modo completamente diverso rispetto a a[[1, 2, 3], [1, 2, 3]], perché le sequenze/matrici che stai trasmettendo sono bidimensionali.

In [4]: a[[[1, 2, 3]], [[1],[2],[3]]] 
Out[4]: 
array([[11, 21, 31], 
     [12, 22, 32], 
     [13, 23, 33]]) 

In [5]: a[[1, 2, 3], [1, 2, 3]] 
Out[5]: array([11, 22, 33]) 

Per essere un po 'più precisi,

a[[[1, 2, 3]], [[1],[2],[3]]] 

è trattata esattamente come:

i = [[1, 1, 1], 
    [2, 2, 2], 
    [3, 3, 3]]) 
j = [[1, 2, 3], 
    [1, 2, 3], 
    [1, 2, 3]] 
a[i, j] 

In altre parole, se l'ingresso è un vettore di riga/colonna viene una scorciatoia per come gli indici dovrebbero ripetersi nell'indicizzazione.


np.meshgrid e np.ix_ sono modi solo comoda per trasformare tuoi sequenze 1D nelle loro versioni 2D per l'indicizzazione:

In [6]: np.ix_([1, 2, 3], [1, 2, 3]) 
Out[6]: 
(array([[1], 
     [2], 
     [3]]), array([[1, 2, 3]])) 

Analogamente (l'argomento sparse renderebbe identico al ix_ sopra):

In [7]: np.meshgrid([1, 2, 3], [1, 2, 3], indexing='ij') 
Out[7]: 
[array([[1, 1, 1], 
     [2, 2, 2], 
     [3, 3, 3]]), 
array([[1, 2, 3], 
     [1, 2, 3], 
     [1, 2, 3]])] 
+0

Grazie per le vostre spiegazioni. Essendo più familiare con MATLAB, trovo la convenzione di subsetting in numpy un po 'strana, ma almeno ora so come farlo nel modo giusto. – CrossEntropy

6

Si potrebbe utilizzare np.meshgrid per dare il n1, n2 array la forma corretta per eseguire l'indicizzazione desiderato:

In [104]: a[np.meshgrid(n1,n2, sparse=True, indexing='ij')] 
Out[104]: 
array([[ 0, 1, 2, 3, 4], 
     [10, 11, 12, 13, 14], 
     [20, 21, 22, 23, 24], 
     [30, 31, 32, 33, 34], 
     [40, 41, 42, 43, 44]]) 

Oppure, senza meshgrid:

In [117]: a[np.array(n1)[:,np.newaxis], np.array(n2)[np.newaxis,:]] 
Out[117]: 
array([[ 0, 1, 2, 3, 4], 
     [10, 11, 12, 13, 14], 
     [20, 21, 22, 23, 24], 
     [30, 31, 32, 33, 34], 
     [40, 41, 42, 43, 44]]) 

C'è un simile esempio con una spiegazione di come questo integer array indexing funziona nei documenti.

Vedere anche la ricetta del ricettario Picking out rows and columns.

8

altro metodo rapido per costruire l'indice desiderato è quello di utilizzare la funzione np.ix_:

>>> a[np.ix_(n1, n2)] 
array([[ 0, 1, 2, 3, 4], 
     [10, 11, 12, 13, 14], 
     [20, 21, 22, 23, 24], 
     [30, 31, 32, 33, 34], 
     [40, 41, 42, 43, 44]]) 

Questo fornisce un modo conveniente per costruire una maglia aperta da sequenze di indici.

0

Sembra che un caso d'uso per la tua particolare domanda riguarderebbe la manipolazione delle immagini n. Nella misura in cui si utilizza l'esempio per modificare gli array numpy derivanti dalle immagini, è possibile utilizzare la libreria Python Imaging (PIL).

# Import Pillow: 
from PIL import Image 

# Load the original image: 
img = Image.open("flowers.jpg") 

# Crop the image 
img2 = img.crop((0, 0, 5, 5)) 

L'oggetto img2 è una matrice numerica dell'immagine ritagliata risultante.

Si può leggere di più su di manipolazione delle immagini qui con la Pillow package (una forchetta facile da usare sulla confezione PIL):