Numpy es una paquete para realizar cálculos sobre vectores y matrices.
En esencia, numpy provee dos cosas:
Está implementado en C y Fortran, de modo que cuando los cálculos son vectorizados (formulados con vectores y matrices), el rendimiento es muy bueno.
Para trabajar con el paquete numpy debemos importarlo de la siguiente forma:
In [1]:
import numpy
Para acceder a las funciones del paquete
In [2]:
numpy.random.rand(10, 5)
Out[2]:
La funcion rand
está dentro del modulo random del paquete numpy
Para simplificar el código podemos usar
In [3]:
import numpy as np
In [4]:
np.random?
Lo que hacemos es crear un alias al paquete NumPy de nombre np
. Es simplemente una forma de abreviar el código. Esta forma de separar las funciones en paquetes (que se llaman espacios de nombres o namespaces) conduce a una mayor legibilidad del código y a la supresión de ambigüedades.
Para buscar ayuda sobre ciertos temas podemos usar la funcion lookfor
.
In [5]:
np.lookfor("fourier")
Un array multidimensional de numpy es un conjunto de elementos estructurados de cierta forma.
Podemos crear arreglos de diferentes maneras:
array
arange
, zeros
, ones
, linspace
, rand
)
In [6]:
np.array((1,2,3))
Out[6]:
In [7]:
a = np.array([[1,2,3], [4,5,6]])
a
Out[7]:
In [8]:
type(a)
Out[8]:
Podemos obtener la cantidad de elementos de un arreglo accediendo al atributo size
(cuidado no usar el operador len
)
In [9]:
a.size
Out[9]:
Podemos obtener las dimensiones del arreglo accedediendo al atributo shape
In [10]:
a.shape
Out[10]:
Podemos cambiar la forma del arreglo usando la función reshape
In [11]:
a.reshape(6)
Out[11]:
In [12]:
a.reshape(6).shape
Out[12]:
In [13]:
b = a.reshape((1,6))
print b.shape
b
Out[13]:
In [14]:
a = np.array([1, 2.0, 3])
a
Out[14]:
In [15]:
a.dtype
Out[15]:
La función array nos permite especificar el tipo de datos que queremos
In [16]:
a = np.array([1, 2, 3, 4, 5], dtype=float)
a
Out[16]:
O si queremos podemos obtener un array de otro tipo de datos usando la función astype
In [17]:
a.astype(complex)
Out[17]:
In [18]:
a.astype(int)
Out[18]:
Hasta el momento el arreglo numpy.ndarray
luce como una lista Python (anidada). Entonces, ¿por qué simplemente no usar listas para hacer cálculos en lugar de crear un tipo nuevo de arreglo?
Existen varias razones:
Las listas Python son muy generales. No permiten usar funciones matemáticas tales como la multiplicación de matricies, el producto escalar, etc. El implementar tales funciones para las listas Python no sería muy eficiente debido a la asignación dinámica de su tipo.
In [19]:
[1,2,3] + [4,5,6]
Out[19]:
In [20]:
a = np.array([1,2,3])
b = np.array([4,5,6])
a+b
Out[20]:
In [21]:
min([[1,2, 3], [4,5,6]])
Out[21]:
In [22]:
np.min(np.array([[1,2, 3], [4,5,6]]))
Out[22]:
Las listas de Python pueden contener cualquier tipo de objeto.
Los arreglos Numpy tienen tipo estático y homogéneo. Esto los hace eficientes en el uso de memoria.
Debido a su tipo estático, se pueden desarrollar implementaciones rápidas de funciones matemáticas tales como la multiplicación y la suma de arreglos numpy
usando lenguajes compilados (se usan C y Fortran).
Ejemplo: crear dos mastrices b y c de 100x100 elementos y realizar sobre ellas la suma matricial definida como:
$$ a_{ij} = b_{ij} + c_{ij} $$Guardar el resultado en la matriz a
In [23]:
a = [[None] * 100]*100
b = [range(100)] * 100
c = [range(100)] * 100
In [24]:
%%timeit
for i in range(100):
for j in range(100):
a[i][j] = b[i][j] + c[i][j]
In [25]:
a = np.empty(10000, dtype=int).reshape((100,100))
b = np.arange(10000, dtype=int).reshape((100,100))
c = np.arange(10000, dtype=int).reshape((100,100))
In [26]:
%%timeit
for i in range(100):
for j in range(100):
a[i,j] = b[i,j] + c[i,j]
In [27]:
def suma(a,b):
return a + b
In [28]:
np.vectorize?
In [29]:
suma_vectorizada = np.vectorize(suma)
In [30]:
%%timeit
a = suma_vectorizada(b,c)
In [31]:
%%timeit
a = b + c
In [32]:
a
Out[32]:
Una de las herramientas más importantes a la hora de trabajar con arrays es el indexado. Consiste en seleccionar elementos aislados o secciones de un array. Nosotros vamos a ver la indexación básica, pero existen técnicas de indexación avanzada que convierten los arrays en herramientas potentísimas.
In [33]:
a = np.arange(10, dtype=float) # otra forma de generar un arreglo: a través de funciones específicas de numpy
a
Out[33]:
In [34]:
a[1]
Out[34]:
In [35]:
a[0], a[-1]
Out[35]:
In [36]:
a[[0,2,4,6,8]]
Out[36]:
In [37]:
a[0:9:2]
Out[37]:
In [38]:
a[::2]
Out[38]:
In [39]:
a = np.array([
[0,1,2,3,4,5],
[10,11,12,13,14,15],
[20,21,22,23,24,25],
[30,31,32,33,34,35],
[40,41,42,43,44,45],
[50,51,52,53,54,55]
])
a
Out[39]:
In [40]:
a[0,3:5]
Out[40]:
In [41]:
a[4:, 4:]
Out[41]:
In [42]:
a[:,2]
Out[42]:
In [43]:
a[2::2, ::2]
Out[43]:
In [44]:
a = np.arange(5)
a
Out[44]:
In [45]:
print a + 1
print a - 1
print a * 10
print a / 5
print a ** 2
In [46]:
a = np.arange(3)
a
Out[46]:
In [47]:
b = np.ones(3, dtype=int) * 2
b
Out[47]:
In [48]:
print a + b
print a * b
print a / b
print a ** b
In [49]:
c = (np.arange(4) * 10).reshape((4,1))
c
Out[49]:
In [50]:
d = np.arange(3)
d
Out[50]:
In [51]:
c + d
Out[51]:
In [52]:
a = np.array([1,2,3,4]).reshape((2,2))
a
Out[52]:
In [53]:
b = np.ones(4, dtype=int).reshape((2,2))
b
Out[53]:
In [54]:
a * b
Out[54]:
In [55]:
a.dot(b)
Out[55]:
In [56]:
b.dot(a)
Out[56]:
In [57]:
a = np.arange(6)
b = np.ones(6).astype(int)
a, b
Out[57]:
In [58]:
a < b
Out[58]:
In [59]:
np.any(a < b)
Out[59]:
In [60]:
np.all(a < b)
Out[60]:
In [61]:
0.1 +0.2 + 0.3
Out[61]:
In [62]:
0.3 + 0.2 + 0.1
Out[62]:
In [63]:
np.array([0.1 +0.2 + 0.3]) == np.array([0.3 + 0.2 + 0.1])
Out[63]:
In [64]:
np.isclose(np.array([0.1 +0.2 + 0.3]), np.array([0.3 + 0.2 + 0.1]))
Out[64]:
In [65]:
np.allclose(np.array([0.1 +0.2 + 0.3]), np.array([0.3 + 0.2 + 0.1]), atol=1e-10)
Out[65]:
In [66]:
tablero = np.zeros((8, 8))
tablero
Out[66]:
In [67]:
tablero[1::2, ::2] = 1
tablero[::2, 1::2] = 1
tablero
Out[67]:
Webpage numpy: http://www.numpy.org/
Referencia de Numpy: http://docs.scipy.org/doc/numpy/