Geoinformática - Práctica 6

Agrupamiento, Regionalización y Segmentación Geodemográfica

Parte 2 - Homogeneidad y Correlación de Variables

En la sección anterior de la práctica, se estudiaron los conceptos generales relacionados con el Agrupamiento de datos, así como de su variante espacial en la Regionalización. En esta parte, se analizarán con un poco más de detalle alguna de las particularidades de los procesos estudiados; en específico, se estudiarán algunas formas de discernir las variables correctas para realizar una Regionalización, así como algunos parámetros para conocer si los resultados obtenidos son lo suficientemente aceptables para ser considerados como válidos o ciertos.

In [1]:
# Librerías a Utilizar
import seaborn as sns
import pandas as pd
import pysal as ps
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import cluster
/home/datalab/miniconda3/envs/geoinf/lib/python3.7/site-packages/pysal/model/spvcm/abstracts.py:10: UserWarning: The `dill` module is required to use the sqlite backend fully.
  from .sqlite import head_to_sql, start_sql

Generalización contra Detalle

Cuando se ejecuta algún algoritmo de Regionalización, en el transfondo se está realizando una generalización sobre el espacio, esto es, se está asignando un mismo valor o atributo a áreas que resultan más grandes que las que se tenían en un inicio.

Esta Generalización puede resultar positiva, pues permite visualizar algunos patrones reproducibles en el espacio y, por ende, permitir deducir si la componente espacial es significativa sobre el fenómeno estudiado, dando otra herramienta para explicarlo. Por otra parte, el generalizar provoca que se pierda el detalle que se tenía inicialmente con áreas más pequeñas y, por ende, la explicación que pueda derivarse también pierde de profundidad. Como tal, es importante encontrar un balance entre el poder explicativo que se gana al momento de Regionalizar y la pérdida de profundidad implícita en la Generalización.

Para estudiar este fenómeno, se tomarán nuevamente los datos utilizados en la sección anterior de la práctica, esto es, las calificaciones de los AirBnb contenidos en las AGEB's de la Alcaldía Cuauhtémoc de la CDMX. Como tal, es necesario importar estos datos:

In [2]:
airbnb = gpd.read_file('data/agebs_airbnb.shp')  # Importar el ShapeFile
airbnb = airbnb.set_index('ageb')                # Establecer la Clave Geográfica del AGEB como Índice
airbnb.head()                                    # Observar el GeoDataFrame importado
Out[2]:
no_prop calif expec limp checkin com ubi precio geometry
ageb
0901500010235 4.0 96.5000 10.00000 10.00000 10.00000 10.00000 10.00000 9.75000 POLYGON ((484786.4564242049 2151318.632194062,...
0901500010362 1.0 100.0000 10.00000 10.00000 10.00000 10.00000 10.00000 10.00000 POLYGON ((484415.3002532361 2150990.921335925,...
0901500010606 14.0 96.3571 9.92857 10.00000 10.00000 9.92857 9.71429 9.85714 POLYGON ((483366.716567839 2150099.090658844, ...
0901500011017 204.0 96.1569 9.83823 9.64706 9.86765 9.86765 9.92157 9.68627 POLYGON ((483869.0666524998 2147955.234262425,...
0901500010201 1.0 100.0000 9.00000 10.00000 10.00000 10.00000 10.00000 10.00000 POLYGON ((484252.7970705956 2151134.263629142,...

En la siguiente celda se repetirán los pasos ejecutados en la sección anterior de la práctica para generar una Regionalización; en este caso, se generarán 15 Regiones utilizando el Algoritmo AZP. Asimismo, se utilizará sólo una de las variables anteriores, la de Calificación General (calif), para facilitar el análisis posterior:

In [3]:
# Importar la librería local 'clusterpy'
import clusterpy

# Generar el objeto de tipo 'layer' que la librería necesita
layer = clusterpy.importArcData('data/agebs_airbnb')

# Ejecutar el Algoritmo AZP de Regionalización
layer.cluster('azp', ['calif'], 15, wType='queen')

# Indicar a qué región pertenece cada AGEB como una nueva columna
airbnb['azpcls_15'] = layer.region2areas

# Visualizar el resultado en la tabla original
airbnb.head()
ClusterPy: Library of spatially constrained clustering algorithms
Some functions are not available, reason: No module named 'Polygon'
Some functions are not available, reason: No module named 'Polygon'
Loading data/agebs_airbnb.dbf
Loading data/agebs_airbnb.shp
Done
Getting variables
Variables successfully extracted
Running original AZP algorithm (Openshaw and Rao, 1995)
Number of areas:  153
Number of regions:  15
Constructing regions
initial Solution:  [4, 4, 11, 11, 4, 10, 4, 10, 4, 11, 10, 0, 10, 10, 10, 10, 10, 5, 10, 10, 11, 11, 11, 11, 7, 11, 11, 11, 10, 11, 10, 10, 10, 10, 11, 10, 7, 13, 9, 7, 9, 13, 11, 11, 11, 11, 10, 10, 10, 10, 2, 6, 4, 10, 11, 4, 0, 11, 11, 4, 10, 10, 10, 10, 10, 11, 11, 11, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 9, 11, 11, 11, 11, 9, 11, 9, 13, 7, 11, 11, 11, 7, 11, 11, 11, 9, 7, 11, 11, 11, 11, 9, 8, 9, 13, 13, 11, 11, 11, 11, 9, 9, 9, 13, 7, 1, 1, 1, 1, 11, 11, 11, 1, 11, 11, 11, 11, 11, 11, 14, 14, 7, 7, 9, 9, 7, 11, 0, 10, 10, 10, 10, 4, 4, 10, 10, 3, 3, 2]
initial O.F:  680.8235108816119
Performing local search
FINAL SOLUTION:  [4, 4, 11, 11, 4, 11, 4, 11, 4, 11, 10, 0, 10, 11, 11, 10, 11, 5, 10, 11, 11, 11, 11, 11, 7, 11, 11, 11, 10, 11, 10, 10, 10, 10, 11, 10, 7, 13, 9, 7, 8, 13, 11, 11, 11, 11, 10, 10, 11, 10, 2, 6, 4, 10, 11, 4, 0, 11, 11, 4, 10, 10, 5, 5, 10, 11, 11, 11, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 8, 11, 11, 13, 7, 11, 11, 11, 7, 11, 11, 11, 8, 7, 11, 11, 11, 11, 9, 8, 8, 13, 13, 11, 11, 11, 11, 9, 9, 8, 13, 7, 11, 1, 1, 1, 11, 11, 11, 1, 11, 11, 11, 11, 11, 11, 14, 14, 7, 7, 8, 11, 7, 11, 0, 10, 10, 5, 11, 4, 4, 10, 10, 3, 3, 2]
FINAL O.F.:  554.4412126332717
Done
Adding variables
Done
Out[3]:
no_prop calif expec limp checkin com ubi precio geometry azpcls_15
ageb
0901500010235 4.0 96.5000 10.00000 10.00000 10.00000 10.00000 10.00000 9.75000 POLYGON ((484786.4564242049 2151318.632194062,... 4
0901500010362 1.0 100.0000 10.00000 10.00000 10.00000 10.00000 10.00000 10.00000 POLYGON ((484415.3002532361 2150990.921335925,... 4
0901500010606 14.0 96.3571 9.92857 10.00000 10.00000 9.92857 9.71429 9.85714 POLYGON ((483366.716567839 2150099.090658844, ... 11
0901500011017 204.0 96.1569 9.83823 9.64706 9.86765 9.86765 9.92157 9.68627 POLYGON ((483869.0666524998 2147955.234262425,... 11
0901500010201 1.0 100.0000 9.00000 10.00000 10.00000 10.00000 10.00000 10.00000 POLYGON ((484252.7970705956 2151134.263629142,... 4

Como se estudió en la práctica anterior, es posible obtener Estadísticos Descriptivos tanto de la variable original, utilizada para la Regionalización, como de las regiones estudiadas. En el caso de la variable original, sus estadísticos pueden obtenerse a través de la función .describe():

In [4]:
airbnb['calif'].describe()
Out[4]:
count    153.000000
mean      94.668812
std        4.188733
min       76.750000
25%       93.714300
50%       95.350700
75%       96.589700
max      100.000000
Name: calif, dtype: float64

Para el análisis, únicamente resulta de interés obtener la Desviación Estándar ($\sigma$ o std) de la variable. Para esto, sólo es necesario llamar la función .std(), que permite aislar a este estadístico en específico:

In [5]:
airbnb['calif'].std()
Out[5]:
4.188732594789283

Lo que el estadístico anterior pretende comunicar es el rango de valores en el cual se encuentran la mayoría de los datos, tomando como referencia la media de los mismos. En el ejemplo de trabajo, teniendo una Desviación Estándar de $4.18$ y una Media de $94.67$ para la variable, puede asegurarse que la mayoría de las Calificaciones Generales de los AirBnb se encuentran en un rango de $94.67 \pm 4.18$, en otras palabras, entre $90.49$ y $98.85$. Lo anterior ayuda a tener una idea de qué tan dispersos se encuentran los datos; mientras más dispersos, mayor será la Desviación Estándar y, por ende, menos similares son los datos entre sí.

Para el caso de las regiones, es necesario utilizar primero la función .groupby(), agrupando a través de la etiqueta, para después poder obtener los estadísticos descriptivos de interés; cabe notar el uso del doble corchete para aislar únicamente a la variable utilizada en la regionalización y la de referencia para el agrupamiento:

In [6]:
airbnb[['calif' , 'azpcls_15']].groupby('azpcls_15').describe()
Out[6]:
calif
count mean std min 25% 50% 75% max
azpcls_15
0 3.0 85.366667 2.150194 83.2000 84.300000 85.40000 86.450000 87.5000
1 4.0 94.647500 0.521256 93.8973 94.497600 94.82310 94.973000 95.0465
2 2.0 86.516650 3.087723 84.3333 85.424975 86.51665 87.608325 88.7000
3 2.0 80.500000 2.121320 79.0000 79.750000 80.50000 81.250000 82.0000
4 10.0 99.325000 1.202139 96.5000 98.875000 100.00000 100.000000 100.0000
5 4.0 92.000000 1.519926 89.7500 91.750025 92.62500 92.874975 93.0000
6 1.0 93.000000 NaN 93.0000 93.000000 93.00000 93.000000 93.0000
7 10.0 98.301900 1.744938 95.0000 97.297600 98.20000 100.000000 100.0000
8 7.0 94.863214 0.948502 93.3333 94.452200 95.00000 95.183650 96.4375
9 4.0 89.744050 2.003507 86.7500 89.562500 90.66665 90.848200 90.8929
10 23.0 93.869604 3.387771 85.0000 92.730250 94.26420 94.998500 100.0000
11 74.0 96.046008 1.592665 92.5846 95.191700 95.85865 96.500000 100.0000
12 1.0 82.000000 NaN 82.0000 82.000000 82.00000 82.000000 82.0000
13 6.0 91.304733 1.569350 90.0000 90.075000 90.86875 91.927550 94.0000
14 2.0 79.541650 3.947989 76.7500 78.145825 79.54165 80.937475 82.3333

Nuevamente, si se desea obtener únicamente la Desviación Estándar, basta con llamar sólo a .std():

In [7]:
airbnb[['calif' , 'azpcls_15']].groupby('azpcls_15').std()
Out[7]:
calif
azpcls_15
0 2.150194
1 0.521256
2 3.087723
3 2.121320
4 1.202139
5 1.519926
6 NaN
7 1.744938
8 0.948502
9 2.003507
10 3.387771
11 1.592665
12 NaN
13 1.569350
14 3.947989

Comparando los valores anteriores con la Desviación Estándar original de la variable ($4.18$), puede notarse que la gran mayoría de éstos tienden a ser menores, o por lo menos muy cercanos, a la original; lo anterior significa no sólo que la Regionalización esta logrando el objetivo buscado, de agrupar las cosas similares entre sí, sino también que el nivel de detalle que contiene ésta se asemeja al detalle que se tenía originalmente. Esto resulta positivo, pues se han disminuído el número total de Unidades Espaciales (de 153 AGEB's a sólo 15 Regiones) con las que se está trabajando, sin sacrificar enormemente la cantidad de información que se podría derivar de los datos originales.

Pero, ¿qué ocurre si el número de regiones se quiere disminuir? En otras palabras, si se desea generalizar aun más la información contenida, dado los objetivos de la investigación; a través de clusterPy, es posible disminuir estas regiones únicamente modificando el argumento pertinente:

In [8]:
# Ejecutar el Algoritmo AZP de Regionalización
layer.cluster('azp', ['calif'], 10, wType='queen')

# Indicar a qué región pertenece cada AGEB como una nueva columna
airbnb['azpcls_10'] = layer.region2areas
Getting variables
Variables successfully extracted
Running original AZP algorithm (Openshaw and Rao, 1995)
Number of areas:  153
Number of regions:  10
Constructing regions
initial Solution:  [7, 7, 4, 4, 7, 2, 7, 2, 7, 2, 9, 6, 2, 2, 9, 2, 9, 9, 9, 9, 4, 4, 4, 4, 3, 4, 4, 3, 2, 2, 3, 3, 3, 3, 4, 3, 3, 3, 5, 3, 4, 3, 4, 4, 4, 4, 3, 3, 2, 0, 4, 7, 7, 0, 2, 7, 6, 3, 3, 7, 3, 3, 9, 9, 3, 3, 4, 4, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 4, 4, 4, 4, 5, 4, 3, 3, 3, 4, 4, 4, 4, 5, 5, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 3, 3, 4, 4, 3, 3, 6, 9, 0, 9, 2, 7, 7, 0, 0, 1, 1, 4]
initial O.F:  959.1269150482051
Performing local search
FINAL SOLUTION:  [7, 7, 4, 3, 7, 2, 7, 2, 7, 2, 9, 6, 2, 2, 2, 2, 2, 9, 9, 2, 4, 4, 4, 4, 3, 4, 4, 4, 2, 2, 4, 3, 4, 4, 4, 4, 3, 4, 5, 3, 4, 4, 7, 3, 4, 4, 3, 4, 2, 0, 4, 4, 7, 0, 2, 7, 6, 3, 3, 7, 3, 4, 9, 9, 3, 3, 4, 3, 3, 4, 4, 3, 4, 3, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4, 3, 3, 4, 4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 3, 4, 3, 4, 4, 4, 3, 5, 4, 4, 4, 4, 3, 3, 4, 4, 5, 5, 4, 4, 3, 4, 4, 4, 4, 3, 4, 4, 4, 3, 4, 3, 4, 4, 4, 8, 8, 3, 3, 4, 4, 3, 3, 6, 9, 0, 9, 2, 7, 7, 0, 0, 1, 1, 4]
FINAL O.F.:  733.9847878058853
Done
Adding variables
Done

Y, al igual que con el caso anterior, obtener la Desviación Estándar para cada una de las regiones creadas:

In [9]:
airbnb[['calif' , 'azpcls_10']].groupby('azpcls_10').std()
Out[9]:
calif
azpcls_10
0 3.698187
1 2.121320
2 2.269376
3 1.632589
4 2.565466
5 2.003507
6 2.150194
7 1.302924
8 3.947989
9 1.479818

Comparando con la Desviación Estándar del caso en el que se generaron 15 Regiones, se notará que el estadístico derivado de únicamente 10 Regiones tiende a ser mucho más grande, lo cual significa que las regiones generadas son más heterogéneas que en el caso anterior y, por ende, se ha perdido detalle en los datos. Lo anterior no es necesariamente negativo; el hecho de que se hayan agrupado las AGEB's de la forma anterior indica que existe suficiente similitud entre ellas como para que la Componente Espacial sea significativa sobre el comportamiento de la variable, aunque la variable en sí ya no se encuentre tan detallada y especificada como se tenía en las 15 Regiones, y mucho menos con la variable original.

Puede estudiarse qué es lo que ocurriría si se disminuyen aún más las regiones; por ejemplo, si se generaran únicamente 5 de éstas:

In [10]:
# Ejecutar el Algoritmo AZP de Regionalización
layer.cluster('azp', ['calif'], 5, wType='queen')

# Indicar a qué región pertenece cada AGEB como una nueva columna
airbnb['azpcls_5'] = layer.region2areas
Getting variables
Variables successfully extracted
Running original AZP algorithm (Openshaw and Rao, 1995)
Number of areas:  153
Number of regions:  5
Constructing regions
initial Solution:  [2, 0, 0, 2, 0, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 2, 2, 2, 2, 1, 0, 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 4]
initial O.F:  2384.2988059327445
Performing local search
FINAL SOLUTION:  [0, 0, 3, 3, 0, 3, 0, 3, 0, 2, 2, 2, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 2, 2, 3, 3, 2, 0, 2, 0, 3, 3, 2, 3, 2, 1, 0, 0, 2, 3, 0, 2, 3, 3, 0, 3, 2, 2, 2, 3, 2, 3, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 0, 0, 2, 2, 2, 2, 4]
FINAL O.F.:  1558.643554817644
Done
Adding variables
Done

Y, al igual que en los casos anteriores, obtener la Desviación Estándar de todas las regiones generadas:

In [11]:
airbnb[['calif' , 'azpcls_5']].groupby('azpcls_5').std()
Out[11]:
calif
azpcls_5
0 2.065733
1 NaN
2 5.044402
3 1.726389
4 NaN

Nuevamente, el reducir el número de regiones aumentó los valores de la Desviación Estándar; en otras palabras, generó regiones más heterogéneas con un menor nivel de detalle de los datos. Como tal, aunque el número de Unidades Espaciales se ha reducido enormemente, y el trabajo con otro tipo de herramientas de análisis puede simplificarse de sobremanera, también se ha perdido una gran cantidad de detalle en los datos, eliminándose la posibilidad de identificar AGEB's muy particulares que puedan estar generando problemas.

Como tal, es importante considerar la influencia de la Regionalización sobre el nivel de detalle buscado; si no resulta vital para los objetivos del proyecto el considerar todas las particularidades de cada área sobre el resultado final, entonces puede recurrirse a generar pocas regiones muy generalizadas; por el contrario, si estos aspectos específicos si resultan importantes, entonces tal vez resulte más útil generar una gran cantidad de regiones, pero todas bien detalladas. Después de todo, es necesario encontrar un balance para asegurar que los resultados obtenidos no sólo sean útiles, sino también acertados y pertinentes a lo buscado.


Homogeneidad

Al momento de Regionalizar no sólo resulta pertinente el asegurarse que el Número de Regiones generado ofrezca la suficiente cantidad de detalle en los datos al mismo tiempo que simplifique a un número de Unidades Espaciales fácil de manejar; también es importante verificar que las regiones generadas sean lo suficientemente válidad por si mismas. Para esto existe el estadístico de Homogeneidad, que pretende medir el grado de similitud entre las áreas que componen a la región; en el ejemplo trabajado hasta ahora, el estadístico mediría el grado de semejanza de Calificaciones de los AirBnb's de los AGEB's que componen a cada una de las regiones.

La Homogeneidad se obtiene sencillamente siguiendo la fórmula:

\begin{equation*} h_i = \frac{\sigma _i^ 2}{\sigma_t ^ 2} \end{equation*}

Donde $h_i$ es la Homogeneidad de la $i$-ésima región, $\sigma_i^2$ el cuadrado de la Desviación Estándar (también conocida como Varianza) de esa misma región, y $\sigma_t^2$ es la varianza original de la variable, es decir, el valor del estadístico en la variable original antes de la Regionalización.

De forma similar a los estadísticos anteriores, la Varianza ($\sigma^2$) puede ser obtenida fácilmente a través de la función .var():

In [12]:
airbnb['calif'].var()
Out[12]:
17.545480750650157

Como tal, si se desea obtener la Varianza de cada una de las regiones generadas (tomando como referencia el ejercicio anterior con 15 Regiones):

In [13]:
airbnb[['calif' , 'azpcls_15']].groupby('azpcls_15').var()
Out[13]:
calif
azpcls_15
0 4.623333
1 0.271707
2 9.534034
3 4.500000
4 1.445139
5 2.310176
6 NaN
7 3.044809
8 0.899656
9 4.014039
10 11.476992
11 2.536583
12 NaN
13 2.462859
14 15.586619

De modo que, para obtener la Homogeneidad de cada una de estas regiones, únicamente se necesita dividir los valores anteriores entre la Varianza de la variable original:

In [14]:
# Almacenar la Varianza Inicial en una variable
var_inicial = airbnb['calif'].var()

# Obtener la Homogeneidad
homogeneidad = airbnb[['calif' , 'azpcls_15']].groupby('azpcls_15').var() / var_inicial

# Visualizar el resultado
homogeneidad
Out[14]:
calif
azpcls_15
0 0.263506
1 0.015486
2 0.543390
3 0.256476
4 0.082365
5 0.131668
6 NaN
7 0.173538
8 0.051276
9 0.228779
10 0.654128
11 0.144572
12 NaN
13 0.140370
14 0.888355

Mientras menor sea el valor del estadístico, más homogéneas son las regiones, es decir, más se asemejan las AGEB's que la componen entre sí; de lo contrario, mientras mayor sea el valor del estadístico, más heterogéneas son, es decir, las AGEB's que le componen no son tan semejantes entre sí.

Esto puede comprobarse sencillamente comparando los valores de la variable de regionalización (en este caso, Calificación General de los AirBnb, o calif) entre cada uno de los integrantes de la región. La siguiente celda muestra automáticamente los elementos de la región más Homogénea (es decir, con un valor menor del estadístico):

In [15]:
# Ordenar los valor de Homogeneidad de menor a mayor con '.sort_values()', y almacenar sólo el primer elemento
homogeneidad_menor = homogeneidad.sort_values(by = 'calif').head(1)['calif']

# Registrar el valor de la homogeneidad para la región almacenada
value = homogeneidad_menor.values[0]

# Registrar el índice (o Número de Región)
index = homogeneidad_menor.index[0]

# Imprimir un mensaje que establezca la región con la que se está trabajando
print("AGEB's que conforman a la Región No. "+str(index)+", con Homogeneidad = "+str(value))

# Utilizar '.loc()' para localizar las AGEB's que pertenecen a dicha región.
airbnb.loc[airbnb['azpcls_15']==index]
AGEB's que conforman a la Región No. 1, con Homogeneidad = 0.015485888580734576
Out[15]:
no_prop calif expec limp checkin com ubi precio geometry azpcls_15 azpcls_10 azpcls_5
ageb
0901500011318 263.0 93.8973 9.63878 9.48669 9.76046 9.59316 9.96198 9.50951 POLYGON ((481350.352975982 2146097.283505031, ... 1 4 3
0901500011322 129.0 95.0465 9.72868 9.61240 9.85271 9.78295 9.96899 9.59690 POLYGON ((481914.2023319801 2146387.535019326,... 1 4 3
0901500011337 97.0 94.9485 9.69072 9.54639 9.77320 9.71134 9.94845 9.51546 POLYGON ((481593.0032369185 2146200.214535777,... 1 4 3
0901500011411 86.0 94.6977 9.75581 9.55814 9.83721 9.90698 9.94186 9.54651 POLYGON ((481564.8120910946 2145993.262948391,... 1 4 3

Asimismo, la siguiente celda muestra autmoáticamente los elementos de la región más Heterogénea (es decir, con un valor mayor del estadístico):

In [16]:
# Ordenar los valor de Homogeneidad de menor a mayor con '.sort_values()', y almacenar sólo el primer elemento
homogeneidad_mayor = homogeneidad.sort_values(by = 'calif', ascending = False).head(1)['calif']

# Registrar el valor de la homogeneidad para la región almacenada
value = homogeneidad_mayor.values[0]

# Registrar el índice (o Número de Región)
index = homogeneidad_mayor.index[0]

# Imprimir un mensaje que establezca la región con la que se está trabajando
print("AGEB's que conforman a la Región No. "+str(index)+", con Homogeneidad = "+str(value))

# Utilizar '.loc()' para localizar las AGEB's que pertenecen a dicha región.
airbnb.loc[airbnb['azpcls_15']==index]
AGEB's que conforman a la Región No. 14, con Homogeneidad = 0.888355221866597
Out[16]:
no_prop calif expec limp checkin com ubi precio geometry azpcls_15 azpcls_10 azpcls_5
ageb
0901500011498 4.0 76.7500 10.00000 10.00000 8.75000 8.75000 8.75000 9.75000 POLYGON ((485670.6305742488 2145523.384087983,... 14 8 2
0901500011500 9.0 82.3333 8.66667 8.33333 9.66667 9.33333 9.88889 8.11111 POLYGON ((486121.5641669477 2145442.810028769,... 14 8 2

Tomando en cuenta que la variable de Calificación General de los Airbnb (calif) fue la única utilizada para la Regionalización, ésta es la única considerada al momento de calcular la Homogeneidad; como tal, puede notarse que los valores de esta variable en la región más homogénea son muy similares, mientras que en la más heterogénea tienen una gran diferencia entre sí.

Dependiendo del objetivo de la investigación, es importante considerar lo anterior en cuenta; si cierta región no cumple con un mínimo de homogeneidad, determinado por los objetivos buscados, entonces es un buen indicio para modificar el número de regiones generadas o, inclusive, el algoritmo utilizado. De una u otra forma, el estadístico de Homogeneidad permite brindar de mayor certeza a los resultados obtenidos.

Lo anterior también puede comprobarse de forma visual, representando las regiones en un mapa a través de las funciones estudiadas anteriormente:

In [17]:
# Almacenar las columnas del 'GeoDataFrame' que son de interés para '.dissolve()'
tmp = airbnb[['azpcls_15','geometry']]

# Unir las AGEB's de cada región en una sola geometría a través de '.dissolve()'
regiones = tmp.dissolve(by = 'azpcls_15')

# Adicionar columna con valores de Homogeneidad para cada región
regiones['homogeneidad'] = homogeneidad

# Convertir los valores nulos en ceros (Función estudiada en Práctica posterior)
regiones = regiones.fillna(0)

# Crear la figura y sus ejes
fig, ejes = plt.subplots(1, figsize=(12, 12))

# Graficar las Regiones
regiones.plot(column = 'homogeneidad', scheme = 'Fisher_Jenks', cmap='RdYlGn_r' , ax = ejes , legend = True, edgecolor = 'black')

# Remover los ejes del mapa
ejes.set_axis_off()

# Asignar un título
plt.title('Homogeneidad para las Regiones generadas con AZP')

# Mostrar el resultado
plt.show()

Aunque muchos de los comandos anteriores ya han sido estudiados a lo largo del curso, vale la pena resaltar algunos detalles:

  • En la Línea 8, se adiciona al resultado de la función .dissolve() (llamado regiones) la columna con el cálculo de Homogeneidad. Debido a que el resultado de este cálculo ya posee como índices el Número de Región, y la función .dissolve() asigna el mismo índice a su resultado, no es necesario realizar ninguna específicación adicional, pues a través de éste el comando automáticamente identifica qué valor va para cada región.
  • En la Línea 11 se utiliza la función .fillna() para convertir los valores nulos de Homogeneidad a ceros, ya que se asocian directamente a regiones donde sólo existe 1 AGEB y, por ende, son completamente homogéneas. El uso detallado de esta función se analizará más adelante.
  • En la Línea 17, al momento de graficar con el comando .plot(), el argumento cmap, que indica qué colores debe de utilizar la gráfica, es llenado con una secuencia de colores de Rojo-Amarillo-Verde (RdYlGn). Si se dejara este nombre como tal, en automático los valores más bajos del estadístico se colorearían de rojo, y los más altos de verde; sin embargo, esto no es completamente correcto pues, dado que los valores más altos se asocian a las regiones menos Homogéneas, es preferible asignar el color rojo a éstos. Como tal, se adiciona el sufijo _r al nombre de la paleta para generar este cambio.

Ejercicio Opcional

Ejecuta el algoritmo de regionalización múltiples veces, modificando el Número de Regiones generadas y la Variable de Regionalización, calculando el estadístico de Homogeneidad ($h_i$) en cada caso. Con esto, responde a la pergunta: ¿Qué variable es la más útil para separar los datos?


Regionalización con Múltiples Variables

Por ahora, en esta sección de la práctica, se ha regionalizado utilizando únicamente una de las variables disponibles; sin embargo, en la sección anterior, la regionalización se hizo utilizando un vector de todas las variables disponibles. Aunque los resultados son interesantes, no es posible asegurar que sean lo suficientemente válidos, mucho menos si se consideran los conceptos estudiados hasta ahora.

Como tal, resulta pertinente estudiar más a detalle la relación que guardan entre sí las variables utilizadas para la Regionalización. En la sección anterior, esta relación se estudió rápidamente de forma visual a través de Diagramas de Dispersión e Histogramas generados con la función .pairplot() de la librería seaborn. En este caso, se recurrirá a un estadístico, la Correlación Lineal, para realizar esta comparación, la cual puede ser obtenida sencillamente a través de la función .corr():

In [18]:
# Almacenar el nombre de las variables en una lista
calificaciones = ['calif', 'expec', 'limp', 'checkin', 'com', 'ubi', 'precio']

# Obtener la Correlación Lineal únicamente entre los valores de interés
airbnb[calificaciones].corr()
Out[18]:
calif expec limp checkin com ubi precio
calif 1.000000 0.567749 0.545356 0.520931 0.495567 0.370641 0.671752
expec 0.567749 1.000000 0.581343 0.431542 0.699229 0.343926 0.679563
limp 0.545356 0.581343 1.000000 0.550542 0.480033 0.290340 0.730677
checkin 0.520931 0.431542 0.550542 1.000000 0.506156 0.314130 0.552520
com 0.495567 0.699229 0.480033 0.506156 1.000000 0.194988 0.538741
ubi 0.370641 0.343926 0.290340 0.314130 0.194988 1.000000 0.323321
precio 0.671752 0.679563 0.730677 0.552520 0.538741 0.323321 1.000000

La Correlación Lineal únicamente puede obtener valores en un rango de entre -1 y 1; mientras más cercano se encuentre el estadístico a cualquiera de estos dos, mayor la correlación que existe entre las dos variables comparadas (es decir, más influencia tienen entre sí), siendo Negativa si se acerca al -1 o Positiva si se acerca al 1. Por otra parte, cuando el estadístico se acerca al 0, significa que las variables comparadas no se encuentran correlacionadas (es decir, tienen poca influencia entre sí).

Lo anterior puede comprobarse generando las gráficas correspondientes. De la tabla anterior, eliminando la diagonal principal, se tiene que la correlación más grande existe entre la Calificación de Limpieza del Airbnb (limp) y su Calificación de Precio (precio), siendo ésta de tipo positivo; esto significa que, en general, mientras más alta sea una de estas calificaciones, más alta será la otra. Graficamente la relación entre ambas variables puede visualizarse a través de la función .regplot() de la librería seaborn:

In [19]:
_ = sns.regplot(x = 'limp' , y = 'precio' , data = airbnb)

Por otra parte, la correlación más pequeña es la que existe entre la Calificación de Comunicación con el anfitrión (com) y la Calificación de Ubicación del AirBnb (ubi); esto significa que muy poco tienen que ver ambas calificaciones entre sí. Gráficamente, se tiene:

In [20]:
_ = sns.regplot(x = 'com' , y = 'ubi' , data = airbnb)

En la primera gráfica, con las variables más correlacionadas, puede observarse cierta dispersión de los datos, pero ésta permite deducir una relación lineal lo suficientemente acetable entre ambas, marcada por el hecho de que la línea que representa esta relación es lo suficientemente inclinada y el intervalo de confianza de ésta (marcada por el área azul) es relativamente pequeño. Por otra parte, la segunda gráfica, de las variables menos correlacionadas, muestra una dispersión que poco sigue un patrón lineal, siendo la recta generada casi horizontal, y teniendo un intervalo de confianza demasiado grande.

Lo anterior es de gran importancia la momento de regionalizar. Mientras más correlacionadas se encuentren dos variables, más similitud existe en la información que son capaces de explicar; en otras palabras, si dos variables se encuentran muy correlacionadas, el fenómeno que las originó muy probablemente es el mismo y, por ende, la cantidad de información que puede propoprcionar una es muy similar a la otra. Por otra parte, entre menos se encuentren correlacionadas dos variables, mayor será la cantidad de información que se pueda derivar de ellas pues, dado que el proceso que los originó muy probablemente es distinto, existe gran probabilidad de abarcar mayor información con ambas.

Como tal, al momento de Regionalizar, dado lo que se desea es abarcar la mayor diversidad de fenómenos posibles para agrupar correctamente las Unidades Espaciales, lo correcto es seleccionar un conjunto de variables que posean una baja correlación entre sí. En el caso de este ejercicio, sería incorrecto colocar en un mismo algoritmo de regionalización a las variables de limp y precio, dada su alta correlación, y los resultados más enriquecedores se tendrían utilizando juntas a las variables de com y ubi, dada su baja correlación.

Para ejemplificar lo anterior, resulta pertinente visualizar los resultados de estos casos. Primero, se realiza la regionalización utilizando las variables altamente correlacionadas:

In [21]:
# Ejecutar el Algoritmo AZP de Regionalización con variables altamente correlacionadas
layer.cluster('azp', ['limp' , 'precio'], 33, wType='queen')

# Indicar a qué región pertenece cada AGEB como una nueva columna
airbnb['azpcls_altacorr'] = layer.region2areas

# Almacenar las columnas del 'GeoDataFrame' que son de interés para '.dissolve()'
tmp = airbnb[['azpcls_altacorr','geometry']]

# Unir las AGEB's de cada región en una sola geometría a través de '.dissolve()'
regiones_altacorr = tmp.dissolve(by = 'azpcls_altacorr')
Getting variables
Variables successfully extracted
Running original AZP algorithm (Openshaw and Rao, 1995)
Number of areas:  153
Number of regions:  33
Constructing regions
initial Solution:  [5, 5, 31, 0, 5, 11, 5, 11, 5, 11, 30, 18, 11, 11, 11, 11, 11, 32, 13, 15, 0, 0, 0, 0, 16, 0, 0, 0, 11, 11, 0, 0, 0, 0, 28, 0, 3, 25, 27, 16, 25, 25, 28, 0, 28, 28, 0, 0, 11, 19, 26, 26, 5, 19, 18, 5, 25, 25, 18, 0, 0, 0, 15, 25, 15, 29, 0, 0, 25, 25, 29, 29, 0, 0, 0, 25, 25, 17, 29, 10, 0, 0, 17, 0, 0, 0, 0, 25, 17, 17, 25, 16, 0, 0, 0, 16, 0, 0, 0, 25, 16, 0, 0, 0, 0, 8, 25, 14, 16, 16, 0, 0, 0, 27, 21, 22, 14, 16, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 16, 16, 16, 16, 25, 25, 6, 24, 18, 15, 1, 4, 11, 5, 5, 23, 20, 7, 12, 2]
initial O.F:  13.748818164073574
Performing local search
FINAL SOLUTION:  [5, 5, 31, 0, 5, 11, 5, 11, 5, 19, 30, 18, 19, 11, 11, 19, 11, 32, 13, 15, 27, 0, 0, 0, 16, 0, 0, 0, 11, 11, 0, 0, 0, 0, 28, 0, 3, 25, 27, 14, 25, 25, 28, 0, 0, 28, 0, 0, 11, 19, 26, 26, 5, 19, 18, 5, 25, 29, 18, 0, 0, 0, 15, 25, 15, 29, 0, 0, 25, 25, 29, 29, 0, 0, 0, 25, 25, 17, 29, 10, 0, 0, 17, 0, 0, 0, 0, 25, 17, 17, 25, 29, 0, 0, 0, 29, 0, 0, 0, 25, 29, 0, 0, 0, 0, 8, 25, 3, 29, 29, 0, 0, 0, 27, 21, 22, 14, 29, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 16, 14, 16, 16, 25, 25, 6, 24, 18, 15, 1, 4, 11, 5, 5, 23, 20, 7, 12, 2]
FINAL O.F.:  10.364406029454647
Done
Adding variables
Done

Se realiza lo mismo, pero esta vez con las variables que se encuentran menos correlacionadas:

In [22]:
# Ejecutar el Algoritmo AZP de Regionalización
layer.cluster('azp', ['com' , 'ubi'], 33, wType='queen')

# Indicar a qué región pertenece cada AGEB como una nueva columna
airbnb['azpcls_bajacorr'] = layer.region2areas

# Almacenar las columnas del 'GeoDataFrame' que son de interés para '.dissolve()'
tmp = airbnb[['azpcls_bajacorr','geometry']]

# Unir las AGEB's de cada región en una sola geometría a través de '.dissolve()'
regiones_bajacorr = tmp.dissolve(by = 'azpcls_bajacorr')
Getting variables
Variables successfully extracted
Running original AZP algorithm (Openshaw and Rao, 1995)
Number of areas:  153
Number of regions:  33
Constructing regions
initial Solution:  [4, 4, 30, 25, 4, 6, 4, 6, 4, 6, 24, 6, 24, 24, 6, 6, 22, 24, 24, 22, 25, 25, 11, 25, 32, 25, 25, 11, 6, 6, 30, 30, 22, 14, 30, 14, 2, 31, 0, 27, 15, 31, 30, 25, 30, 30, 22, 11, 6, 6, 30, 26, 4, 24, 6, 4, 3, 6, 6, 4, 30, 30, 22, 28, 22, 11, 14, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 9, 11, 18, 11, 11, 25, 11, 11, 2, 11, 17, 31, 31, 25, 25, 25, 13, 25, 25, 25, 2, 13, 25, 25, 25, 25, 20, 0, 15, 2, 13, 25, 25, 25, 0, 12, 5, 15, 13, 32, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 0, 23, 32, 32, 32, 2, 2, 13, 21, 10, 19, 1, 7, 24, 4, 4, 16, 29, 4, 4, 8]
initial O.F:  7.2795059542466385
Performing local search
FINAL SOLUTION:  [4, 4, 11, 25, 4, 6, 24, 6, 4, 6, 24, 6, 24, 24, 6, 6, 6, 24, 24, 22, 2, 25, 25, 25, 32, 25, 25, 11, 6, 6, 30, 30, 22, 14, 30, 14, 2, 31, 0, 27, 15, 31, 30, 25, 30, 11, 22, 11, 6, 6, 30, 26, 4, 24, 6, 4, 3, 6, 6, 4, 24, 30, 22, 28, 22, 11, 14, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 9, 11, 18, 11, 25, 25, 11, 11, 2, 11, 17, 31, 31, 18, 25, 18, 13, 25, 25, 25, 2, 13, 25, 25, 25, 25, 20, 2, 15, 2, 13, 25, 25, 25, 2, 12, 5, 15, 13, 32, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 23, 32, 32, 32, 2, 2, 13, 21, 10, 19, 1, 7, 24, 4, 4, 16, 29, 24, 24, 8]
FINAL O.F.:  6.563979761839495
Done
Adding variables
Done

Y, para poder comparar los resultados obtenidos con lo que se observa en la realidad, se importar las colonias que conforman a la Alcaldía Cuauhtémoc:

In [23]:
colonias_reales = gpd.read_file('data/colonias_cuauh.shp')
colonias_reales = colonias_reales.set_index('id')

Por último, se pueden graficar estos tres últimos conjuntos de regiones de forma paralela para facilitar la comparación entre ellas, utilizando el mismo método estudiado en la sección anterior:

In [24]:
# Crear la figura y sus ejes
fig, ejes = plt.subplots(nrows = 1, ncols = 3, figsize=(20, 20))

# Aislar los ejes (cuadros) en sus propias variables
eje1 = ejes[0]
eje2 = ejes[1]
eje3 = ejes[2]

# Para el primer eje (cuadro)
# Graficar las regiones derivadas de las variables con Alta Correlación
regiones_altacorr.plot(figsize = (10,10), edgecolor = 'black', facecolor = '#50C878', ax = eje1)
# Remover los ejes del mapa
eje1.set_axis_off()
# Asignar un título
eje1.set_title('Colonias utilizando Alta Correlación')

# Para el segundo eje (cuadro)
# Graficar las regiones derivadas de las variables con Baja Correlación
regiones_bajacorr.plot(figsize = (10,10), edgecolor = 'black', facecolor = '#50C878', ax = eje2)
# Remover los ejes del mapa
eje2.set_axis_off()
# Asignar un título
eje2.set_title('Colonias utilizando Baja Correlación')

# Para el segundo eje (cuadro)
# Graficar las colonias originales
colonias_reales.plot(figsize = (10,10), edgecolor = 'black', facecolor = '#50C878', ax = eje3)
# Remover los ejes del mapa
eje3.set_axis_off()
# Asignar un título
eje3.set_title('Colonias Administrativas de la Alcaldía Cuauhtémoc')

plt.show()

Aunque ninguna de las regionalizaciones es idéntica a lo que se observa en la realidad, si pueden notarse diferencias fundamentales entre ambas. Por un lado, las regiones generadas con variables altamente correlacionadas aún guardan las características de caos, desorden y falta de proporción observadas en las colonias en la primera parte de la práctica; por otra parte, las regiones de variables poco correlacionadas, aunque no idénticas a las colonias verdaderas de la alcaldía, si empiezan a guardar entre sí cierto orden y proporción, aún mejor que el de las regiones de la sección anterior, llegando inclusive a delimitar ciertas zonas características de la Alcaldía.

Claro está que las variables utilizadas en la Regionalización no son necesariamente las más ideales para obtener una regionalización tan detallada y socialmente marcada como la que generan las colonias en la realidad; sin embargo, el ejercicio permite visualizar con bastante claridad la importancia seleccionar las variables correctas para obtener resultados con mayor validez.


Ejercicio Final

En el Ejercicio Final de la sección anterior de la práctica utilizaste las variables de educación disponibles en el archivo agebs_educacion.shp para practicar los conceptos estudiados. Esta vez, repite el ejercicio de regionalización, pero ahora utilizando los conceptos aprendidos para generar regiones lo suficientemente adecuadas para que, a tu criterio, sean consideradas válidas.

Como tal, además de presentar las regiones finales obtenidas, debes de responder lo siguiente:

  • ¿Por qué generaste ese número de regiones? (Generalización contra Detalle)
  • ¿Qué tan homogéneas son las regiones generadas? ¿Son válidas? (Homogeneidad)
  • ¿Qué variables utilizaste para la regionalización? ¿Por qué? (Correlación)

No debes de generar regiones perfectas; es suficiente conque utilices los conceptos aprendidos a lo largo de la práctica para justificar tus resultados, de modo que no sean fiel representación de lo que ocurre en la realidad, pero tampoco lo suficientemente descabellados como para alejarse totalmente de ella.