Skip to content

Capítulo libro: Herramientas de Ciencia de datos para Administración Pública

Updated: at 12:00 AM

Table of contents

Open Table of contents

Visualización

Los siguientes temas podrían incluirse en Análisis exploratorio, si no se han incluid ya explícitamente en Visualización

Análisis Exploratorio de Datos (Estadística Descriptiva)

Con la creciente digitalización de muchas actividades humanas cada vez es más sencillo acceder a variadas fuentes de datos. Idealmente antes de recopilar los datos hay un proceso de diseño experimental, en el que se definen unos objetivos y se plantean unas hipótesis; aunque no siempre es el caso.

Una vez tenemos los datos es importante describirlos o caracterizarlos. Aquí entra el Análisis Exploratorio de datos. Éste método es una revisión de la estructura de los datos, y su fortaleza está en usar gráficos y visualizaciones para ese proceso de obtener información que pueda usarse en formular o refutar validar las hipótesis relativas a las variables bajo estudio; como también validar las suposiciones que tengamos sobre ellas.

Aquí seguimos el libro de Chen1, que propone las siguientes fases.

FasePreguntas comunes
Coherencia conceptual de los datos¿Cómo se recogieron los datos?
¿Que fuentes potenciales de error hay en los datos?
¿Qué significa cada variable?
¿Que unidades de medida tiene la variable y tiene sentido para el análisis que se plantea hacer?
Preprocesamiento¿Estamos leyendo/interpretando los tipos de datos de manera adecuada?
¿Que manipulaciones son necesarias para asignar/definir las variables en la forma requerida (por ejemplo, como vector, como matriz, como conjunto de datos)
¿Es necesario agregar los datos?
Calidad de la Señal¿es posible determinar la tendencia central?
¿A que típo de distribución se asemeja más?
¿Hay una dependencia temporal o espacial en los datos?
Identificación de datos problemáticos¿las gráficas muestran datos posiblemente errados?
¿Al graficar como serie, hay datos que tengan valores muy diferentes a los demás?
¿Hay datos faltantes?
Identificación de variables importantes¿Hay variables correlacionadas?
¿Se puede hacer una modificación para mejorar la correlación?

Tipos de datos faltantes

Según Chen et al., existen tres tipos de datos faltantes:

Además proponemos la siguiente categoría:

Coeficiente de Sesgo

En una distribución simétrica habrá en términos generales el mismo número de valores por encima o por debajo del promedio. Cuando hay un desbalance entre los valores arriba y abajo del promedio se habla de un sesgo.

Matemáticamente el valor de sesgo se calcula mediante:

g1=i=1n(xix)3ns3g_1 = \frac{\sum_{i=1}^n (x_i - \overline{x})^3}{n s^3}

Como la expresión está elevada al cubo, los valores negativos (xix_i menores que x\overline{x} al elevarse al cubo mantienen su signo y si la diferencia es grande estamos sumando un valor negativo muy grande a la suma. De la misma forma si xix_i es mucho más grande que x\overline{x}, la resta da un valor positivo, que al elevarse al cubo suma un valor muy grande a la suma.

Ejemplo: Amortización de la vivienda en Colombia.

El Departamento Nacional de Estadística de Colombia (Dane) publica los microdatos de diferentes proceso estadísticos que lleva a cabo. Tenemos por ejemplo la encuesta nacional de calidad de vida, que busca caracterizar dimensiones del bienestar, como la salud, educación, actividades laborales y tenencia de bienes del hogar2.

Enfoquémonos en la variable P5100, ¿Cuánto pagan mensualmente por cuota de amortización?. En la descripción del análisis estadístico3 se explica la forma de recolección de datos de la encuesta.

Revisemos la fase de coherencia conceptual:

FasePreguntas comunesAnálisis
Coherencia conceptual de los datos¿Cómo se recogieron los datos?Se describe en la sección ‘muestreo’ de la descripción de los datos3.
¿Que fuentes potenciales de error hay en los datos?En este caso puede haber datos faltantes, errores de unidades (ejemplo, registrar 1 en lugar de 1000000 para un millón), o errores al registrar con texto en lugar de valores numéricos.
¿Qué significa cada variable?Se define como “el gasto en que incurre el hogar para cubrir el crédito de vivienda que ha adquirido”.
¿Que unidades de medida tiene la variable y tiene sentido para el análisis que se plantea hacer?La descripción de la variable indica ”$”, es necesario confirmarlo mediante el análisis exploratorio.

Descargamos los datos de la página del Dane, en este caso el archivo de Tenencia y financiación de la vivienda que ocupa, de la página del dane 4. Descomprimimos los datos desde el sistema operativo.

Comenzamos con el preprocesamiento. Inicialmente incluímos las librerías:

# coding: utf-8
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

Cerramos las ventanas que puedan estar abiertas:

plt.close('all')
plt.ion()

En Python procedemos a cargar los datos

datos = pd.read_csv('Tenencia y financiación de la vivienda que ocupa el hogar.CSV')

Nos hacemos la pregunta:

FasePreguntas comunesAnálisis
Preprocesamiento¿Estamos leyendo/interpretando los tipos de datos de manera adecuada?Para responder esta pregunta, imprimimos las variables. Como la información está guardada en el dataframe datos, esto se hace con la expresión: datos.columns.

La salida de éste comando es el texto:

Index(['DIRECTORIO;SECUENCIA_ENCUESTA;SECUENCIA_P;ORDEN;FEX_C;P5095;P5100;P3197;P5120;P8692;P8692S1A1;P8692S2;P5610;P5610S1;P8693;P5110;P5130;P5140;P3006;P5650;P5160;P5160S1;P5160S1A1;P5160S2;P5160S2A1'], dtype='object')

Notese que se trata de una lista (se define porque abre el paréntesis cuadrado [ y cierra con ]). Los elementos de las listas se separan por comas, pero aquí tenemos punto y coma. Esto indica que probablemente la primera línea, que debía tener varias variables, se interpretó como una sóla variable.

Otra forma de verlo es imprimir la variable shape del DataFrame:

print(datos.shape)

Se obtiene (86405, 1), es decir, este DataFrame tiene 86405 registros, para una única variable. Otra forma de confirmar esta situación errónea es usar la variable head del dataframe: print(datos.head) 5:

bound method NDFrame.head of       DIRECTORIO;SECUENCIA_ENCUESTA;SECUENCIA_P;ORDEN;FEX_C;P5095;P5100;P3197;P5120;P8692;P8692S1A1;P8692S2;P5610;P5610S1;P8693;P5110;P5130;P5140;P3006;P5650;P5160;P5160S1;P5160S1A1;P5160S2;P5160S2A1
0      7910114;1;1;1;282.37094522;3;;;;;;;;;;;;400000...                                                                                                                                               
1      7910115;1;1;1;3.0542169534;6;;;;;;;;;;;200000;...                                                                                                                                               
2      7910119;1;1;1;31.288476991;4;;;;;;;;;;;250000;...                                                                                                                                               
3      7910120;1;1;1;73.57774897;1;;2;2;;;;0;;0;12000...

Vemos que los datos están separados por ;, entonces se deben indicar que éste es el separador. Es decir:

datos = pd.read_csv('Tenencia y financiación de la vivienda que ocupa el hogar.CSV',sep=';')

Volvemos a revisar que se haya cargado bien, con datos.columns, obteniendo:

print(datos.columns)
Index(['DIRECTORIO', 'SECUENCIA_ENCUESTA', 'SECUENCIA_P', 'ORDEN', 'FEX_C',
       'P5095', 'P5100', 'P3197', 'P5120', 'P8692', 'P8692S1A1', 'P8692S2',
       'P5610', 'P5610S1', 'P8693', 'P5110', 'P5130', 'P5140', 'P3006',
       'P5650', 'P5160', 'P5160S1', 'P5160S1A1', 'P5160S2', 'P5160S2A1'],
      dtype='object')

Esto es una lista, separada por comas, donde hay 25 datos, correspondientes a 25 variables. Esto también se ve de la variable datos.shape, que ahora es la pareja: (86405, 25), es decir 86405 registros de 25 variables.

Explorando datos faltantes (DSPP)

Los datos que nos interesan corresponden a la variable P5100, entonces para estar seguros de que tenemos los datos de esa variable, procedemos a imprimir los valores únicos. En Python se obtiene con: datos.P5100.unique(). Aquí tenemos un vector de 271 elementos. El primer número de ese vector es nan, o ‘Not A Number’, lo que refuerza nuestra hipótesis de que en los datos podemos tener valores faltantes.

Para construir un vector indique la posición de los valores faltantes, usamos: datos.P5100.isnull(). La suma de éste vector nos dará el número de datos faltantes: sum(datos.P5100.isnull()), en este caso 84745. La proporción de datos faltantes sería:

print(sum(datos.P5100.isnull())/len(datos.P5100))

Esto da una fracción de 0.98 del total de datos.

Revisando el diccionario de datos, existe otra variable relacionada.

PreguntaCódigoDescripciónOpciones
¿La vivienda ocupada por este hogar es?P5095Con esta pregunta se quiere conocer la forma de tenencia de la vivienda que ocupa el hogar1. Propia, totalmente pagada
2. Propia, la están pagando
3. En arriendo o subarriendo
4. Con permiso del propietario, sin pago alguno (usufructuario)
5. Posesión sin título (ocupante de hecho)
6. Propiedad colectiva

Entonces tenemos que los únicos registros donde debería haber un valor de pago por amortización son quienes están pagando. O al contrario, los que no deberían tener registro del valor son los que no están pagando. Estos últimos registros no tienen un número 2 en la variable P5095. Es decir, son los registros para los que: datos.P5095!=2. La suma de estos valores se construye con: sum)datos.P5095!=2. Lo que también nos dá como resultado 84745. Es decir, los valores faltantes de la variable P5100 son faltantes no necesarios.

Continuamos el preprocesamiento:

FasePreguntas comunesAnálisis
Preprocesamiento¿Que manipulaciones son necesarias para asignar/definir las variables en la forma requerida (por ejemplo, como vector, como matriz, como conjunto de datos)Los datos están cargados en un DataFrame. Requerimos filtrar los datos faltantes. Para ello podemos definir una variable que corresponda a los valores no nulos.

El comando es: pagoamortizacion = datos.P5100[~datos.P5100.isnull()]

Estadísticos de centro

Continuamos con el preprocesamiento:

FasePreguntas comunesAnálisis
Preprocesamiento¿Es necesario agregar los datos?En este caso tiene sentido agregar, porque esperamos que la variable tenga una dispersión y justamente queremos saber cómo se distribuye. Para esto podemos calcular estadísticos de centro y variabilidad y construir un histograma.

En Python los estadísticos de centro se hallan con la función describe:

print(pagoamortizacion.describe())

Y se obtiene:

count    1.660000e+03
mean     8.007266e+05
std      8.576620e+05
min      9.900000e+01
25%      3.000000e+05
50%      5.500000e+05
75%      1.000000e+06
max      9.000000e+06
Name: P5100, dtype: float64

Luego tenemos 1660 datos, con un promedio de 800726.6, un mínimo de 99 y un máximo de 9000000. Estos valores refuerzan la hipótesis de que la unidad de la variable son los pesos. Podemos planear entonces construir un histograma que tenga divisiones cada 500 mil pesos, desde 0 hasta 9 millones.

binamort  = np.linspace(0,9e6,19)

Construimos el histograma normalizado:

plt.hist(pagoamortizacion,edgecolor='w',bins=binamort,density=True,label='histograma normalizado')

El análisis no está completo si no se incluyen las unidades de medida, y los nombres de las clases. Lo hacemos así:

binamortmillones  = binamort/1e6  # para usar en el texto
xbinstxt = [str(binamortmillones[i])+' a '+str(binamortmillones[i+1]) for i in range(len(binamortmillones)-1)]  # Genera el texto de las etiquetas 
plt.subplots_adjust(bottom=0.2,left=0.2) # mejora el espacio abajo y  la izquierda
plt.ticklabel_format(style = 'plain') # simplifica las unidades
plt.xlabel("Cuota (millones)") # texto en el eje x
plt.title("Pagos de amortización") # título de la gráfica
xbins = [(binamort[i]+binamort[i+1])/2 for i in range(len(binamort)-1)] # posición del texto de las clases
ax.set_xticks(xbins,xbinstxt,rotation=45) # texto de las clases
plt.legend()

Adicionalmente se puede incluir una aproximación continua a la distribución. Una forma de hacerlo es usar la librería seaborn de Python6. Chen et al. explican cómo hacerlo en R, en la página 91.

sns.kdeplot(data=pagoamortizacion,label="densidad",fill=True, alpha=0.6)

Adicionalmente se puede exportar la gráfica:

plt.savefig("amortizacion_ecv_23.png")

Con esto se obtiene la siguiente gráfica:

Histograma de la distribución de pagos de amortización. La primera clase es la más alta, entre 0 a 0.5 millones. A partir de esta bajan los valores. Es una distribución con cola a la izquierda. Detrás del histograma se ve una curva suave, que representa una distribución continua. La cuota está en millones.

Continuamos con el análisis.

FasePreguntas comunesAnálisis
Calidad de la Señal¿es posible determinar la tendencia central?Calculamos el valor del promedio en alrededor de 800 mil pesos, y la mediana en 550 mil pesos como medidas de tendencia central
¿A que típo de distribución se asemeja más?Es una distribución unimodal, asimétrica, con cola a la izquierda.
¿Hay una dependencia temporal o espacial en los datos?No tenemos información temporal en este conjunto de datos. La encuesta es en un momento del tiempo, luego la dependencia temporal podría revisarse comparando con otras versiones de la encuesta
Identificación de datos problemáticos¿las gráficas muestran datos posiblemente errados?No encontramos datos extraños.
¿Hay datos faltantes?Si, ya se discutió anteriormente.
¿Al graficar como serie, hay datos que tengan valores muy diferentes a los demás?Construimos una gráfica de dispersión para los datos. En ésta gráfica no se ven valores atípicos.

El diagrama de dispersión se genera con el siguiente código:

plt.figure()
plt.plot(pagoamortizacion,'.')
plt.title('valores amortización')
plt.xlabel('consecutivo')
plt.ticklabel_format(style = 'plain')
plt.savefig("amortizacion_consecutivo_ecv_23.png")

Obtenemos la siguiente gráfica:

Diagrama de dispersión de los valores de amortización. En el eje horizontal está el consecutivo, simplemente el orden en el consecutivo del dato. Los puntos se distribuyen principalmente en una franja cercana a 500 mil, en el eje vertical.

Aquí vemos que los puntos se distribuyen alrededor de el valor 500 mil, reforzando lo que habíamos encontrado anteriormente. No hay datos especialmente extraños.

Skewed variables (DSPP)

El sesgo se calcula en Pandas usando la función skew del DataFrame. En este caso tenemos:

print(pagoamortizacion.skew())

Obtenemos un valor de 3.705296, lo que corresponde a un sesgo positivo. Esto era de esperarse por la forma de la distribución que habíamos visto anteriormente.

Ejercicio, Edades en Cundinamarca

Utilizando los datos del Censo Nacional de Población y Vivienda 2018, lleve a cabo los análisis similares a los que hicimos en ésta sección para la variable de Edades. Encuentre los valores faltantes. El histograma debe ser similar al siguiente:

Histograma de edades en el censo 2018 en Cundinamarca. Las clases incluyen intervalos de 5 años. Es una distribución monomodal, con el máximo en la clase 15 a 19 años, en un valor cercano a 250000 habitantes.

Construya también el diagrama de dispersión y analícelo.

Correlación (DSS) (PSDS) (OIS)

La correlación es una medida sobre que tanto están relacionadas dos variables. Toma valores numéricos en el intervalo (1,1)(-1,1). Por ejemplo, el coeficiente de correlación de Pearson se calcula multiplicando las desviaciones de la media de la primera variable con las desviaciones de la media de la segunda variable y dividiendo entre el producto de las desviaciones estandar y los grados de libertad. Llamemos las variables xx y yy. El coeficiente de Pearson es:

r=1(n1)i=1n(xixsx)(yiysy)r = \frac{1}{(n-1)} \sum_{i=1}^n \left(\frac{x_i - \overline{x}}{s_x}\right) \left(\frac{y_i - \overline{y}}{s_y}\right)

Correlación entre diferentes variables de la ECV

En el texto de Chen1 se incluyen dos preguntas relacionadas con la correlación entre las variables.

Para llevar a cabo el análisis, construimos un DataFrame que incluya variables de las categorías educación y salud de la encuesta calidad de vida. La fuente de datos está en https://microdatos.dane.gov.co/index.php/catalog/827/get-microdata.

Se cargan los datos:

educacion = pd.read_csv('Educación.CSV',sep=';',usecols=['DIRECTORIO','SECUENCIA_P','SECUENCIA_ENCUESTA','P8587','P6216','P6167'])
salud = pd.read_csv('Salud_ajustada/Salud.CSV',sep=';',usecols=['DIRECTORIO','SECUENCIA_P','SECUENCIA_ENCUESTA','P6181','P6127'])

Aquí es necesario hacer un análisis cuantitativo de las variables de cada DataFrame. La variable P6181 es ‘la calidad de servicio de las EPS’, en escala desde 1: ‘muy buena’ hasta 5 ‘muy mala’ y 5 ‘no sabe’. La variable P6127 es ‘el estado de salud’, en escala desde 1 ‘Muy Bueno’ hasta 4 ‘Malo’.

print(salud.describe())
         DIRECTORIO  SECUENCIA_ENCUESTA    SECUENCIA_P          P6181          P6127
count  2.402120e+05       240212.000000  240212.000000  230206.000000  240212.000000
mean   8.031243e+06            2.296455       1.004138       2.256457       1.999546
std    9.681928e+04            1.404147       0.071554       1.081347       0.560144
min    7.910114e+06            1.000000       1.000000       1.000000       1.000000
25%    7.948281e+06            1.000000       1.000000       2.000000       2.000000
50%    7.993168e+06            2.000000       1.000000       2.000000       2.000000
75%    8.162668e+06            3.000000       1.000000       2.000000       2.000000
max    8.201927e+06           24.000000       6.000000       9.000000       4.000000

Aquí encontramos una inconsistencia en la variable P6181, porque el resumen indica que el valor máximo de la variable es 5, pero aparece registrado un valor de 9. En este caso podemos hacer un filtro para remover éstos valores. Primero generamos una nueva variable y luego procedemos a remover la columna.

salud['P6181f'] = salud.P6181[salud.P6181!=9] 
salud = salud.drop(columns=['P6181'])

Queda como tarea a quien está leyendo este texto el hacer un análisis descriptivo siguiendo las etapas que están anteriormente para describir las variables del dataframe de salud y de educación que hemos incluido.

Construimos un DataFrame con las varialbes de ambos DataFrames:

df_es_i = pd.merge(left=educacion,right=salud,how='inner',left_on=["DIRECTORIO",'SECUENCIA_P',"SECUENCIA_ENCUESTA"], right_on=["DIRECTORIO",'SECUENCIA_P',"SECUENCIA_ENCUESTA"])

Las tres primeras variables corresponden a la identificación de los registros, por lo tanto no es necesario incluirlas en el análisis de correlación.

dfsinetiquetas = df.drop(columns=['DIRECTORIO','SECUENCIA_ENCUESTA', 'SECUENCIA_P'])

En Pandas se incluye la función corr, que permite calcular diferentes tipos de correlación. Para construir la gráfica de la correlación, usamos las siguientes instrucciones:

plt.figure()
plt.title("Correlación")                            
sns.heatmap(dfsinetiquetas.corr(), vmin=-1, vmax=1,cmap=sns.diverging_palette(20, 220, as_cmap=True))
plt.savefig("correlacion.png")

Lo que nos genera la siguiente gráfica:

Diagrama de correlación. Aparecen las variables P5857, 6216, 6167, 6127, 6181f.

El texto de Chen1 propone dos preguntas sobre la correlación. La primera:

FasePreguntas comunesAnálisis
Identificación de variables importantes¿Hay variables correlacionadas?Se encuentra una correlación positiva entre la variable P6127, “el estado de salud en general” y P6181, “la calidad del servicio de la EPS”.
FasePreguntas comunesAnálisis
Identificación de variables importantes¿Se puede hacer una modificación para mejorar la correlación?

Cruzar variables

los diferentes censos. ¿qué dice un análisis exploratorio de las variables ‘Edad’ y ‘Lugar de Residencia hace 5 años’ de la categoría personas?

Paradoja de Simpson o Falacia de Causa Simple (DSS)

Es el fenómeno en el que las correlaciones pueden llevar a conclusiones erróneas, cuando no se tiene en cuenta las llamadas “variables de confusión”.

TODO : DSS tiene un ejemplo en el que se calcula el promedio de una variable (# amigos), y se ordena mediante otra (Costa: Este u Oeste), ocultando una tercera, Degree (PhD o No PhD). Incluir algo similar.

Correlación y Causación (DSS)

TODO: DSS explica con un ejemplo, se podría incluir otro

Hipótesis e Inferencias (DSS)

A/B (PSDS)

Statistical Hypothesis testing (DSS) (PSDS)

Confidence Intervals (DSS)

p-Hcking (DSS)

Inferencia Bayesiana (DSS)

ANOVA (PSDS)

Inferencia

Regresión

Regresión Lineal Simple

Regresión Múltiple

Regresión Cuadrática

Regresión Discontinua (evaluación de impacto)

Bibliografía

Footnotes

  1. Chen (DSPP) 2 3

  2. Dane, https://microdatos.dane.gov.co/index.php/catalog/827

  3. https://microdatos.dane.gov.co/index.php/catalog/827/study-description 2

  4. https://microdatos.dane.gov.co/index.php/catalog/827/get-microdata

  5. También se puede revisar usando el comando head de la terminal, en entornos Linux o MacOS X.

  6. https://python-graph-gallery.com/density-plot/


Next Post
Intervalo de Confianza