6.12. Selectie en herschikking

Reducties vouwden een array samen tot een scalair of een resultaat met lagere rang. Selectie dekt de bewerkingen die kiezen welke elementen overleven en waar ze terechtkomen: voorwaardelijke keuze, afkappen, sorteren, indices opzoeken, herordenen langs een as.

6.12.1. Voorwaardelijke keuze

where() geeft een array terug die elementen uit x neemt waar de voorwaarde waar is en anders uit y. De drie operanden broadcasten samen:

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

Dit is het juiste gereedschap voor een “if/else per element” zonder een Python-lus te schrijven.

clip() is een afkorting voor maximum(lo, minimum(a, hi)) – verzadig de waarden tot een bereik:

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

maximum() en minimum() nemen twee operanden en geven elementsgewijs de grotere / kleinere terug:

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

6.12.2. Indices vinden

nonzero() geeft de coördinaten terug van elk niet-nul element, opgesplitst in één index-array per dimensie. Voor een 2-D-invoer is het resultaat een tuple van twee arrays: de eerste bevat de rij-indices, de tweede bevat de kolom-indices. Door ze kolomsgewijs te koppelen krijg je de (row, col) van elke niet-nul positie:

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

De niet-nul elementen in m zijn m[0, 1] = 2 en m[1, 0] = 3. De eerste teruggegeven array [0, 1] geeft hun rij-indices; de tweede [1, 0] geeft hun kolom-indices. Door de twee arrays naast elkaar te lezen, herstel je de posities (0, 1) en (1, 0).

Twee reducties produceren ook indices:

  • argmin() / argmax() – index van het kleinste / grootste element.

  • argsort() – een geheeltallige array die de invoer langs de gegeven as zou sorteren (standaard de laatste):

    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 geeft altijd uint16 terug; de te sorteren array mag daarom niet meer dan 65.535 elementen op de gesorteerde as hebben.

bincount() telt het aantal voorkomens van elk niet-negatief geheel getal in een 1-D uint8 / uint16-invoer:

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

Handig om histogrammen van pixelwaarden met kleine gehele getallen te bouwen zonder een Python-lus te schrijven.

6.12.3. Sorteren en herordenen

sort() geeft een gesorteerde kopie van de array langs de gegeven as terug (standaard de laatste). Gebruik sort() rechtstreeks op de array voor een in-place versie:

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

flip() keert de volgorde langs de gegeven as om (elke as wanneer er geen axis wordt meegegeven):

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

roll() verschuift elementen cyclisch met het gegeven aantal. Handig voor het implementeren van een schuifregister in ringbufferstijl:

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

take() is de expliciete vorm van fancy indexing – kies elementen op willekeurige indices:

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. Filteren en structurele bewerkingen

compress() is de expliciete vorm van booleaanse indexing – geef de segmenten van a terug die door de booleaanse voorwaarde zijn geselecteerd:

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

delete() geeft een kopie terug waarin de elementen op de gegeven indices zijn verwijderd:

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

diff() geeft het n-de discrete voorwaartse verschil van de array langs een as terug. Gebruikt om eerste-orde veranderingen tussen aangrenzende monsters te berekenen:

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. Wat elke bewerking kost

Bijna elke functie op deze pagina geeft een vers toegewezen array terug. Twee uitzonderingen:

  • sort() sorteert in-place; de vrije functie sort() geeft een gesorteerde kopie terug.

  • take() accepteert een out=-trefwoord om in een al bestaande buffer te schrijven.

Geef in een lus die vele keren per seconde draait de voorkeur aan de in-place sort() en hergebruik overal elders vooraf toegewezen buffers. Booleaanse maskers zelf worden elke keer dat de vergelijking draait toegewezen – bouw een masker eenmalig en hergebruik het over bewerkingen heen in plaats van het binnen elke iteratie opnieuw op te bouwen.