6.12. Výběr a přeuspořádání

Redukce sbalily pole na skalár nebo výsledek nižšího řádu. Výběr pokrývá operace, které vybírají, které prvky přežijí a kam skončí: podmíněná volba, oříznutí, řazení, vyhledávání indexů, přeuspořádání podél osy.

6.12.1. Podmíněná volba

where() vrací pole, které přebírá prvky z x tam, kde je podmínka pravdivá, a z y jinak. Tyto tři operandy se broadcastují dohromady:

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 je správný nástroj pro „if/else na každý prvek“ bez psaní smyčky v Pythonu.

clip() je zkratka pro maximum(lo, minimum(a, hi)) – nasytí hodnoty do daného rozsahu:

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

maximum() a minimum() přijímají dva operandy a vrací po prvcích větší / menší hodnotu:

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

6.12.2. Hledání indexů

nonzero() vrací souřadnice každého nenulového prvku rozdělené do jednoho indexového pole na dimenzi. Pro dvourozměrný vstup je výsledkem n-tice dvou polí: první obsahuje indexy řádků, druhé indexy sloupců. Jejich spárování po sloupcích dává (row, col) každé nenulové pozice:

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))

Nenulové položky v m jsou m[0, 1] = 2 a m[1, 0] = 3. První vrácené pole [0, 1] udává jejich indexy řádků; druhé [1, 0] udává jejich indexy sloupců. Čtení obou polí vedle sebe obnovuje pozice (0, 1) a (1, 0).

Indexy produkují i dvě redukce:

  • argmin() / argmax() – index nejmenšího / největšího prvku.

  • argsort() – celočíselné pole, které by seřadilo vstup podél dané osy (výchozí je poslední):

    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 vždy vrací uint16; řazené pole proto nesmí mít na řazené ose více než 65 535 prvků.

bincount() počítá výskyty každého nezáporného celého čísla v jednorozměrném vstupu typu uint8 / uint16

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

Užitečné pro vytváření histogramů hodnot pixelů s malými celými čísly bez psaní smyčky v Pythonu.

6.12.3. Řazení a přeuspořádání

sort() vrací seřazenou kopii pole podél dané osy (ve výchozím nastavení poslední). Pro variantu na místě použijte sort() přímo na poli:

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

flip() obrací pořadí podél dané osy (každé osy, když není axis předáno):

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

roll() cyklicky posouvá prvky o daný počet. Užitečné pro implementaci posuvného registru ve stylu kruhového bufferu:

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

take() je explicitní forma pokročilého indexování (fancy indexing) – výběr prvků na libovolných indexech:

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. Filtrování a strukturální úpravy

compress() je explicitní forma logického indexování – vrací řezy pole a vybrané logickou podmínkou:

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

delete() vrací kopii s odstraněnými položkami na daných indexech:

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

diff() vrací n-tou diskrétní dopřednou diferenci pole podél osy. Používá se k výpočtu změn prvního řádu mezi sousedními vzorky:

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. Kolik každá operace stojí

Téměř každá funkce na této stránce vrací nově alokované pole. Dvě výjimky:

  • sort() řadí na místě; volná funkce sort() vrací seřazenou kopii.

  • take() přijímá klíčové slovo out= pro zápis do již existujícího bufferu.

Ve smyčce, která běží mnohokrát za sekundu, dejte přednost sort() na místě a všude jinde znovu využívejte předem alokované buffery. Logické masky se samy alokují pokaždé, když proběhne porovnání – vytvořte masku jednou a využívejte ji napříč operacemi, místo abyste ji znovu vytvářeli uvnitř každé iterace.