rOpenSci | Usos interesantes de censo2017 un año después de su publicación

Usos interesantes de censo2017 un año después de su publicación

Vea la versión en inglés de esta entrada del blog: Interesting Uses of censo2017 a Year After Publishing

🔗 Resumen

Este artículo trata sobre los usos sorprendentes que he observado y las preguntas que recibo sobre el paquete R censo2017, una herramienta para acceder a los datos del censo chileno de 2017, que he recibido desde que fue revisado por pares a través de rOpenSci hace un año. El artículo original sobre el paquete no cubría los diferentes ejemplos que presento aquí, incluyendo una versión Python del paquete R.

🔗 Organizar los datos del censo

Hace tres años, tuve que realizar una tarea que me exigía extraer los datos de un software sólo para Windows en formato DVD, lo que se hizo muy complicado.

Necesitaba acceder a los archivos REDATAM y obtener unos resúmenes de población con un software específico para ese formato. Para mi sorpresa, la tarea fue increíblemente difícil, y exporté los datos a SQL para una extracción de datos más accesible.

Lo que inicialmente organicé en PostgreSQL, terminó siendo organizado en una base de datos local (embebida), que es más conveniente para los usuarios finales y puede instalarse con dos líneas de código.

Instalación en R:

remotes::install_github("ropensci/censo2017")
censo2017::censo_descargar()

Instalación en Python:

pip install git+https://github.com/pachadotdev/censo2017python.git#egg=censo2017

import censo2017
censo2017.descargar()

🔗 Uso inesperado I: Viviendas precarias (en R)

Podemos utilizar los datos del censo para determinar dónde son comunes las viviendas precarias, lo que nos permite obtener ideas valiosas para la política pública. Esto es algo que nunca contemplé cuando hice el software.

¿Qué tipo de información sobre la vivienda podemos buscar en el censo? Para simplificar vamos a explorar la variable p01, que podemos consultar en la tabla variables_codificación, la cual creé para organizar mi trabajo.

library(censo2017)
library(dplyr)

con <- censo_conectar()

tbl(con, "variables") %>% 
  filter(variable == "p01") %>% 
  collect() %>% 
  kable()
tabla variable descripcion tipo rango
viviendas p01 Tipo de Vivienda integer 1 - 10
tbl(con, "variables_codificacion") %>% 
  filter(variable == "p01") %>% 
  select(valor, descripcion) %>% 
  collect() %>% 
  kable()

La variable se refiere al “Tipo de Vivienda” y contiene diez valores numéricos que significan:

valor descripcion
1 Casa
2 Departamento en Edificio
3 Vivienda Tradicional Indígena (Ruka, Pae Pae u Otras)
4 Pieza en Casa Antigua o en Conventillo
5 Mediagua, Mejora, Rancho o Choza
6 Móvil (Carpa, Casa Rodante o Similar)
7 Otro Tipo de Vivienda Particular
8 Vivienda Colectiva
9 Operativo Personas en Tránsito (No Es Vivienda)
10 Operativo Calle (No Es Vivienda)
11 Valor Perdido
0 No Aplica

En este ejemplo, considero las viviendas codificadas como 5 (Mediagua, Mejora, Rancho o Choza) como subestándar. Debido a la organización de los datos del censo, para obtener la proporción de estos tipos de vivienda por región, necesitamos

  1. Crear el código de cada división política (es decir, región) a partir del código geográfico de la tabla de zonas.
  2. Unir la tabla de zonas con la tabla de viviendas para hacer coincidir cada unidad con su ubicación (por ejemplo, el hogar ID 100 está en la región “Los Ríos (14)").
  3. Agrupar por p01 y contar el número de unidades.

Por ejemplo, si queremos ver la 14ava región específicamente, una forma de hacerlo podría ser:

tbl(con, "zonas") %>% 
  mutate(region = str_sub(geocodigo, 1, 2)) %>% 
  filter(region == 14) %>% 
  
  inner_join(tbl(con, "viviendas")) %>% 
  
  group_by(p01) %>% 
  count() %>% 
  
  collect() %>% 
  kable()
p01 n
1 144081
2 4793
3 44
4 494
5 2187
6 35
7 1875
8 467
9 6
10 8

Podemos mejorar esta salida con una unión opcional y un paso para extraer los porcentajes:

tbl(con, "zonas") %>% 
  mutate(region = str_sub(geocodigo, 1, 2)) %>% 
  filter(region == "14") %>% 
  
  inner_join(tbl(con, "viviendas")) %>% 
  
  group_by(p01) %>% 
  count() %>% 
  
  left_join(
    tbl(con, "variables_codificacion") %>% 
      filter(variable == "p01") %>% 
      select(p01 = valor, p01_desc = descripcion)
  ) %>% 
  select(p01, p01_desc, n) %>% 
  
  arrange(-n) %>% 
  ungroup() %>% 
  mutate(p = round(100 * n / sum(n), 2)) %>% 
  
  collect() %>% 
  kable()
p01 p01_desc n p
1 Casa 144081 93.57
2 Departamento en Edificio 4793 3.11
5 Mediagua, Mejora, Rancho o Choza 2187 1.42
7 Otro Tipo de Vivienda Particular 1875 1.22
4 Pieza en Casa Antigua o en Conventillo 494 0.32
8 Vivienda Colectiva 467 0.30
3 Vivienda Tradicional Indígena (Ruka, Pae Pae u Otras) 44 0.03
6 Móvil (Carpa, Casa Rodante o Similar) 35 0.02
10 Operativo Calle (No Es Vivienda) 8 0.01
9 Operativo Personas en Tránsito (No Es Vivienda) 6 0.00

Para la 14ava región, la calidad de la vivienda no parece ser un problema. ¿Y si nos preguntamos por el número de personas que viven en cada uno de estos tipos de unidades? Esto requeriría pasos adicionales para unir a las personas con los hogares y los hogares con las viviendas.

tbl(con, "zonas") %>% 
  mutate(region = str_sub(geocodigo, 1, 2)) %>% 
  filter(region == "14") %>% 
  
  inner_join(tbl(con, "viviendas")) %>% 
  
  inner_join(tbl(con, "hogares")) %>% 
  inner_join(tbl(con, "personas")) %>% 
  
  group_by(p01) %>% 
  count() %>% 
  
  left_join(
    tbl(con, "variables_codificacion") %>% 
      filter(variable == "p01") %>% 
      select(p01 = valor, p01_desc = descripcion)
  ) %>% 
  select(p01, p01_desc, n) %>% 
  
  arrange(-n) %>% 
  ungroup() %>% 
  mutate(p = round(100 * n / sum(n), 2)) %>% 
  
  collect() %>% 
  kable()
p01 p01_desc n p
1 Casa 359343 93.38
2 Departamento en Edificio 9139 2.37
8 Vivienda Colectiva 7905 2.05
5 Mediagua, Mejora, Rancho o Choza 3980 1.03
7 Otro Tipo de Vivienda Particular 3008 0.78
4 Pieza en Casa Antigua o en Conventillo 727 0.19
9 Operativo Personas en Tránsito (No Es Vivienda) 494 0.13
3 Vivienda Tradicional Indígena (Ruka, Pae Pae u Otras) 96 0.02
10 Operativo Calle (No Es Vivienda) 84 0.02
6 Móvil (Carpa, Casa Rodante o Similar) 61 0.02

¿Cómo se compara el resultado anterior con el de la capital (13ava región)?

tbl(con, "zonas") %>% 
  mutate(region = str_sub(geocodigo, 1, 2)) %>% 
  filter(region == "13") %>% 
  
  inner_join(tbl(con, "viviendas")) %>% 
  
  inner_join(tbl(con, "hogares")) %>% 
  inner_join(tbl(con, "personas")) %>% 
  
  group_by(p01) %>% 
  count() %>% 
  
  left_join(
    tbl(con, "variables_codificacion") %>% 
      filter(variable == "p01") %>% 
      select(p01 = valor, p01_desc = descripcion)
  ) %>% 
  select(p01, p01_desc, n) %>% 
  
  arrange(-n) %>% 
  ungroup() %>% 
  mutate(p = round(100 * n / sum(n), 2)) %>% 
  
  collect() %>% 
  kable()
p01 p01_desc n p
1 Casa 5265416 74.03
2 Departamento en Edificio 1613157 22.68
8 Vivienda Colectiva 78763 1.11
4 Pieza en Casa Antigua o en Conventillo 72142 1.01
5 Mediagua, Mejora, Rancho o Choza 52710 0.74
7 Otro Tipo de Vivienda Particular 17335 0.24
9 Operativo Personas en Tránsito (No Es Vivienda) 9113 0.13
10 Operativo Calle (No Es Vivienda) 2740 0.04
3 Vivienda Tradicional Indígena (Ruka, Pae Pae u Otras) 865 0.01
6 Móvil (Carpa, Casa Rodante o Similar) 567 0.01

De los datos resumidos anteriores podemos ver que en la capital, 52.710 personas viven en viviendas precarias (Mediagua, Mejora, Rancho o Choza).

No debemos olvidar cerrar la conexión de base de datos.

censo_desconectar()

🔗 Uso inesperado II: Fuente de agua (en Python)

La motivación de este ejemplo es mostrar que el objetivo de organizar los datos del censo no era promover R, sino mostrar algunos de los beneficios de tener los datos organizados en un formato abierto, independientemente del lenguaje de programación utilizado para el análisis. Para mí es más fácil completar este ejemplo en R, pero quería incluir a las personas que utilizan estos datos del censo con Python.

La variable para la fuente de agua es p05. El análisis es muy similar al anterior, y empezamos por ver su descripción. Este ejemplo es relevante, ya que permite cuantificar el número de personas que podrían estar expuestas a agua contaminada en una zona determinada.

import censo2017
import duckdb

con = duckdb.connect(database = censo2017.archivo_sql())

con.execute("SELECT * FROM variables_codificacion WHERE variable = 'p05'").df()

##        tabla variable  valor                                descripcion
## 0  viviendas      p05      1                                Red Pública
## 1  viviendas      p05      2                               Pozo o Noria
## 2  viviendas      p05      3                              Camión Aljibe
## 3  viviendas      p05      4  Río, Vertiente, Estero, Canal, Lago, Etc.
## 4  viviendas      p05     99                              Valor Perdido
## 5  viviendas      p05     98                                  No Aplica

Utilizando esta variable, podemos conocer el número de personas para cada fuente de agua de la misma manera que en el caso anterior.

con.execute("""
SELECT "p05", "p05_desc", "n",
  ROUND((100.0 * "n") / SUM("n") OVER (), CAST(ROUND(2.0, 0) AS INTEGER)) AS "p"
FROM (
  SELECT "LHS"."p05" AS "p05", "n", "p05_desc"
  FROM (
    SELECT "p05", COUNT(*) AS "n"
    FROM (
      SELECT *, "LHS"."hogar_ref_id" AS "hogar_ref_id"
      FROM (
        SELECT *, "LHS"."vivienda_ref_id" AS "vivienda_ref_id"
        FROM (
          SELECT *, "LHS"."zonaloc_ref_id" AS "zonaloc_ref_id"
          FROM (
            SELECT *
            FROM (
              SELECT *, SUBSTR("geocodigo", 1, 2) AS "region"
              FROM "zonas"
            ) "q01"
            WHERE ("region" = '14')
          ) "LHS"
          INNER JOIN "viviendas" AS "RHS"
            ON ("LHS"."zonaloc_ref_id" = "RHS"."zonaloc_ref_id")
        ) "LHS"
        INNER JOIN "hogares" AS "RHS"
          ON ("LHS"."vivienda_ref_id" = "RHS"."vivienda_ref_id")
      ) "LHS"
      INNER JOIN "personas" AS "RHS"
        ON ("LHS"."hogar_ref_id" = "RHS"."hogar_ref_id")
    ) "q02"
    GROUP BY "p05"
  ) "LHS"
  LEFT JOIN (
    SELECT "valor" AS "p05", "descripcion" AS "p05_desc"
    FROM "variables_codificacion"
    WHERE ("variable" = 'p05')
  ) "RHS"
    ON ("LHS"."p05" = "RHS"."p05")
) "q03"
ORDER BY -"n"
""").df()

##    p05                                   p05_desc       n      p
## 0    1                                Red Pública  299464  77.82
## 1    4  Río, Vertiente, Estero, Canal, Lago, Etc.   38796  10.08
## 2    2                               Pozo o Noria   32150   8.35
## 3   98                                  No Aplica    8483   2.20
## 4    3                              Camión Aljibe    3558   0.92
## 5   99                              Valor Perdido    2386   0.62

Aquí vemos que para la 14ava región, el 22,18% de la población no tiene acceso a la red pública de agua corriente. La ventaja de este enfoque es que aquí utilizamos SQL puro para la consulta, que funcionaría en cualquier lenguaje en el que podamos pasar consultas SQL. La desventaja es que no tenemos una herramienta como dplyr y está la necesidad de aprender la sintaxis SQL.

No debemos olvidar cerrar la conexión a la base de datos.

con.close()

🔗 Observaciones finales

Los ejemplos mostrados aquí se han simplificado. La mayoría de las consultas que recibo son sobre resúmenes de datos a nivel subcomunitario, por ejemplo cómo contar las ocurrencias de una variable categórica en un barrio, como hicimos en nuestro ejemplo de acceso al agua (es decir, pozo o red pública). Usando censo2017 podemos extraer información útil para las políticas públicas basada en la evidencia cuando se trata, por ejemplo, de priorizar las decisiones presupuestarias.