6.12. Selección y reordenación

Las reducciones colapsaban un arreglo en un escalar o en un resultado de rango inferior. La selección cubre las operaciones que eligen qué elementos sobreviven y dónde acaban: elección condicional, recorte, ordenación, búsqueda de índices y reordenación a lo largo de un eje.

6.12.1. Elección condicional

where() devuelve un arreglo que toma elementos de x donde la condición es verdadera y de y en caso contrario. Los tres operandos se difunden juntos:

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

Es la herramienta adecuada para un «if/else por elemento» sin escribir un bucle de Python.

clip() es la forma abreviada de maximum(lo, minimum(a, hi)): satura los valores a un rango:

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

maximum() y minimum() toman dos operandos y devuelven el mayor / menor elemento a elemento:

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

6.12.2. Encontrar índices

nonzero() devuelve las coordenadas de cada elemento distinto de cero, divididas en un arreglo de índices por dimensión. Para una entrada 2-D el resultado es una tupla de dos arreglos: el primero contiene los índices de fila y el segundo los índices de columna. Emparejarlos columna a columna da el (row, col) de cada posición distinta de cero:

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

Las entradas distintas de cero en m son m[0, 1] = 2 y m[1, 0] = 3. El primer arreglo devuelto [0, 1] da sus índices de fila; el segundo [1, 0] da sus índices de columna. Leer los dos arreglos en paralelo recupera las posiciones (0, 1) y (1, 0).

Dos reducciones también producen índices:

  • argmin() / argmax() – índice del elemento más pequeño / más grande.

  • argsort() – un arreglo de enteros que ordenaría la entrada a lo largo del eje dado (por defecto, el último):

    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 siempre devuelve uint16; por tanto, el arreglo que se ordena no debe tener más de 65.535 elementos en el eje ordenado.

bincount() cuenta las apariciones de cada entero no negativo en una entrada 1-D uint8 / uint16:

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

Útil para construir histogramas de valores de píxel de enteros pequeños sin escribir un bucle de Python.

6.12.3. Ordenación y reordenación

sort() devuelve una copia ordenada del arreglo a lo largo del eje dado (el último por defecto). Usa sort() directamente sobre el arreglo para una versión en el sitio (in-place):

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

flip() invierte el orden a lo largo del eje dado (todos los ejes cuando no se pasa axis):

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

roll() desplaza cíclicamente los elementos según el conteo dado. Útil para implementar un registro de desplazamiento al estilo de un búfer circular:

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

take() es la forma explícita de la indexación avanzada (fancy indexing): selecciona elementos en índices arbitrarios:

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. Filtrado y ediciones estructurales

compress() es la forma explícita de la indexación booleana: devuelve las porciones de a seleccionadas por la condición booleana:

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

delete() devuelve una copia con las entradas de los índices dados eliminadas:

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

diff() devuelve la n-ésima diferencia discreta hacia adelante del arreglo a lo largo de un eje. Se usa para calcular cambios de primer orden entre muestras adyacentes:

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. Qué cuesta cada operación

Casi todas las funciones de esta página devuelven un arreglo recién asignado. Dos excepciones:

  • sort() ordena en el sitio; la función libre sort() devuelve una copia ordenada.

  • take() acepta una palabra clave out= para escribir en un búfer que ya existe.

En un bucle que se ejecuta muchas veces por segundo, prefiere la versión en el sitio sort() y reutiliza búferes preasignados en todo lo demás. Las propias máscaras booleanas se asignan cada vez que se ejecuta la comparación: construye una máscara una vez y reutilízala entre operaciones en lugar de reconstruirla dentro de cada iteración.