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])
argsortzawsze zwracauint16; 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 funkcjasort()zwraca posortowaną kopię.take()przyjmuje słowo kluczoweout=, 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.