Esta sección sirve como repaso o introducción, por lo cual participantes que ya tienen familiaridad en Python y lo han utilizado recientemente no necesitan revisar esta parte sino pueden proceder directamente a la sección de paralelismo.
Python es una herramienta muy útil y versátil para el cómputo científico. Funciona en Windows, Linux & Mac OS. Está disponible en línea de forma gratuita en www.python.org/downloads.
El material de estudio en esta página incluye las instrucciones requeridas para diferentes operaciones necesarias. Conviene hacer esta ventana de navegador delgada para que ocupe la mitad de la pantalla por máximo y colocar a su lado otra ventana que tenga Python ejecutándose.
Cada código de Python (que se muestra en esta página en letra azul) conviene copiarlo al intérprete interactivo de Python y probarlo uno mismo, modificando y explorando hasta que quede claro qué y por qué está sucediendo en la herramienta.
Para salir de Python usa la instrucción quit()
.
La asignación de valores a variables en Python (interactivo) y la consulta de ellas se realiza de la siguiente forma:
>>> X = 3
>>> X
3
>>> X = 4
>>> X
4
El texto de arriba incluye la salida de Python, por lo
cual incluye los símbolos ">>>" que marcan los inicios de
instrucciones en Python interactivo en consola. No se
teclean esos símbolos. El usuario escribe lo
de X = 3
mientras Python ya produjo el ">>>" para
indicar que está listo para recibir
instrucciones. Solamente se ocupa escribir la parte en letra
azul.
La creación de una lista en Python y la consulta de ello se realiza de la siguiente forma:
>>> datos = [4, 2, 4, 5]
>>> n = len(datos)
>>> datos
[4, 2, 4, 5]
>>> n
4
>>> min(datos)
2
>>> max(datos)
5
>>> sum(datos)
15
>>> ord = sorted(datos)
>>> ord[0]
2
>>> ord[-1]
5
Otras rutinas útiles para analizar listas son las
siguientes y requieren
los paquetes numpy
y scipy
(véase instalación de paquetes):
>>> data = [1, 4, 2, 4, 2, 5, 6, 7, 4, 76, 3, 2, 5, 6, 7]
>>> import numpy as np
>>> np.mean(data)
8.933333
>>> np.median(data)
4
>>> from scipy.stats import describe
>>> describe(data)
DescribeResult(nobs=15, minmax=(1, 76), mean=8.933333333333334, variance=347.78095238095233, skewness=3.4131591099307736, kurtosis=9.810409945242618)
>>> np.quantile(data, 0.9)
7.0
>>> np.unique(data, return_counts = True)
(array([ 1, 2, 3, 4, 5, 6, 7, 76]), array([1, 3, 1, 3, 2, 2, 2, 1]))
>>> np.var(data, ddof=1)
347.78095238095233
>>> np.std(data, ddof=1)
18.648886089548412
>>> otra = [1, 5, 3, 5, 3, 65, 4, 6, 3, 6, 3, 56, 4]
>>> len(otra)
13
>>> len(data)
15
>>> otra += [2, 4]
>>> np.corrcoef(data, [1, 5, 3, 5, 3, 65, 4, 6, 3, 6, 3, 56, 4, 2, 4])[0,1]
-0.08119100801154079
Para crear una matríz con puros ceros,
se ocupa la rutina zeros
. Toma como argumentos la
cantidad de filas y columnas. Por ejemplo, M =
np.zeros((3, 3))
produce una matriz tres
por tres con puros ceros:
>>> M = np.zeros((3, 3))
>>> M
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
Para comparaciones entre listas, las
subrutinas all
y any
sirven para
determinar si todos o algunos de los elementos son iguales:
>>> a = [1, 2, 3]
>>> b = [1, 2, 4]
>>> c = [x == y for (x, y) in zip(a, b)]
>>> c
[True, True, False]
>>> all(c)
False
>>> any(c)
True
La aritmética funciona en grandes rasgos como uno esperaría, con el
detalle que las operaciones vectoriales y matriciales de álgebra
lineal no se hacen con listas sino arreglos de numpy
:
>>> a = 3
>>> b = 4
>>> c = np.array([5, 6, 7])
>>> d = np.array([8, 10, 12])
>>> a + b
7
>>> a - b
-1
>>> a * b
12
>>> a / b
0.75
>>> a**b
81
>>> b**a
64
>>> a + c
array([ 8, 9, 10])
>>> c + d
array([13, 16, 19])
>>> c * d
array([40, 60, 84])
>>> np.inner(c, d)
184
>>> e = np.matrix([[1, 3], [2, 4]])
>>> e
matrix([[1, 3],
[2, 4]])
>>> f = np.matrix([[2, 6], [4, 8]])
>>> f
matrix([[2, 6],
[4, 8]])
>>> e + f
matrix([[3, 9],
[6, 12]])
>>> e - f
matrix([[-1, -3],
[-2, -4]])
>>> np.multiply(e, f)
matrix([[ 2, 18],
[ 8, 32]])
>>> e * f
matrix([[14, 30],
[20, 44]])
El redondeo de enteros a decimales se hace con las tres reglas:
hacia abajo con la función piso floor
, hacia
arriba con la función techo ceil
y al entero
más cercano con round
— los primeros
dos provienen de la librería estándar math
. La
división entera se logra con /
, la división
entera con //
y el residuo (también
llamado modulo con %
que en Python
funciona hasta con decimales:
>>> d = 7.3
>>> from math import floor, ceil
>>> floor(d)
7
>>> ceil(d)
8
>>> round(d)
7
>>> d / 2
3.65
>>> d // 2
3.0
>>> d % 2
1.2999999999999998
>>> round(d) % 2
1
Las funciones matemáticas tienen sus nombres
típicos y están disponibles en la librería math
:
>>> from math import sqrt, exp, sin, cos, tan, tanh, log
>>> x = 123.4
>>> sqrt(x)
11.108555261599053
>>> exp(x)
3.9078606320089135e+53
>>> sin(x)
-0.7693905459455221
>>> cos(x)
-0.6387786688749486
>>> tan(x)
1.2044712565318034
>>> tanh(2.5)
0.9866142981514303
>>> log(x)
4.8154311114712876
>>> log(x, 10)
2.091315159697223
>>> log(x, 2)
6.947198584262056
Para permutaciones y combinaciones, existen subrutinas para el factorial y la coeficiente binomial:
>>> from math import factorial
>>> factorial(5)
120
>>> from scipy.special import binom
>>> binom(9, 3)
84.0
>>> factorial(9) / (factorial(6) * factorial(3))
84.0
>>> s = np.arange(4, 100, 2)
>>> s
array([ 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36,
38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70,
72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98])
>>> r = [13] * 20
>>> r
[13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13]
>>> i = [j for j in range(2, 13)]
>>> i
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> c = np.histogram(s, bins = 4)
>>> c
(array([12, 12, 12, 12]), array([ 4. , 27.5, 51. , 74.5, 98. ]))
>>> s[-5:]
array([90, 92, 94, 96, 98])
>>> s[-10:]
array([80, 82, 84, 86, 88, 90, 92, 94, 96, 98])
>>> s[:5]
array([ 4, 6, 8, 10, 12])
>>> s[:8]
array([ 4, 6, 8, 10, 12, 14, 16, 18])
Para expresiones condicionales, repeticiones, y lógica, sirve lo siguiente:
>>> x = 3
>>> y = None
>>> y = x * 3 if x > 5 else 6 - x
>>> y
3
>>> z = None
>>> z = 2 if x % 2 == 1 and y % 2 == 1 else 3
>>> z
2
>>> if z != 1:
... print('no es uno')
...
no es uno
>>> print('hay un dos' if x == 2 or y == 2 or z == 2 else 'no hay un dos')
hay un dos
>>> for i in range(1, 6):
... print(2**i)
...
2
4
8
16
32
>>> while x > 0:
... print('quito uno')
... x -= 1
...
quito uno
quito uno
quito uno
>>> x
0
Cuando algún cálculo ocupa ser reutilizado en múltiples ocasiones, posiblemente con algunas variaciones, conviene encapsularlo en una subrutina como una función:
def nombre(p1, p2, ... ):
...
return resultado
Conviene probar la creación y llamadas a subrutinas propias.
>>> def potdos(x):
... return 2**x
...
>>> potdos(3)
8
>>> from math import sqrt
>>> def eucl(x1, y1, x2, y2):
... dx = x1 - x2
... dy = y1 - y2
... return sqrt(dx**2 + dy**2)
....
>>> eucl(1, 2, 3, 4)
2.8284271247461903
Generación pseudoaleatoria refiere a la creación de datos que parecen ser al azar. Verdaderamente no lo son si se generan de forma mecánica/aritmética. El sitio web random.org proporciona datos que están basados en bits generados por ruido atmosférico que son "más aleatorios" que cualquier cosa que se genere de forma mecánica.
Para números pseudoaleatorios uniformemente distribuidos, se
usa runif
, con parámetros opciones en el caso que
no se quiera que sean entre cero y uno sino de otro rango de
valores. Para crear valores de verdad o falso, 50–50,
por ejemplo, se puede condicionar al valor uniforme. La
subrutina sample
sirve para muestreo y
permutaciones.
>>> from random import random
>>> random()
0.4542473300983174
>>> from numpy.random import rand
>>> rand(5)
array([0.56381609, 0.74918022, 0.57375601, 0.12773787, 0.41589151])
>>> from numpy.random import uniform
>>> uniform(4, 10, size = 2)
array([7.27280135, 7.5751913 ])
>>> random() < 0.5
True
>>> random() < 0.5
False
>>> random() < 0.5
True
>>> rand(5) < 0.5
array([False, False, True, False, False])
>>> from random import sample
>>> sample([i for i in range(1, 11)], 3)
[2, 6, 9]
>>> sample([i for i in range(1, 11)], 3)
[7, 1, 4]
>>> from random import shuffle
>>> a = [i for i in range(1, 11)]
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> shuffle(a)
>>> a
[3, 5, 2, 10, 4, 9, 6, 8, 1, 7]
>>> from numpy.random import choice
>>> choice([2, 4, 6], size = 10, replace = True, p = [0.1, 0.2, 0.7])
array([2, 6, 6, 6, 6, 6, 6, 6, 6, 2])
Python es capaz de producir números pseudoaleatorios en una varias distributiones (c.f. documentación), además de contar con las funciones de densidad y cumulativas de las distribuciones (c.f. documentación). Ejemplos útiles incluyen las distribución normal (gaussiana) y exponencial.
>>> from numpy.random import normal, exponential
>>> normal(size = 10) # media cero, desv. est. 1
array([-3.62250207, -0.85582587, -0.77536386, 0.07319879, -0.7018473 ,
-0.17808432, -0.19535574, 0.80870695, 0.44887121, 1.58750338])
>>> normal(loc = 5, scale = 1.3, size = 10)
array([5.08377992, 4.63262477, 4.29651358, 3.85644244, 4.41892421,
5.42060481, 4.02797161, 6.83631189, 4.73899607, 6.94469645])
>>> exponential(size = 2)
array([0.42913609, 4.47809099])
>>> exponential(scale = 1 / 3, size = 2)
array([0.00398857, 0.12845763])
Para traer datos a Python, lo más conveniente es utilizar
archivos de texto, o importar desde hojas de cálculo en el
formato CSV (comma separated values), aunque en Python se puede
utilizar cualquier separador. Se ocupa instalar y cargar el
paquete pandas
.
Para probar, colocamos los siguientes datos en un archivo
llamado ejemplo.csv
con un editor de texto básico tipo
notepad o emacs:
Nombre Estatura Gatos Perros
Elisa 184 2 0
Gonzalo 177 0 1
Jorge 180 1 1
Mariana 158 0 0
Carlos 164 1 0
>>> import pandas as pd
>>> datos = pd.read_csv('ejemplo.csv', sep=' ')
>>> datos
Nombre Estatura Gatos Perros
0 Elisa 184 2 0
1 Gonzalo 177 0 1
2 Jorge 180 1 1
3 Mariana 158 0 0
4 Carlos 164 1 0
>>> datos.Estatura
0 184
1 177
2 180
3 158
4 164
Name: Estatura, dtype: int64
>>> sum(datos.Gatos)
4
>>> sum(datos.Gatos) > sum(datos.Perros)
True
>>> datos.Gatos == datos.Perros
0 False
1 False
2 True
3 True
4 False
dtype: bool
>>> datos.loc[datos['Nombre'] == 'Elisa']
Nombre Estatura Gatos Perros
0 Elisa 184 2 0
>>> prom = np.mean(datos.Estatura)
>>> datos.loc[datos['Estatura'] > prom]
Nombre Estatura Gatos Perros
0 Elisa 184 2 0
1 Gonzalo 177 0 1
2 Jorge 180 1 1
>>> datos[datos$Estatura > prom,]$Gatos
2 0 1
>>> datos = datos.append({'Nombre': 'Tania', 'Estatura': 175, 'Gatos': 0, 'Perros': 2}, ignore_index = True)
>>> datos
Nombre Estatura Gatos Perros
0 Elisa 184 2 0
1 Gonzalo 177 0 1
2 Jorge 180 1 1
3 Mariana 158 0 0
4 Carlos 164 1 0
5 Tania 175 0 2
>>> datos['Estudiante'] = [False, True, True, True, False, False]
>>> datos
Nombre Estatura Gatos Perros Estudiante
0 Elisa 184 2 0 False
1 Gonzalo 177 0 1 True
2 Jorge 180 1 1 True
3 Mariana 158 0 0 True
4 Carlos 164 1 0 False
5 Tania 175 0 2 False
>>> datos.loc[2,]
Nombre Jorge
Estatura 180
Gatos 1
Perros 1
Estudiante True
Name: 2, dtype: object
>>> datos.loc[1:3,]
Nombre Estatura Gatos Perros Estudiante
1 Gonzalo 177 0 1 True
2 Jorge 180 1 1 True
3 Mariana 158 0 0 True
>>> datos.loc[1:3].Gatos
1 0
2 1
3 0
Name: Gatos, dtype: int64
Python contiene bastantes funcionalidades para graficar
información en el paquete matplotlib
(hay que
instalarla una vez y cargarla antes de cada uso). La
subrutina plot
crea una gráfica (será de
puntos si no se solicita que sea de líneas
con -
). Se le pueden agregar cosas en ese mismo dibujo
si no se cierra con close()
. El título se coloca
con la rutina title()
:
>>> x = np.arange(1, 21)
>>> y = np.sin(x)
>>> import matplotlib.pyplot as plt
>>> plt.plot(x, y)
>>> plt.show()
>>> plt.close()
>>> plt.plot(x, y, 'o')
>>> plt.show()
>>> plt.close()
>>> plt.plot(x, y, '-o')
>>> plt.plot(x, np.cos(x), color ='red')
>>> plt.axhline(y = 0, color = 'lime')
>>> plt.show()
>>> plt.close()
>>> plt.plot(x, y)
>>> plt.plot(x + 1, np.cos(x + 1), marker = 's', color = 'green')
>>> plt.show()
>>> plt.close()
>>> plt.plot(x, y)
>>> plt.title('Texto arriba')
>>> plt.xlabel('Etiqueta')
>>> plt.ylabel('Otra etiqueta')
>>> plt.show()
>>> plt.close()
Se puede guardar gráficas a archivos, por ejemplo para compartirlos o cuando uno no está trabajando en un ambiente gráfico que pueda abrir ventanas:
>>> plt.plot(x, y, marker = '^')
>>> plt.savefig('salida.png')
>>> plt.close()
>>> d1 = [10, 32, 34, 24, 49, 42, 89]
>>> d2 = [30, 23, 45, 24, 75, 34, 12, 56, 33]
>>> plt.boxplot(d1)
>>> plt.show()
>>> plt.close()
>>> plt.boxplot(d2)
>>> plt.show()
>>> plt.close()
>>> plt.boxplot([d1, d2])
>>> plt.show()
>>> plt.close()
Existe una rutina en matplotlib
que se
llama hist
para dibujar histogramas. Se le
puede indicar a la rutina a cuántas cubetas realizar
la división: hist(datos, bins = 5)
, lo
que produce cinco barras.
>>> datos = [1.6, 4.6, 2.6, 3.6, 5.6, 6.6, 3.5, 2.2, 4.4, 5.2, 5.4, 7.6, 5.8, 4.4, 6.4]
>>> plt.hist(datos)
>>> plt.show()
>>> plt.close()
>>> plt.hist(datos, bins = 2)
>>> plt.show()
>>> plt.close()
>>> plt.hist(datos, bins = 4, normed = True)
>>> plt.show()
>>> plt.close()
>>> histograma = np.histogramhisto(datos)
>>> histograma
(array([1, 2, 0, 2, 2, 1, 4, 0, 2, 1]), array([1.6, 2.2, 2.8, 3.4, 4. , 4.6, 5.2, 5.8, 6.4, 7. , 7.6]))
>>> densidad = histograma[0] / len(datos)
>>> densidad
array([0.06666667, 0.13333333, 0. , 0.13333333, 0.13333333,
0.06666667, 0.26666667, 0. , 0.13333333, 0.06666667])
>>> sum(densidad)
1.0
Se utiliza la herramienta pip3
en la línea
de instrucciones del sistema operativo (no
dentro del mismo Python) para incorporar una nueva funcionalidad a
Python. Ya viene incluido en la instalación
básica. Abriendo la línea de instrucciones, si Python
no está en la variable ambiental PATH, primero se navega a la
carpeta de instalación y de ahí se ejecuta.
https://satuelisa.github.io/simulation/tutorial.html