wake-up-neo.net

Subsetting eines 2D-numpy-Arrays

Ich habe hier Dokumentationen und auch andere Fragen durchgesehen, aber es scheint, als hätte ich noch nicht das Gefühl, in numpy Arrays zu subsetting. 

Ich habe ein numpy-Array Um Argumente zuzulassen, sei es wie folgt definiert:

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 möchte ich Zeilen und Spalten von a auswählen, die durch die Vektoren n1 und n2 angegeben werden. 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 ausgewählt, nicht der gesamte 5x5-Block. Die Lösung, die ich gefunden habe, ist es so zu machen:

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

Ich bin mir jedoch sicher, dass es eine Möglichkeit geben sollte, diese einfache Aufgabe mit nur einem Befehl auszuführen.

21
CrossEntropy

Sie haben ein paar schöne Beispiele dafür bekommen, wie Sie das tun, 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 einige einfache Regeln, die Ihnen in Zukunft helfen werden.

Es gibt einen großen Unterschied zwischen "ausgefallener" Indizierung (d. H. Verwendung einer Liste/Sequenz) und "normaler" Indizierung (unter Verwendung eines Slice). Der zugrunde liegende Grund hat damit zu tun, ob das Array "regelmäßig verschoben" werden kann oder nicht und ob daher eine Kopie erstellt werden muss oder nicht. Willkürliche Sequenzen müssen daher unterschiedlich behandelt werden, wenn wir "Ansichten" erstellen möchten, 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,:]]

Eine ausgefallene Indizierung mit 1D-Sequenzen ist grundsätzlich gleichbedeutend mit dem Zusammenfassen und Indizieren mit dem Ergebnis. 

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

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

Wenn die indizierten Sequenzen jedoch mit der Dimensionalität des zu indizierenden Arrays übereinstimmen (in diesem Fall 2D), wird die Indizierung unterschiedlich behandelt. Anstatt "die beiden zusammenzippen", verwendet numpy die Indizes wie eine Maske.

Mit anderen Worten, a[[[1, 2, 3]], [[1],[2],[3]]] wird völlig anders behandelt als a[[1, 2, 3], [1, 2, 3]], da 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 etwas genauer zu sein, 

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

wird genau so behandelt:

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

Mit anderen Worten, ob es sich bei der Eingabe um einen Zeilen-/Spaltenvektor handelt, ist eine Abkürzung für die Wiederholung der Indizes bei der Indizierung.


np.meshgrid und np.ix_ sind nur praktische Methoden, um Ihre 1D-Sequenzen für die Indizierung in ihre 2D-Versionen umzuwandeln:

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

In ähnlicher Weise (das Argument sparse würde es mit dem obigen ix_ identisch 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]])]
19
Joe Kington

Ein weiterer schneller Weg, um den gewünschten Index zu erstellen, ist die Funktion 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]])

Dies bietet eine bequeme Möglichkeit zum Erstellen eines offenen Netzes aus Indexfolgen.

9
Alex Riley

Sie können np.meshgrid verwenden, um den n1- und n2-Arrays die richtige Form zu geben, um die gewünschte Indizierung durchzufü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 Maschennetz:

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 ein ähnliches Beispiel mit einer Erläuterung der Funktionsweise dieser Ganzzahl-Array-Indizierung in den Dokumenten.

Siehe auch Kochbuchrezept Zeilen und Spalten auswählen .

6
unutbu

Es scheint, dass ein Anwendungsfall für Ihre spezielle Frage die Bildmanipulation betreffen würde. In dem Maße, in dem Sie in Ihrem Beispiel aus Bildern bestehende numpy-Arrays bearbeiten, 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 img2-Objekt ist ein numpy-Array des resultierenden zugeschnittenen Bildes. 

Mehr über die Bildbearbeitung erfahren Sie hier mit dem Pillow-Paket (eine benutzerfreundliche Verzweigung für das PIL-Paket): 

0
mkultra