%pylab inline
from haggerstrand.diffusion import SimpleDiffusion
La primera línea sigue la misma estructura que el comando %matplotlib inline
utilizado en prácticas anteriores; en este caso, se importa la librería pylab
y, a través de la instrucción inline
se asegura que todas las gráficas aparezcan dentro del espacio de trabajo.
La siguiente línea requiere un poco más de atención. Ésta sigue exactamente la misma estructura que cualquier otra importación de librerías, siendo en este caso que se llama una bajo el nombre de haggerstrand
; sin embargo, si se intenta instalar ésta a través del asistente Conda, o si quiera buscarla en internet, se encontrará que no se encuentra disponible o no puede ser encontrada.
Esto es porque, a diferencia de otras prácticas, se está trabajando con una librería local; si se revisa el directorio de trabajo, se encontrará que existe una carpeta con el nombre de ésta, haggerstrand
, la cual contiene múltiples archivos de Python que almacenan todas las funciones y clases asociadas a la librería. Como tal, es importante destacar que, en Python, no sólo se pueden trabajar con librerías estandarizadas y vastamente distribuidas, como numpy
o pandas
, sino también con librerías locales creadas por el usuario o equipos de trabajo, como haggerstrand
.
En la celda anterior, de la librería haggerstrand
se importó la Clase llamada SimpleDiffusion
, que es en la cual se está implementando el algoritmo. Para ejecutar el modelo, primero es necesario instanciar a la clase, es decir, expresarla en el código:
s = SimpleDiffusion(N=50 , M=50 , mif_size=9 , pob=20 , initial_diff=[(20,20)] , p0=0.3 , max_iter=15)
Los parámetros que componen a la clase SimpleDiffusion
son los siguientes:
N
- Número de Renglones en el Espacio de la SimulaciónM
- Número de Columnas en el Espacio de la Simulaciónmif_size
- Tamaño de la Matriz Cuadrada, que debe ser un número imparpob
- Número de Habitantes en cada Celdainitial_diff
- Coordenadas en donde se encuentran los Difusores Inicialesp0
- Probabilidad de contactar a la Celda Origenmax_iter
- Número de Iteraciones a simularEsta información, junto con otra relacionada a la Clase, pueden consultarse a través de help()
:
help(SimpleDiffusion)
Una vez instanciada la clase, es posible ejecutar la simulación, utilizando la función .spatial_diffusion()
sobre la variable que contiene a la clase:
s.spatial_diffusion()
El algoritmo almacena los resultados de cada una de las iteraciones realizadas en una Matriz Tridimensional de M
xN
xmax_iter
, teniéndose el resultado final en la última sección, o rebanada, de esta matriz, la cual puede ser llamada a través del atributo result
:
s.result[: , : , s.max_iter - 1]
Resulta interesante revisar a detalle la notación utilizada; Dado que s.result
es una lista, o un array, es posible utilizar las técnicas ya estudiadas para recuperar los elementos en su interior. Debido a que es una Matriz Tridimensional, se necesitan de tres índices para explorar su interior; los primeros dos son populados por el caracter :
, el cual indica que se llaman a todas filas y columnas de la lista, mientras que s.max_iter - 1
es la sección que índica que sólo se necesita la última rebanada de la matriz. Por otra parte, el -1
es utilizado debido a que, en Python, las numeraciones comienzan desde el cero.
Utilizando la notación s.result
, se ha llamado al atributo llamado result
de la clase SpatialDiffusion
que se encuentra contenida en la variable s
; éste no es el único atributo asociado a esta clase y, a través del comando help(SimpleDiffusion)
presentado en celdas anteriores, es posible ver todos los atributos que es posible llamar.
Claro está que observar los números de la matriz resultante del algoritmo no es la mejor manera de visualizar y analizar el proceso, por lo que es útil realizar una gráfica para entender de mejor manera lo realizado:
img_s = plt.imshow(s.result[: , : , s.max_iter - 1] , vmin = 0 , vmax = 20) # Generación de la Gráfica
plt.colorbar(img_s) # Referencia de Valores
plt.show() # Mostrar el resultado
Los pasos realizados en la celda anterior son:
.imshow()
de la librería matplotlib
; debido a que lo que se coloca dentro de la función es una matriz, ésta puede ser interpretada directamente como una imagen, razón por la cual no es necesario colocar más argumentos. La gráfica es guardada en la variable img_s
; además, los argumentos vmin
y vmax
son utilizados para definir los límites de la escala de colores utilizada en la gráfica, y corresponden al número mínimo y máximo de habitantes que pueden recibir el Mensaje en cada celda..colorbar()
, se genera una Barra de Colores de la gráfica creada, la cual sirve como referencia para entender los valores observados..show()
El resultado observado es el espacio en el cual se realizó el proceso de Difusión Espacial. Dentro de las propiedades asignadas a la clase SimpleDiffusion
, se determinó que el espacio del experimento sería de 50x50 (a través de N
y M
), verificable con los ejes de la gráfica, y que la población en cada una de las celdas sería de 20 habitantes (pob
). Lo que se observa es que, después de 15 iteraciones (max_iter
), el Mensaje contenido inicialmente en la celda (20,20) (initial_diff
) se difundió en el espacio al resto de las celdas marcadas, teniéndose que no todos los 20 habitantes existentes en cada una de las celdas recibieron el Mensaje, sino únicamente la cantidad determinada por su color.
A través de la instrucción s.result
es posible visualizar diferentes cortes temporales del proceso, es decir, el comportamiento de la Difusión Espacial a través de las diferentes iteraciones. Por ejemplo, si se desease observar lo ocurrido después de la séptima iteración, se podría:
img_s = plt.imshow(s.result[: , : , 7] , vmin = 0 , vmax = 20)
plt.colorbar(img_s)
plt.show()
Visualiza lo ocurrido a diferentes cortes temporales.
Para entender más a detalle el proceso de Difusión Espacial, conviene comparar los resultados de éste con lo que sucedería en un proceso completamente aleatorio. Para esto, en lugar de utilizar el método .spatial_diffusion()
, se recurrirá al llamado .random_diffusion()
:
# Se instancia nuevamente la clase, utilizando exactamente los mismos argumentos, en una variable diferente
r = SimpleDiffusion(N=50 , M=50 , mif_size=9 , pob=20 , initial_diff=[(20,20)] , p0=0.3 , max_iter=15)
# Se aplica la función '.random_diffusion()' sobre esta nueva variable
r.random_diffusion()
Utilizando exactamente los mismos comandos que en el caso anterior, es posible observar el resultado final del nuevo proceso:
r.result[: , : , r.max_iter - 1]
Y, de igual forma, graficar para tener una mejor visualización y poder realizar el análisis deseado:
img_r = plt.imshow(r.result[: , : , r.max_iter - 1] , vmin = 0 , vmax = 20) # Generación de la Gráfica
plt.colorbar(img_r) # Referencia de Valores
plt.show() # Mostrar el resultado
Con lo anterior, es posible comparar los resultados obtenidos a través del Modelo de Difusión Espacial (.spatial_diffusion()
) y la Difusión Aleatoria (.random_diffusion()
):
# Crear la figura donde aparecerán las gráficas
fig , filas = plt.subplots(nrows = 1 , ncols = 2, figsize = (7,7))
# Graficar lo obtenido a través de '.spatial_diffusion()'
img_s = filas[0].imshow(s.result[: , : , s.max_iter - 1] , vmin = 0 , vmax = 20)
# Graficar lo obtenido a través de '.random_diffusion()'
img_r = filas[1].imshow(r.result[: , : , r.max_iter - 1] , vmin = 0 , vmax = 20)
# Graficar la Barra de Colores para referencia, y colocarla de forma estética
fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(img_r, cax=cbar_ax)
plt.show()
En la celda anterior, se crea una figura para graficar, como se ha hecho tradicionalmente en prácticas anteriores, esta vez utilizando los argumentos nrows
y ncols
para especificar el número de filas y columnas que tendrá la imagen resultante; asimismo, al momento de generar las gráficas, debe notarse cómo plt.imshow()
ha sido sustituido por filas[].imshow()
, indicando dentro de los corchetes el número de la columna dentro de la cual aparecerá la gráfica. Por otra parte, las indicaciones que acompañan a la función .colorbar()
son puramente estéticas, para asegurar que la Barra de Colores aparezca sin alterar la apariencia de las gráficas.
También es posible realizar diferentes cortes temporales del proceso ejecutado a través de la Difusión Aleatoria; por ejemplo, si se quisiera observar el estado del espacio después de la séptima iteración:
img_r = plt.imshow(r.result[: , : , 7] , vmin = 0 , vmax = 20)
plt.colorbar(img_r)
plt.show()
Y, como tal, también es posible comparan los resultados de la Difusión Espacial y la Aleatoria a través de sus diferentes estados en el tiempo:
fig , filas = plt.subplots(nrows = 1 , ncols = 2, figsize = (7,7))
img_s = filas[0].imshow(s.result[: , : , 7] , vmin = 0 , vmax = 20)
img_r = filas[1].imshow(r.result[: , : , 7] , vmin = 0 , vmax = 20)
fig.subplots_adjust(right=0.8)
cbar_ax = fig.add_axes([0.85, 0.15, 0.05, 0.7])
fig.colorbar(img_r, cax=cbar_ax)
plt.show()
Utilizando los comandos anteriores, compara los estados de los dos algoritmos a diferentes estados del tiempo, y responde la pregunta: ¿Qué diferencias puedes observar?
Puede observarse que los resultados de la Difusión Espacial y la Difusión Aleatoria son muy diferentes, tanto en el resultado final obtenido como en la evolución temporal de los mismos; un análisis más detallado implica estudiar este último aspecto más a fondo. Para esto, la clase SimpleDiffusion
posee un atributo llamado time_series
, que arroja una lista del número de habitantes que reciben el Mensaje después de cada iteración; en otras palabras, se muestra el número de adoptantes en cada iteración; observando lo ocurrido en la Difusión Espacial:
s.time_series
Es posible graficar la lista anterior rápidamente a través de la función .plot()
plt.plot(s.time_series)
plt.show()
Puede observarse que el crecimiento parece ser de Tipo Exponencial. Vale la pena preguntarse, ¿qué ocurre si se aumenta el número de iteraciones del proceso? Esto se realiza rápidamente modificando la propiedad max_iter
de la clase SimpleDiffusion
, para después repetir el proceso nuevamente:
# Instanciar la clase, modificando 'max_iter'
s = SimpleDiffusion(N=50 , M=50 , mif_size=9 , pob=20 , initial_diff=[(20,20)] , p0=0.3 , max_iter=20)
# Ejecutar el algoritmo
s.spatial_diffusion()
# Graficar el atributo 'time_series'
plt.plot(s.time_series)
plt.show()
Aunque en un principio se observa el mismo comportamiento observado anteriormente, después de la decimoquinta iteración el número de adoptantes comienza a decrecer. Esto no significa que el número total de personas que han adoptado el Mensaje empieza a disminuir; más bien, que, conforme avanza el tiempo, cada vez menos personas comienzan a adoptar el Mensaje.
Responde a las preguntas: ¿Por qué en un principio se observa un comportamiento aparentemente exponencial? ¿Por qué, después de un cierto número de iteraciones, el número de adoptantes disminuye? ¿Ocurrirá lo mismo en el caso de la Difusión Aleatoria? Complementa esta última pregunta ejecutando el código que te permita responderla.