{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Modelos basados en Agentes con Python" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Esta notebook fue creada originalmente como un blog post por [Raúl E. López Briega](https://relopezbriega.com.ar/) en [Matemáticas, Analisis de datos y Python](https://relopezbriega.github.io). El contenido esta bajo la licencia BSD.*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> \"Conocer la realidad implica construir sistemas en continua transformación que se corresponden, más o menos, a la realidad\"\n", "\n", "**[Jean Piaget](https://es.wikipedia.org/wiki/Jean_Piaget)**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introducción\n", "\n", "A medida que el mundo se hace más interconectado y complejo, nuestra habilidad para comprenderlo se hace menos efectiva. Los modelos simples que solíamos utilizar ya no alcanzan para responder muchas de nuestras preguntas. Para poder entender las dinámicas de los [sistemas complejos](https://relopezbriega.github.io/blog/2020/03/26/sistemas-dinamicos-complejidad-y-caos-con-python/); necesitamos de nuevas herramientas. El poder de cálculo de las computadoras actuales nos permite realizar nuevos tipos de experimentos. Una de las nuevas metodologías que disponemos para analizar los [sistemas complejos](https://relopezbriega.github.io/blog/2020/03/26/sistemas-dinamicos-complejidad-y-caos-con-python/) son los [modelos basados en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente). \n", "\n", "## Modelos basados en Agentes\n", "\n", "La idea central de los [modelos basados en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente) es que muchos de los fenómenos que vemos en el mundo pueden ser modelados con *agentes*, un *ambiente*, y la descripción de las interacciones entre los *agentes* y entre los *agentes* y el *ambiente*.\n", "Un *agente* es un individuo autónomo o un objeto con propiedades particulares, acciones y posibles objetivos. El *ambiente* es el territorio en el que los *agentes* interactúan. Las interacciones pueden ser de los *agentes* entre sí o de éstos con el *ambiente* y pueden llegar a ser bastante complejas e incluso pueden llegar a cambiar con el paso del tiempo. Como las interacciones están construidas en base a un intercambio de información, luego de una interacción el *agente* puede actualizar su estado interno o tomar distintas decisiones. \n", "\n", "Los [modelos basados en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente) constituyen una nueva generación de métodos computacionales que permiten modelar la estructura de un [sistema complejo](https://relopezbriega.github.io/blog/2020/03/26/sistemas-dinamicos-complejidad-y-caos-con-python/) y simular su evolución dinámica a lo largo del tiempo. Constituyen un tercer modo de hacer ciencia, distinto y complementario a los dos métodos científicos clásicos: la inducción y la deducción. \n", "\n", "Un [modelo basado en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente) es un tipo particular de modelo científico que se implementa como una simulación computacional. Por lo tanto, son modelos formales que deben ser distinguidos tanto de los matemáticos (basados en [ecuaciones diferenciales](https://relopezbriega.github.io/blog/2016/01/10/ecuaciones-diferenciales-con-python/) o de otro tipo) como de los [estadísticos](https://relopezbriega.github.io/blog/2015/06/27/probabilidad-y-estadistica-con-python/) (orientados por variables y expresados como ecuaciones de regresión, estructurales, o de otro tipo). \n", "\n", "### Vínculo micro-macro\n", "\n", "Los [modelos basados en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente) abordan el vínculo *micro-macro* en una doble dirección. En primer lugar, permiten modelar y simular el vínculo de lo *micro* a lo *macro*, es decir, \"cómo las interacciones locales y descentralizadas entre *agentes* autónomos y heterogéneos generan una determinada regularidad\" *macrosocial*. Se suele utilizar el concepto de *emergente* para referirse a la aparición de \"cualidades o propiedades de un sistema que presentan un carácter de novedad con relación a las cualidades o propiedades de los elementos considerados aisladamente\". Los fenómenos *emergentes* son, en consecuencia, difíciles de explicar y predecir en la medida en que las cualidades nuevas a nivel *macro* de un sistema no pueden deducirse ni reducirse al conocimiento analítico de las partes a nivel *micro*.\n", "\n", "En segundo lugar, los [modelos basados en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente) contribuyen a comprender el vínculo de lo *macro* a lo *micro*, relativo al modo en que \"las estructuras sociales construyen e influyen las acciones futuras y las interacciones entre los actores individuales\". El interés analítico de esta fase del modelado del vínculo *micro-macro* se centra en comprender cómo \"los individuos elaboran representaciones mentales de construcciones sociales\" que influyen en su propia conducta práctica. En otros términos, la acción social (nivel micro) no puede escindirse del modo en que los individuos piensan y razonan sobre el orden social (nivel macro). Esta problemática ha sido conceptualizada bajo el nombre *emergente de segundo orden*, es decir, a la aptitud reflexiva de los *agentes* para razonar sobre las propiedades *emergentes* que la misma interacción social produce (emergencia de primer orden).\n", "\n", "\n", "## Pensar con modelos\n", "\n", "Aprender es explorar. Organizar e interpretar datos con modelos se ha convertido en una competencia fundamental para la estrategia en los negocios, la planificación urbana, la economía, la medicina, la ingeniería y la ecología, entre muchas otras. Pero no necesariamente debemos quedarnos con un solo modelo, muchas veces la aplicación de un conjunto de modelos puede ayudarnos a dar sentido a fenómenos [complejos](https://relopezbriega.github.io/blog/2020/03/26/sistemas-dinamicos-complejidad-y-caos-con-python/). La idea central es que el pensamiento con múltiples modelos produce sabiduría a través de un conjunto diverso de marcos lógicos. Los distintos modelos acentúan diferentes fuerzas causales. Sus conocimientos e implicaciones se superponen y se entrelazan. Al utilizar muchos modelos como marcos, desarrollamos una comprensión más profunda de la realidad.\n", "\n", "Algunas de las ventajas de utilizar modelos son:\n", "\n", "* Los **modelos son explicativos** porque señalan los mecanismos esenciales que subyacen un fenómeno. Pueden funcionar como una prueba de que los mecanismos hipotéticos son suficientes para dar cuenta de una observación. Los modelos nos proporcionan una prueba de concepto de que algo es posible.\n", "\n", "* Los **modelos facilitan la experimentación.** Los modelos se pueden ejecutar repetidamente para notar variaciones en su dinámica y sus resultados. Los parámetros del modelo se pueden variar para ver el efecto en su comportamiento y resultados.\n", "\n", "* Los **modelos nos proporcionan analogías.** Dado que los modelos son simplificaciones de la realidad, nos permiten encontrar similitudes con otras simplificaciones similares, incluso si los fenómenos modelados son aparentemente muy diferentes.\n", "\n", "* Los modelos se pueden utilizar como vehículo de **comunicación y educación**. Los modelos nos brindan una herramienta educativa que encapsula conocimientos que pueden no estar fácilmente disponibles en la observación del mundo real.\n", "\n", "\n", "## Mesa\n", "\n", "Una de las razones por las que amo [Python](https://www.python.org/); es que siempre es posible encontrar alguna librería para hacer casi cualquier cosa en Ciencia con su ayuda. Obviamente, modelar [modelos basados en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente) no podía ser una excepción.\n", "\n", "[Mesa](https://mesa.readthedocs.io/en/master/index.html) es un paquete de [Python](https://www.python.org/) de código abierto con licencia [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) que nos permite crear rápidamente [modelos basados en agentes](https://es.wikipedia.org/wiki/Modelo_basado_en_agente) utilizando componentes centrales incorporados (como programadores de agentes y cuadrículas espaciales) o implementaciones personalizadas; visualizarlos usando una interfaz basada en el navegador; y analizar sus resultados con las herramientas de análisis de datos [Python](https://www.python.org/).\n", "\n", "El principio rector de la arquitectura de [Mesa](https://mesa.readthedocs.io/en/master/index.html) es la modularidad; hace suposiciones mínimas sobre la forma que tomará un modelo. Se divide en tres categorías principales: modelado, análisis y visualizaciones. El componente principal de modelado, nos brinda todo lo que necesitamos para construir un modelo. La clase **Model** para guardar los parámetros a nivel modelo; una o más clases **Agent** que describen a los *agentes* del modelo; un **Scheduler** que controla la activación de los *agentes* y maneja el tiempo y el espacio o red en la que se desarrollan las interacciones. Los componentes de análisis son el **data collector** utilizado para grabar los datos de cada ejecución del modelo y los **batch runners** para automatizar múltiples ejecuciones con distintos parámetros. Finalmente, los componentes de visualización se utilizan para mapear desde un objeto modelo a uno o más representaciones visuales a través de una interfaz de servidor con el navegador web. \n", "\n", " \n", "## Modelando el COVID19 - Modelo SIR\n", "\n", "Tiempo de pasar a un ejemplo concreto, y que mejor que aprovechar la popularidad de los [modelos epidemiológicos](https://es.wikipedia.org/wiki/Modelaje_matem%C3%A1tico_de_epidemias) que trajo la pandemia global del [COVID19](https://www.argentina.gob.ar/salud/coronavirus-COVID-19). Para este ejemplo en particular vamos a utilizar el [modelo SIR](https://es.wikipedia.org/wiki/Modelo_SIR); el cual es uno de los más simples para captar las características típicas de los brotes epidémicos. El nombre del modelo proviene de las iniciales S (población susceptible), I (población infectada) y R (población recuperada); relaciona las variaciones de las tres poblaciones (Susceptible, Infectada y Recuperada) a través de la tasa de infección y el período infeccioso promedio.\n", "\n", "Veamos como implementarlo para una población de 10000 individuos." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# \n", "# Importando las librerías que vamos a utilizar\n", "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from datetime import date as datemethod\n", "from datetime import datetime\n", "from mesa import Agent\n", "from mesa import Model\n", "from mesa.time import RandomActivation\n", "from mesa.space import NetworkGrid\n", "from mesa.datacollection import DataCollector\n", "from mesa_SIR import SIR\n", "import mesa_SIR.calculations_and_plots as c_p\n", "\n", "# graficos incrustados\n", "%matplotlib inline\n", "\n", "parametros = {'I0':0.01, 'ptrans':0.25, 'progression_period':3, \n", " 'progression_sd':2, 'population':10000, 'interactions':6,\n", " 'reinfection_rate':0.00, 'death_rate':0.0200, \n", " 'recovery_days':15, 'recovery_sd':7, 'severe':0.18, 'steps':20}\n", "\n", "# Parámetros\n", "###################################################################################################################\n", "# ptrans = Probabilidad de transmisión.\n", "# population = Total de población.\n", "# progression_period = Número de días hasta que se busca tratamiento.\n", "# progression_sd = Desvio estandar para buscar tratamiento.\n", "# interactions = Número de interacciones por persona (baja con distancia social)\n", "# reinfection_rate = Probalidad de volver a enfermar luego de recuperado.\n", "# I0 = Probalidad inicial de estar infectado.\n", "# death_rate = Probabilidad de muerte luego de ser infectado.\n", "# recovery_days = Promedio de días para recuperarse\n", "# recovery_sd = Desvio estandar de los días de recuperación.\n", "# severe = Probabilidad de desarrollar síntomas severos.\n", "# steps = número de días de la simulación.\n", "\n", "#Agent class\n", "class humano(Agent):\n", " \n", " def __init__(self, unique_id, model):\n", " super().__init__(unique_id, model)\n", " self.pos = unique_id\n", " self.infected, self.susceptible, self.severe = self.model.SIR_instance.initial_infection()\n", " self.was_infected = False\n", " self.recovered = False\n", " self.alive = True\n", " self.day = 0\n", " self.induced_infections = 0\n", " self.infected_others = False\n", " \n", " def step(self):\n", "\n", " self.model.SIR_instance.interact(self)\n", " self.day += 1\n", " \n", "class modelo_COVID(Model):\n", " \n", " def __init__(self):\n", " super().__init__(Model)\n", " \n", " self.susceptible = 0\n", " self.dead = 0\n", " self.recovered = 0\n", " self.infected = 0\n", " interactions = parametros['interactions']\n", " self.population = parametros['population']\n", " self.SIR_instance = SIR.Infection(self, ptrans = parametros['ptrans'],\n", " reinfection_rate = parametros['reinfection_rate'],\n", " I0= parametros[\"I0\"],\n", " severe = parametros[\"severe\"],\n", " progression_period = parametros[\"progression_period\"],\n", " progression_sd = parametros[\"progression_sd\"],\n", " death_rate = parametros[\"death_rate\"],\n", " recovery_days = parametros[\"recovery_days\"],\n", " recovery_sd = parametros[\"recovery_sd\"])\n", "\n", "\n", " G = SIR.build_network(interactions, self.population)\n", " self.grid = NetworkGrid(G)\n", " self.schedule = RandomActivation(self)\n", " self.dead_agents = []\n", " self.running = True\n", " \n", " for node in range(self.population):\n", " new_agent = humano(node, self) \n", " self.grid.place_agent(new_agent, node)\n", " self.schedule.add(new_agent)\n", "\n", " self.datacollector = DataCollector(model_reporters={\"infectados\": lambda m: c_p.compute(m,'infected'),\n", " \"recuperados\": lambda m: c_p.compute(m,'recovered'),\n", " \"susceptibles\": lambda m: c_p.compute(m,\"susceptible\"),\n", " \"muertes\": lambda m: c_p.compute(m, \"dead\"),\n", " \"R0\": lambda m: c_p.compute(m, \"R0\"),\n", " \"casos_severos\": lambda m: c_p.compute(m,\"severe\")})\n", " self.datacollector.collect(self)\n", " \n", " def step(self):\n", " self.schedule.step()\n", " \n", " self.datacollector.collect(self)\n", "\n", " if self.dead == self.schedule.get_agent_count():\n", " self.running = False\n", " else:\n", " self.running = True" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Ejecución del modelo\n", "# Guardar salida del modelo\n", "today = datemethod.strftime(datetime.utcnow(), '%Y%m%dZ%H%M%S')\n", "filename = 'COVID_output_' + today + '.csv'\n", "output_path = '/home/raul/Documentos/'\n", "output_file = output_path + filename\n", "\n", "# iniciar modelo\n", "covid = modelo_COVID()\n", "dias=parametros[\"steps\"]\n", "\n", "for i in range(dias):\n", " covid.step()\n", "\n", "# generar salida. \n", "output_data = covid.datacollector.get_model_vars_dataframe()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", " | infectados | \n", "recuperados | \n", "susceptibles | \n", "muertes | \n", "R0 | \n", "casos_severos | \n", "
---|---|---|---|---|---|---|
0 | \n", "94 | \n", "0 | \n", "9906 | \n", "0.0 | \n", "0.000000 | \n", "12 | \n", "
1 | \n", "1357 | \n", "10 | \n", "8633 | \n", "0.0 | \n", "2.791667 | \n", "229 | \n", "
2 | \n", "6748 | \n", "88 | \n", "3161 | \n", "3.0 | \n", "2.310806 | \n", "1193 | \n", "
3 | \n", "9322 | \n", "311 | \n", "355 | \n", "12.0 | \n", "2.119270 | \n", "1651 | \n", "
4 | \n", "9288 | \n", "638 | \n", "53 | \n", "21.0 | \n", "2.105173 | \n", "1629 | \n", "
5 | \n", "8860 | \n", "1088 | \n", "9 | \n", "43.0 | \n", "2.097285 | \n", "1545 | \n", "
6 | \n", "8265 | \n", "1665 | \n", "3 | \n", "67.0 | \n", "2.097300 | \n", "1409 | \n", "
7 | \n", "7572 | \n", "2337 | \n", "1 | \n", "90.0 | \n", "2.098090 | \n", "1283 | \n", "
8 | \n", "6731 | \n", "3157 | \n", "1 | \n", "111.0 | \n", "2.098667 | \n", "1126 | \n", "
9 | \n", "5839 | \n", "4029 | \n", "1 | \n", "131.0 | \n", "2.098212 | \n", "968 | \n", "
10 | \n", "4858 | \n", "4997 | \n", "0 | \n", "145.0 | \n", "2.097477 | \n", "791 | \n", "
11 | \n", "3826 | \n", "6013 | \n", "0 | \n", "161.0 | \n", "2.097345 | \n", "609 | \n", "
12 | \n", "2901 | \n", "6929 | \n", "0 | \n", "170.0 | \n", "2.097471 | \n", "453 | \n", "
13 | \n", "2108 | \n", "7713 | \n", "0 | \n", "179.0 | \n", "2.097772 | \n", "323 | \n", "
14 | \n", "1402 | \n", "8417 | \n", "0 | \n", "181.0 | \n", "2.097577 | \n", "217 | \n", "
15 | \n", "864 | \n", "8952 | \n", "0 | \n", "184.0 | \n", "2.097814 | \n", "145 | \n", "
16 | \n", "489 | \n", "9324 | \n", "0 | \n", "187.0 | \n", "2.098052 | \n", "83 | \n", "
17 | \n", "246 | \n", "9564 | \n", "0 | \n", "190.0 | \n", "2.098052 | \n", "40 | \n", "
18 | \n", "110 | \n", "9699 | \n", "0 | \n", "191.0 | \n", "2.098052 | \n", "20 | \n", "
19 | \n", "46 | \n", "9763 | \n", "0 | \n", "191.0 | \n", "2.098052 | \n", "9 | \n", "
20 | \n", "22 | \n", "9787 | \n", "0 | \n", "191.0 | \n", "2.098052 | \n", "5 | \n", "