Arreglos de NumPy ================= Numpy es el paquete fundamental para cómputo científico en Python. Se trata de una librería para Python que proporciona un arreglo multidimensional, varios objetos derivados (tales como arreglos enmascarados y matrices), y varias rutinas para operaciones rápidas sobre arreglos, incluyendo manipulación matemática, lógica y de la "forma" de los arreglos; ordenamiento, selección, I/O, transformadas discretas de Fourier, álgebra lineal básica, operaciones estadísticas básicas, simulación aleatoria y mucho más. Para ilustrar la importancia de los arreglos de NumPy, considere el siguiente problema: multiplicar cada elemento de una secuencia unidimensional con el correspondiente elemento de otra secuencia de la misma longitud. Si los datos están almacenados en dos listas de Python, `a` y `b`, podríamos iterar sobre cada elemento, de la siguiente manera:: c = [] for i in range(len(a)): c.append(a[i]*b[i]) Esto produce el resultado correcto, pero si `a` y `b` contienen cada uno un millón de números, pagaremos el precio por las ineficiencias de los ciclos en Python. Podríamos conseguir la misma tarea mucho más rápido en C al escribir (por simplicidad no escribimos las declaraciones de variables, inicializaciones, la reservación del espacio de memoria, etc.):: for (i = 0; i < numero_elementos; i++) { c[i] = a[i]*b[i]; } Utilizando arreglos de Numpy `a` y `b`, podemos realizar la misma operación con:: c = a * b casi a la misma velocidad que con C, pero con la simplicidad en la programación que se espera de código escrito en Python. Un ejemplo más realista en C ---------------------------- El siguiente ejemplo muestra el producto punto de dos vectores en C (se está utilizando el framework "Accelerate", específico para Mac OS X, que incluye una librería para BLAS, es decir "Basic Linear Algebra"):: #include #include #include void runSDOTSample (void) { float *X, *Y; int N, i; float resultFloat; // Initialize the inputs and allocate space N = 10; X = ( float* ) malloc ( sizeof ( float ) * N ); Y = ( float* ) malloc ( sizeof ( float ) * N ); for ( i = 0; i < N; i++ ) { X[i] = 1.0; Y[i] = 2.0; } resultFloat = cblas_sdot ( N, X, 1, Y, 1 ); printf("the result of cblas_sdot is %4.4f\n", resultFloat); //deallocate space free ( X ); free ( Y ); } int main(int argc, const char * argv[]) { runSDOTSample(); return 0; } Práctica 4 ---------- Imagine que desea sumar dos vectores llamados `a` y `b`. El vector `a` contiene los cuadrados de los enteros `0` hasta `n` (excluyendo `n`); por ejemplo, si `n = 3`, entonces `a = (0, 1, 4)`. El vector `b` contiene los cubos de los enteros `0` hasta `n` (excluyendo `n`); por ejemplo, si `n = 3`, entonces `b = (0, 1, 8)`. ¿Cómo haría eso con Python únicamente? Sumando vectores utilizando Python "puro" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ La siguiente función resuelve el problema de la suma de vectores utilizando Python sin NumPy:: def pythonsum(n): a = range(n) b = range(n) c = [] for i in range(len(a)): a[i] = i ** 2 b[i] = i ** 3 c.append(a[i] + b[i]) return c Sumando vectores utilizando NumPy ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ La siguiente función resuelve el mismo problema con NumPy:: def numpysum(n): a = numpy.arange(n) ** 2 b = numpy.arange(n) ** 3 c = a + b return c Comparando los tiempos de ejecución ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Con ayuda del siguiente código, compare los tiempos de ejecución para `n = 1000, 2000, 3000`. * Utilice **Emacs** para editar su código. * Ejecute su programa desde la línea de comandos. * Utilice **Emacs** para *ejecutar* su programa (pida instrucciones a su profesor). :: import sys from datetime import datetime import numpy def numpysum(n): a = numpy.arange(n) ** 2 b = numpy.arange(n) ** 3 c = a + b return c def pythonsum(n): a = range(n) b = range(n) c = [] for i in range(len(a)): a[i] = i ** 2 b[i] = i ** 3 c.append(a[i] + b[i]) return c size = int(sys.argv[1]) start = datetime.now() c = pythonsum(size) delta = datetime.now() - start print "The last 2 elements of the sum", c[-2:] print "PythonSum elapsed time in microseconds", delta.microseconds start = datetime.now() c = numpysum(size) delta = datetime.now() - start print "The last 2 elements of the sum", c[-2:] print "NumPySum elapsed time in microseconds", delta.microseconds Arreglos multidimensionales --------------------------- Los arreglos de NumPy son homogéneos, es decir, los elementos del arreglo tienen que ser del mismo tipo. La ventaja es que ahora es fácil determinar el tamaño que se requiere para almacenar el arreglo. Los arreglos de NumPy se indexan como en Python, a partir de 0. Utilizando el siguiente código, averigüe cuál es el tipo de los elementos del arreglo `a` (las respuestas son diferentes dependiendo de si utiliza Python de 32 bits o de 64 bits):: >>> a = arange(5) >>> a.dtype Además del tipo de los elementos del arreglo, también es importante conocer su "forma":: >>> a >>> array([0, 1, 2, 3, 4]) >>> a.shape >>> (5,) El atributo *shape* (forma) del arreglo es una **tupla**, en este caso, una tupla de 1 elemento, que contiene el tamaño del arreglo en cada dimensión. Creando un arreglo multidimensional ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Muestre la forma y el tipo de los elementos del arreglos siguientes:: >>> m1 = array([arange(2), arange(2)]) >>> m2 = array([ [1, 2], [3, 4] ]) Ahora cree una matriz de tamaño :math:`3 \times 3` con los siguientes elementos .. math:: \begin{bmatrix} 0 & 1 & 2 \\ 3 & 4 & 5 \\ 6 & 7 & 8 \end{bmatrix} Compruebe que también puede crearlo de la siguiente manera: >>> m3 = arange(9).reshape(3, 3) Los elementos de m3 pueden accederse mediante la siguiente sintáxis:: >>> m3[0, 0] ¿Cuál es la salida de ``m3[2, 1]``? Un poco de Álgebra Lineal con NumPy ----------------------------------- El paquete ``numpy.linalg`` contiene funciones de álgebra lineal. Con este módulo, usted puede invertir matrices, calcular eigen-valores, resolver sistemas de ecuaciones lineales, calcular determinantes, entre otras cosas. La inversa de una matriz ^^^^^^^^^^^^^^^^^^^^^^^^ La inversa de una matriz cuadrada :math:`A` de :math:`n \times n` se denota por :math:`A^{-1}`, que cuando se multiplica por la matriz original, es igual a la matriz identidad. Esto puede escribirse como se indica a continuación: .. math:: A A^{-1} = I_{n} La función ``inv`` del paquete ``numpy.linalg`` puede invertir una matriz por nosotros. Vamos a resolver un ejemplo: 1. **Crea la matriz** :math:`A` con la función ``mat``:: >>> A = numpy.mat("0 1 2; 1 0 3; 4 -3 8") 2. **Invierte la matriz** :math:`A` con la función ``inv``:: >>> A_inv = numpy.linalg.inv(A) Si la matriz es singular, o no es cuadrada, resulta un error ``LinAlgError``. 3. **Verifica** utilizando multiplicación matricial:: >>> print "Verifica\n", A * A_inv Resolviendo un sistema de ecuaciones lineales ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ La función ``solve`` de ``numpy.linalg`` resuelve sistemas de ecuaciones de la forma :math:`Ax = b`, donde :math:`A` es una matriz, :math:`b` puede ser un arreglo unidimensional o bidimensional, y :math:`x` es la variable desconocida. 1. Crea la matriz :math:`A` y el vector :math:`b`:: >>> A = numpy.mat("1 -2 1; 0 2 -8; -4 5 9") >>> print "A\n", A >>> b = numpy.array([0, -8, 9]) >>> print "b\n", b 2. Invoque la función ``solve`` para resolver el sistema de ecuaciones lineales:: >>> x = numpy.linalg.solve(A, b) >>> print "Solucion: ", x 3. Verifique que la solución es correcta (``dot`` realiza una multiplicación matricial):: >>> print "Verifica\n", numpy.dot(A, x) Gráficas simples con Matplotlib ------------------------------- La visualización de una curva :math:`f(x)` se realiza al graficar lineas rectas entre :math:`n` puntos en la curva. Entre más puntos se usen, más suave es la forma de la gráfica. Com ejemplo, vamos a graficar la curva :math:`y = t^2 e^{-t^2}` para valores de :math:`t` entre 0 y 3. Primero generamos coordenadas igualmente espaciadas para :math:`t`, para este ejemplo, 51 valores (50 intervalos). Entonces calculamos los correspondientes valores para :math:`y` en estos puntos, antes de invocar el comando ``plot(t, y)`` para obtener la gráfica. A continuación mostramos el programa: .. plot:: :include-source: from matplotlib.pylab import * def f(t): return t**2*exp(-t**2) t = linspace(0, 3, 51) # 51 puntos entre 0 y 3 y = zeros(len(t)) # reservar espacio de memoria para y con elementos flotantes. # Se puede hacer utilizando ciclos, pero es mas lento # for i in xrange(len(t)): # y[i] = f(t[i]) # Se obtiene una ejecucion mas rapida (y la sintaxis tambien es mas simple) # actuando sobre todo el arreglo t y = f(t) plot(t, y) xlabel('t') ylabel('y') legend(['t^2*exp(t^2)']) show()