6.3. Arrays erstellen

Jedes Beispiel auf den übrigen dieser Seiten beginnt mit einem bereits vorhandenen ndarray. Diese Seite ist der Katalog dafür, wie dieses Array entsteht. Es gibt vier Familien von Konstruktoren:

  • Aus einem Python-Iterable – die übliche Literal-/Listen-/Tupel-Form.

  • Vorgefüllt mit einer bestimmten Form – Nullen, Einsen, ein konstanter Wert, eine Einheitsmatrix.

  • Als Sequenz erzeugt – bereichsbasierte oder gleichmäßig verteilte Werte.

  • Umhüllung eines bereits im RAM befindlichen Puffers – der Peripheriefall.

Jeder Konstruktor nimmt ein dtype=-Schlüsselwort und verwendet standardmäßig float. Sensordaten möchten fast immer einen kleineren dtype als die Voreinstellung.

Jedes Beispiel unten beginnt mit:

from ulab import numpy as np

6.3.1. Aus einem Python-Iterable

array() baut ein ndarray aus einem beliebigen Iterable von Zahlen:

a = np.array([1, 2, 3, 4])
print(a)

Ausgabe:

array([1.0, 2.0, 3.0, 4.0], dtype=float)

Verschachtelte Iterables erzeugen mehrdimensionale Arrays. Die inneren Iterables müssen alle dieselbe Länge haben, andernfalls wird ein ValueError ausgelöst:

m = np.array([[1, 2, 3],
              [4, 5, 6]], dtype=np.uint8)

Ein bereits vorhandenes ndarray ist ebenfalls eine gültige Eingabe; array() kopiert immer. Um die Kopie zu vermeiden, wenn sie nicht benötigt wird, verwende asarray():

b = np.asarray(a, dtype=np.float)   # same dtype -> no copy

6.3.2. Vorgefüllt mit einer bestimmten Form

Wenn die Zielform bekannt ist, der Inhalt aber noch nicht, allokiere den Puffer im Voraus und schreibe später hinein:

  • zeros() – mit Nullen gefüllt.

  • ones() – mit Einsen gefüllt.

  • full() – mit einem gegebenen Wert gefüllt.

  • empty() – Alias für zeros() (ulab lässt den Puffer nicht uninitialisiert).

  • eye() – einheitsmatrix-ähnliche N-mal-M-Matrix mit Einsen auf der k-ten Diagonale.

  • diag() – eine Diagonalmatrix aus einem Vektor oder die Diagonale einer Matrix.

np.zeros((3, 3))                   # 3x3 of zeros
np.ones(5, dtype=np.uint8)         # length-5 vector of ones
np.full((2, 3), 7, dtype=np.int8)  # 2x3, all 7
np.eye(4)                          # 4x4 identity
np.diag([1, 2, 3])                 # 3x3, [1, 2, 3] on the diagonal

Das shape-Argument ist entweder ein einzelner Integer (für ein 1-D-Array) oder ein Tupel.

6.3.3. Als Sequenz erzeugt

  • arange() – gleichmäßig verteilte Werte wie das eingebaute range(), aber immer ein ndarray zurückgebend:

    np.arange(0, 10, 2)            # array([0, 2, 4, 6, 8])
    
  • linspace()num gleichmäßig verteilte Punkte zwischen zwei Grenzen, wobei die obere Grenze eingeschlossen wird, wenn endpoint=True ist:

    np.linspace(0, 1, num=11)      # 0.0, 0.1, ..., 1.0
    
  • logspace() – geometrisch verteilte Punkte. start und stop sind Exponenten, keine Endpunkte; das Ergebnis läuft von base ** start bis base ** stop:

    np.logspace(0, 3, num=4)       # 1.0, 10.0, 100.0, 1000.0
    
  • meshgrid() – baut zwei Koordinatenmatrizen aus zwei 1-D-Arrays, damit eine Pro-Pixel-Funktion f(x, y) über ein ganzes Gitter in einem einzigen vektorisierten Aufruf ausgewertet werden kann. Gegeben einen x-Vektor der Länge W und einen y-Vektor der Länge H, gibt meshgrid zwei H-mal-W-Matrizen zurück: X ist der x-Vektor, der in jeder Zeile nach unten wiederholt wird, Y ist der y-Vektor, der über jede Spalte hinweg wiederholt wird, sodass X[i, j] die x-Koordinate und Y[i, j] die y-Koordinate der Zelle in Zeile i und Spalte j ist:

    x = np.arange(4)            # [0, 1, 2, 3]
    y = np.arange(3)            # [0, 1, 2]
    X, Y = np.meshgrid(x, y)
    # X = [[0, 1, 2, 3],
    #      [0, 1, 2, 3],
    #      [0, 1, 2, 3]]
    # Y = [[0, 0, 0, 0],
    #      [1, 1, 1, 1],
    #      [2, 2, 2, 2]]
    

    f(X, Y) wertet dann die Funktion an jeder Zelle des Gitters in einem Ausdruck aus. Eine Karte des Abstands vom Mittelpunkt über ein (H, W)-Einzelbild ist zum Beispiel np.sqrt((X - cx)**2 + (Y - cy)**2) angewandt auf die von meshgrid() zurückgegebenen Matrizen.

6.3.4. Verbinden

concatenate() verbindet ein Tupel von Arrays entlang einer bestehenden Achse:

a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
b = np.array([[5, 6]],         dtype=np.uint8)
np.concatenate((a, b), axis=0)
# array([[1, 2], [3, 4], [5, 6]], dtype=uint8)

Alle Eingaben müssen denselben dtype und ndim teilen und auf jeder Achse außer der verbindenden übereinstimmen. concatenate() allokiert ein frisches Array, das groß genug ist, um jede Eingabe aufzunehmen, und kopiert die Daten hinein, daher ist es das richtige Werkzeug für das einmalige Verbinden bereits vorhandener Arrays; es ist das falsche Werkzeug innerhalb einer Streaming-Schleife, wo das einmalige Vorab-Allokieren des Ziels und das Hineinschreiben über Slice-Zuweisung das richtige Muster ist.

6.3.5. Umhüllen eines vorhandenen Puffers

Der nützlichste Konstruktor auf einer Kamera ist frombuffer(). Er interpretiert einen vorhandenen bytesähnlichen Puffer als 1-D-ndarray neu, ohne ein einziges Byte zu kopieren:

buf = bytearray(8)
audio = np.frombuffer(buf, dtype=np.int16)
# 4 int16 samples, sharing memory with buf

Schreibvorgänge über audio sind in buf sichtbar und umgekehrt. Der gewählte dtype muss die Pufferlänge gleichmäßig teilen.

offset= überspringt einen Header am Anfang des Puffers; count= begrenzt, wie viele Elemente gelesen werden:

np.frombuffer(buf, dtype=np.uint8, offset=2, count=4)

Dies ist der richtige Konstruktor, wann immer ein Peripheriegerät der Anwendung einen rohen Puffer übergibt – ADC-Samples in einem bytearray, eine aus SPI gezogene Nutzlast. Die Bytes, die das Peripheriegerät geschrieben hat, sind das Array.

Wenn ein Peripheriegerät Mehrbyte-Werte in einer Byte-Reihenfolge schreibt, die die CPU der Kamera nicht nativ liest, kehrt byteswap() die Byte-Reihenfolge jedes Elements um, damit die Werte korrekt gelesen werden. Standardmäßig wird ein neues Array zurückgegeben; die Übergabe von inplace=True modifiziert die Quelle an Ort und Stelle.

frombuffer() verarbeitet nur die dtypes, die numpy selbst definiert. Für Peripheriegeräte, die 32-Bit-Integer-Samples erzeugen, konvertieren from_int32_buffer() und verwandte Funktionen in einem Durchgang nach float.