NumPy - Avanzado#
Copias y vistas#
Al operar y manipular arrays, sus datos a veces se copian en un nuevo array y otras veces no. Esto puede ser muy confuso. Hay tres casos:
Las asignaciones simples no hacen copia del array#
import numpy as np
A = np.arange(12)
print(A)
B = A # No se crea un array nuevo
B is A # A y B son dos nombres para el mismo array
[ 0 1 2 3 4 5 6 7 8 9 10 11]
True
B.shape = 3,4 # por ejemplo, cambiarle la forma a B, le cambia la forma a A
A.shape
print(A)
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
B[0,:] = 80
print(A)
[[80 80 80 80]
[ 4 5 6 7]
[ 8 9 10 11]]
Python pasa arrays ‘como referencia’, por lo que las llamadas a funciones no hacen copia.
def f(X):
'''
Una función que espera un array X y cambia X[0] a 2.0
'''
X[0] = 2.0
A = np.ones([4,5]) # <--- array de unos, de 4 filas y 5 columnas
print('Antes de llamar a la función:', A[0]) # <--- imprimo la primer fila
f(A)
print('Luego de llamar a la función:', A[0])
Antes de llamar a la función: [1. 1. 1. 1. 1.]
Luego de llamar a la función: [2. 2. 2. 2. 2.]
Vistas.#
Diferentes arrays pueden compartir los mismos datos. El método de view crea un nuevo array que “mira” los mismos datos.
print(A)
C = A.view()
C is A
[[2. 2. 2. 2. 2.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]]
False
C.shape = 2,6 # Cambiar la forma de C, no cambia la de A
print(C)
A.shape
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[7], line 1
----> 1 C.shape = 2,6 # Cambiar la forma de C, no cambia la de A
2 print(C)
3 A.shape
ValueError: cannot reshape array of size 20 into shape (2,6)
C[0,:] = 3.0 # Pero cambiar los datos de C, cambia los datos de A
A
array([ 3, 3, 3, 3, 3, 3, 6, 7, 8, 9, 10, 11])
Hacer un slicing de un array devuelve una vista:
S = A[1:3]
S[:] = 10
A
array([ 3, 10, 10, 3, 3, 3, 6, 7, 8, 9, 10, 11])
Copia#
D = A.copy() # se crea un nuevo array y se copian los datos
D is A
False
D[0] = 23
print(D)
print(A)
[23 10 10 3 3 3 6 7 8 9 10 11]
[ 3 10 10 3 3 3 6 7 8 9 10 11]
Indexado (más sofisticado)#
NumPy ofrece más técnicas de indexación que las secuencias regulares de Python. Además de indexar por enteros y sectores, como vimos anteriormente, los arrays pueden indexarse mediante arrays de enteros y arrays lógicos.
Indexado con un array de índices¶#
import numpy as np
A = np.arange(12)**2 # array de cuadrados de los números del 0 al 11
print(A)
I = np.array([1, 1, 3, 8, 5]) # array de índices
A[I] # Los elementos de A en las posiciones dada por I
[ 0 1 4 9 16 25 36 49 64 81 100 121]
array([ 1, 1, 9, 64, 25])
J = np.array([[3, 4], [9, 7]]) # array bidimensional de índices
A[J] # <--- con la misma forma que J
array([[ 9, 16],
[81, 49]])
Cuando el array indexado es multidimensional, un array de índices se refiere a la primera dimensión del array indexado.
El siguiente ejemplo muestra este comportamiento al convertir una imagen ‘grayscale’ en una imagen ‘truecolor’ (o RGB) usando una paleta.
paleta = np.array( [ [0.0, 0.0, 0.0], # negro
[1.0, 0.0, 0.0], # rojo
[0.0, 1.0, 0.0], # verde
[0.0, 0.0, 1.0], # azul
[1.0, 1.0, 1.0] ] ) # blanco
imagen = np.array([[ 0, 1, 2, 0 ], # cada valor, sirve como indice en el mapa de colores (paleta)
[ 0, 3, 4, 0 ]])
print(imagen.shape)
print(paleta[imagen].shape)
import matplotlib.pyplot as plt
plt.figure()
plt.imshow(imagen, cmap = 'gray')
plt.axis('off')
plt.show()
plt.figure()
plt.imshow(paleta[imagen], cmap = 'gray')
plt.axis('off')
plt.show()
También podemos proporcionar índices para más de una dimensión. Los arrays de índices para cada dimensión deben tener la misma forma.
A = np.arange(12).reshape(3,4)
A
I = np.array([[0,1], # Índices para la primera dimesión de A
[1,2]])
J = np.array([[2,1], # Índices para la segunda dimesión de A
[3,3]])
A[I,J] # I y J deben tener la misma forma
A[I,2]
A[:,J]
Podemos poner I y J en una lista y luego hacer la indexación con la lista
L = [I,J]
print(L)
A[L] # equivalente a A[I,J]
Sin embargo, no podemos hacer esto poniendo I y J en un array, ya que este array se interpretará como indexando la primera dimensión de A.
S = np.array([I,J])
print(S)
A[S] # No es lo mismo que lo visto arriba
A[tuple(S)] # lo mismo que A[I,J]
Otro ejemplo de indexación con arrays de índices, es la búsqueda del valor máximo de series dependientes del tiempo:
time = np.linspace(20, 145, 5) # array arbitrario que simula el tiempo
data = np.random.random((5,4)) # 4 series arbitrarias (columnas) que simulan depender del tiempo
print(time)
print(data)
ind = data.argmax(axis=0) # índices de los máximos para cada columna
ind
time_max = time[ind] # tiempos correspondientes al máximo
data_max = data[ind, range(data.shape[1])] # => data[ind[0],0], data[ind[1],1]...
print(time_max)
print(data_max)
# graficando los resultados...
plt.figure()
plt.plot(time, data)
plt.plot(time_max, data_max, 'ok')
plt.show()
También puede usar la indexación para asignar valores a arrays:
A = np.arange(5)
A
A[[1,3,4]] = 0
A
Sin embargo, cuando la lista de índices contiene repeticiones, la asignación se realiza varias veces, pisando el último valor:
A = np.arange(5)
A
A[[0,0,2]] = [1,2,3]
A
Indexado lógico#
A = np.arange(12).reshape(3,4)
B = A > 4
print(A)
print(B) # B es un array lógico de las mismas forma de A
A[B] # array 1D con los elementos de A donde B es True
Esto es muy util para hacer asignaciones basados en un criterio:
A[B] = 0 # Esto hace cero todos los elementos de A que son mayores a 4
A
Como ejemplo, de indexado lógico, trataremos de segmentar el “tejido blando” de un fantoma:
import matplotlib.pyplot as plt
I = plt.imread('./Data/cirs_slice.png')[...,0]
plt.figure(figsize = (6,6))
plt.imshow(I, cmap = 'gray')
plt.show()
plt.figure()
plt.hist(I.ravel(), bins=100, normed=1) # matplotlib version (plot)
plt.show()
Mask = np.zeros(I.shape)
Mask[(I>0.4) & (I<0.45)] = 1 #usar 0.6 y 0.8 para la médula y 0.4 y 0.45 para el tejido blando.¿y para pulmón?
print(Mask.max())
print(Mask.min())
plt.figure(figsize = (6,6))
plt.imshow(Mask, cmap = 'gray')
plt.show()
red_mask = np.dstack([Mask, np.zeros_like(Mask), np.zeros_like(Mask)])
print(red_mask.shape)
plt.figure(figsize = (6,6))
plt.imshow(I, cmap = 'gray')
plt.imshow(red_mask, alpha = 0.5, cmap = 'gray')
plt.show()
Un poco de IO#
De y hacia texto#
data = np.loadtxt('./Data/ejemplo_texto_tabla.txt', delimiter = ',', skiprows = 7)
data.shape
plt.figure()
plt.plot(data[:,0], data[:,1], '-*')
plt.show()
z = -np.cos(data[:,0])
print(z.shape)
nueva_data = np.concatenate([data, z[:,np.newaxis]], axis = 1)
plt.figure()
plt.plot(nueva_data[:,0], nueva_data[:,1], '-*')
plt.plot(nueva_data[:,0], nueva_data[:,2], '-+')
plt.show()
mi_header = "Este es un ejemplo de header\ncon más de una línea\n\nx, y, z\n\n"
np.savetxt('otro_ejemplo.txt', nueva_data, delimiter=',', header = mi_header)
Formatos propios#
X = np.ones((3, 3))
np.save('mi_array.npy', X)
X2 = np.load('mi_array.npy')
print(X2)
Y = 2*X
Z = Y + 23
np.savez('mis_arrays.npz', a = X, b = Y, c = Z)
data = np.load('mis_arrays.npz')
print(data['a'])
print(data['b'])
print(data['c'])