6.12. Wybór i porządkowanie

Redukcje zwijały tablicę do skalara lub wyniku o niższej randze. Wybór obejmuje operacje, które decydują o tym, które elementy przetrwają i gdzie trafią: wybór warunkowy, przycinanie, sortowanie, wyszukiwanie indeksów, zmiana kolejności wzdłuż osi.

6.12.1. Wybór warunkowy

where() zwraca tablicę, która pobiera elementy z x tam, gdzie warunek jest prawdziwy, a w przeciwnym razie z y. Trzy operandy są wspólnie rozpropagowane:

a = np.array([1, 2, 3, 4, 5], dtype=np.float)
np.where(a < 3, a, 0.0)
# array([1.0, 2.0, 0.0, 0.0, 0.0])

To właściwe narzędzie do realizacji „if/else dla każdego elementu” bez pisania pętli w Pythonie.

clip() to skrót dla maximum(lo, minimum(a, hi)) – nasyca wartości do zakresu:

np.clip(a, 2.0, 4.0)
# array([2.0, 2.0, 3.0, 4.0, 4.0])

maximum() i minimum() przyjmują dwa operandy i zwracają element po elemencie większą / mniejszą wartość:

np.maximum(a, 3.0)
np.minimum(a, np.array([5, 4, 3, 2, 1]))

6.12.2. Wyszukiwanie indeksów

nonzero() zwraca współrzędne każdego niezerowego elementu, podzielone na jedną tablicę indeksów na wymiar. Dla danych dwuwymiarowych wynikiem jest krotka dwóch tablic: pierwsza zawiera indeksy wierszy, druga indeksy kolumn. Zestawienie ich kolumnami daje (row, col) każdej niezerowej pozycji:

m = np.array([[0, 2, 0],
              [3, 0, 0]], dtype=np.float)
np.nonzero(m)
# (array([0, 1], dtype=uint16), array([1, 0], dtype=uint16))

Niezerowe wpisy w m to m[0, 1] = 2 oraz m[1, 0] = 3. Pierwsza zwrócona tablica [0, 1] podaje ich indeksy wierszy; druga [1, 0] podaje ich indeksy kolumn. Odczytanie obu tablic obok siebie odtwarza pozycje (0, 1) oraz (1, 0).

Dwie redukcje również produkują indeksy:

  • argmin() / argmax() – indeks najmniejszego / największego elementu.

  • argsort() – tablica liczb całkowitych, która posortowałaby dane wejściowe wzdłuż zadanej osi (domyślnie ostatniej):

    a = np.array([40, 10, 30, 20], dtype=np.uint8)
    idx = np.argsort(a)             # array([1, 3, 2, 0], dtype=uint16)
    a[idx]                          # array([10, 20, 30, 40])
    

    argsort zawsze zwraca uint16; sortowana tablica nie może zatem mieć więcej niż 65 535 elementów na sortowanej osi.

bincount() zlicza wystąpienia każdej nieujemnej liczby całkowitej w jednowymiarowych danych uint8 / uint16

histogram = np.bincount(np.array([0, 1, 1, 2, 2, 2], dtype=np.uint8))
# array([1, 2, 3], dtype=uint16)

Przydatne do budowania histogramów małych całkowitych wartości pikseli bez pisania pętli w Pythonie.

6.12.3. Sortowanie i zmiana kolejności

sort() zwraca posortowaną kopię tablicy wzdłuż zadanej osi (domyślnie ostatniej). Użyj sort() bezpośrednio na tablicy, aby uzyskać wersję wykonywaną w miejscu:

np.sort(np.array([3, 1, 2], dtype=np.float))
# array([1.0, 2.0, 3.0])

flip() odwraca kolejność wzdłuż zadanej osi (każdej osi, gdy nie podano axis):

np.flip(np.array([1, 2, 3, 4]))
# array([4, 3, 2, 1])

roll() cyklicznie przesuwa elementy o zadaną liczbę pozycji. Przydatne do implementacji rejestru przesuwnego w stylu bufora pierścieniowego:

np.roll(np.array([1, 2, 3, 4]), 1)
# array([4, 1, 2, 3])

take() to jawna forma zaawansowanego indeksowania – wybiera elementy pod dowolnymi indeksami:

a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.take(a, [0, 2, 4])
# array([10, 30, 50], dtype=uint8)

6.12.4. Filtrowanie i edycje strukturalne

compress() to jawna forma indeksowania logicznego – zwraca wycinki a wybrane przez warunek logiczny:

a = np.array([10, 20, 30, 40], dtype=np.uint8)
np.compress(a > 15, a)
# array([20, 30, 40], dtype=uint8)

delete() zwraca kopię z usuniętymi wpisami pod zadanymi indeksami:

a = np.array([10, 20, 30, 40, 50], dtype=np.uint8)
np.delete(a, [1, 3])
# array([10, 30, 50], dtype=uint8)

diff() zwraca n-tą dyskretną różnicę w przód tablicy wzdłuż osi. Używana do obliczania zmian pierwszego rzędu między sąsiednimi próbkami:

samples = np.array([1, 3, 6, 10, 15], dtype=np.float)
np.diff(samples)
# array([2.0, 3.0, 4.0, 5.0])

6.12.5. Ile kosztuje każda operacja

Niemal każda funkcja na tej stronie zwraca świeżo zaalokowaną tablicę. Dwa wyjątki:

  • sort() sortuje w miejscu; wolna funkcja sort() zwraca posortowaną kopię.

  • take() przyjmuje słowo kluczowe out=, aby zapisywać do już istniejącego bufora.

W pętli wykonywanej wiele razy na sekundę preferuj sortowanie w miejscu sort() i wszędzie indziej wykorzystuj ponownie wstępnie zaalokowane bufory. Same maski logiczne są alokowane za każdym razem, gdy uruchamiane jest porównanie – zbuduj maskę raz i używaj jej ponownie w wielu operacjach, zamiast odbudowywać ją w każdej iteracji.