2015-06-18 20 views
20

Ich habe in Dokumentationen und auch andere Fragen hier untersucht, aber es scheint, dass ich haben nicht den Hang der Unterteilung in numpy Arrays noch.Subsetting ein 2D-numpy-Array

Ich habe eine numpy Array, und für die Zwecke der Beweisführung, lassen Sie es wie folgt definiert werden:

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

jetzt will ich Zeilen und Spalten von a durch Vektoren n1 und n2 angegeben wählen. Als Beispiel:

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

Aber wenn ich benutze:

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

Dann werden nur die ersten fünften Diagonalelemente gewählt werden, nicht die ganze 5x5-Block. Die Lösung, die ich gefunden habe, ist es, wie dies zu tun:

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

Aber ich bin sicher, es sollte ein Weg sein, um diese einfache Aufgabe in nur einen Befehl zu tun.

Antwort

17

Sie haben eine Handvoll nette Beispiele bekommen, wie Sie tun können, was Sie wollen. Es ist jedoch auch nützlich zu verstehen, was passiert und warum die Dinge so funktionieren, wie sie es tun. Es gibt ein paar einfache Regeln, die Ihnen in Zukunft helfen werden.

Es gibt einen großen Unterschied zwischen "ausgefallener" Indizierung (d. H. Mit einer Liste/Sequenz) und "normaler" Indizierung (mit einem Slice). Der zugrunde liegende Grund hat damit zu tun, ob das Array "regelmäßig" durchlaufen werden kann und daher ob eine Kopie erstellt werden muss oder nicht. Willkürliche Sequenzen müssen daher anders behandelt werden, wenn wir "Ansichten" erstellen wollen, ohne Kopien zu erstellen.

In Ihrem Fall:

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,:]] 

Fancy Indizierung mit 1D-Sequenzen ist im Grunde gleichwertig sie zusammen und Indizierung mit dem Ergebnis zu zippen.

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

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

Wenn jedoch die Sequenzen, die Sie mit Spiel sind die Indizierung der Dimensionalität des Arrays Sie Indizierung (2D, in diesem Fall) ist, die Indizierung unterschiedlich behandelt werden. Anstatt die beiden zusammen zu ziehen, benutzt numpy die Indizes wie eine Maske.

Mit anderen Worten, a[[[1, 2, 3]], [[1],[2],[3]]] wird komplett anders behandelt als a[[1, 2, 3], [1, 2, 3]], weil die Sequenzen/Arrays, die Sie übergeben, zweidimensional sind.

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

Um ein wenig genauer zu sein,

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

ist genau wie behandelt:

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

In anderen Worten, ob die Eingabe eine Zeilen-/Spaltenvektor ist, eine Kurzform für die Wiederholung der Indizes bei der Indizierung.


np.meshgrid und np.ix_ sind nur convienent Möglichkeiten, um Ihre 1D-Sequenzen in ihre 2D-Versionen für die Indizierung zu aktivieren:

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

ähnlich (das sparse Argument wäre es identisch mit ix_ oben machen):

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

Vielen Dank für Ihre Erklärungen:

Sie können mehr über die Bildbearbeitung hier mit der Pillow package (einer benutzerfreundliche Gabel auf dem PIL-Paket) lesen. Da ich mit MATLAB besser vertraut bin, finde ich die Subsetting-Konvention in numpy ein bisschen merkwürdig, aber zumindest weiß ich jetzt, wie man es richtig macht. – CrossEntropy

6

Sie könnten np.meshgrid verwenden, um den n1, n2 Arrays die richtige Form zu geben, um die gewünschte Indizierung auszuführen:

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

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

Es gibt eine ähnliche Beispiel mit einer Erklärung, wie diese integer array indexing in der Dokumentation funktioniert.

Siehe auch das Kochbuch Rezept Picking out rows and columns.

8

Eine weitere schnelle Weise des gewünschten Index zu erstellen ist, die np.ix_ Funktion zu verwenden:

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

Dies bietet eine bequeme Möglichkeit, eine offenmaschige aus Sequenzen von Indices zu konstruieren.

0

Es scheint, dass ein Anwendungsfall für Ihre spezielle Frage Bildmanipulation behandeln würde n. In dem Maße, in dem Sie Ihr Beispiel zum Bearbeiten numperiger Arrays aus Bildern verwenden, können Sie die Python Imaging Library (PIL) verwenden.

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

Das Objekt img2 ist ein numpliges Array des resultierenden beschnittenen Bildes.