Introducción a NumPy

Hasta ahora hemos visto los tipos de datos más básicos que nos ofrece Python: integer, real, complex, boolean, list, tuple... Pero ¿no echas algo de menos? Efectivamente, los arrays.

En este notebook nos adentraremos en el paquete NumPy: aprenderemos a crear distintos arrays y a operar con ellos.

¿Qué es un array?

Un array es un bloque de memoria que contiene elementos del mismo tipo. Básicamente:

  • nos recuerdan a los vectores, matrices, tensores...
  • podemos almacenar el array con un nombre y acceder a sus elementos mediante sus índices.
  • ayudan a gestionar de manera eficiente la memoria y a acelerar los cálculos.

Índice 0 1 2 3 ... n-1 n
Valor 2.1 3.6 7.8 1.5 ... 5.4 6.3

¿Qué solemos guardar en arrays?

  • Vectores y matrices.
  • Datos de experimentos:
    • En distintos instantes discretos.
    • En distintos puntos del espacio.
  • Resultado de evaluar funciones con los datos anteriores.
  • Discretizaciones para usar algoritmos de: integración, derivación, interpolación...
  • ...

¿Qué es NumPy?

NumPy es un paquete fundamental para la programación científica que proporciona un objeto tipo array para almacenar datos de forma eficiente y una serie de funciones para operar y manipular esos datos. Para usar NumPy lo primero que debemos hacer es importarlo:


In [1]:
import numpy as np
#para ver la versión que tenemos instalada:
np.__version__


Out[1]:
'1.11.3'

Nuestro primer array

¿No decíamos que Python era fácil? Pues creemos nuestros primeros arrays:


In [2]:
import numpy as np

In [3]:
# Array de una dimensión
mi_primer_array = np.array([1, 2, 3, 4]) 
mi_primer_array


Out[3]:
array([1, 2, 3, 4])

In [4]:
# Podemos usar print
print(mi_primer_array)


[1 2 3 4]

In [5]:
# Comprobar el tipo de mi_primer_array
type(mi_primer_array)


Out[5]:
numpy.ndarray

In [6]:
# Comprobar el tipo de datos que contiene
mi_primer_array.dtype


Out[6]:
dtype('int32')

Los arrays de una dimensión se crean pasándole una lista como argumento a la función np.array. Para crear un array de dos dimensiones le pasaremos una lista de listas:


In [7]:
# Array de dos dimensiones
mi_segundo_array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
Podemos continuar en la siguiente línea usando `\`, pero no es necesario escribirlo dentro de paréntesis o corchetes

Esto sería una buena manera de definirlo, de acuerdo con el PEP 8 (indentation):


In [8]:
mi_segundo_array = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
    ])

Funciones y constantes de NumPy

Hemos dicho que NumPy también incorporá funciones. Un ejemplo sencillo:


In [9]:
# Suma
np.sum(mi_primer_array)


Out[9]:
10

In [10]:
# Máximo
np.max(mi_primer_array)


Out[10]:
4

In [11]:
# Seno
np.sin(mi_segundo_array)


Out[11]:
array([[ 0.84147098,  0.90929743,  0.14112001],
       [-0.7568025 , -0.95892427, -0.2794155 ],
       [ 0.6569866 ,  0.98935825,  0.41211849]])

Y algunas constantes que podemos neccesitar:


In [12]:
np.pi, np.e


Out[12]:
(3.141592653589793, 2.718281828459045)

Funciones para crear arrays

¿Demasiada teoría? vayamos a la práctica. Ya hemos visto que la función np.array() nos permite crear arrays con los valores que nosotros introduzcamos manualmente a través de listas. Más adelante, aprenderemos a leer ficheros y almacenarlos en arrays. Mientras tanto, ¿qué puede hacernos falta?

array de ceros


In [13]:
# En una dimensión
np.zeros(100)


Out[13]:
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

In [14]:
# En dos dimensiones
np.zeros([10,10])


Out[14]:
array([[ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])
Nota: En el caso 1D es válido tanto `np.zeros([5])` como `np.zeros(5)` (sin los corchetes), pero no lo será para el caso nD

array "vacío"


In [15]:
np.empty(10)


Out[15]:
array([  6.90969857e-316,   0.00000000e+000,   6.89993741e-316,
         6.91220487e-316,   6.91102543e-316,   5.49595186e-316,
         5.49337046e-316,   0.00000000e+000,   7.39617853e-317,
         1.27319747e-310])
Importante: El array vacío se crea en un tiempo algo inferior al array de ceros. Sin embargo, el valor de sus elementos será arbitrario y dependerá del estado de la memoria. Si lo utilizas asegúrate de que luego llenas bien todos sus elementos porque podrías introducir resultados erróneos.

array de unos


In [16]:
np.ones([3, 2])


Out[16]:
array([[ 1.,  1.],
       [ 1.,  1.],
       [ 1.,  1.]])
Nota: Otras funciones muy útiles son `np.zeros_like` y `np.ones_like`. Usa la ayuda para ver lo que hacen si lo necesitas.

array identidad


In [17]:
np.identity(4)


Out[17]:
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]])
Nota: También puedes probar `np.eye()` y `np.diag()`.

Rangos

np.arange

NumPy, dame un array que vaya de 0 a 5:


In [18]:
a = np.arange(0, 5)
a


Out[18]:
array([0, 1, 2, 3, 4])

Mira con atención el resultado anterior, ¿hay algo que deberías grabar en tu cabeza para simpre? El último elemento no es 5 sino 4

NumPy, dame un array que vaya de 0 a 10, de 3 en 3:


In [19]:
np.arange(0, 11, 3)


Out[19]:
array([0, 3, 6, 9])

np.linspace

Si has tenido que usar MATLAB alguna vez, seguro que esto te suena:


In [20]:
np.linspace(0, 10, 21)


Out[20]:
array([  0. ,   0.5,   1. ,   1.5,   2. ,   2.5,   3. ,   3.5,   4. ,
         4.5,   5. ,   5.5,   6. ,   6.5,   7. ,   7.5,   8. ,   8.5,
         9. ,   9.5,  10. ])

En este caso sí que se incluye el último elemento.

Nota: También puedes probar `np.logspace()`

reshape

Con np.arange() es posible crear "vectores" cuyos elementos tomen valores consecutivos o equiespaciados, como hemos visto anteriormente. ¿Podemos hacer lo mismo con "matrices"? Pues sí, pero no usando una sola función. Imagina que quieres crear algo como esto:

\begin{pmatrix} 1 & 2 & 3\\ 4 & 5 & 6\\ 7 & 8 & 9\\ \end{pmatrix}
  • Comenzaremos por crear un array 1d con los valores $(1,2,3,4,5,6,7,8,9)$ usando np.arange().
  • Luego le daremos forma de array 2d. con np.reshape(array, (dim0, dim1)).

In [21]:
a = np.arange(1, 10)
M = np.reshape(a, [3, 3])
M


Out[21]:
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

In [22]:
# También funciona como método
N = a.reshape([3,3])
N


Out[22]:
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
Nota: No vamos a entrar demasiado en qué son los métodos, pero debes saber que están asociados a la programación orientada a objetos y que en Python todo es un objeto. Lo que debes pensar es que son unas funciones especiales en las que el argumento más importante (sobre el que se realiza la acción) se escribe delante seguido de un punto. Por ejemplo: `.método(argumentos)`

Operaciones

Operaciones elemento a elemento

Ahora que pocas cosas se nos escapan de los arrays, probemos a hacer algunas operaciones. El funcionamiento es el habitual en FORTRAN y MATLAB y poco hay que añadir:


In [23]:
#crear un arra y y sumarle un número
arr = np.arange(11)
arr + 55


Out[23]:
array([55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65])

In [24]:
#multiplicarlo por un número
arr * 2


Out[24]:
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18, 20])

In [25]:
#elevarlo al cuadrado
arr ** 2


Out[25]:
array([  0,   1,   4,   9,  16,  25,  36,  49,  64,  81, 100])

In [26]:
#calcular una función
np.tanh(arr)


Out[26]:
array([ 0.        ,  0.76159416,  0.96402758,  0.99505475,  0.9993293 ,
        0.9999092 ,  0.99998771,  0.99999834,  0.99999977,  0.99999997,  1.        ])
Entrenamiento: Puedes tratar de comparar la diferencia de tiempo entre realizar la operación en bloque, como ahora, y realizarla elemento a elemento, recorriendo el array con un bucle.

Si las operaciones involucran dos arrays también se realizan elemento a elemento


In [27]:
#creamos dos arrays
arr1 = np.arange(0, 11)
arr2 = np.arange(20, 31)

In [28]:
#los sumamos
arr1 + arr2


Out[28]:
array([20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40])

In [29]:
#multiplicamos
arr1 * arr2


Out[29]:
array([  0,  21,  44,  69,  96, 125, 156, 189, 224, 261, 300])

Comparaciones


In [30]:
# >,<
arr1 > arr2


Out[30]:
array([False, False, False, False, False, False, False, False, False,
       False, False], dtype=bool)

In [31]:
# ==
arr1 == arr2 # ¡ojo! los arrays son de integers, no de floats


Out[31]:
array([False, False, False, False, False, False, False, False, False,
       False, False], dtype=bool)
Nota: Por cierto, ¿qúe ocurrirá si los arrays con los que se quiere operar no tiene la misma forma? ¿apuestas? Quizá más adelante te interese buscar algo de información sobre __broadcasting__.

Hemos aprendido:

  • A usar las principales funciones para crear arrays.
  • A operar con arrays.

En definitiva:

  • Ingenieros y científicos $\heartsuit$ arrays.
  • Ingenieros y científicos necesitan NumPy.

¡Quiero más!Algunos enlaces:

Algunos enlaces en Pybonacci:

Algunos enlaces en otros sitios:



¡Síguenos en Twitter!



Este notebook ha sido realizado por: Juan Luis Cano y Álex Sáez



<span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">Curso AeroPython</span> por <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo</span> se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional.