<jupyter_start><jupyter_text>"Etiquetado de variables y valores en las encuestas de INEGI usando Python"> "Para entender los datos de encuestas es necesario contar con un diccionario de variables y un catálogo de valores para las variables que se analizan. Organizar esta información puede ser un reto cuando estos metadatos vienen en archivos planos como `.csv`. Aquí muestro una aplicación usando datos de la ENSU de INEGI."- toc: true- badges: true- comments: true- categories: [pandas, encuestas, inegi]- permalink: /etiquetas-encuestas-inegi/ > Elaborado por <NAME> ([@jjsantoso](https://twitter.com/jjsantoso))  IntroducciónHace poco me tocó trabajar con los datos de una encuesta de INEGI y usé Python para hacer el análisis descriptivo. Quienes trabajamos con datos del INEGI hemos visto que es usual que los archivos de datos abiertos vengan en varias carpetas que contienen tanto los datos como los metadatos e información adicional sobre la encuesta. Por ejemplo, si descargamos los datos para la [Encuesta Nacional de Seguridad Pública Urbana (ENSU)](https://www.inegi.org.mx/programas/ensu/Datos_abiertos) veremos que vienen 3 carpetas, cada una corresponde a una sección de la encuesta:> Note: Los datos se pueden descargar directamente de la página de INEGI, pero aquí dejo una copia de los que yo usé para este tutorial. [Descargar datos](http://jjsantoso.com/blog/datos/conjunto_de_datos_ENSU_2020_4t_csv.zip)* `conjunto_de_datos_VIV_ENSU_12_2020`: Cuestionario sociodemográfico sección I y II* `conjunto_de_datos_CS_ENSU_12_2020`: Cuestionario sociodemográfico sección III* `conjunto_de_datos_CB_ENSU_12_2020`: Cuestionario principal de la encuesta sección I, II, III y IVSi vemos al interior de uno de estos módulos, la estructura incluye las siguientes carpetas:* **Catálogos**: tiene los catálogos para cada variable en el cuestionario* **Conjunto de datos**: tiene los datos principales de la encuesta* **Diccionario de datos**: el nombre e información de cada variable* **Metadatos**: tiene información de la encuesta.* **Modelo entidad relación**: es un diagrama que muestra cómo se relacionan los diferentes conjuntos de datos. ![](imagenes/estructura_folders.png) Si echamos un vistazo rápido a los datos en Excel (`conjunto_de_datos/conjunto_de_datos_CB_ENSU_12_2020.csv`) veremos que la mayor parte de las variables viene codificada. Solo con este archivo no podemos saber qué es cada columna y cuáles es el significado de sus valores. Nos hace falta el diccionario de variables y los catálogos para poder interpretarlas. ![](imagenes/datos_csv.png) En el archivo  `diccionario_de_datos/diccionario_de_datos_CB_ENSU_12_2020.csv` tenemos cuál es el texto de cada pregunta. De ahí sabemos que, por ejemplo, la pregunta `BP1_1` es "Percepción de seguridad en la ciudad". Estos valores se conocen como etiquetas de las variables.Por otro lado, dentro de la carpeta `catalogos` viene un archivo `csv` por cada variable del conjunto de datos.  ![](imagenes/archivos_catalogo.png) Este archivo nos dice cómo debemos transformar los valores numéricos de las categorías por sus valores de texto. Por ejemplo, si abrimos el archivo "BP1_1.csv" su contenido nos muestra que para la variable `BP1_1` debemos interpretar que un 1 corresponde a la categorías "seguro?", el 2 corresponde a "inseguro?" y el 9 a "No sabe/No responde". Estos valores se conocen como etiquetas de los valores.> Note: Las etiquetas de valores tiene sentido para variables categóricas, es decir las que tienen pocos valores nominales. Para variables numéricas o puramente de texto no es necesario usar etiquetas de valores. ![](imagenes/catalogo__bp1.png) Es evidente que para manejar una encuesta es fundamental conocer las etiquetas de las variables y sus valores. Sería mucho más fácil si estas etiquetas estuvieran incluidas en el mismo archivo junto con los datos, pero como están en formato `.csv` no es posible guadar esa información en un solo archivo y por tanto, termina repartida en muchos. Entonces, nuestro objetivo es integrar el diccionario y el catálogo a los datos para que sea más fácil hacer nuestro análisis. Queremos que en las tablas o gráficas que hagamos, las variables categóricas aparezcan como texto, en lugar de los valores numéricos que asignó INEGI. De igual forma, nos gustaría que en lugar de aparecer el nombre de la variable como en la base de datos, aparezca su descripción. Para lograr esto usaremos objetos tipo diccionario nativos de Python y dataframes de Pandas.> Tip: Otros formatos de datos, como por ejemplo los archivos `.dta` de Stata o `.sav` de SPSS sí permiten guardar esas etiquetas junto con los datos, sin embargo, esos no son formatos de datos abiertos que sean fácilmente accesible. En algunos casos, como en la [ENOE](https://www.inegi.org.mx/programas/enoe/15ymas/Microdatos), INEGI también publica archivos `.dta` y `.sav`. DatosPrimero, vamos a importar las librerías necesarias.<jupyter_code>import glob
import sys
import pandas as pd

print('Python', sys.version)
print(pd.__name__, pd.__version__)<jupyter_output>Python 3.8.5 (default, Sep  3 2020, 21:29:08) [MSC v.1916 64 bit (AMD64)]
pandas 1.1.3<jupyter_text>Para ilustrar, vamos a seleccionar los datos de la carpeta `conjunto_de_datos_CB_ENSU_12_2020` (Cuestionario principal de la encuesta sección I, II, III y IV).<jupyter_code>datos = pd.read_csv('conjunto_de_datos_CB_ENSU_12_2020/conjunto_de_datos/conjunto_de_datos_CB_ENSU_12_2020.csv')
datos.head()
datos.info()<jupyter_output><class 'pandas.core.frame.DataFrame'>
RangeIndex: 22283 entries, 0 to 22282
Columns: 176 entries, ID_VIV to EST_DIS
dtypes: float64(1), int64(84), object(91)
memory usage: 29.9+ MB<jupyter_text>Necesitamos el diccionario de variables y los catálogos. Para el diccionario de variables cargamos el archivo `conjunto_de_datos_CB_ENSU_12_2020/diccionario_de_datos/diccionario_de_datos_CB_ENSU_12_2020.csv`<jupyter_code>preguntas = pd.read_csv('conjunto_de_datos_CB_ENSU_12_2020/diccionario_de_datos/diccionario_de_datos_CB_ENSU_12_2020.csv', encoding='latin1')
preguntas.head(10)<jupyter_output><empty_output><jupyter_text>Este nos muestra cómo aparece cada variable en el archivo de datos ("NEMONICO") y cuál es su descripción ("NOMBRE_CAMPO"), además contiene el tipo y rango de valores que puede tener cada variable. Por esta razón, el nombre de cada variable puede estar repetido varias veces. Lo que haremos es eliminar los duplicados y quedarnos con los valores únicos, para después crear un objeto diccionario que tenga como llaves el "NEMONICO" y como valores el "NOMBRE_CAMPO". A continuación se ve el proceso y el resultado:<jupyter_code>dicc_preguntas = preguntas.drop_duplicates(subset=['NEMONICO']).set_index('NEMONICO')['NOMBRE_CAMPO'].to_dict()
print(str(dicc_preguntas)[:500])<jupyter_output>{'ID_VIV': 'Identificador de vivienda seleccionada', 'ID_PER': 'Identificador del informante seleccionado', 'UPM': 'Unidad Primaria de Muestreo ', 'VIV_SEL': 'Vivienda seleccionada', 'R_SEL': 'Número de renglón de la persona seleccionada', 'CVE_ENT': 'Clave Entidad', 'NOM_ENT': 'Nombre de la Entidad', 'CVE_MUN': 'Clave Municipio', 'NOM_MUN': 'Nombre del Municipio', 'LOC': 'Localidad', 'CD': 'Ciudad', 'NOM_CD': 'Nombre de la Ciudad', 'PER': 'Periodo de la entrevista', 'R_DEF': 'Resultado definiti<jupyter_text>Para el catálogo tenemos que trabajar un poco más porque la información está en muchos archivos. Primero vamos a generar una lista de todos los archivos en la carpeta `catalogo`.<jupyter_code>dir_catalogos = 'conjunto_de_datos_CB_ENSU_12_2020/catalogos/'
archivos_catalogo_respuestas = glob.glob1(dir_catalogos, '*.csv')
archivos_catalogo_respuestas[:10]<jupyter_output><empty_output><jupyter_text>A continuación vamos a leer cada uno de los archivos individuales y generar un diccionario por comprensión que contenga como llaves el "NEMONICO" y los valores serán otro diccionario que tiene la relación de los valores numéricos y de texto para los valores de cada variable.<jupyter_code>dicc_respuestas = {
    f[:-4]: pd.read_csv(f'{dir_catalogos}/{f}', encoding='latin1', index_col=f[:-4])['descrip'].to_dict() for f in archivos_catalogo_respuestas
}
print(str(dicc_respuestas)[:500])<jupyter_output>{'BP1_1': {1: 'seguro?', 2: 'inseguro?', 9: 'No sabe / no responde'}, 'BP1_10_1': {1: 'Mucha confianza', 2: 'Algo de confianza', 3: 'Algo de desconfianza', 4: 'Mucha desconfianza', 9: 'No sabe / no responde'}, 'BP1_10_2': {1: 'Mucha confianza', 2: 'Algo de confianza', 3: 'Algo de desconfianza', 4: 'Mucha desconfianza', 9: 'No sabe / no responde'}, 'BP1_10_3': {1: 'Mucha confianza', 2: 'Algo de confianza', 3: 'Algo de desconfianza', 4: 'Mucha desconfianza', 9: 'No sabe / no responde'}, 'BP1_10_4'<jupyter_text>Ahora tenemos dos diccionarios, uno con las etiquetas de las variables (`dicc_preguntas`) y otro con las etiquetas de los valores de cada pregunta `dicc_respuestas`. En ambos diccionarios la llave principal es el némonico de la pregunta, por tanto si queremos saber cuáles son las etiquetas de variables y valores solo tenemos que indexar los diccionarios con el nemónico correspondiente, por ejemplo para "SEX" y "BP1_1" obtenemos:<jupyter_code>print('Etiqueta de variable:', dicc_preguntas['SEX'], '\nEtiqueta de valores',dicc_respuestas['SEX'])
print('Etiqueta de variable:', dicc_preguntas['BP1_1'], '\nEtiqueta de valores',dicc_respuestas['BP1_1'])<jupyter_output>Etiqueta de variable: Percepción de seguridad en la ciudad 
Etiqueta de valores {1: 'seguro?', 2: 'inseguro?', 9: 'No sabe / no responde'}<jupyter_text>Uso de las etiquetasYa que tenemos las etiquetas, veamos cómo podemos usarlas para interpretar mejor en nuestros análisis. Hagamos una tabulación cruzada para ver la distribución de respuestas de las variables "SEX" y "BP1_1"> Warning: En estos ejemplos no estamos considerando que cada observación tiene una ponderación distinta (variable `FAC_SEL`) por tanto los resultados no son estimaciones válidas. En una próxima entrada veremos cómo integrar las ponderaciones.<jupyter_code>pd.crosstab(datos['SEX'], datos['BP1_1'])<jupyter_output><empty_output><jupyter_text>El índice del dataframe y los nombres de las columnas contienen los valores numéricos de las categorías. Vamos a reemplazarlos por sus valores de texto renombrándolo con el método `.rename()`<jupyter_code>pd.crosstab(datos['SEX'], datos['BP1_1'])\
    .rename(index=dicc_respuestas['SEX'],
            columns=dicc_respuestas['BP1_1'])<jupyter_output><empty_output><jupyter_text>Ahora es mucho más fácil de entender esta tabla. Vamos a hacer otra tabla similar a la anterior, pero en este caso desagregando por más variables y usando el método groupby:<jupyter_code>vars_by = ['SEX', 'BP1_1', 'BP1_5_1']
grouped = datos.groupby(vars_by).agg(N=('ID_PER', 'count'))
grouped.head(15)<jupyter_output><empty_output><jupyter_text>Nuevamente reemplazamos los valores de las categorías usando el método `.rename()`. Hacemos un loop para reemplazar las categorías de cada variable. Como tenemos varios niveles en el índice, especificamos la opción `level` para que use solo el catálogo con la variable que le corresponde.<jupyter_code>for v in vars_by:
    grouped.rename(index=dicc_respuestas[v], level=v, inplace=True)

grouped<jupyter_output><empty_output><jupyter_text>Para que sea más fácil de ver, reestructuramos la tabla usando `.unstack()`.<jupyter_code>grouped.unstack('BP1_1')<jupyter_output><empty_output><jupyter_text>Ya integramps las etiquetas de los valores, ahora faltan las etiquetas de las variables. Para eso usaremos el método `.rename_axis()`<jupyter_code>cuadro = grouped.unstack('BP1_1')\
    .rename_axis(index=dicc_preguntas, columns=dicc_preguntas)

cuadro<jupyter_output><empty_output><jupyter_text>Nuestro cuadro ya es entendible, podemos exportarlo a Excel y verificar que tenemos las etiquetas:<jupyter_code>cuadro.to_excel('reporte.xlsx')<jupyter_output><empty_output><jupyter_text>![](imagenes/datos_excel.png)  Si usas Stata...(o incluso si no)Como dije antes, el formato `.dta` permite guardar las etiquetas de variables y valores junto con los datos. Los DataFrames de Pandas traen de forma nativa el método [`.to_stata()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_stata.html) para exportar la información a este formato. Para que Stata reconozca correctamnete las etiquetas de valores es necesario que en pandas las variables sean de [tipo categoria](https://pandas.pydata.org/pandas-docs/stable/user_guide/categorical.html). A continuación, vamos a seleccionar un subconjunto de variables, reemplazaremos sus valores numéricos por las categorías de texto y convertiremos la variable en tipo categórica:<jupyter_code>vars_export = ['SEX', 'BP1_1', 'BP1_2_01', 'BP1_2_02', 'BP1_2_03', 'BP1_2_04', 'BP1_2_05', 'BP1_2_06', 'BP1_2_07', 'BP1_2_08', 'BP1_2_09', 'BP1_2_10', 'BP1_2_11', 'BP1_2_12']
datos_stata = datos[vars_export].apply(lambda s: s.map(dicc_respuestas[s.name])).astype('category')
datos_stata<jupyter_output><empty_output><jupyter_text>Este dataframe lo exportaremos a Stata, junto con el diccionario que contiene las etiquetas de variables, especificando en la opción `variable_labels`.<jupyter_code>datos_stata.to_stata('datos_stata.dta', write_index=False, variable_labels=dicc_preguntas)<jupyter_output><empty_output><jupyter_text>Al abrir el arcivo en Stata podemos ver que efectivamente se guardaron las etiquetas de los valores y de las variables: ![](imagenes/datos_stata.png) Hasta donde entiendo, esta sería una forma más fácil de etiquetar datos provenientes de INEGI para usuarios de Stata que haciendo el procedimiento entero en Stata. Igualmente, para los que no son usuarios de Stata, pero sí de Python o R y quieren conservar las variables categóricas etiquetadas este es un buen formato.<jupyter_code>datos_2 = pd.read_stata('datos_stata.dta')
datos_2.dtypes<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08355523569629363, 'fraction_numerical': 0.020007196833393307, 'mean_word_length': 6.360169491525424, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 72, 'https://': 5, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '510909', 'license': 'Apache-2.0', 'n_tokens_mistral': 5142, 'n_tokens_neox': 4930, 'n_words': 1615, 'path': '_notebooks/2021-03-08-etiquetas en encuestas.ipynb', 'repo_name': 'jjsantos01/jjsantosoblog', 'size': '13913'}