La idea de hoy es que aprendan un poquito sobre otro tipo de objeto que existe en Python: los diccionarios
y sobre una función que puede serles útil en el futuro: find_peaks
.
Una vez aprendido esto, ¡Ponemos las manos en la masa y nos peleamos mucho rato con ejercicios re divertidos!.
Los diccionarios son otro tipo de objetos útiles, en cierta forma similares a las listas, que nos permiten vincular un valor con otro.
Estos se definen abriendo y cerrando llaves.
Supongamos que queremos guardar en una variable los nombres de personajes importantes en la ciencia y relacionarlos a los campos en los que trabajaron.
diccionario = {'Rosalind Franklin':'Química', 'Luis Caffarelli':'Matemática', 'Juan G. Roederer':'Física', 'Emmy Noether':'Matemática'}
La parte que va a la izquierda de los :
se llama llave, mientras que lo que escribimos a la derecha se llama valor.
Es decir, la llave Rosalind Franklin
tiene el valor Química
.
¿Cómo accedo a esto?
print(diccionario["Rosalind Franklin"])
Química
Podemos acceder a todas las llaves y valores del diccionario.
llaves = diccionario.keys()
print(llaves)
print("\n")
valores = diccionario.values()
print(valores)
dict_keys(['Rosalind Franklin', 'Luis Caffarelli', 'Juan G. Roederer', 'Emmy Noether']) dict_values(['Química', 'Matemática', 'Física', 'Matemática'])
Hay varias formas de recorrer estos objetos con un for
, pero la más importante es la siguiente, utilizando el método .items()
:
for llave, valor in diccionario.items():
print(f"La persona {llave} se dedica a la {valor}")
print()
La persona Rosalind Franklin se dedica a la Química La persona Luis Caffarelli se dedica a la Matemática La persona Juan G. Roederer se dedica a la Física La persona Emmy Noether se dedica a la Matemática
Para agregar un elemento al diccionario se hace de la siguiente manera:
diccionario['Ada Lovelace'] = 'Computación'
print(diccionario)
{'Rosalind Franklin': 'Química', 'Luis Caffarelli': 'Matemática', 'Juan G. Roederer': 'Física', 'Emmy Noether': 'Matemática', 'Ada Lovelace': 'Computación'}
Cada llave tiene un valor único y pueden tener adentro números, strings, listas o inclusive otro diccionario.
diccionario_raro = {"Milanesa": 42, "Almohada": "No tengo", "Precios boleto": [270,300,330,404, "Carísimo"]}
diccionario_raro[[12,13,14]] = "¿Podré hacer esto?"
find_peaks
: como encontrar máximos y mínimos¶# Importamos las librerías que vamos a usar
import numpy as np
import matplotlib.pyplot as plt
Supongamos que tenemos los datos de la siguiente figura y queremos saber cuales son los máximos de la señal.
¿Cómo hacemos?
# Definimos el dominio y la imagen
x = np.linspace(0, 2*np.pi, 500)
y = np.abs(np.sin(x)*np.sin(2.5*x))
# Graficamos la función
plt.plot(x, y)
plt.xlabel('Eje de $\hat x$', fontsize = 14)
plt.ylabel('Eje de $\hat y$', fontsize = 14)
plt.grid()
plt.show()
Para eso existe find_peaks
, una función de la librería llamada scipy
que tiene un montón de cosas útiles para hacer ciencia.
# De esta manera importamos solo la función y no la librería entera, que puede ser pesada
from scipy.signal import find_peaks
find_peaks
recibe una lista/array de una dimensión y calcula los máximos locales.
Apliquemos la función a nuestra tira de datos y
para ver que nos devuelve.
picos, diccionario = find_peaks(y) # Recordemos esta forma de nombrar varias variables
print(picos)
print("\n")
print(diccionario)
[ 62 146 223 276 353 437] {}
find_peaks
devuelve primero un array con los índices donde encontró máximos y luego un diccionario, que está vacío, pero más adelante vamos a ver que cosas puede contener.
De momento, graficamos los picos para ver que encontró find_peaks
de base.
# Graficamos la función
plt.plot(x, y)
plt.plot(x[picos], y[picos],"o", color = "k")
plt.xlabel('Eje de $\hat x$', fontsize = 14)
plt.ylabel('Eje de $\hat y$', fontsize = 14)
plt.grid()
plt.show()
¡find_peaks
logró hallar todos los picos! ¡Es increíble!
Veamos ahora otro caso.
# Definimos el dominio y la imagen
x = np.linspace(0, 2*np.pi, 500)
y = np.abs(np.sin(x))+np.cos(5*x)/1.2
# Graficamos la función
plt.plot(x, y)
plt.xlabel('Eje de $\hat x$', fontsize = 14)
plt.ylabel('Eje de $\hat y$', fontsize = 14)
plt.grid()
plt.show()
Ahora quiero los picos más grandes nada más.
¿Que parámetro los caracteriza como para que find_peaks
encuentre solo esos?
Hay varias cosas que podríamos usar:
Si utilizamos la altura de los picos obtenemos:
# Calculamos los picos
picos, diccionario = find_peaks(y, height = 1.5) # Acá agrego el parámetro
# Graficamos la función
plt.plot(x, y)
plt.plot(x[picos], y[picos],".", c = "k", ms = 10)
plt.xlabel('Eje de $\hat x$', fontsize = 14)
plt.ylabel('Eje de $\hat y$', fontsize = 14)
plt.grid()
plt.show()
¡Conseguimos lo que queremos! También podriamos haberle pasado la distancia u otro parámetro para que la función cuente con la mayor información posible.
Podemos ver ahora que cambio el diccionario, este contiene ahora la altura de los picos.
diccionario
{'peak_heights': array([1.78657323, 1.78657323])}
Veamos otro parámetro más: el distance
.
El distance
define la cantidad de datos intermedios que debe haber entre picos. Hay que tener en cuenta que find_peaks
recibe un array 1D, por lo que esta distancia es en índices y no en nuestra variable x
.
# Calculamos los picos
picos, diccionario = find_peaks(y, distance = 100) # Acá agrego el parámetro
# Graficamos la función
plt.plot(x, y)
plt.plot(x[picos], y[picos],".", c = "k", ms = 10)
plt.xlabel('Eje de $\hat x$', fontsize = 14)
plt.ylabel('Eje de $\hat y$', fontsize = 14)
plt.grid()
plt.show()
Existen muchos otros parámetros opcionales más tales como:
Si no, siempre conviene leer la documentación.
find_peaks
?¶Muy fácil: ¡Invertimos los datos!
Así los mínimos pasan a ser los máximos y viceversa.
# Definimos el dominio y la imagen
x = np.linspace(0, 2*np.pi, 500)
y = np.abs(np.sin(x))+np.cos(5*x)/1.2
# Calculamos los picos
picos, diccionario = find_peaks(-y) # Acá agrego el parámetro
# Graficamos la función
plt.plot(x, y, label = "Datos originales")
plt.plot(x, -y, label = "Datos invertidos")
plt.xlabel('Eje de $\hat x$', fontsize = 14)
plt.ylabel('Eje de $\hat y$', fontsize = 14)
plt.legend(loc = "upper left", fontsize = 12)
plt.grid()
plt.show()
# Calculamos los picos
picos, diccionario = find_peaks(-y) # Le paso los datos invertidos
# Graficamos la función
plt.plot(x, y)
plt.plot(x[picos], y[picos],".", c = "k", ms = 10)
plt.xlabel('Eje de $\hat x$', fontsize = 14)
plt.ylabel('Eje de $\hat y$', fontsize = 14)
plt.grid()
plt.show()
En un laboratorio se armó el montaje que se muestra a continuación: un carrito sujeto a un resorte fijo en uno de sus extremos.
Se propuso estudiar como la masa del carrito (que se podía variar agregando pesas sobre el) afectaba su frecuencia de oscilación. Con este objetivo, se realizó el siguiente procedimiento 9 veces, comenzando con el carrito vacío y agregando una pesa en cada iteración:
[n].mp4
, con [n]
el número de pesas sobre el carrito.Finalmente se subieron todas las grabaciones a un software de trackeo, calibrado para poder medir la distancia del carrito a su posición de equilibrio cada $0.2~\mathrm{s}$ desde que se soltó. Los datos de cada grabación se guardaron en archivos nombrados [n].csv
, con [n]
el número de pesas correspondiente.
La idea es ahora hacer el análisis de las mediciones, que se pueden encontrar en este link, en Python.
Escribir una función llamada masa_carrito(n)
que tome:
n
: el número de pesas colocadas en el carro.y que devuelva:
m
: la masa total del carrito.Considerar que la masa del carrito sin pesas era de $0.15~\mathrm{kg}$ y cada una de las pesas agregadas tenía una masa de $0.10~\mathrm{kg}$.
Cada .csv
tiene 3 columnas: "T [s]" para los tiempos, "X [cm]" para la posición, y "X_err [cm]" para la incerteza en la posicion. Las mediciones de tiempo también tienen incerteza, pero mucho más chica y la vamos a despreciar en el análisis. También despreciariemos las incertezas en las mediciones de masa. (Si llegan a encontrarse con un experimento similar en un labo, registren todas las incertezas y hablen con sus profes antes de no tener en cuenta incertezas en algún cálculo o ajuste).
De cada archivo queremos obtener una frecuencia de oscilación. Para enfrentar esta tarea, vamos a empezar analizando solo un archivo, 0.csv
, y luego veremos como adaptar el código para poder analizar los otros .csv's sin hacer mucho más trabajo.
0.csv
¶Queremos empezar graficando la posición en función del tiempo del carrito sin pesitas, para observar las mediciones registradas que pueden encontrar acá.
Importar numpy
y matplotlib.pyplot
para poder acceder a las funciones de Numpy y del módulo pyplot
de Matplotlib. Usar las abreviaciones estándar np
y plt
(ver clase pasada).
Cargar los datos del archivo "0.csv"
subiendolo a Colab y usando la función np.loadtxt
. Guardar:
ts
,xs
,xs_err
.Para usar la función np.loadtxt
tienen de referencia el material de la clase pasada o la documentación de Numpy.
Usar la función plt.errorbar
para gráficar xs
en función de ts
(ts
corresponde al eje $\hat x$ del gráfico, mientras que xs
corresponde al eje $\hat y$). Incluir las incertezas en la posición.
Etiquetar los ejes del gráfico "Tiempo [s]" (eje $\hat x$) y "Posición [cm]" (eje $\hat y$). De título poner "0 pesas sobre el carrito".
(opcional) Embellecer.
Ahora que vimos que los datos tienen un comportamiento oscilatorio, como es de esperar por la naturaleza del experimento, nuestro objetivo es medir su frecuencia de oscilación. Nos gustaría hacer un ajuste sinusoidal, pero como vimos la vez pasada, curve_fit
no siempre devuelve los parámetros más óptimos. A veces devuelve los parámetros correspondientes a algún otro mínimo local de la función RMSE. Veamoslo con los datos de 0.csv
, ajustandolos con un coseno:
#@title código secreto (solo abrir con doble-click **luego de haber intentado el Ejercicio 2**)
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
ts, xs, xs_err = np.loadtxt("0.csv", delimiter = ",", unpack = True, skiprows = 1)
def sinusoidal(t, A, w, phi):
return A*np.cos(w*t + phi)
popt, pcov = curve_fit(sinusoidal, ts, xs)
A, w, phi = popt
ts_ajuste = np.linspace(min(ts), max(ts), 1000)
xs_ajuste = sinusoidal(ts_ajuste, A, w, phi)
plt.errorbar(ts, xs, yerr = xs_err, fmt = "s", ms = 2, capsize = 2, color = "k")
plt.plot(ts_ajuste, xs_ajuste, color = "r")
plt.xlabel("$t$ [s]")
plt.ylabel("$x$ [cm]")
plt.show()
¿Como solucionamos esto? Como repaso, queremos que curve_fit
empieze a buscar los parámetros óptimos cerca de la solución que esperamos, que se puede hacer pasandole parámetros iniciales a través del argumento p0
:
popt, pcov = curve_fit(sinusoidal, t, x, p0 = [5, w_aprox, 0])
De esta forma, la función va a buscar la frecuencia óptima cerca de la frecuencia aproximada w_aprox
.
La pregunta es ahora, ¿de donde sacamos estos parámetros iniciales? Algunas posibilidades:
phi
del coseno debe estar cerca del 0 (si no les resulta intuitivo pregunten).La frecuencia aproximada se calcula entonces con $\omega_\text_{aprox} = 2\pi/T$.
find_peaks
: Una forma de automatizar el método "a ojo" es midiendo el período de las oscilaciones usando find_peaks
. Esto es lo que vamos a usar ahora, ya que tenemos muchos datos para analizar y el método "a ojo" puede ser tedioso.find_peaks
¶Lo que queremos hacer es medir el intervalo de tiempo entre los dos primeros picos de amplitud. Recordemos que find_peaks
devuelve los indices de los picos del array que le damos y un diccionario con más información.
Importar find_peaks
de scipy.signal
y aplicarlo al array de posiciónes xs
de 0.csv
. Imprimir picos
y el diccionario.
Graficar los datos de 0.csv
nuevamente (podés copiar y pegar!!). Luego, graficar los picos encontrados por find_peaks
con plt.plot(ts[picos], xs[picos], "o")
. ¿Coinciden los picos con lo que esperan?
Calcular el periódo T
e imprimir el resultado (f'Período estimado: {T} s'
). Ayuda: ¿cuanto vale el tiempo en el segundo pico? ¿y en el primero? Restando estos valores van a poder obtener la respuesta!
Calcular la frecuencia w0
usando que está dada por $2\pi / T$. Imprimir el resultado (f'Frecuencia estimada: {w0} 1/s'
).
Nota: Estamos usando este método para estimar los parámetros iniciales, pero se podría usar para medir la frecuencia. El tema es que no estamos calculando la incerteza del valor del $\omega$ que obtenemos, y una medición tiene incerteza. Además, un ajuste usa todos los datos, no solo los picos, lo que está bueno para tener más información. Si queres conversar un poco más del tema, llama a un profe!
sinusoidal
que tome la variable independiente t
y los parámetros a determinar A, w
y phi
(amplitud, frecuencia, y fase), y que devuelva el resultado de $A\cos(\text{w}t + \text{phi})$.curve_fit
(hay que importarlo), ajustar los datos de xs
en función de ts
y obtener la frecuencia óptima w
de popt
(los parámetros óptimos), y su error w_err
de pcov
(la matriz de covarianza). Imprimir el resultado usando f'Frecuencia: ({w} ± {w_err}) 1/s'
. Ayuda: ver clase pasada! Y configuren los parámetros iniciales (p0
) en curve_fit
.Acá, esto es porque programar es super útil. Ya trabajaste un montón para analizar 0.csv
. El solo pensamiento de repetirlo 10 veces te pone los pelos de punta. ¡Pero no lo tenés que hacer! Copiando el código que ya hiciste, ordenandolo un poco y usando la mágia de las iteraciones vas a haber analizado todo en un suspiro.
Inicializar tres listas vacías, []
, guardandolas en las variables ms
, ws
, y ws_err
. En estas listas vamos a guardar la masa y frecuencia correspondientes a cada archivo.
Recorrer los archivos "0.csv"
a "8.csv"
(recordar subirlos a Colab) con un for
, obteniendo la frecuencia de los datos y la masa total del carrito en cada uno. Si les ayuda, pueden usar la siguiente planilla. TIP: hagan un gráfico en cada ciclo del for para verificar que find_peaks está agarrando "bien" los picos y vayan probando si el for funciona mientras lo van armando.
for n in range(9):
ts, xs, xs_err = np.loadtxt(f"{COMPLETAR}.csv", delimiter = COMPLETAR, skiprows = COMPLETAR, unpack = COMPLETAR)
# USAR FIND_PEAKS PARA ESTIMAR LA FRECUENCIA (COPIAR CÓDIGO ANTERIOR, OJO CON LAS INDENTACIONES).
# GRAFICAR SEÑAL Y PICOS
plt.show() # para que los gráficos queden en figuras distintas
# USAR CURVE_FIT PARA CONSEGUIR EL VALOR DE LA FRECUENCIA Y SU ERROR (COPIAR CÓDIGO ANTERIOR, OJO CON LAS INDENTACIONES).
# AGREGAR FRECUENCIA OBTENIDA A LA LISTA ws
# AGREGAR ERROR DE LA FRECUENCIA OBTENIDA A LA LISTA ws_err
# AGREGAR MASA CORRESPONDIENTE A LA LISTA ms (usar función que hicieron al princípio)
El modelo de un oscilador sin amortiguamiento cuya fuerza restitutiva está dada por: $$ F_\text{resorte} = -kx\qquad \text{(Ley de Hooke)} $$ con $k$ una constante y $x$ el desplazamiento del oscilador del equilibrio, tiene como frecuencia natural: $$ \omega(m) = \sqrt{\frac{k}{m}} $$
ws
en función de ms
, mostrando el error de ws
. (ws
en el eje $\hat y$, ms
en el eje $\hat x$)AUTOCORRECCIÓN: Les debería dar $k = (2.000 \pm 0.002) ~\text{N/m}$
Vamos a resolver probabilísticamente un problema simple.
Si tiramos 100 monedas y las ordenamos en una secuencia como la siguiente:
"... Cara Cruz Cara Cara Cruz ..."
¿Qué es más probable? ¿Encontrar la secuencia "Cara Cara" o encontrar la secuencia "Cruz Cara"? ¿Son equiprobables?
Rompamos nuestra cabeza.
Vamos a hacer todo de a pasos.
generar_secuencia(largo)
¶Lo primero que tenemos que poder hacer es generar una secuencia de caras y cruces. Como tanto cara como cruz empiezan con la misma letra, las vamos a cambiar por 0 y 1, es decir, estudiaremos cuantas veces aparece la secuencia 00 y cuantas aparece la secuencia 01.
Creá una función que reciba el largo de la secuencia y devuelva una lista llena de ceros y unos. Para esto, realizá un bucle for
en el genere con cada iteración un 0 o un 1.
AYUDA: La función np.random.randint(0,n)
genera un número aleatorio entre 0
y n-1
, es decir, no incluye a n
, al igual que el range
.
calcular_puntajes(secuencia)
¶Ahora tenemos que calcular los puntajes de las secuencias que generamos, es decir, hay que recorrer la secuencia y si encuentro un 0 y en el siguiente elemento de la lista otro 0 sumo un punto para la secuencia 00 mientras que si encuentro un 1 y luego un 0 sumo un punto para la secuencia 01.
Creá una función que reciba una secuencia y devuelva el puntaje obtenido para la secuencia 00 y para la secuencia 01.
AYUDITA: Recorrer la secuencia y fijarse si estoy parado en un 0 ¿Cómo debe ser el siguiente elemento de la lista para que sume un punto a la secuencia 00?
Ahora necesitamos hacer estadística, queremos ver que secuencia es más probable que la otra.
Vamos a estudiar secuencias de distintos largos, desde 2 hasta 100 números. ¿Por qué empezamos en 2?
Para cada largo, realizá 1000 secuencias utilizando la función generar_secuencia(largo)
y calculá los puntajes de cada secuencia con calcular_puntajes(secuencia)
. Guardá en variables la cantidad de victorias de cada secuencia ¡y acordate de los empates!, transformalas en probabilidad (dividilas por 1000, cof cof) y guardalas en tres listas (POR FAVOR TE LO PIDO), una para 00, otra para 01 y otra para los empates.
No hagan más de 1000 secuencias porque sino el código va a empezar a tardar en ejecutarse y acá queremos las cosas ¡YA!. Si tienen ganas de esperar más tiempo pasen a 10000, pero solo pueden hacerlo una vez saben que funciona el código, sino la compu explota.
¿Que secuencia crees que va a ser más probable que la otra?
Llegó el momento de responder la tan esperada pregunta y vamos a hacerlo utilizando un gráfico.
Graficá las probabilidades de cada secuencia y los empates, en función del largo de la secuencia.
Es decir, en el eje $\hat{x}$ tendremos el largo de la secuencia y en el eje $\hat{y}$ la probabilidad de cada secuencia.
¡Hacé el gráfico lo más hermoso posible! Ponele grilla, leyenda, nombres a los ejes y labels a cada curva. Si tenés ganas jugá con los colores y preguntanos que tan lindo te quedó el gráfico (por contrato tenemos que responder que quedó hermoso), (mentira no hay contrato).
Respondimos la pregunta de que secuencia tiene más chances de ganar, pero el gráfico no responde el porqué ocurre esto, así que vamos a ahondar más profundo.
Generá 100000 secuencias de largo 4, calculá la diferencia de puntaje entre 00 y 01 y guardalas en una lista. Hacé un histograma de esa lista utilizando plt.hist(lista)
y hacé el gráfico lo más bello que puedas. ¿Hay alguna tendencia? ¿Con cuanto puntaje máximo gana una secuencia y con cuanto gana la otra?
Luego, LUEGO de hacer esto. Abrí el siguiente enlace. *¡¡LUEGO ES LUEGO!!*
¡SOLO LUEGO DE RESOLVER EL INCISO!
Si querés jugá a cambiar el largo de la secuencia para ver como cambian los histogramas.
Con esto hemos podido responder la pregunta, o al menos iluminar bastante la respuesta. Sin embargo podemos seguir aprendiendo cosas.
Calculá el valor medio y la desviación estándar del histograma que calculaste recién. Utilizá las funciones de numpy np.mean(lista)
y np.std(lista)
y aprendé inglés para saber cual es cual. Anotate en el Laboratorio de idiomas de la UBA y... bueno paro.
¿Tenes ganas de charlar un rato? Preguntanos que significa ddof = 1
.
Vamos a estudiar la dinámica de poblaciones según un modelo clásico de depredador-presa.
¡Lotka-Volterra!
Tanto Lotka como Volterra propusieron de manera independiente un sistema de ecuaciones diferenciales para describir la dinámica de las poblaciones de dpredadores y presas, para modelar como interactúan entre sí.
Para arrancar a trabajar necesitamos los datos, sino no podemos hacer nada.
Utilizando la función np.loadtxt()
(recordar la clase pasada) cargá los datos del archivo "datos_poblaciones.csv"
. A cada columna asignale el nombre apropiado, es decir, definí las variables tiempo
, pob_presas
y pob_depredadores
.
Los datos pueden encontrarse dentro de una carpeta en el siguiente enlace: Mercado Pago de la FIFA
Lo primero que tenemos que hacer cuando nos dan unos datos, a parte de cargarlos, es graficarlos, así vemos cual es su pinta y podemos pensar en que análisis realizar.
Utilizando lo que vimos la clase pasada de matplotlib
, graficá las poblaciones de presas y depredadores en función del tiempo y hacé que el gráfico sea lo más canchero posible, ponele grilla, leyenda, los colores que vos quieras, etc.
¿Son las poblaciones como esperabas? ¿No? $\;$ Enseñanos algo de biología... ¡Por favor!
Estaría bueno tener cierto conocimiento general de las poblaciones, como su promedio y cuanto se desvían las poblaciones de este.
Calculá el promedio de la población de presas utilizando la función de numpy np.mean(pob_presas)
.
¿Es un valor representativo de la población?
Graficá este promedio en la figura anterior que hiciste, utilizando plt.axhline(valor_medio, c = "g", linestyle = "--")
. Probá cambiando los parámetros opcionales.
Calculá también la desviación estandar, utilizando np.std(pob_presas)
.
¿Este valor te dice algo más respecto al promedio?
find_peaks
¶Observamos que las poblaciones oscilan y en particular notamos varios picos.
Utilizar la función find_peaks
aplicada a la población de las presas, para determinar la posición de los máximos. Grafica los picos en un nuevo gráfico junto a la población de las presas en función del tiempo.
¿Cuanta población de presas hay en estos picos?
Vamos a tomarnos el laburo de hacer un gráfico bello, pero que les puede resultar complicado, así que vamos de a poco.
Graficá la población de depredadores en función de la población de presas. ¿Qué se observa? ¿¿Alguna especie de ciclo?? Guiño guiño.
Si lo pensas, en este gráfico perdemos la noción del tiempo, no sabemos de donde parte la población ni donde termina (aunque quizá sí te des cuenta), así que vamos a tomarnos el trabajo de agregarle esto.
Copiá las siguientes líneas (no hace falta que las entiendas del todo):
from matplotlib import colors
colormap = plt.cm.plasma
norm = colors.Normalize(vmin=min(tiempo), vmax= max(tiempo))
scalar_map = plt.cm.ScalarMappable(norm=norm, cmap=colormap)
Buscá en el navegador: matplotlib colormaps y cambia donde dice plasma
por el colormap que más te guste. Puede ser que el que más te guste sea plasma
, a mí me gusta...
Ahora se viene lo lindo.
Realizá un ciclo for
que recorra las listas de las poblaciones y graficá para cada i
(esto si debería entenderse):
plt.plot([pob_presa[i], pob_presa[i + 1]], [pob_depredador[i], pob_depredador[i + 1]], color = colormap(tiempo[i]/max(tiempo)))
¿Que tal el gráfico ahora? Para agregar la barra de colores del tiempo, copiá:
cbar = plt.colorbar(scalar_map)
cbar.set_label("Tiempo")
¿Te molesta como quedaron los números del eje x?
Agregale al gráfico la línea plt.xticks(rotation = 45)
, mirá a los ojos a algún docente y decile que pensas que no queda mucho mejor.
Este fue un ejercicio de biología pensado por una persona que no sabe de biología, así que nos re sirve tener tu opinión!
Te dejamos tu última misión.
Aprendé programación por tu cuenta el tiempo que sea necesario para formar parte del plantel docente del taller de Python de la FIFA, cambiá este ejercicio y lentamente añadí gente de Biología al taller hasta que pase a ser un taller de estudiantes de Biología para* estudiantes de Biología. El taller de Python de la FIBA...*
El modelo de Lotka-Volterra con el que se generaron numéricamente los datos (te pido disculpas no fuimos a medir durante 5 años las poblaciones), sigue las siguientes ecuaciones diferenciales, donde $u$ es la población de presas y $v$ la de depredadores:
$$ \frac{du}{dt} = au - buv $$$$ \frac{dv}{dt} = - cv + duv $$El resto de parámetros son:
Estas ecuaciones se pueden resolver numéricamente para jugar con los distintos parámetros y entender como evolucionan las poblaciones. Para esto utilizaremos la siguiente función de Scipy:
from scipy.integrate import odeint
Definí un array de tiempos como vimos la clase pasada, que vaya de 0 a 5 y tenga longitud 5000. Si querés probar otro intervalo de tiempo o cantidad de puntos, hacelo!
Definí $a$, $b$, $c$ y $d$ con ávida intuición, para que pase lo que vos querés.
Por ejemplo, si $a$ es muy grande, la población de presas crecerá muy rápido. Si $d$ es muy chico, la población de depredadores crecerá muy poco al alimentarse de las presas.
Definí las poblaciones iniciales de las presas y depredadores en una lista que se llame X0 y tenga en cada posición, la población inicial de cada uno.
Definí una función, llamada lotka_volterra
, que tome de input
X (que ahora te digo que es, aunque es parecido a lo de arriba), el array de tiempos que hiciste, y los parámetros $a$, $b$, $c$ y $d$.
En la primera línea definí u, v = X (ya te dije que es, ponele) (si, hay que escribirlo en un solo parámetro, no podés poner u y v como parámetros de la función, así es la vida).
La función debe devolver, como output
, una lista donde cada elemento es la parte derecha de las ecuaciones diferenciales de más arriba, es decir, al derivada temporal de cada población.
Utilizá la siguiente línea para integrar numéricamente las ecuaciones
u, v = odeint(lotka_volterra,X0, tiempo, args = (a,b,c,d)).T
El .T del final es para transponer la matriz con la que nos dan los datos, probá si hace falta o nó.
Graficá las poblaciones en función del tiempo, probá todos los parámetros que te gusten y sé feliz el resto de tu vida. La última no es opcional.
Si querés chequear que funciona bien, probá que las interacciones sean nulas y que evolucione cada población por separado. ¿El crecimiento debería ser? ¡Expone-
¡Ahora ya sabés como resolver ecuaciones diferenciales y podés modelar lo que vos quieras! ¿Supongo?
La idea de estos ejercicios es que son cortos pero requieren conceptos que son importantes a la hora de programar o son problemas que se van a encontrar en algún momento en el futuro.
Algunos son más divertidos o fáciles que otros, pero no por eso son menos importantes.
Queremos generar numeros aleatorios con una cierta distribución de probabilidad, en este caso una Gaussiana, que es una distribución muy normal (jajá que buen juego de palabras jajajajajajajajajajajajajajajajajajajajajajaja) que aparece en todas las ciencias.
Utilizando la función de numpy
llamada np.random.normal()
(si, a la Gaussiana también se la llama la normal, ahora entendiste el chiste de arriba) podemos generar números aleatorios según una distribución Gaussiana.
1) Generá 100 números aleatorios utilizando np.random.normal()
y guardalos en una lista
. Realizá un histograma utilizando plt.hist(lista)
y hacé que el gráfico sea lo más canchero posible (recomendamos utilizar el parámetro opcional edgecolor = "k"
para ponerle color negro a los bordes).
2) Calcula np.mean(lista)
y np.std(lista)
. Estos devuelven el valor medio y la desviación estándar de los datos, respectivamente.
3) Si conoces a la Gaussiana, sabés que esta tiene un valor medio, donde está centrada la campana, y una desviación estandar, que determina que tan dispersos están los datos. En particular np.random.normal()
genera números aleatorios con valor medio 0 y desviación estándar 1. Probá cambiando los valores de la siguiente manera np.random.normal(valor_medio, desviacion_estandar)
y ponele los valores que quieras. También jugá a cambiar la cantidad de números aleatorios que generas (100 por 1000 y así) y fijate como van cambiando np.mean(lista)
y np.std(lista)
.
4) Si te pareció que hay demasiados chistes, dejalo en las encuestas, aprendé programación todo un cuatrimestre, vení la siguiente edición y modificá este ejercicio.
Tenemos la siguiente lista de nombres y carreras y queremos armar un diccionario que asocie cada nombre con su respectiva carrera.
nombres = ['Rosalind Franklin', 'Juan G. Roederer', 'Albert-László Barabási', 'Emmy Noether', 'Phyllis Nicolson',
'Ada Lovelace', 'Luis Caffarelli', 'Miguel Ángel Virasoro', 'Luis Federico Leloir']
carreras = ['Química', 'Física', 'Física', 'Matemática', 'Física',
'Computación', 'Matemática', 'Física', 'Química']
Realizá un ciclo for
para recorrer las listas. Ayuda: Al nombre i-ésimo le corresponde la carrera i-ésima.
Una vez hayas hecho el diccionario, recorrelo e imprimí en pantalla los nombres de las personas cuya carrera sea "Física" (si así no sale ya no se que más hacer, te pido perdón).
Definir la función factorial(n)
, que recibe un número entero y devuelve el factorial dicho número.
Expresión del factorial:
$$n! = n \cdot (n-1) \cdot (n-2) \cdot \; ... \cdot 1$$math
ya tiene una función para realizar esto, llamada math.factorial()
. Comparar los resultados.
Definir la función absoluto(x)
que recibe un número real (float
) x
y devuelve su módulo.
texto en cursiva
Si $x < 0$ debería devolver $-x$ y si $x \geq 0$ debería devolver $x$.
En Python ya existe una función que hace esto, llamada abs()
. Comparar los resultados.
La idea de este ejercicio es familiarizarse con funciones super útiles aplicables a listas.
numeros = [20,12,10,3,8,14,1,42]
Copie la lista de arriba y aplique las funciones max()
y min()
.
¿Qué devuelven?
Ahora aplicaremos métodos
a la lista de números.
Aplique el método pop()
a la lista de números, de la siguiente manera numeros.pop()
(recuerde el append()
de la primera clase).
Por último, aplicamos otro método.
Aplique el método insert()
a la lista de números, de la siguiente manera numeros.insert(0,77)
.
¿Qué hace cada método?