Características de los arrays de NumPy

En este notebook veremos como las principales características de los arrays de NumPy y cómo mejoran la eficiencia de nuestro código.

El objeto tipo array que proporciona NumPy (Python ya dispone de un tipo array que sirve para almacenar elementos de igual tipo pero no proporciona toda la artillería matemática necesaria como para hacer operaciones de manera rápida y eficiente) se caracteriza por:

1) Homogeneidad de tipo:

Comencemos viendo que ocurre con las listas:


In [1]:
import numpy as np

In [2]:
lista = [ 1, 1+2j, True, 'aerodinamica', [1, 2, 3] ]
lista


Out[2]:
[1, (1+2j), True, 'aerodinamica', [1, 2, 3]]

En el caso de los arrays:


In [3]:
array = np.array([ 1, 1+2j, True, 'aerodinamica'])
array


Out[3]:
array(['1', '(1+2j)', 'True', 'aerodinamica'], 
      dtype='<U64')

¿Todo bien? Pues no. Mientras que en la lista cada elemento conserva su tipo, en el array, todos han de tener el mismo y NumPy ha considerado que todos van a ser string.

2) Tamaño fijo en el momento de la creación:

¡Tranquilo! los allocate son automáticos...

Igual que en el caso anterior, comencemos con la lista:


In [4]:
print(id(lista))
lista.append('fluidos')
print(lista)
print(id(lista))


120491272
[1, (1+2j), True, 'aerodinamica', [1, 2, 3], 'fluidos']
120491272

In [5]:
print(id(array))
array = np.append(array, 'fluidos')
print(array)
print(id(array))


125789648
['1' '(1+2j)' 'True' 'aerodinamica' 'fluidos']
125791328

Si consultamos la ayuda de la función np.append escribiendo en una celda help(np.append) podemos leer:

Returns
-------
append : ndarray
    A copy of `arr` with `values` appended to `axis`.  Note that `append` does not occur in-place: a new array is allocated and filled.  If `axis` is None, `out` is a flattened array.

3) Eficiencia

Hasta el momento los arrays han demostrado ser bastante menos flexibles que las listas, luego olvidemos estos últimos 10 minutos y manejemos siempre listas... ¿no? ¡Pues no! Los arrays realizan una gestión de la memoria mucho más eficiente que mejora el rendimiento.

Prestemos atención ahora a la velocidad de ejecución gracias a la función mágica %%timeit, que colocada al inicio de una celda nos indicará el tiempo que tarda en ejecutarse.


In [6]:
lista = list(range(0,100000))
type(lista)


Out[6]:
list

In [7]:
%%timeit
sum(lista)


2 ms ± 74.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [8]:
array = np.arange(0, 100000)

In [9]:
%%timeit
np.sum(array)


52.6 µs ± 810 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Como ves, las mejoras en este caso son de 2 órdenes de magnitud. NumPy nos ofrece funciones que se ejecutan prácticamente en tiempos de lenguaje compilado (Fortran, C, C++) y optimizado, pero escribiendo mucho menos código y con un nivel de abstracción mayor. Conociendo una serie de buenas prácticas, podremos competir en velocidad con nuestros códigos en Python. Para casos en los que no sea posible, existen herramientas que nos permiten ejecutar desde Python nuestros códigos en otros lengujes como f2py. Este tema puede resultarte algo avanzado a estas alturas, pero bastante útil; puedes consultar este artículo de pybonacci si lo necesitas.

Ejercicio

Para recordar los primeras lecciones vamos a implementar nuestra propia función linspace usando un bucle (estilo FORTRAN) y usando una list comprehension (estilo pythonico). Después compararemos el rendimiento comparado con la de NumPy.


In [10]:
def my_linspace_FORTRAN(start, stop, number=50):
    x = np.empty(number)
    step = (stop - start) / (number - 1)
    for ii in range(number):
        x[ii] = ii * step
    x += start
    return x

In [11]:
def my_linspace_PYTHONIC(start, stop, number=50):
    step = (stop - start) / (number - 1)
    x = np.array([ii * step  for ii in range(number)]) #esto es una list comprehension
    x += start
    return x

In [12]:
%%timeit
np.linspace(0,100,1000000)


8.16 ms ± 185 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [13]:
%%timeit
my_linspace_FORTRAN(0,100,1000000)


245 ms ± 4.95 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [14]:
%%timeit
my_linspace_PYTHONIC(0,100,1000000)


235 ms ± 12.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Hemos aprendido:

  • Las características de los arrays de NumPy:
    • Homogeneidad de tipo.
    • Tamaño fijo en el momento de la creación.

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.