Estructura básica#
Atributos del array#
Hemos visto algunos atributos, aunque en las manipulaciones normales rara
vez se usan. Veremos ahora atributos más interesantes. Para ello definimos
un array de tres dimensiones. Los tres primeros atributos son el ńumero
de dimensiones ndim
, la forma shape
y el tamaño (número de elementos) size
.
import numpy as np
x3 = np.ones((3, 4, 5))
print('x3.ndim=', x3.ndim)
print('x3.shape=', x3.shape)
print('x3.size=', x3.size)
x3.ndim= 3
x3.shape= (3, 4, 5)
x3.size= 60
Otros atributos importantes son los que contienen el tipo de datos dtype
.
Hablaremos de ellos en otra sección. Otros atributos relacionados son
itemsize
y nbytes
.
print('x3.dtype=', x3.dtype)
print('x3.nbytes=', x3.nbytes)
print('x3.itemsize=', x3.itemsize)
x3.dtype= float64
x3.nbytes= 480
x3.itemsize= 8
Uso de índices#
La manera más sencilla de usar índices es equivalente al de las listas de Python. Empezamos con un array unidimensional.
x1 = np.arange(10)
print(x1)
print('x1.ndim=', x1.ndim)
print('x1.shape=', x1.shape)
print('x1.size=', x1.size)
print('x1.dtype=', x1.dtype)
print('x1.nbytes=', x1.nbytes)
print('x1.itemsize=', x1.itemsize)
[0 1 2 3 4 5 6 7 8 9]
x1.ndim= 1
x1.shape= (10,)
x1.size= 10
x1.dtype= int64
x1.nbytes= 80
x1.itemsize= 8
Los índices empiezan en cero y los valores negativos implican empezar desde el final (se empieza en -1).
print("x1[2]=", x1[2])
print("x1[0]=", x1[0])
print("x1[-1]=", x1[-1])
print("x1[-2]=", x1[-2])
x1[2]= 2
x1[0]= 0
x1[-1]= 9
x1[-2]= 8
Para un array multimensional hay que pasar los índices separados por comas. Más concretamente, una tupla de índices
print("x3[2, 3, 0]=", x3[2, 3, 0])
print("x3[0, -1, 2]=", x3[0, -1, 2])
idx = (2, 3, 0)
print("x3[(2, 3, 0)]=", x3[idx])
x3[2, 3, 0]= 1.0
x3[0, -1, 2]= 1.0
x3[(2, 3, 0)]= 1.0
Los índices puede utilizarse para asignar valores. Al contrario que en las listas, el tipo interno es homogéneo, así que se realiza una conversión automática en la asignación.
print("x3[2, 3, 0]=", x3[2, 3, 0])
x3[2, 3, 0] = -1.5
print("x3[2, 3, 0]=", x3[2, 3, 0])
print("x3[2, 3, 1]=", x3[2, 3, 1])
x3[2, 3, 1] = False # Conversión automática
print("x3[2, 3, 1]=", x3[2, 3, 1])
x3[2, 3, 0]= 1.0
x3[2, 3, 0]= -1.5
x3[2, 3, 1]= 1.0
x3[2, 3, 1]= 0.0
Regiones con slice
#
Seguimos con las similitudes con las listas. De la misma manera que podemos
acceder a una sublistas con la notación [a:b:c]
, los arrays permiten
crear subarrays, con algunas diferencias notables.
En una lista, [a:b:c]
crea una lista que empieza en el índice a
,
llega hasta el índice b
sin incluirlo, con paso c
. Si cualquier
se omite toma como valor por defecto a=0
, c=1
y b=dimensión
.
print(x1[:5])
print(x1[5:])
print(x1[3:8])
print(x1[::2])
print(x1[1::2])
[0 1 2 3 4]
[5 6 7 8 9]
[3 4 5 6 7]
[0 2 4 6 8]
[1 3 5 7 9]
Si c
es negativo, entonces los valores por defecto se invierten.
Además pueden producirse algunos casos límite que pueden inducir a error
print(x1[::-1])
print(x1[5::-2])
[9 8 7 6 5 4 3 2 1 0]
[5 3 1]
El resultado del subarray puede ser vacio:
print(x1[10:0])
[]
No es posible recorrer el array con paso negativo e índices explícitos:
print(x1[10:0:-1]) # 0 no incluido
print(x1[10::-1]) # Ahora sí
[9 8 7 6 5 4 3 2 1]
[9 8 7 6 5 4 3 2 1 0]
Podemos utilizar índices fuera el array sin error:
print(x1[5:50]) # Se incluyen solo los valores existentes
[5 6 7 8 9]
Regiones multidimensionales#
La extensión es obvia, pasamos las regiones separadas por comas (en realidad estamos pasando un tupla de subregiones)
x2 = np.array([[8, 4, 5, 0], [8, 8, 1, 2], [2, 0, 12, 1]])
print(x2.shape)
print(x2)
(3, 4)
[[ 8 4 5 0]
[ 8 8 1 2]
[ 2 0 12 1]]
x2[:2, :3] # Filas [0, 2), columnas [0, 3)
array([[8, 4, 5],
[8, 8, 1]])
x2[::2, ::-1] # Filas pares, columnas invertidas
array([[ 0, 5, 4, 8],
[ 1, 12, 0, 2]])
Es habitual querer acceder a las filas o columnas de un array. Esto se puede hacer combinando índices numéricos con subarrays.
Por ejemplo:
x2[1, :] # Fila 1, todas las columnas
array([8, 8, 1, 2])
El elemento :
es equivalente a utilizar un índice de subarray con todos
los valores por defecto
De la misma forma
x2[:, 3] # Todas las filas, columna 3
array([0, 2, 1])
Algunas sutilezas:
Estas dos expresiones no tienen el mismo tamaño pero sí los mismos valores:
print(x2[:, 3:4])
print(x2[:, 3:4].shape)
print(x2[:, 3])
print(x2[:, 3].shape)
[[0]
[2]
[1]]
(3, 1)
[0 2 1]
(3,)
El resultado de la operación depende de si aportamos un valor o un rango.
Las expresiones a:b:c
son equivalentes a slice(a, b, c)
, así podemos
hacer:
print(x2[:2, :3]) # Filas [0, 2), columnas [0, 3)
# https://docs.python.org/3/library/functions.html#slice
sl1 = slice(2)
sl2 = slice(3)
idx = (sl1, sl2)
print(x2[idx])
[[8 4 5]
[8 8 1]]
[[8 4 5]
[8 8 1]]
Cuando el array tiene varios ejes, puede resultar complicado seleccionar secciones. Supongamos que tenemos un array 4D
x4 = np.arange(120).reshape(2, 3, 4, 5)
print(x4.shape)
(2, 3, 4, 5)
Si quisiéramos acceder al array 3D fijando el último ejes tendríamos que hacer:
print(x4[:, :, :, 0]) # Último eje
[[[ 0 5 10 15]
[ 20 25 30 35]
[ 40 45 50 55]]
[[ 60 65 70 75]
[ 80 85 90 95]
[100 105 110 115]]]
print(x4[1, :, :, :]) # Primer eje
[[[ 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]]
[[100 101 102 103 104]
[105 106 107 108 109]
[110 111 112 113 114]
[115 116 117 118 119]]]
Por defecto, numpy completa todos los ejes que no existen, hacia la derecha, así que la última forma puede ponerse como:
print(x4[1]) # Primer eje
[[[ 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]]
[[100 101 102 103 104]
[105 106 107 108 109]
[110 111 112 113 114]
[115 116 117 118 119]]]
Existe otro operador que se utiliza en indexación de listas
llamado elipsis ...
. En numpy se puede utilizar para completar todos
los ejes sin asignar siempre que no sea ambiguo.
Así, el primer ejemplo se puede poner como:
print(x4[..., 0]) # Último eje
[[[ 0 5 10 15]
[ 20 25 30 35]
[ 40 45 50 55]]
[[ 60 65 70 75]
[ 80 85 90 95]
[100 105 110 115]]]
y el último como:
print(x4[1, ...]) # Primer eje
[[[ 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]]
[[100 101 102 103 104]
[105 106 107 108 109]
[110 111 112 113 114]
[115 116 117 118 119]]]
Incluso se podría hacer:
print(x4[1, ..., 0])
[[ 60 65 70 75]
[ 80 85 90 95]
[100 105 110 115]]
Pero no:
print(x4[..., 1, ...])
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
Cell In[26], line 1
----> 1 print(x4[..., 1, ...])
IndexError: an index can only have a single ellipsis ('...')
dado que es ambiguo