6.3. Skapa arrayer

Varje exempel på resten av dessa sidor börjar med en ndarray redan i handen. Denna sida är katalogen över hur den arrayen blir till. Det finns fyra familjer av konstruktorer:

  • Från en Python-iterabel – den vanliga litteral-/list-/tupelformen.

  • Förfylld med en given form – nollor, ettor, ett konstant värde, en identitetsmatris.

  • Genererad som en sekvens – värden i ett intervall eller jämnt fördelade.

  • Omslutning av en buffert som redan finns i RAM – fallet med kringutrustning.

Varje konstruktor tar ett dtype=-nyckelord och har som standard float. Sensordata vill nästan alltid ha en mindre dtype än standardvärdet.

Varje exempel nedan börjar med:

from ulab import numpy as np

6.3.1. Från en Python-iterabel

array() bygger en ndarray från valfri iterabel av tal:

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

Utdata:

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

Nästlade iterabler producerar flerdimensionella arrayer. De inre iterablerna måste alla ha samma längd, annars utlöses ValueError

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

En redan befintlig ndarray är också en giltig indata; array() kopierar alltid. För att undvika kopian när den inte behövs, använd asarray()

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

6.3.2. Förfylld med en given form

När målformen är känd men innehållet ännu inte är det, allokera bufferten i förväg och skriv in i den senare:

  • zeros() – fylld med nollor.

  • ones() – fylld med ettor.

  • full() – fylld med ett givet värde.

  • empty() – alias för zeros() (ulab lämnar inte bufferten oinitialiserad).

  • eye() – identitetsliknande N-gånger-M-matris med ettor på den k:te diagonalen.

  • diag() – en diagonalmatris från en vektor, eller diagonalen i en matris.

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

Argumentet shape är antingen ett enskilt heltal (för en 1-D-array) eller en tupel.

6.3.3. Genererad som en sekvens

  • arange() – jämnt fördelade värden som den inbyggda range(), men som alltid returnerar en ndarray

    np.arange(0, 10, 2)            # array([0, 2, 4, 6, 8])
    
  • linspace()num jämnt fördelade punkter mellan två gränser, med den övre gränsen inkluderad när endpoint=True

    np.linspace(0, 1, num=11)      # 0.0, 0.1, ..., 1.0
    
  • logspace() – geometriskt fördelade punkter. start och stop är exponenter, inte ändpunkter; resultatet löper från base ** start till base ** stop

    np.logspace(0, 3, num=4)       # 1.0, 10.0, 100.0, 1000.0
    
  • meshgrid() – bygger två koordinatmatriser från två 1-D-arrayer så att en per-pixel-funktion f(x, y) kan utvärderas över ett helt rutnät i ett enda vektoriserat anrop. Givet en x-vektor av längd W och en y-vektor av längd H, returnerar meshgrid två H-gånger-W-matriser: X är x-vektorn upprepad nedför varje rad, Y är y-vektorn upprepad tvärs över varje kolumn, så att X[i, j] är x-koordinaten och Y[i, j] är y-koordinaten för cellen på rad i och kolumn j

    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) utvärderar sedan funktionen i varje cell i rutnätet i ett enda uttryck. En karta över avstånd-från-centrum över en (H, W)-bildruta är till exempel np.sqrt((X - cx)**2 + (Y - cy)**2) mot matriserna som meshgrid() returnerade.

6.3.4. Sammanfogning

concatenate() sammanfogar en tupel av arrayer längs en befintlig axel:

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)

Alla indata måste dela samma dtype och ndim och matcha på varje axel utom den som sammanfogas. concatenate() allokerar en ny array som är tillräckligt stor för att rymma varje indata och kopierar in datan, så det är rätt verktyg för engångssammanfogning av arrayer som redan finns; det är fel verktyg inuti en strömmande loop, där mönstret är att förallokera destinationen en gång och skriva in i den via tilldelning till delar (slice assignment).

6.3.5. Omslutning av en befintlig buffert

Den mest användbara konstruktorn på en kamera är frombuffer(). Den omtolkar en befintlig byteliknande buffert som en 1-D ndarray utan att kopiera en enda byte:

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

Skrivningar via audio syns i buf och vice versa. Den valda dtype måste dela buffertlängden jämnt.

offset= hoppar över ett sidhuvud i början av bufferten; count= begränsar hur många element som läses:

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

Detta är rätt konstruktor närhelst en kringutrustning lämnar över en rå buffert till applikationen – ADC-sampel i en bytearray, en nyttolast hämtad från SPI. De byte som kringutrustningen skrev är arrayen.

När en kringutrustning skriver flerbytesvärden i en byteordning som kamerans CPU inte läser nativt, vänder byteswap() byteordningen för varje element så att värdena läses korrekt. Den returnerar en ny array som standard; att ange inplace=True modifierar källan på plats.

frombuffer() hanterar endast de dtyper som numpy själv definierar. För kringutrustning som producerar 32-bitars heltalssampel konverterar from_int32_buffer() med flera till float i en enda omgång.