NumPy práctico (III): Indexación y slicing

La indexación de arrays de NumPy es un tema amplio, y hay muchas formas diferentes de seleccionar elementos de un array.

Empecemos con el caso más simple: seleccionar una entrada de un array unidimensional.

import numpy as np

arr = np.arange(10)
print(arr)
[0 1 2 3 4 5 6 7 8 9]

Puedes acceder a elementos de un array unidimensional en NumPy usando la misma sintaxis que usarías para listas regulares de Python. Tanto la indexación positiva como la negativa funcionan como esperarías:

at_index_three = arr[3]
print(at_index_three)
3
penultimate_element = arr[-2]
print(penultimate_element)
8

La sintaxis para seleccionar slices también funciona de forma muy similar a Python regular. Sigue el mismo patrón:

array[desde_este_indice_inclusivo, hasta_este_indice_exclusivo]

Por ejemplo, al escribir arr[2:6] estás diciendo algo como selecciona de arr todos los elementos desde la entrada en el índice 2 (inclusivo) hasta la entrada en el índice 6 (exclusivo).

subsect = arr[2:6]
print(subsect)
[2 3 4 5]

Si omites el segundo elemento de la selección de slice tomará todo hasta el final del array. Si omites el primero, obtendrás todo desde el principio hasta el índice especificado.

from_index_two_until_end = arr[2:] # Selecciona desde el índice dos hasta el final del array
print(from_index_two_until_end)
[2 3 4 5 6 7 8 9]
until_index_five = arr[:5] # Selecciona desde el principio hasta el índice 5 (exclusivo)
print(until_index_five)
[0 1 2 3 4]

Hay una diferencia importante entre los slices de ndarray y los slices regulares de Python que vale la pena mencionar: No copian datos, en su lugar, devuelven una vista al array original. Esto significa que cualquier cambio que hagas a una variable que referencia el slice se reflejará en el array original.

slice_ref = arr[2:6]
print(slice_ref)
[2 3 4 5]
# Los cambios hechos en slice_ref se reflejarán en arr
slice_ref[2] = 7171 # El índice 2 de slice_ref apunta al índice 4 de arr
print(arr)
[   0    1    2    3 7171    5    6    7    8    9]

Arrays multi-dimensionales

Para arrays con dimensiones mayores que uno solo necesitas proporcionar un índice por dimensión para acceder a entradas individuales.

darr = np.arange(16).reshape(4,4)
print(darr)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]
# Seleccionar el elemento en la tercera fila, segunda columna (recuerda que todos están indexados desde 0)
element = darr[2,1]
print(element)
9
# También puedes usar esta sintaxis para lograr lo mismo
element = darr[2][1]
print(element)
9

Si no proporcionas un índice para un array con dimensiones mayores que 1, seleccionará todos los contenidos de esa entrada. Por ejemplo, si proporcionas solo 1 índice cuando seleccionas de un array bidimensional obtendrás una fila completa:

row = darr[-1] # Obtener la última fila
print(row)
[12 13 14 15]

El slicing funciona de forma muy similar al caso unidimensional, la única diferencia es que ahora puedes proporcionar rangos de selección extra. Esto es mucho más fácil de entender con ejemplos, así que echemos un vistazo a algunas de las cosas que puedes hacer.

# Seleccionar las primeras dos filas
ddslice = darr[:2]
print(ddslice)
[[0 1 2 3]
 [4 5 6 7]]
# Seleccionar el tercer elemento de las primeras dos filas
ddslice2 = darr[:2, 2]
print(ddslice2)
[2 6]
# Seleccionar las primeras tres filas y los primeros dos elementos de cada una
ddslice3 = darr[:3,:2]
print(ddslice3)
[[0 1]
 [4 5]
 [8 9]]
# Seleccionar cada entrada en cada columna pero saltar la primera
ddslice4 = darr[:, 1:]
print(ddslice4)
[[ 1  2  3]
 [ 5  6  7]
 [ 9 10 11]
 [13 14 15]]
# Seleccionar los elementos del cuadrado central de la matriz
# Es decir, los elementos en las coordenadas [1,1] [1,2] [2,1] y [2,2]
ddslice5 = darr[1:3, 1:3]
print(ddslice5)
[[ 5  6]
 [ 9 10]]

No importa si estás seleccionando slices de un array unidimensional o mayor, siempre devolverán vistas a la subsección original del array. Esto significa que cualquier cambio realizado en la vista se refleja de vuelta en el array original:

ddslice5[:] = 255
print(darr)
[[  0   1   2   3]
 [  4 255 255   7]
 [  8 255 255  11]
 [ 12  13  14  15]]

Indexación elegante (Fancy indexing)

La indexación elegante es simplemente, bueno, un término elegante para un concepto simple: Puedes pasar un array con índices cuando seleccionas elementos de un ndarray y devolverá los elementos en el orden correcto.

tarr = np.arange(100).reshape(10, 10)
print(tarr)
[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]
 [20 21 22 23 24 25 26 27 28 29]
 [30 31 32 33 34 35 36 37 38 39]
 [40 41 42 43 44 45 46 47 48 49]
 [50 51 52 53 54 55 56 57 58 59]
 [60 61 62 63 64 65 66 67 68 69]
 [70 71 72 73 74 75 76 77 78 79]
 [80 81 82 83 84 85 86 87 88 89]
 [90 91 92 93 94 95 96 97 98 99]]
# Obtengamos los elementos en la 8va, 4ta y 2da fila en ese orden (siendo la primera fila la cero)
ordered = tarr[[8,4,2]]
print(ordered)
[[80 81 82 83 84 85 86 87 88 89]
 [40 41 42 43 44 45 46 47 48 49]
 [20 21 22 23 24 25 26 27 28 29]]

Esta es una característica muy útil que viene muy bien muy a menudo, así que asegúrate de recordar que existe y consulta la documentación si olvidas cómo usarla.

Practica slicing un poco más por tu cuenta

Seleccionar elementos de arrays de muchas dimensiones es muy fácil una vez que le agarras la onda, pero puede tomar un tiempo acostumbrarse a la sintaxis.

Por mucho la forma más fácil de aprender cómo usar esta característica es practicar. Ve y genera un array bidimensional e intenta seleccionar diferentes subconjuntos de elementos. Si puedes navegar el escenario 2d no tendrás problema con más dimensiones.

En el siguiente artículo, hablaremos sobre funciones universales y programación orientada a arrays, recuerda echarle un vistazo.

¡Gracias por leer!

Qué hacer después

Juan Luis Orozco Villalobos

¡Hola! Soy Juan, ingeniero de software y consultor en Budapest. Me especializo en computación en la nube e IA, y me encanta ayudar a otros a aprender sobre tecnología e ingeniería