Clase 8: Introducción a Numpy
Introducción
Dos paquetes que van a resultar muy importantes para nosotros son los
paquetes numpy y matplotlib. Como con todos los módulos, se
cargan utilizando la palabra import
, tal como hicimos en los
ejemplos anteriores. Existen variantes en la manera de importar los
módulos que son “equivalentes”. En este caso le vamos a dar un alias que
sea más corto de tipear. Después podemos utilizar sus funciones y
definiciones.
import numpy as np # Importa el paquete numpy para trabajo numérico
import matplotlib.pyplot as plt # Importa el paquete matplotlib para graficación
Un ejemplo muy común es la graficación de datos que obtuvimos previamente:
x, y = np.loadtxt('../data/ejemplo_plot_07_1.dat', unpack=True)
plt.plot(x, y)
[<matplotlib.lines.Line2D at 0x7f8543fe7fd0>]
Lectura y escritura de datos a archivos
Numpy tiene funciones que permiten escribir y leer datos de varias maneras, tanto en formato texto como en binario. En general el modo texto ocupa más espacio pero puede ser leído y modificado con un editor.
Veamos qué datos hay en el archivo:
Hay dos columnas, en la primera fila hay texto, y en las siguientes hay valores separados por un espacio.
Dos funciones simples y útiles para entrada y salida de datos son
np.loadtxt()
para lectura, y np.savetxt()
para escritura.
x, y = np.loadtxt('../data/ejemplo_plot_07_1.dat', unpack=True)
La función np.loadtxt()
carga estos valores a las variables x
e
y
len(x), len(y)
(300, 300)
print(x[:10])
[0. 0.010507 0.021014 0.031521 0.042028 0.05253499
0.06304199 0.07354899 0.08405599 0.09456299]
Vemos que, con este uso, la variable x
contiene los valores de la
primera columna y la variable y
los de la segunda.
Para grabar datos a un archivo le damos como primer argumento el nombre del archivo y como segundo los datos a guardar. Vamos a ver detalles más adelante.
np.savetxt('test.out', y)
!head test.out
En la primera línea hay texto explicativo, en las siguientes líneas el archivo tiene dos columnas.
Veamos que tipo de variable son x
e y
:
type(x), type(y)
(numpy.ndarray, numpy.ndarray)
Como vemos, el tipo de la variable no es una lista sino un nuevo tipo: ndarray, o simplemente array. Veamos cómo trabajar con ellos.
Características de arrays en Numpy
Numpy define unas nuevas estructuras llamadas ndarrays o arrays para trabajar con vectores de datos, en una dimensión o más dimensiones (“matrices”). Los arrays son variantes de las listas de python preparadas para trabajar a mayor velocidad y menor consumo de memoria. Por ello se requiere que los arrays sean menos generales y versátiles que las listas usuales. Analicemos brevemente las diferencias entre estos tipos y las consecuencias que tendrá en su uso para nosotros.
Comparación de listas y arrays
Comparemos como operamos sobre un conjunto de números cuando los representamos por una lista, o por un array:
dlist = [1.5, 3.8, 4.9, 12.3, 27.2, 35.8, 70.2, 90., 125., 180.]
d = np.array(dlist)
d is dlist
False
print(dlist)
[1.5, 3.8, 4.9, 12.3, 27.2, 35.8, 70.2, 90.0, 125.0, 180.0]
print(d)
[ 1.5 3.8 4.9 12.3 27.2 35.8 70.2 90. 125. 180. ]
Veamos cómo se hace para operar con estos dos tipos. Si los valores representan ángulos en grados, hagamos la conversión a radianes (radián = \(\pi/180\) grado)
from math import pi
drlist= [a*pi/180 for a in dlist]
print(drlist)
[0.02617993877991494, 0.06632251157578452, 0.08552113334772216, 0.21467549799530256, 0.47472955654245763, 0.62482787221397, 1.2252211349000193, 1.5707963267948966, 2.1816615649929116, 3.141592653589793]
dr= d*(pi/180)
print(dr)
[0.02617994 0.06632251 0.08552113 0.2146755 0.47472956 0.62482787
1.22522113 1.57079633 2.18166156 3.14159265]
Vemos que el modo de trabajar es más simple ya que los array permiten trabajar con operaciones elemento-a-elemento mientras que para las listas tenemos que usar comprensiones de listas. Veamos otros ejemplos:
print([np.sin(a*pi/180) for a in dlist])
[0.02617694830787315, 0.06627390040000014, 0.08541692313736748, 0.2130303862749766, 0.4570979270586941, 0.5849576749872153, 0.9408807689542255, 1.0, 0.819152044288992, 1.2246467991473532e-16]
print(np.sin(np.deg2rad(d)))
[2.61769483e-02 6.62739004e-02 8.54169231e-02 2.13030386e-01
4.57097927e-01 5.84957675e-01 9.40880769e-01 1.00000000e+00
8.19152044e-01 1.22464680e-16]
Además de la simplicidad para trabajar con operaciones que actúan sobre
cada elemento, el paquete tiene una gran cantidad de funciones y
constantes definidas (como por ejemplo np.pi
para \(\pi\)).
plt.plot(d, np.sin(np.deg2rad(d)),'o-')
plt.show()
Uso de memoria de listas y arrays
Las listas son sucesiones de elementos, completamente generales y no necesariamente todos iguales. Un esquema de su representación interna se muestra en el siguiente gráfico para una lista de números enteros (Las figuras y el análisis de esta sección son de www.python-course.eu/numpy.php)
Básicamente en una lista se guarda información común a cualquier lista, un lugar de almacenamiento que referencia donde buscar cada uno de sus elementos (que puede ser un objeto diferente) y luego el lugar efectivo para guardar cada elemento. Veamos cuanta memoria se necesita para guardar una lista de enteros:
from sys import getsizeof
lst = [24, 12, 57]
size_of_list_object = getsizeof(lst) # La lista sin sus datos
#size_of_elements = getsizeof(lst[0]) + getsizeof(lst[1]) + getsizeof(lst[2])
size_of_elements = sum(getsizeof(l) for l in lst)
total_list_size = size_of_list_object + size_of_elements
print("Tamaño sin considerar los elementos: ", size_of_list_object)
print("Tamaño de los elementos: ", size_of_elements)
print("Tamaño total: ", total_list_size)
Tamaño sin considerar los elementos: 88
Tamaño de los elementos: 84
Tamaño total: 172
Para calcular cuánta memoria se usa en cada parte de una lista analicemos el tamaño de distintos casos:
print('Una lista vacía ocupa: {} bytes'.format(getsizeof([])))
print('Una lista con un elem: {} bytes'.format(getsizeof([24])))
print('Una lista con 2 elems: {} bytes'.format(getsizeof([24,12])))
print('Un entero en Python : {} bytes'.format(getsizeof(24)))
Una lista vacía ocupa: 56 bytes
Una lista con un elem: 64 bytes
Una lista con 2 elems: 72 bytes
Un entero en Python : 28 bytes
Vemos que la “Información general de listas” ocupa 56 bytes, y la referencia a cada elemento entero ocupa adicionalmente 8 bytes, por lo que la lista con dos elementos ocupa 72 bytes. Además, cada elemento, un entero de Python, en este caso ocupa 28 bytes, por lo que el tamaño total de una lista de \(n\) números enteros será:
# Notar que no siempre es fácil identificar el tamaño correcto
print('Una lista con 3 elems: {} bytes'.format(getsizeof([24,12,57])))
print('Una lista con 4 elems: {} bytes'.format(getsizeof([24,12,57,38])))
print('Una lista con 5 elems: {} bytes'.format(getsizeof([24,12,57,38,47])))
En contraste, los arrays deben ser todos del mismo tipo por lo que su representación es más simple (por ejemplo, no es necesario guardar sus valores separadamente)
a = np.array(lst, dtype='int')
print(getsizeof(a))
136
Para analizar como se distribuye el consumo de memoria en un array vamos a calcular el tamaño de cada uno de los elementos como hicimos con las listas:
print('Un array vacío ocupa: {} bytes'.format(getsizeof(np.array([]))))
print('Un array con un elem: {} bytes'.format(getsizeof(np.array([24]))))
print('Un array con 2 elems: {} bytes'.format(getsizeof(np.array([24,12]))))
print('Un entero de Numpy es: {}'.format(type(a[0])))
print('Un entero de Numpy usa: {}'.format(getsizeof(a[0])))
Un array vacío ocupa: 112 bytes
Un array con un elem: 120 bytes
Un array con 2 elems: 128 bytes
Un entero de Numpy es: <class 'numpy.int64'>
Un entero de Numpy usa: 32
Vemos que la información general sobre arrays ocupa 96 bytes (en
contraste a 64 para listas), y por cada elemento otros 8 bytes
adicionales (numpy.int64
corresponde a 64 bits), por lo que el
tamaño total será:
from sys import getsizeof
lst1 = list(range(1000))
total_list_size = getsizeof(lst1) + sum(getsizeof(l) for l in lst1)
print("Tamaño total de la lista: ", total_list_size)
a1 = np.array(lst1)
print("Tamaño total de array: ", getsizeof(a1))
Tamaño total de la lista: 36056
Tamaño total de array: 8112
Velocidad de Numpy
Una de las grandes ventajas de usar Numpy está relacionada con la velocidad de cálculo. Veamos (superficialmente) esto
# %load scripts/timing.py
# Ejemplo del libro en www.python-course.eu/numpy.php
import numpy as np
from timeit import Timer
Ndim = 100000
def pure_python_version():
X = range(Ndim)
Y = range(Ndim)
Z = []
for i in range(len(X)):
Z.append(X[i] + Y[i])
return Z
def numpy_version():
X = np.arange(Ndim)
Y = np.arange(Ndim)
Z = X + Y
return Z
timer_obj1 = Timer("pure_python_version()", "from __main__ import pure_python_version")
timer_obj2 = Timer("numpy_version()", "from __main__ import numpy_version")
t1 = timer_obj1.timeit(10)
t2 = timer_obj2.timeit(10)
print(f"Numpy es en este ejemplo {t1 / t2 : .3f} más rápido")
Numpy es en este ejemplo 105.120 más rápido
Como vemos, utilizar Numpy puede ser considerablemente más rápido que usar Python puro.
Creación de arrays en Numpy
Un array
en numpy es un tipo de variable parecido a una lista, pero
está optimizado para realizar trabajo numérico.
Todos los elementos deben ser del mismo tipo, y además de los valores, contiene información sobre su tipo. Veamos algunos ejemplos de cómo crearlos y utilizarlos:
Creación de Arrays unidimensionales
i1 = np.array([1, 2, 3, 1, 5, 1, 9, 22, 0])
r1 = np.array([1.4 ,2.3 ,3.0 ,1, 5, 1, 9, 22, 0])
print(i1)
print(r1)
[ 1 2 3 1 5 1 9 22 0]
[ 1.4 2.3 3. 1. 5. 1. 9. 22. 0. ]
print('tipo de i1: {} \ntipo de r1: {}'.format(i1.dtype, r1.dtype))
tipo de i1: int64
tipo de r1: float64
print('Para i1:\n Número de dimensiones: {}\n Longitud: {}'.format(np.ndim(i1), len(i1)))
Para i1:
Número de dimensiones: 1
Longitud: 9
print('Para r1:\n Número de dimensiones: {}\n Longitud: {}'.format(r1.ndim, len(r1)))
Para r1:
Número de dimensiones: 1
Longitud: 9
Arrays multidimensionales
Podemos crear explícitamente arrays multidimensionales con la función
np.array
si el argumento es una lista anidada
L = [ [1, 2, 3], [.2, -.2, -1], [-1, 2, 9], [0, 0.5, 0] ]
A = np.array(L)
A
array([[ 1. , 2. , 3. ],
[ 0.2, -0.2, -1. ],
[-1. , 2. , 9. ],
[ 0. , 0.5, 0. ]])
print(A)
[[ 1. 2. 3. ]
[ 0.2 -0.2 -1. ]
[-1. 2. 9. ]
[ 0. 0.5 0. ]]
print(np.ndim(A),A.ndim) # Ambos son equivalentes
2 2
print(len(A))
4
Vemos que la dimensión de A
es 2, pero la longitud que me reporta
Python corresponde al primer eje. Los arrays tienen un atributo
que es la “forma” (shape)
print(A.shape)
(4, 3)
r1.shape # una tupla de un solo elemento
(9,)
Generación de datos equiespaciados
Para obtener datos equiespaciados hay dos funciones complementarias
a1 = np.arange(0,190,10)
a2 = np.linspace(0,180,19)
a1
array([ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120,
130, 140, 150, 160, 170, 180])
a2
array([ 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.,
110., 120., 130., 140., 150., 160., 170., 180.])
Como vemos, ambos pueden dar resultados similares, y es una cuestión de conveniencia cual utilizar. El uso es:
np.arange([start,] stop[, step,], dtype=None)
np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
Mientras que a arange()
le decimos cuál es el paso a utilizar, a
linspace()
debemos (podemos) darle como tercer argumento el número
de valores que queremos.
# Si queremos que devuelva enteros:
np.arange(0,180.,7.8, dtype=int)
array([ 0, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84,
91, 98, 105, 112, 119, 126, 133, 140, 147, 154, 161])
# Pedimos que devuelva el paso también
v1, step1 = np.linspace(0,10,20, endpoint=True, retstep=True)
v2, step2 = np.linspace(0,10,20, endpoint=False, retstep=True)
print(step1)
print(step2)
0.5263157894736842
0.5
v1
array([ 0. , 0.52631579, 1.05263158, 1.57894737, 2.10526316,
2.63157895, 3.15789474, 3.68421053, 4.21052632, 4.73684211,
5.26315789, 5.78947368, 6.31578947, 6.84210526, 7.36842105,
7.89473684, 8.42105263, 8.94736842, 9.47368421, 10. ])
v2
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])
plt.plot(a2, np.sin(np.deg2rad(a2)),'o-')
plt.show()
Además de valores linealmente espaciados podemos obtener valores espaciados en escala logarítmica
w = np.logspace(0,10,10)
plt.plot( w, 'o-')
plt.show()
w1 = np.logspace(0,2,3) # Start y Stop son los exponentes
print(w1)
[ 1. 10. 100.]
w2 = np.geomspace(1,100,3) # Start y Stop son los valores
print(w2)
[ 1. 10. 100.]
Otras formas de creación
Hay otras maneras de crear numpy arrays. Algunas, de las más comunes es cuando necesitamos crear un array con todos ceros o unos o algún valor dado
a = np.zeros(5)
a.dtype # El tipo default es float de 64 bits
dtype('float64')
print(a)
[0. 0. 0. 0. 0.]
i= np.zeros(5, dtype=int)
print(i)
[0 0 0 0 0]
i.dtype
dtype('int64')
c= np.zeros(5,dtype=complex)
print(c)
print(c.dtype)
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
complex128
En lugar de inicializarlo en cero podemos inicializarlo con algún valor
np.ones(5, dtype=complex) # Algo similar pero inicializando a unos
array([1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j])
Ya vimos que también podemos inicializarlos con valores “equiespaciados”
con np.arange()
, con np.linspace()
o con np.logspace()
v = np.arange(2,15,2) # Crea un array con una secuencia (similar a la función range)
Para crear arrays multidimensionales usamos:
np.ones((4,5))
array([[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]])
np.ones((4,3,6))
array([[[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]],
[[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]],
[[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]],
[[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1.]]])
np.eye(4)
array([[1., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 0., 1.]])
np.eye(3,7)
array([[1., 0., 0., 0., 0., 0., 0.],
[0., 1., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0., 0.]])
En este último ejemplo hemos creado matrices con unos en la diagonal y ceros en todos los demás lugares.
Acceso a los elementos
El acceso a los elementos tiene una forma muy parecida a la de las listas (pero no exactamente igual).
print(r1)
[ 1.4 2.3 3. 1. 5. 1. 9. 22. 0. ]
Si queremos uno de los elementos usamos la notación:
print(r1[0], r1[3], r1[-1])
1.4 1.0 0.0
y para “tajadas” (slices)
print(r1[:3])
[1.4 2.3 3. ]
print(r1[-3:])
[ 9. 22. 0.]
print(r1[5:7])
[1. 9.]
print(r1[0:8:2])
[1.4 3. 5. 9. ]
Como con vectores unidimensionales, con arrays multidimensionales, se puede ubicar un elemento o usar slices:
arr = np.arange(55).reshape((5,11))
arr
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
[22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
[33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
[44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54]])
print("primer y segundo elementos", arr[0,0], arr[0,1])
primer y segundo elementos 0 1
print( 'Slicing parte de la segunda fila :', arr[1, 2:4])
print('Todas las filas, tercera columna :', arr[:, 2])
Slicing parte de la segunda fila : [13 14]
Todas las filas, tercera columna : [ 2 13 24 35 46]
print( 'Primera fila :\n', arr[0], '\nes igual a :\n', arr[0,:])
Primera fila :
[ 0 1 2 3 4 5 6 7 8 9 10]
es igual a :
[ 0 1 2 3 4 5 6 7 8 9 10]
print( 'Segunda fila :\n', arr[1], '\nes igual a :\n', arr[1,:])
Segunda fila :
[11 12 13 14 15 16 17 18 19 20 21]
es igual a :
[11 12 13 14 15 16 17 18 19 20 21]
print( 'Primera columna:', arr[:,0])
Primera columna: [ 0 11 22 33 44]
print( 'Última columna : \n', arr[:,-1])
Última columna :
[10 21 32 43 54]
print( 'Segunda fila, elementos impares (0,2,...) : ', arr[1,::2])
Segunda fila, elementos impares (0,2,...) : [11 13 15 17 19 21]
print( 'Segunda fila, todos los elementos pares : ', arr[1,1::2])
Segunda fila, todos los elementos pares : [12 14 16 18 20]
Cuando el slicing se hace de la forma [i:f:s]
significa que
tomaremos los elementos entre i
(inicial), hasta f
(final, no
incluido), pero tomando sólo uno de cada s
(stride) elementos
En Scipy Lectures at http://scipy-lectures.github.io hay una descripción del acceso a arrays.
Ejercicios 08 (a)
Genere arrays en 2d, cada uno de tamaño 10x10 con:
Un array con valores 1 en la “diagonal principal” y 0 en el resto (Matriz identidad).
Un array con valores 0 en la “diagonal principal” y 1 en el resto.
Un array con valores 1 en los bordes y 0 en el interior.
Un array con números enteros consecutivos (empezando en 1) en los bordes y 0 en el interior.
Diga qué resultado produce el siguiente código, y explíquelo
# Ejemplo propuesto por Jake VanderPlas
print(sum(range(5),-1))
from numpy import *
print(sum(range(5),-1))
Propiedades de Numpy arrays
Propiedades básicas
Hay tres propiedades básicas que caracterizan a un array:
shape
: Contiene información sobre la forma que tiene un array (sus dimensiones: vector, matriz, o tensor)dtype
: Es el tipo de cada uno de sus elementos (todos son iguales)stride
: Contiene la información sobre como recorrer el array. Por ejemplo si es una matriz, tiene la información de cuántos bytes en memoria hay que pasar para pasar de una fila a la siguiente y de una columna a la siguiente.
import numpy as np
arr = np.arange(55).reshape((5,11))+1
print( 'shape :', arr.shape)
print( 'dtype :', arr.dtype)
print( 'strides:', arr.strides)
shape : (5, 11)
dtype : int64
strides: (88, 8)
print(np.arange(55).shape)
print(arr.shape)
(55,)
(5, 11)
Otras propiedades y métodos de los array
Los array tienen atributos que nos dan información sobre sus características:
print( 'Número total de elementos :', arr.size)
print( 'Número de dimensiones :', arr.ndim)
print( 'Memoria usada : {} bytes'.format( arr.nbytes))
Número total de elementos : 55
Número de dimensiones : 2
Memoria usada : 440 bytes
Además, tienen métodos que facilitan algunos cálculos comunes. Veamos algunos de ellos:
print( 'Mínimo y máximo :', arr.min(), arr.max())
print( 'Suma y producto de sus elementos :', arr.sum(), arr.prod())
print( 'Media y desviación standard :', arr.mean(), arr.std())
Mínimo y máximo : 1 55
Suma y producto de sus elementos : 1540 6711489344688881664
Media y desviación standard : 28.0 15.874507866387544
Para todos estos métodos, las operaciones se realizan sobre todos los elementos. En array multidimensionales uno puede elegir realizar los cálculos sólo sobre un dado eje:
print( 'Para el array:\n', arr)
Para el array:
[[ 1 2 3 4 5 6 7 8 9 10 11]
[12 13 14 15 16 17 18 19 20 21 22]
[23 24 25 26 27 28 29 30 31 32 33]
[34 35 36 37 38 39 40 41 42 43 44]
[45 46 47 48 49 50 51 52 53 54 55]]
print( 'La suma de todos los elementos es :', arr.sum())
La suma de todos los elementos es : 1540
print( 'La suma de elementos de las filas es :', arr.sum(axis=1))
La suma de elementos de las filas es : [ 66 187 308 429 550]
print( 'La suma de elementos de las columnas es :', arr.sum(axis=0))
La suma de elementos de las columnas es : [115 120 125 130 135 140 145 150 155 160 165]
print(arr.min(axis=0))
print(arr.min(axis=1))
[ 1 2 3 4 5 6 7 8 9 10 11]
[ 1 12 23 34 45]
Operaciones sobre arrays
Operaciones básicas
Los array se pueden usar en operaciones:
arr + 1
array([[ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
[24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34],
[35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45],
[46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56]])
# Suma de una constante
arr1 = 1 + arr[:,::-1] # Creamos un segundo array
arr1
array([[12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
[23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13],
[34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24],
[45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35],
[56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46]])
# Multiplicación por constantes y suma de arrays
-2* arr + 3*arr1
array([[ 34, 29, 24, 19, 14, 9, 4, -1, -6, -11, -16],
[ 45, 40, 35, 30, 25, 20, 15, 10, 5, 0, -5],
[ 56, 51, 46, 41, 36, 31, 26, 21, 16, 11, 6],
[ 67, 62, 57, 52, 47, 42, 37, 32, 27, 22, 17],
[ 78, 73, 68, 63, 58, 53, 48, 43, 38, 33, 28]])
# División por constantes
arr/5
array([[ 0.2, 0.4, 0.6, 0.8, 1. , 1.2, 1.4, 1.6, 1.8, 2. , 2.2],
[ 2.4, 2.6, 2.8, 3. , 3.2, 3.4, 3.6, 3.8, 4. , 4.2, 4.4],
[ 4.6, 4.8, 5. , 5.2, 5.4, 5.6, 5.8, 6. , 6.2, 6.4, 6.6],
[ 6.8, 7. , 7.2, 7.4, 7.6, 7.8, 8. , 8.2, 8.4, 8.6, 8.8],
[ 9. , 9.2, 9.4, 9.6, 9.8, 10. , 10.2, 10.4, 10.6, 10.8, 11. ]])
# Multiplicación entre arrays
arr * arr1
array([[ 12, 22, 30, 36, 40, 42, 42, 40, 36, 30, 22],
[ 276, 286, 294, 300, 304, 306, 306, 304, 300, 294, 286],
[ 782, 792, 800, 806, 810, 812, 812, 810, 806, 800, 792],
[1530, 1540, 1548, 1554, 1558, 1560, 1560, 1558, 1554, 1548, 1540],
[2520, 2530, 2538, 2544, 2548, 2550, 2550, 2548, 2544, 2538, 2530]])
arr / arr1
array([[0.08333333, 0.18181818, 0.3 , 0.44444444, 0.625 ,
0.85714286, 1.16666667, 1.6 , 2.25 , 3.33333333,
5.5 ],
[0.52173913, 0.59090909, 0.66666667, 0.75 , 0.84210526,
0.94444444, 1.05882353, 1.1875 , 1.33333333, 1.5 ,
1.69230769],
[0.67647059, 0.72727273, 0.78125 , 0.83870968, 0.9 ,
0.96551724, 1.03571429, 1.11111111, 1.19230769, 1.28 ,
1.375 ],
[0.75555556, 0.79545455, 0.8372093 , 0.88095238, 0.92682927,
0.975 , 1.02564103, 1.07894737, 1.13513514, 1.19444444,
1.25714286],
[0.80357143, 0.83636364, 0.87037037, 0.90566038, 0.94230769,
0.98039216, 1.02 , 1.06122449, 1.10416667, 1.14893617,
1.19565217]])
Como vemos, están definidas todas las operaciones por constantes y entre arrays. En operaciones con constantes, se aplican sobre cada elemento del array. En operaciones entre arrays se realizan elemento a elemento (y el número de elementos de los dos array debe ser compatible).
Comparaciones
También se pueden comparar dos arrays elemento a elemento
v = np.linspace(0,19,20)
w = np.linspace(0.5,18,20)
print (v)
print (w)
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.
18. 19.]
[ 0.5 1.42105263 2.34210526 3.26315789 4.18421053 5.10526316
6.02631579 6.94736842 7.86842105 8.78947368 9.71052632 10.63157895
11.55263158 12.47368421 13.39473684 14.31578947 15.23684211 16.15789474
17.07894737 18. ]
# Comparación de un array con una constante
print(v > 12)
[False False False False False False False False False False False False
False True True True True True True True]
# Comparación de un array con otro
print(v > w)
[False False False False False False False True True True True True
True True True True True True True True]
Funciones definidas en Numpy
Algunas de las funciones definidas en numpy se aplican a cada elemento. Por ejemplo, las funciones matemáticas:
np.sin(arr1)
array([[-0.53657292, -0.99999021, -0.54402111, 0.41211849, 0.98935825,
0.6569866 , -0.2794155 , -0.95892427, -0.7568025 , 0.14112001,
0.90929743],
[-0.8462204 , -0.00885131, 0.83665564, 0.91294525, 0.14987721,
-0.75098725, -0.96139749, -0.28790332, 0.65028784, 0.99060736,
0.42016704],
[ 0.52908269, 0.99991186, 0.55142668, -0.40403765, -0.98803162,
-0.66363388, 0.27090579, 0.95637593, 0.76255845, -0.13235175,
-0.90557836],
[ 0.85090352, 0.01770193, -0.83177474, -0.91652155, -0.15862267,
0.74511316, 0.96379539, 0.29636858, -0.64353813, -0.99177885,
-0.42818267],
[-0.521551 , -0.99975517, -0.55878905, 0.39592515, 0.98662759,
0.67022918, -0.26237485, -0.95375265, -0.76825466, 0.12357312,
0.90178835]])
np.exp(-arr**2/2)
array([[6.06530660e-001, 1.35335283e-001, 1.11089965e-002,
3.35462628e-004, 3.72665317e-006, 1.52299797e-008,
2.28973485e-011, 1.26641655e-014, 2.57675711e-018,
1.92874985e-022, 5.31109225e-027],
[5.38018616e-032, 2.00500878e-037, 2.74878501e-043,
1.38634329e-049, 2.57220937e-056, 1.75568810e-063,
4.40853133e-071, 4.07235863e-079, 1.38389653e-087,
1.73008221e-096, 7.95674389e-106],
[1.34619985e-115, 8.37894253e-126, 1.91855567e-136,
1.61608841e-147, 5.00796571e-159, 5.70904011e-171,
2.39425476e-183, 3.69388307e-196, 2.09653176e-209,
4.37749104e-223, 3.36244047e-237],
[9.50144065e-252, 9.87710872e-267, 3.77724997e-282,
5.31406836e-298, 2.75032531e-314, 0.00000000e+000,
0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
0.00000000e+000, 0.00000000e+000],
[0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
0.00000000e+000, 0.00000000e+000]])
Podemos elegir elementos de un array basados en condiciones:
v[w > 9]
array([10., 11., 12., 13., 14., 15., 16., 17., 18., 19.])
Aquí eligiendo elementos de v
basados en una condición sobre los
elementos de w
. Veamos en más detalle:
c = w > 9 # Array con condición
print(c)
x = v[c] # Creamos un nuevo array con los valores donde c es verdadero
print(x)
print(len(c), len(w), len(v), len(x))
[False False False False False False False False False False True True
True True True True True True True True]
[10. 11. 12. 13. 14. 15. 16. 17. 18. 19.]
20 20 20 10
Ejercicios 08 (b)
Escriba una función
suma_potencias(p, n)
(utilizando arrays y Numpy) que calcule la operación\[s_{2} = \sum_{k=0}^{n}k^{p}\].
Usando las funciones de numpy
sign
ymaximum
definir las siguientes funciones, que acepten como argumento un array y devuelvan un array con el mismo shape:
función de Heaviside, que vale 1 para valores positivos de su argumento y 0 para valores negativos.
La función escalón, que vale 0 para valores del argumento fuera del intervalo \((-1,1)\) y 1 para argumentos en el intervalo.
La función rampa, que vale 0 para valores negativos de \(x\) y \(x\) para valores positivos.
Caída libre Cree una función que calcule la posición y velocidad de una partícula en caída libre para condiciones iniciales dadas (\(h_{0}\), \(v_{0}\)), y un valor de gravedad dados. Se utilizará la convención de que alturas y velocidades positivas corresponden a vectores apuntando hacia arriba (una velocidad positiva significa que la partícula se aleja de la tierra).
La función debe realizar el cálculo de la velocidad y altura para un conjunto de tiempos equiespaciados. Los valores de velocidad inicial, altura inicial, valor de gravedad, y número de puntos deben ser argumentos de la función con valores por defecto adecuadamente provistos. La tabla de valores debe darse hasta que la partícula toca el piso (valor \(h=0\)).
Guarde los resultados en tres columnas (t, v(t), h(t)) en un archivo de nombre “caida_vel_alt.dat” donde “vel” corresponde al valor de la velocidad inicial y “alt” al de la altura inicial.
PARA ENTREGAR Queremos realizar numéricamente la integral
\[\int_{a}^{b}f(x)dx\]utilizando el método de los trapecios. Para eso partimos el intervalo \([a,b]\) en \(N\) subintervalos y aproximamos la curva en cada subintervalo por una recta
La línea azul representa la función \(f(x)\) y la línea roja la interpolación por una recta (figura de https://en.wikipedia.org/wiki/Trapezoidal_rule)
Si llamamos \(x_{i}\) (\(i=0,\ldots,n,\) con \(x_{0}=a\) y \(x_{n}=b\)) los puntos equiespaciados, entonces queda
Escriba una función
trapz(x, y)
que reciba dos arrays unidimensionalesx
ey
y aplique la fórmula de los trapecios.Escriba una función
trapzf(f, a, b, npts=100)
que recibe una funciónf
, los límitesa
,b
y el número de puntos a utilizarnpts
, y devuelve el valor de la integral por trapecios.Escriba una función que calcule la integral logarítmica de Euler:
\[\mathrm{Li}(t) = \int_2^t \frac{1}{\ln x} dx\]usando la función ´trapzf´ para valores de
npts=10, 20, 30, 40, 50, 60
NOTA: Envíe el programa llamado 08_Suapellido.py en un adjunto por correo electrónico, con asunto: 08_Suapellido.