6.3. Vytváření polí

Každý příklad na zbytku těchto stránek začíná tím, že již máme po ruce ndarray. Tato stránka je katalogem toho, jak takové pole vznikne. Existují čtyři rodiny konstruktorů:

  • Z Python iterovatelného objektu – obvyklá forma literálu / seznamu / n-tice.

  • Předvyplněné na daný tvar – nuly, jedničky, konstantní hodnota, jednotková matice.

  • Vygenerované jako posloupnost – hodnoty v rozsahu nebo rovnoměrně rozložené.

  • Zabalení bufferu, který už je v RAM – případ periferie.

Každý konstruktor přijímá klíčové slovo dtype= a má výchozí hodnotu float. Data ze senzorů téměř vždy chtějí menší dtype, než je výchozí.

Každý níže uvedený příklad začíná takto:

from ulab import numpy as np

6.3.1. Z Python iterovatelného objektu

array() sestaví ndarray z libovolného iterovatelného objektu obsahujícího čísla:

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

Výstup:

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

Vnořené iterovatelné objekty produkují vícerozměrná pole. Vnitřní iterovatelné objekty musí mít všechny stejnou délku, jinak je vyvolána výjimka ValueError

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

Platným vstupem je i již existující ndarray; array() vždy kopíruje. Pokud se chcete kopii vyhnout, když není potřeba, použijte asarray()

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

6.3.2. Předvyplněné na daný tvar

Když je cílový tvar známý, ale obsah ještě ne, alokujte buffer předem a zapisujte do něj později:

  • zeros() – vyplněné nulami.

  • ones() – vyplněné jedničkami.

  • full() – vyplněné danou hodnotou.

  • empty() – alias pro zeros() (ulab nenechává buffer neinicializovaný).

  • eye() – matice N-krát-M podobná jednotkové s jedničkami na k-té diagonále.

  • diag() – diagonální matice z vektoru, nebo diagonála matice.

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

Argument shape je buď jediné celé číslo (pro 1-D pole), nebo n-tice.

6.3.3. Vygenerované jako posloupnost

  • arange() – rovnoměrně rozložené hodnoty jako u vestavěné funkce range(), ale vždy vracející ndarray

    np.arange(0, 10, 2)            # array([0, 2, 4, 6, 8])
    
  • linspace()num rovnoměrně rozložených bodů mezi dvěma mezemi, s horní mezí zahrnutou, pokud je endpoint=True

    np.linspace(0, 1, num=11)      # 0.0, 0.1, ..., 1.0
    
  • logspace() – geometricky rozložené body. start a stop jsou exponenty, nikoli koncové body; výsledek probíhá od base ** start do base ** stop

    np.logspace(0, 3, num=4)       # 1.0, 10.0, 100.0, 1000.0
    
  • meshgrid() – sestaví dvě souřadnicové matice ze dvou 1-D polí, takže lze funkci na pixel f(x, y) vyhodnotit nad celou mřížkou v jediném vektorizovaném volání. Při zadání x-vektoru délky W a y-vektoru délky H vrací meshgrid dvě matice H-krát-W: X je x-vektor opakovaný v každém řádku, Y je y-vektor opakovaný v každém sloupci, takže X[i, j] je x-souřadnice a Y[i, j] je y-souřadnice buňky v řádku i a sloupci 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) pak vyhodnotí funkci v každé buňce mřížky v jediném výrazu. Mapa vzdálenosti od středu nad snímkem (H, W) je například np.sqrt((X - cx)**2 + (Y - cy)**2) proti maticím, které funkce meshgrid() vrátila.

6.3.4. Spojování

concatenate() spojí n-tici polí podél existující osy:

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)

Všechny vstupy musí sdílet stejný dtype a ndim a shodovat se na každé ose kromě té spojované. concatenate() alokuje nové pole dostatečně velké, aby pojalo každý vstup, a data do něj zkopíruje, takže je to správný nástroj pro jednorázové spojení polí, která už existují; je to nesprávný nástroj uvnitř streamovací smyčky, kde je vzorem jednorázová předalokace cíle a zápis do něj pomocí přiřazení do řezu.

6.3.5. Zabalení existujícího bufferu

Nejužitečnějším konstruktorem na kameře je frombuffer(). Reinterpretuje existující buffer podobný bytes jako 1-D ndarray bez kopírování jediného bajtu:

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

Zápisy přes audio jsou viditelné v buf a naopak. Zvolený dtype musí délku bufferu beze zbytku dělit.

offset= přeskočí hlavičku na začátku bufferu; count= omezuje, kolik prvků se přečte:

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

Toto je správný konstruktor, kdykoli periferie předá aplikaci surový buffer – vzorky z ADC v bytearray, data získaná z SPI. Bajty, které periferie zapsala, jsou tím polem.

Když periferie zapisuje vícebajtové hodnoty v pořadí bajtů, které CPU kamery nativně nečte, byteswap() obrátí pořadí bajtů každého prvku, takže se hodnoty čtou správně. Ve výchozím nastavení vrací nové pole; předání inplace=True upraví zdroj na místě.

frombuffer() zvládá jen dtypy, které numpy sám definuje. Pro periferie produkující 32bitové celočíselné vzorky převedou from_int32_buffer() a příbuzné funkce data na float v jednom průchodu.