En esta práctica vamos a explorar la forma en la agregación delos datos influye en los resultados de un análisis. Para esto vamos a trabajar con los datos de desaparecidos en la República Mexicana entre 2006 y 2014.
Los datos vienen en dos shapefiles, uno a nivel estatal y otro a nivel municipal. Importemos los archivos en GeoPandas para analizarlos:
In [1]:
from geopandas import GeoDataFrame
estatal = GeoDataFrame.from_file('data/des_rezago_estado.shp')
municipal = GeoDataFrame.from_file('data/muns_geo_des.shp')
Vamos a ver un poco los datos:
In [2]:
estatal.head()
Out[2]:
Como estos datos tienen 22 columnas, no los podemos ver todos. Vamos a listar las columnas para ver qué datos tenemos:
In [3]:
for c in estatal.columns:
print c
En las columnas 2006-2014 tenemos los datos de desaparecidos para cada año. En las demás columnos tenemos alguna información sobre las condiciones socioeconómicas de cada unidad espacial:
Los datos son del CONEVAL a partir del censo de 2010
In [4]:
municipal.head()
Out[4]:
El primer ejercicio que vamos a hacer es una regresión lineal del total de desaparecidos (sobre todos los años), contra alguna variable socioeconómica para observar cómo cambia el resultado con la escala de análisis.
El primer paso es crear y calcular una columna con el total de desaparecidos:
In [5]:
des_estado = estatal[['cvegeo','2006','2007','2008','2009','2010','2011','2012','2013','2014']]
des_estado.head()
Out[5]:
Aquí simplemente seleccionamos las columnas que nos interesan
In [6]:
des_estado['total_des'] = des_estado.sum(axis=1)
des_estado.head()
Out[6]:
Y aquí añadimos una nueva columna a nuestra selección con la suma de desaparecidos.
Ahora vamos a unir la suma a nuestros datos originales:
In [7]:
import pandas as pd
estatal = pd.merge(estatal,des_estado[['total_des','cvegeo']],on='cvegeo' )
estatal.head()
Out[7]:
Repetimos para los municipios (ahora en un solo paso):
In [8]:
des_mun = municipal[['cvegeo_x','2006','2007','2008','2009','2010','2011','2012','2013','2014']]
des_mun['total_des'] = des_mun.sum(axis=1)
municipal = pd.merge(municipal,des_mun[['total_des','cvegeo_x']],on='cvegeo_x' )
municipal.head()
Out[8]:
Para empezar, podemos pensar que la cantidad de desaparecidos puede estar correlacionada con la cantidad de habitantes. Probemos modelar esto a nivel estatal con una regresión lineal simple:
In [9]:
import statsmodels.formula.api as sm
model = sm.OLS(estatal['total_des'], estatal['POB1'])
result = model.fit()
print result.summary()
Lo primero que hicimos fue importar el paquete que contiene los modelos estadísticos. Luego utilizamos un Ordinary Least Squares para hacer nuestra regresión lineal.
Examinando los resultados, lo primero que podemos ver es que la población explica relativamente poco la cantidad de desaparecidos, aunque la relación es significativa.
¿Sucederá lo mismo a nivel municipal?
In [10]:
model = sm.OLS(municipal['total_des'], municipal['POB1'])
result = model.fit()
#model = ols(y=estatal['total_des'], x=estatal['POB1'])
print result.summary()
En este caso, la población a nivel municipal explica aproximadamente el mismo porcentaje de la varianza del total de desaparecidos que a nivel estatal.
Hagan varias regresiones, usando diferentes variables que crean que pueden explicar la cantidad de desaparecidos. Comparen los resultados a nivel estatal y municipal
También podemos hacer pensar en modelos un poco más complicados, por ejemplo, regresiones multivariadas. En un primer experimento, podemos modelar la cantidad de desaparecidos a partir de la población y de el índice de rezago, primero a nivel estatal:
In [11]:
import statsmodels.formula.api as sm
model = sm.ols(formula="total_des ~ POB1 + rezago",data=estatal).fit()
print model.summary()
Nota: Como pueden ver, statsmodels
nos está avisando que tenemos un Condition Number demasiado grande y que esto puede estar indicando que tenemos un problema de colineridad. Esto querría decir que algunas de nuestras varaibles están muy correlacionadas y por lo tanto no deberían usarse en este modelo.
Para ver si este es el caso, podemos examinar la matriz de correlación, en particular sus eigenvalores para ver si tenemos coinearidad:
In [12]:
import numpy as np
corr = estatal[['POB1','rezago','total_des']].corr()
w, v = np.linalg.eig(corr)
print w
Lo que hicimos aquí fue importar la librería numpy
, que incluye métodos de álgebra lineal. Luego calculamos la matriz de correlaciones usando Pandas
y, finalomente, calculamos los eigenvalores de dicha matriz. Como ninguno de los eigenvalores es cercano a cero, podemos concluir que no tenemos un problema de colinearidad y que, por lo tanto, el Condition Number grande se debe a problemas numéricos.
En este caso, podemos pensar que estos problemas derivan del hecho de que la variable POB1 tiene valores mucho más grandes que las otras dos. Intentemos reescalar las variables:
In [13]:
vars_estatal = estatal[['POB1','rezago','total_des']]
vars_estatal_norm = (vars_estatal - vars_estatal.mean()) / (vars_estatal.std())
print vars_estatal_norm.min()
print vars_estatal_norm.max()
print vars_estatal_norm.mean()
print vars_estatal_norm.std()
Lo que hicimos aquí fue reescalar los valores de nuestras variables para que la media esté centrada en cero y lo que cuantificamos es la desviación de la media con respecto al rango.
Ahora veamos si esta transformación de los datos nos sirve para resolver el problema del Condition Number:
In [14]:
model = sm.ols(formula="total_des ~ POB1 + rezago",data=vars_estatal_norm)
result = model.fit()
print result.summary()
Bien, ahora tenemos un Condition Number perfectamente aceptable y, por lo tanto, podemos tener confianza en nuestros resultados.
Repliquemos el proceso para la escala municipal:
In [15]:
vars_municipal = municipal[['POB1','rezago','total_des']]
vars_municipal_norm = (vars_municipal - vars_municipal.mean()) / (vars_municipal.std())
model = sm.ols(formula="total_des ~ POB1 + rezago",data=vars_municipal_norm)
result = model.fit()
print result.summary()
Ahora sí estamos obteniendo algunos resultados interesantes: mientras que a nivel estatal la $ R^2 $ es 0.094, en el caso municipal es de 0.36 ¡Un orden de magnitud más grande! Además, la influencia del rezago (el valor del coeficiente), es un orden de magnitud menor para el caso municipal que para el caso estatal. ¿Qué quiere decir esto? ¿Cuál es la verdadera influencia del rezago en la cantidad de desaparecidos?
Utilicen diferentes combinaciones de las variables disponibes para tratar de explicar mejor el total de desaparecidos, repitan sus ejercicios a nivel estatal y municipal. Recuerden que tienen que reescalar los datos
Hasta el momento hemos trabajado con los datos de total de desaparecidos ¿Sería muy diferente si trabajáramos con tasas? Repitamos los ejercicios usando ahora, como variable dependiente, la tasa de desaparecidos por cada 100,000 habitantes. El primer paso es, por supuesto, calcular dicha tasa y agregarla como columna a nuestros datos:
In [16]:
tasa = estatal['total_des'].divide(estatal['POB1'])*100000
tasa.head()
Out[16]:
La sintaxis es quizá un poco extraña, pero lo que estamos haciendo es realmente sencillo: a la columna total_des
la estamos dividiendo por la columna POB1
, elemento por elemento. Es importante ver que lo que obtenemos no es un DataFrame sino una serie (SERIES), que es un tipo más simple quie sólo tiene una columna y un índice. Para poder unir el resultado a los datos, necesitamos convertir la serie en un DataFrame y proceder de la misma forma en que lo hicimos con el total de desaparecidos:
In [17]:
tasa_df = pd.DataFrame(tasa,columns=['tasa'])
estatal = pd.merge(estatal,tasa_df,left_index=True, right_index=True)
estatal.head()
Out[17]:
La única diferencia es que ahora utilizamos los índices (left_index=True, right_index=True
), como la condición para el merge, ya que no tenemos columnas en común pero sabemos que los datos están bien ordenados.
Una primera pregunta es si la tasa de desaparecidos está correacionada con la población:
Como era de esperarse, la influencia de la cantidad de población sobre la tasa es menor que sobre el total ¿por qué?
Ahora sí, podemos volver a correr nuestros modelos:
In [18]:
vars_estatal = estatal[['POB1','rezago','total_des','tasa']]
vars_estatal_norm = (vars_estatal - vars_estatal.mean()) / (vars_estatal.max() - vars_estatal.min())
model = sm.ols(formula="tasa ~ POB1 + rezago",data=vars_estatal_norm)
result = model.fit()
print result.summary()
¡Ahora no explicamos absolutamente nada de la variable dependiente!
¿Y en la escala municipal?
In [19]:
tasa = municipal['total_des'].divide(municipal['POB1'])*100000
tasa_df = pd.DataFrame(tasa,columns=['tasa'])
tasa_df.head()
municipal = pd.merge(municipal,tasa_df,left_index=True, right_index=True)
vars_municipal = municipal[['POB1','rezago','total_des','tasa']]
vars_municipal_norm = (vars_municipal - vars_municipal.mean()) / (vars_municipal.max() - vars_municipal.min())
model = sm.ols(formula="tasa ~ POB1 + rezago",data=vars_municipal_norm)
result = model.fit()
print result.summary()
In [ ]: