6.3. Tworzenie tablic

Każdy przykład na pozostałych stronach zaczyna się od gotowej tablicy ndarray. Ta strona to katalog sposobów, w jakie taka tablica powstaje. Istnieją cztery rodziny konstruktorów:

  • Z iterowalnego obiektu Pythona – zwykła forma literału / listy / krotki.

  • Wstępnie wypełniona o danym kształcie – zera, jedynki, stała wartość, macierz jednostkowa.

  • Wygenerowana jako sekwencja – wartości z zakresu lub równomiernie rozłożone.

  • Opakowująca bufor już obecny w pamięci RAM – przypadek urządzenia peryferyjnego.

Każdy konstruktor przyjmuje słowo kluczowe dtype= i domyślnie używa float. Dane z sensorów prawie zawsze wymagają mniejszego dtype niż domyślny.

Każdy poniższy przykład zaczyna się od:

from ulab import numpy as np

6.3.1. Z iterowalnego obiektu Pythona

array() buduje ndarray z dowolnego iterowalnego obiektu liczb:

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

Wynik:

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

Zagnieżdżone obiekty iterowalne tworzą tablice wielowymiarowe. Wewnętrzne obiekty iterowalne muszą mieć tę samą długość, w przeciwnym razie zgłaszany jest ValueError

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

Istniejąca już ndarray jest również poprawnym wejściem; array() zawsze kopiuje. Aby uniknąć kopiowania, gdy nie jest potrzebne, użyj asarray()

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

6.3.2. Wstępnie wypełniona o danym kształcie

Gdy docelowy kształt jest znany, ale zawartość jeszcze nie, zaalokuj bufor z wyprzedzeniem i zapisz do niego dane później:

  • zeros() – wypełniona zerami.

  • ones() – wypełniona jedynkami.

  • full() – wypełniona zadaną wartością.

  • empty() – alias dla zeros() (ulab nie pozostawia bufora niezainicjalizowanego).

  • eye() – macierz N-na-M podobna do jednostkowej, z jedynkami na k-tej przekątnej.

  • diag() – macierz diagonalna z wektora lub przekątna macierzy.

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 jest albo pojedynczą liczbą całkowitą (dla tablicy 1-W), albo krotką.

6.3.3. Wygenerowana jako sekwencja

  • arange() – równomiernie rozłożone wartości jak wbudowane range(), ale zawsze zwracające ndarray

    np.arange(0, 10, 2)            # array([0, 2, 4, 6, 8])
    
  • linspace()num równomiernie rozłożonych punktów między dwiema granicami, z dołączoną górną granicą, gdy endpoint=True

    np.linspace(0, 1, num=11)      # 0.0, 0.1, ..., 1.0
    
  • logspace() – punkty rozłożone geometrycznie. start i stopwykładnikami, a nie punktami krańcowymi; wynik biegnie od base ** start do base ** stop

    np.logspace(0, 3, num=4)       # 1.0, 10.0, 100.0, 1000.0
    
  • meshgrid() – buduje dwie macierze współrzędnych z dwóch tablic 1-W, tak aby funkcję per-piksel f(x, y) można było obliczyć dla całej siatki w jednym wektoryzowanym wywołaniu. Dla wektora x o długości W i wektora y o długości H funkcja meshgrid zwraca dwie macierze H-na-W: X to wektor x powtórzony w każdym wierszu, a Y to wektor y powtórzony w każdej kolumnie, więc X[i, j] jest współrzędną x, a Y[i, j] współrzędną y komórki w wierszu i i kolumnie 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) oblicza następnie funkcję w każdej komórce siatki w jednym wyrażeniu. Mapa odległości od środka dla ramki (H, W) to na przykład np.sqrt((X - cx)**2 + (Y - cy)**2) na macierzach zwróconych przez meshgrid().

6.3.4. Łączenie

concatenate() łączy krotkę tablic wzdłuż istniejącej osi:

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)

Wszystkie wejścia muszą mieć ten sam dtype i ndim oraz zgadzać się na każdej osi poza osią łączenia. concatenate() alokuje nową tablicę wystarczająco dużą, aby pomieścić każde wejście, i kopiuje do niej dane, więc jest właściwym narzędziem do jednorazowego łączenia istniejących już tablic; jest natomiast niewłaściwym narzędziem wewnątrz pętli strumieniowej, gdzie wzorcem jest jednorazowe wstępne zaalokowanie miejsca docelowego i zapisywanie do niego przez przypisanie wycinka.

6.3.5. Opakowywanie istniejącego bufora

Najbardziej przydatnym konstruktorem na kamerze jest frombuffer(). Reinterpretuje istniejący bufor bajtopodobny jako 1-W tablicę ndarray bez kopiowania ani jednego bajtu:

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

Zapisy przez audio są widoczne w buf i odwrotnie. Wybrany dtype musi równo dzielić długość bufora.

offset= pomija nagłówek na początku bufora; count= ogranicza liczbę odczytywanych elementów:

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

To właściwy konstruktor, gdy urządzenie peryferyjne przekazuje aplikacji surowy bufor – próbki z ADC w bytearray, ładunek pobrany z SPI. Bajty zapisane przez urządzenie peryferyjne są tą tablicą.

Gdy urządzenie peryferyjne zapisuje wartości wielobajtowe w kolejności bajtów, której procesor kamery nie odczytuje natywnie, byteswap() odwraca kolejność bajtów każdego elementu, tak aby wartości odczytywały się poprawnie. Domyślnie zwraca nową tablicę; przekazanie inplace=True modyfikuje źródło w miejscu.

frombuffer() obsługuje tylko dtype, które definiuje samo numpy. Dla urządzeń peryferyjnych produkujących 32-bitowe próbki całkowitoliczbowe from_int32_buffer() i pokrewne funkcje konwertują do float w jednym przebiegu.