El trabajar con bucles "for" en python resulta muy lento. Si tienes que repetir una operación matemática en muchos elementos consecutivos de una matriz, siempre resulta mejor utilizar una operación vectorizada si es posible, es hasta un 80% más rápido.
En la práctica una operación vectorizada, significa rehacer nuestro código para evitar el uso de bucles y utilizar en su lugar slicing, rebanadas o vectores de arrays de Numpy para aplicar la operación a todo o parte del array.
Vamos a ver la diferencia que existe con un ejemplo. Supongamos que tienes que calcular la diferencia existente entre los elementos consecutivos de una matriz.
Como en este ejemplo solo queremos ver la diferencia de tiempos de ejecución que hay con ambos sistemas vamos a crear una matriz "a" que va a contener todos los números desde el cero al 999.
La matriz "a" seria algo así:
a = [0,1,2,3,4,5,6,..........,998,999], tiene 1000 elementos
Lo que queremos hacer es obtener otra matriz de resultados, a la que llamaremos "b", con la diferencia entre los elementos consecutivos de la matriz. Es decir, la matriz "b" seria algo como esto:
b = [(1-0),(2-1),(3-2),(4-3).....................(998-997),(999-998)] que tiene 999 elementos.
Como ves en esta matriz, todos sus elementos van a ser unos. Pero esto no nos interesa, no nos interesa el resultado, sino el proceso que hay que seguir en python para hacerlo.
Para ello vamos a crear un archivo en python, yo lo llamaré consecutivos.py, con el siguiente código:
# importamos la libreria numpy para crear los array
import numpy as np
# Creamos la matriz a con 1000 números consecutivos del 0 al 999
a = np.arange(1000)
# Creamos la matriz b rellena con ceros para luego almacenar los resultados.
# Al estar formada con la diferencia entre 2 elementos consecutivos, tendrá
# 1000 - 1 elementos, es decir 999
b = np.zeros(999, int)
# Bucle for que calcula la diferencia entre un elemento (i) y el anterior
# (i-1), recorre haciendo el calculo con un bucle for la matriz a
# y asigna el resultado en la matriz b.
# e.j b[0]= a[1] que es 1 menos a[0] que es el cero
for i in range(1,len(a)):
b[i-1]=a[i]-a[i-1]
Pues bien, esto mismo se puede conseguir usando una operación vectorizada, con el siguiente código:
c = a[1:] - a[:-1]
Con esto estaríamos restando, elemento a elemento, la siguiente rebanada de la matriz a:
a[1:] = [1,2,3,4,...,998,999] desde el elemento segundo, cuyo valor es 1, hasta el final. (se empieza a contar en el cero)
a[: -1] = [0,1,2,3,....,997,998] desde el primer elemento que es el 0 hasta el penúltimo, el 998.
Si no lo acabas de ver bien, imagínatelo en pequeñito, con este ejemplo.
[1,2,3,4,5]
arr[1:] → [2,3,4,5]
arr[:-1] → [1,2,3,4]
dif = [1,1,1,1]
Vamos ahora a juntarlo todo y ver lo que se tarda en ejecutar de las dos formas. Pero para que se note un poco más y se vea claramente la diferencia vamos a realizarlo, no con una matriz no de 1.000 elementos sino una de 1.000.000 de números.
# importamos la libreria numpy para crear los array
import numpy as np
# importamos time para medir el tiempo de ejecución
from time import time
# Creamos la matriz a con 1.000.000 de números consecutivos del 0 al 999.999
# en python el guion bajo _ nos permite separar los miles para verlo mejor.
a = np.arange(1_000_000)
# Creamos la matriz b rellena con ceros para luego almacenar los resultados.
# Al estar formada con la diferencia entre 2 elementos consecutivos, tendrá
# 1.000.000 - 1 elementos, es decir 999.999
b = np.zeros(len(a-1), int)
# Bucle for que calcula la diferencia entre un elemento (i) y el anterior
# (i-1), recorre haciendo el calculo con un bucle for la matriz a
# y asigna el resultado en la matriz b.
# e.j b[0]= a[1] que es 1 menos a[0] que es el cero
t0 = time()
for i in range(1,len(a)):
b[i-1]=a[i]-a[i-1]
t1 = time()
# Utilizando vectores de la matriz o slicing en el array.
t2 = time()
c = a[1:]-a[:-1]
t3 = time()
print("tiempo del bucle for ", (t1-t0))
print("tiempo con vectorización", (t3-t2))
print((t1-t0)>(t3-t2))
tiempo del bucle for 0.6465959548950195
tiempo con vectorización 0.0030362606048583984
True
Seleccionar elementos que cumplan una determinada condición en un vector.
>>> import numpy as np >>> matriz = np.arange(10) >>> matriz array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> matriz > 5 array([False, False, False, False, False, False, True, True, True, True])
>>> mayor_5 = matriz > 5
# seleccionamos los elementos de matriz en base a matriz > 5
>>> matriz[mayor_5]
array([6, 7, 8, 9])
Seleccionar elementos en base a múltiples condiciones en Numpy
>>> matriz[np.mod(matriz,2) & matriz > 5]
array([6, 8])
>>> import numpy as np
>>> import matplnotlib.pyplot as plt
>>> dx=0.10
>>> x=np.arange(0,np.pi/2,dx) # Intervalo entre 0 y pi/2 con incrementos de 0.10
>>> f=np.sin(x) # función seno en ese intervalo
>>> g=np.cos(x) # función coseno en ese intervalo.
>>> print(x)
[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. 1.1 1.2 1.3 1.4 1.5]
>>> print(f) # Seno
[0. 0.09983342 0.19866933 0.29552021 0.38941834 0.47942554
0.56464247 0.64421769 0.71735609 0.78332691 0.84147098 0.89120736
0.93203909 0.96355819 0.98544973 0.99749499]
>>> print(g) # Coseno
[1. 0.99500417 0.98006658 0.95533649 0.92106099 0.87758256
0.82533561 0.76484219 0.69670671 0.62160997 0.54030231 0.45359612
0.36235775 0.26749883 0.16996714 0.0707372 ]
>>> df_sen=(f[2:]-f[:-2])/2*dx
>>> print(df_sen)
[0.00993347 0.00978434 0.00953745 0.00919527 0.00876121 0.00823961
0.00763568 0.00695546 0.00620574 0.00539402 0.00452841 0.00361754
0.00267053 0.00169684]
>>> df_cos=(g[2:]-g[:-2])/2*dx
>>> print(df_cos)
[-0.00099667 -0.00198338 -0.00295028 -0.0038877 -0.00478627 -0.00563702
-0.00643145 -0.00716161 -0.00782022 -0.00840069 -0.00889723 -0.00930486
-0.00961953 -0.00983808]
# función f(seno) >>> print(f) [0. 0.09983342 0.19866933 0.29552021 0.38941834 0.47942554 0.56464247 0.64421769 0.71735609 0.78332691 0.84147098 0.89120736 0.93203909 0.96355819 0.98544973 0.99749499] >>> print(f[2:]) [0.19866933 0.29552021 0.38941834 0.47942554 0.56464247 0.64421769 0.71735609 0.78332691 0.84147098 0.89120736 0.93203909 0.96355819 0.98544973 0.99749499] >>> print(f[:-2]) [0. 0.09983342 0.19866933 0.29552021 0.38941834 0.47942554 0.56464247 0.64421769 0.71735609 0.78332691 0.84147098 0.89120736 0.93203909 0.96355819] # Como ves ambos arreglos tienen el mismo número de elementos para poder # luego restarlos y acabar de aplicar la fórmula.
plt.plot(x[1:-1], df_sen)
plt.plot(x[1:-1], f[1:-1])
plt.plot(x[1:-1], df_cos)
plt.plot(x[1:-1], g[1:-1])
plt.show()
Próximo Post. Manipulación de Arrays y Transmisión en Numpy.
No hay comentarios:
Publicar un comentario