Nächstes Kapitel: Matrix-Arithmetik
# invisible
import numpy as np
np.core.arrayprint._line_width = 65
Boolesche Maskierung und Indizierung
In diesem Kapitel geht es um die Boolesche Maskierung (englisch: Boolean Masking) und Booleschen Masken. Wir zeigen, wie man damit die Werte von NumPy-Arrays verändern kann.
Maskierung ist hilfreich, um Daten mit bestimmten Eigenschaften zu extrahieren, zu verändern, zu zählen und so weiter. Außerdem können damit auch sehr leicht einfache Binarisierungen vorgenommen werden, also alle Werte, die über einer bestimmten Schwelle liegen, auf einen Wert setzen und alle darunter liegenden Werte auf einen anderen. Die Benutzung von Maskierungen gestaltet sich meistens nicht nur sehr einfach, sondern dabei handelt es sich meistens auch um die effizienteste Art, diese Operationen durchzuführen.
Im ersten Beispiel werden alle Komponenten des Arrays A
mit der Zahl verglichen. Das Ergebnis der Maskierung besteht in einem neuen Array mit der gleichen Shape, in dem ein True
steht, falls an der entsprechenden Position in A
eine 4 stand, ansonsten wird der Wert auf False
gesetzt.
import numpy as np
A = np.array([4, 7, 3, 4, 2, 8])
print(A == 4)
Analog kann man die einzelnen Komponenten auch mittels der Vergleichsoperatoren "<", "<=", ">" und ">=" bearbeiten. Die Arbeitsweise ist analog zu dem vorigen Fall:
print(A < 5)
Dies lässt sich auch auf Arrays mit höherer Dimension anwenden:
B = np.array([[42, 56, 89, 65],
[99, 88, 42, 12],
[55, 42, 17, 18]])
print(B >= 42)
Damit lassen sich auch Arrays binarisieren. Betrachten wir das folgende Array A
als ein Grauwertbild, so können wir dieses mit der Schwelle 15 binarisieren:
import numpy as np
A = np.array([
[12, 13, 14, 12, 16, 14, 11, 10, 9],
[11, 14, 12, 15, 15, 16, 10, 12, 11],
[10, 12, 12, 15, 14, 16, 10, 12, 12],
[ 9, 11, 16, 15, 14, 16, 15, 12, 10],
[12, 11, 16, 14, 10, 12, 16, 12, 13],
[10, 15, 16, 14, 14, 14, 16, 15, 12],
[13, 17, 14, 10, 14, 11, 14, 15, 10],
[10, 16, 12, 14, 11, 12, 14, 18, 11],
[10, 19, 12, 14, 11, 12, 14, 18, 10],
[14, 22, 17, 19, 16, 17, 18, 17, 13],
[10, 16, 12, 14, 11, 12, 14, 18, 11],
[10, 16, 12, 14, 11, 12, 14, 18, 11],
[10, 19, 12, 14, 11, 12, 14, 18, 10],
[14, 22, 12, 14, 11, 12, 14, 17, 13],
[10, 16, 12, 14, 11, 12, 14, 18, 11]])
B = A < 15
B.astype(np.int)
Alle Werte des Originalarrays A
wurden durch 0 bzw. 1 ersetzt. Im Bild kann man übrigens auch ein großes A erkennen.
Das Prinzip der "Fancy-Indizierung" ist recht einfach: Statt eines einzelnen Indexes benutzt man ein Array mit Indizes. Dadurch kann man mehrere Elemente auf einen Schlag ansprechen.
A = np.array([34, 8, 99, 12, 1, 102, 44])
# umständlich:
B = np.array([A[1], A[3], A[5]])
print(B)
# mit fancy Indizierung:
B2 = A[[1, 3, 5]]
print(B2)
In unserem nächsten Beispiel benutzen wir die boolesche Maske eines Arrays, um die entsprechenden Elemente eines anderen Arrays auszuwählen, d.h. wir indizieren das Array C
mit einer Booleschen Maske, die wir mittels Maskierung des Arrays A
erzeugen. Das Ergebnis ist dann eine Kopie und keine Sicht (View).
Das neue Array R beinhaltet all die Elemente aus C, bei denen im Array A der Test A <= 5 True
liefert.
C = np.array([123, 188, 190, 99, 77, 88, 100])
A = np.array([4, 7, 2,8, 6, 9, 5])
print(A <= 5)
R = C[A <= 5]
print(R)
Indizieren lässt sich auch beispielsweise mit einem Integer-Array oder mit einer Integer-Liste. Letzteres tun wir im nächsten Beispiel:
lst = [0, 2, 3, 1, 4, 1]
C[lst]
Wir wir sehen, können Indizes mehrfach und in beliebiger Reihenfolge auftreten!
Extrahieren Sie aus dem Array np.array([3, 4, 6, 10, 24, 89, 45, 43, 46, 99, 100])
, anhand von boolescher Indizierung, die Werte, die:
- nicht durch 3 teilbar sind
- durch 5 teilbar sind
- die durch 3 und durch 5 teilbar sind
- die durch 3 teilbar sind und setzen Sie diese auf 42
import numpy as np
A = np.array([3, 4, 6, 10, 24, 89, 45, 43, 46, 99, 100])
div3 = A[A % 3 != 0]
print("Elemente von A, die nicht durch 3 teilbar sind:")
print(div3)
div5 = A[A % 5 == 0]
print("Elemente von A, die durch 5 teilbar sind:")
print(div5)
print("Elemente von A, die durch 3 und 5 teilbar sind:")
print(A[(A % 3 == 0) & (A % 5 == 0)])
A[ A % 3 == 0] = 42
print("Alle durch 3 teilbaren Werte von A wurden auf 42 gesetzt:")
print(A)
nonzero und where
Die Methode nonzero
liefert die Indizes der Elemente aus einem Array zurück, die nicht 0 (non-zero) sind. Die Indizes werden als Tupel von eindimensionalen Arrays zurückgeliefert, eins für jede Dimension. Die entsprechenden non-zero-Werte eines Arrays A kann man dann durch Boolesches Indizieren erhalten:
A[numpy.nonzero(A)]
import numpy as np
A = np.array([[0, 2, 3, 0, 1],
[1, 0, 0, 7, 0],
[5, 0, 0, 1, 0]])
print(A.nonzero())
print(A[A.nonzero()])
Möchte man die Elemente als Pärchen von Zeilen und Spalten haben, so kann man transpose
benutzen:
transpose(nonzero(A))
Es wird ein zweidimensionales Array erzeugt. Jede Zeile entspricht den Indizes eines non-zero-Elements in der Form [Zeile, Spalte]
np.transpose(A.nonzero())
Die Funktion nonzero
kann dazu verwendet werden, um die Indizes aus einem Array zu holen, bei denen die Bedingung True ist.
Im folgenden Skript erstellen wir das boolesche Array B >= 42:
B = np.array([[42, 56, 89, 65],
[99, 88, 42, 12],
[55, 42, 17, 18]])
print(B >= 42)
np.nonzero(B >= 42)
produziert die Indizes aus B, auf die die Bedingung zutrifft.
B = np.array([[42, 56, 89, 65],
[99, 88, 42, 12],
[55, 42, 17, 18]])
np.nonzero(B >= 42)
Berechnen Sie die Primzahlen zwischen 0 und 100 mit Hilfe eines booleschen Arrays.
import numpy as np
is_prime = np.ones((100,), dtype=bool)
# Cross out 0 and 1 which are not primes:
is_prime[:2] = 0
# cross out its higher multiples (sieve of Eratosthenes):
nmax = int(np.sqrt(len(is_prime)))
for i in range(2, nmax):
is_prime[2*i::i] = False
print(np.nonzero(is_prime))
Ähnliche Funktionen:
flatnonzero
:
Liefert die Indizes zurück, die non-zero sind, jedoch aus der flachen (eindimensionalen) Version der übergebenen Arrays.
count_nonzero
:Zählt die non-zero-Elemente in dem übergebenen Array.
Nächstes Kapitel: Matrix-Arithmetik