La computadora es una gran gran calculadora que permite hacer cualquier tipo de cuenta de las que necesitemos dentro de nuestra disciplina (y de la vida también) mientras sepamos cómo decirle a la máquina qué cómputos hacer.
La computadora para hacer cuentas tiene que almacenar los números que necesitemos y luego hacer operaciones con ellos. Nuestros valores numéricos se guardan en espacios de memoria, y esos espacios tienen un nombre, un rótulo con el cual los podremos llamar y pedirle a la computadora que los utilice para operar con ellos, los modifique, etc. Ese nombre a cada espacio de memoria se asigna, al menos en Python, con el símbolo = que significa de ahora en más asignación.
Pero no sólamente guardaremos valores numéricos. Además de haber distintos tipos de valores numéricos, como veremos ahora, podemos guardar otros tipos de datos, como texto (strings) y listas (lists) entre muchos otros. Todos los tipos de valores que podremos almacenar difieren entre si el espacio en memoria que ocupan y las operaciones que podremos hacer con ellos.
Veamos un par de ejemplos
In [1]:
x = 5
y = 'Hola mundo!'
z = [1,2,3]
Aquí hemos guardado en un espacio de memoria llamado por nosotros "x" la información de un valor de tipo entero, 5, en otro espacio de memoria, que nosotros llamamos "y" guardamos el texto "Hola mundo!". En Python, las comillas indican que lo que encerramos con ellas es un texto. x no es un texto, así que Python lo tratará como variable para manipular. "z" es el nombre del espacio de memoria donde se almacena una lista con 3 elementos enteros.
Podemos hacer cosas con esta información. Python es un lenguaje interpretado (a diferencia de otros como Java o C++), eso significa que ni bien nosotros le pedimos algo a Python, éste lo ejecuta. Así es que podremos pedirle por ejemplo que imprima en pantalla el contenido en y, el tipo de valor que es x (entero) entre otras cosas.
In [2]:
print(y)
print(type(x))
print(type(y), type(z), len(z))
Vamos a utilizar mucho la función type() para entender con qué tipo de variables estamos trabajando. type() es una función predeterminada por Python, y lo que hace es pedir como argumento (lo que va entre los paréntesis) una variable y devuelve inmediatamente el tipo de variable que es.
In [3]:
# Realice el ejercicio 1
Para las variables integers(enteros) y floats (flotantes) podemos hacer las operaciones matemáticas usuales y esperables. Veamos un poco las compatibilidades entre estos tipos de variables.
In [4]:
a = 5
b = 7
c = 5.0
d = 7.0
print(a+b, b+c, a*d, a/b, a/d, c**2)
In [5]:
# Realice el ejercicio 2. El resultado esperado es -98.01
In [6]:
# Realice el ejercicio 3.
Si queremos definir nuestra propia manera de calcular algo (útil cuando hacemos muchas cuentas muchas veces como con propagación de errores), o si queremos agrupar una serie de órdenes bajo un mismo nombre, podemos definir funciones. Las funciones, al igual que en matemática, toman argumentos de un "dominio" y devuelven algo. En matemática devuelven una imagen pero en este contexto, la función hace operaciones y podemos pedirle que devuelva el resultado de esas operaciones.
Las primeras funciones que vamos a usar se llaman lambda y las vamos a usar más que nada para funciones matemáticas, aunque también tenga otros usos. Definamos el polinomio $f(x) = x^2 - 5x + 6$ que tiene como raíces $x = 3$ y $x = 2$.
In [7]:
f = lambda x: x**2 - 5*x + 6
print(f(3), f(2), f(0))
Las otras funciones, las más generales, se las llama funciones def, y tienen la siguiente forma. Supongamos que quiero calcular el sen(x) como
$sen(x)=x-{\frac {x^{3}}{3!}}+{\frac {x^{5}}{5!}}-{\frac {x^{7}}{7!}}+\cdots$
In [8]:
def senode(x):
y = x-(x**3)/6 + (x**5)/120 - (x**7)/5040 # Es importante que toda la función tenga su contenido indentado
return y
resultado1 = senode(0)# Aquí rompimos la indentación
resultado2 = senode(3.14/2)
print(resultado1, resultado2)
In [9]:
# Realice el ejercicio 4
def f(a, b, c):
pass
f = lambda a, b, c: a
Pero las operaciones básicas de suma, resta, multiplicación y división son todo lo que un lenguaje como Python puede hacer "nativamente". Una potencia o un seno es álgebra no lineal, y para hacerlo, habría que inventarse un algoritmo (una serie de pasos) para calcular por ejemplo sen($\pi$). Pero alguien ya lo hizo, ya lo pensó, ya lo escribió en lenguaje Python y ahora todos podemos usar ese algoritmo sin pensar en él. Solamente hay que decirle a nuestro intérprete de Python dónde está guardado ese algoritmo. Esta posibilidad de usar algoritmos de otros es fundamental en la programación, porque es lo que permite que nuestro problema se limite solamente a entender cómo llamar a estos algoritmos ya pensados y no tener que pensarlos cada vez.
Vamos entonces a llamar a una biblioteca llamada numpy que nos va a extender nuestras posibilididades matemáticas.
In [10]:
import numpy as np # Llamamos a una biblioteca
#Llamamos a las funciones que hay en numpy, que rebautizamos np
r1 = np.sin(np.pi)
r2 = np.log(np.e)
print(r1, r2)
Para entender cómo funcionan estas funciones, es importante recurrir a su documentation. La de estas funciones que usamos en particular se encuentran en
https://docs.scipy.org/doc/numpy/reference/generated/numpy.sin.html
https://docs.scipy.org/doc/numpy/reference/generated/numpy.log.html
De todos modos, si están usando el Spyder, en el inspector de objetos deberían encontrar la misma información si allí escriben numpy.sin y numpy.log, o también escribiendo en terminal:
In [11]:
help(np.sin)
help(np.log)
Esto les será de mucha ayuda para que, después de este curso, ustedes puedan encontrar la información de cualquier función que deseen. Ideal para momentos de "¿y cómo corno uso esta función?" y para posteriores momentos "¿pero qué %&/!?$$ parámetros toma esta función?"
In [12]:
# Realice el ejercicio 5
In [13]:
vec1 = np.array([1,2,3,4,5])
vec2 = np.array([6,7,8,9,10])
print(vec1) #esto imprime el vector entero
print(vec1[1], vec1[0], vec1[-1]) #esto imprime elementos del vector, indicando en corchetes su nombre.
Una función por demás interesante es la función linspace. Mirando el resultado ¿se imaginan qué es lo que hace esa función? (¡Si no saben busquen la documentación!)
In [14]:
x = np.linspace(0,9,10)
x1 = np.linspace(0,10,10)
print(x)
print(x1)
Si notaron qué hace, también notarán la enorme utilidad que tiene no andar poniendo cada valor del vector.
Lo siguiente que Python tiene de interesante para usar son sus facilidades para hacer gráficos. La biblioteca matplotlib nos ayudará en este caso. Primero, definimos un vector que nos hace de dominio, luego, un vector imagen de alguna función, y luego haremos el gráfico. Se muestran aquí algunas de las opciones que tiene matplotlib para presentar un gráfico, pero yendo a la documentación podrán encontrar infinidad de herramientas para hacer esto.
In [15]:
from matplotlib import pyplot as plt
import numpy as np
#%matplotlib pyplot
x = np.linspace(-5, 5, 200) # con la función linspace generaremos un vector con componentes equidistantes.
y = np.sin(x) # el vector imagen será igual de largo que x
plt.plot(x,y, '-', color = 'red', label = 'Sen(x)') # ver qué pasa con 'r', 'g', '*' entre otros
plt.title('Mi primer ploteo')
plt.xlabel('Eje de las x')
plt.ylabel('Eje de las y')
#plt.xlim(-5,5)
#plt.ylim(0,4)
plt.legend(loc = 'best')
plt.grid(True)
plt.show()
In [16]:
# Realice el ejercicio 7
Una cosita más que nos va a ser útil en el laboratorio es poder hacer histogramas. Con pyplot eso lo podemos obtener de la función hist.
Recordemos que en un histograma dividimos una serie de datos en rangos y contamos cuántos de nuestros datos caen en cada rango. A esos rangos se los llama bins.
hist toma como argumentos un array de números, en cuántos bins queremos dividir a nuestro eje x y algunas otras opciones de color como constante de normalización y color de las barras.
Hagamos un histograma simple de un set gaussiano. Para eso, creemos datos alrededor de algún valor medio usando randn de NumPy:
In [17]:
mu, sigma = 100, 15 # mu es mi valor medio, sigma la desviación
x = mu + sigma*np.random.randn(10000) # le sumo ruido gaussiano a mu
n, bins, patches = plt.hist(x, bins=50, normed=1, facecolor='green', alpha=0.75)
# en la variable n se encuentran los datos del histograma
# bins es un vector con los bordes de los rangos de datos
# patches no nos interesa en general
plt.show()
Y ya que estamos, para mostrar cómo afecta la elección de bins, graficamos dos histogramas uno arriba del otro.
In [18]:
n, bins, patches = plt.hist(x, bins=100, normed=1, facecolor='green', alpha=0.75)
n, bins, patches = plt.hist(x, bins=10, normed=1, facecolor='red', alpha=0.75)
plt.show()
El último ejemplo es de mentirillas porque $x$ era un vector fabricado con una función, pero ustedes en el laboratorio miden algo que tiene una distribución, la cargan en una planilla de cálculo y luego... ¿cómo metemos esos datos en Python?
Fácil. Lo que hacemos es exportar esos datos a un formato neutro, de texto plano, amigable a Python y a cualquier programa razonable del mundo que esté pensado para importar datos. Uno de esos formatos es csv. Con Python lo levantamos así.
In [19]:
Data = np.loadtxt('tiempos-de-vuelo.csv', delimiter=',')
n, bins, patches = plt.hist(Data, bins=50, normed=1, facecolor='green', alpha=0.75)
plt.show()
Y después pueden pedir cosas razonables, como valor medio y desviación estándar.
In [20]:
print(np.mean(Data), np.std(Data))
In [21]:
# Hagan el ejercicio 8
Cuando sus datos experimentales tienen alguna correlación, nos va a interesar hallarla. Si el sistema que estamos trabajando tiene una dependencia funcional que es la que queremos estudiar, entonces podemos hacer un ajuste (no presupuestario) para ver esa dependencia. Supongamos que estudio la descarga de un capacitor, eso tiene un comportamiento exponencial del tipo
$$ \displaystyle f(x) = Ae^{-x/\tau}+C $$y supongamos que tenemos unos datos que queremos importar a Python.
In [22]:
Data = np.loadtxt('Datos_taller.csv', delimiter = '\t') #Esta es otra función para importar, dependiendo de lo que tengan
plt.plot(Data[0],Data[1], 'r.') # Veamos que son los mismos datos
plt.errorbar(Data[0], Data[1], Data[2], linestyle = 'None') # De esta forma introducimos las barras de error
plt.show()
Obtenidos los datos, queremos el ajuste. Para eso importamos la biblioteca con la función que usaremos, que aplica cuadrados mínimos para obtener los coeficientes.
In [23]:
from scipy.optimize import curve_fit
El algoritmo de cuadrados mínimos necesita la función con la que queremos ajustar, que ya definimos en un renglón como función lambda, dominio, los datos, un vector con los valores iniciales de los parámetros desde donde debe comenzar a iterar (un buen valor inicial haría que el tiempo que tarde en converger la solución sea menor, nosotros usaremos los que conocemos y propusimos) y una desviación estandar que hayamos considerado.
La función nos devolverá 2 cosas. Primero, los parámetros optimizados por este algoritmo, ordenados como los pusimos en la función lambda cuando la definimos, que lo guardamos en el vector popt. Por otro lado nos dará la matriz de covarianza, que tiene en su diagonal los $\sigma^2$ de cada parámetro, y fuera de ella nos dará el valor de covarianzas entre los parámetros (qué tanto se modifica un parámetro si variáramos un poco otro parámetro). Lo guardaremos en la matriz pcov aunque, para un análisis básico, usaremos sólo las varianzas y las guardaremos en un vector llamado sigmas.
In [24]:
# Ajustamos
f = lambda x, A, T, C: A*np.exp(-x/T)+C # la función modelo, con la que ajustamos
popt, pcov = curve_fit(f, Data[0], Data[1], sigma = Data[2], absolute_sigma=True)
sigmas = np.sqrt([pcov[0,0],pcov[1,1], pcov[2,2]])
for i in range(3):
print("{:.2f}+-{:.2f}".format(popt[i], sigmas[i])) #Busquen lo que significa esta sintaxis
Listo, ahora chequeamos con un gráfico que haya ajustado
In [25]:
plt.plot(Data[0],Data[1], 'r.', label = 'Datos')
plt.plot(Data[0],f(Data[0], popt[0], popt[1],popt[2]), 'g-', label = 'Ajuste') # Hacemos el gráfico en otro color de
#la función evaluada en los parámetros ajustados
plt.errorbar(Data[0], Data[1], Data[2], linestyle = 'None')
# Detalles del gráfico
plt.grid(True)
plt.title('Grafico ejemplo')
plt.xlabel('Valores en x')
plt.ylabel('Valores en y')
plt.legend(loc = 'best')
plt.show()
In [26]:
# Hagan el ejercicio 9
Para más referencias pueden googlear. Dejamos algunas de referencia:
http://pybonacci.org/2012/06/07/algebra-lineal-en-python-con-numpy-i-operaciones-basicas/
http://relopezbriega.github.io/blog/2015/06/14/algebra-lineal-con-python/
http://pendientedemigracion.ucm.es/info/aocg/python/modulos_cientificos/numpy/index.html
Pero es importantísimo manejarse con la documentación de las bibliotecas que se utilizan
https://docs.python.org/2/library/math.html
http://docs.scipy.org/doc/numpy/reference/routines.linalg.html
Para seguir profundizando con la programación en Python, ofrecemos distintos recursos
Un tutorial: http://www.learnpython.org/
How to think like a computer scientist (aprendizaje interactivo): http://interactivepython.org/runestone/static/thinkcspy/index.html
Otro tutorial, en inglés, pero muy completo: http://learnpythonthehardway.org/book
Coursera, que nunca está de más: https://www.coursera.org/learn/interactive-python-1
Otro más: https://es.coursera.org/learn/python
Y por fuera del taller, seguimos en contacto. Tenemos un grupo de Facebook donde pueden hacerse consultas y otros chicos que fueron al taller antes o aprendieron por sus medios podrán responderles. El grupo es https://www.facebook.com/groups/303815376436624/?fref=ts
Tenemos otro taller dentro de una semana donde profundizaremos el uso de algunas herramientas clave para el análisis de datos de Laboratorio. ¡No te lo podés perder!
A parte, en nuestro Github (https://github.com/fifabsas/talleresfifabsas) iremos colgando nuevo material para ejemplificar, con problemas de las materias de Física resueltos numéricamente, herramientas de Labo y mucho más.
Todo esto es posible gracias al aporte de mucha gente.