El Instituto Nacional de Estadística y Censos (INDEC) es el encargado de elaborar la proyecciones de población en Argentina, en base a los censos de población, hogares y vivienda.
La serie de proyecciones más actualizada es la desarrollada en base al Censo Nacional de Población, Hogares y Viviendas 2010 y se encuentra disponible en https://www.indec.gob.ar/indec/web/Nivel4-Tema-2-24-85
El material publicado en el sitio web del Instituto aparece en dos formatos disponibles para descarga:
Ambos formatos resultan habitualmente poco amigables para docentes, investigadores y usuarios relacionados con la estadística y la ciencia de datos. En primer lugar, el formato .pdf resulta adecuado para la impresión del documento y su visualización, pero no para la utilización de los datos en softwares de procesamiento. Por otro lado, las planillas .xls disponibles presentan un diseño en tabulados contiguos, fragmentados en distintas hojas (cada una representa a las jurisdicción diferente), lo que torna muy laborioso reconstruir series temporales sin un trabajo arduo de copiado y pegado que aumenta el riesgo de cometer errores.
El objetivo de este documento es presentar una forma programática de procesamiento de las proyecciones de poblacion 2010-2040 publicadas por el INDEC, usando el software estadístico R (R Core Team 2019) con sus librerías dplyr (Wickham et al. 2023), shiny (Chang et al. 2022), openxlsx (Schauberger and Walker 2023) y readxl (Wickham and Bryan 2023).
Como se mencionó más arriba, se utilizarán las librerías dplyr, openxl y readxl, que deben estar previamente instaladas.
# chequeamos si las librerías están instaladas y si es necesario se instalan
if ("readxl" %in% installed.packages()[,"Package"]) {library(readxl)} else {install.packages("readxl");library(readxl)}
if ("openxlsx" %in% installed.packages()[,"Package"]) {library(openxlsx)} else {install.packages("openxlsx");library(openxlsx)}
if ("highcharter" %in% installed.packages()[,"Package"]) {library(highcharter)} else {install.packages("highcharter");library(highcharter)}
# descarga del archivo
url = "https://www.indec.gob.ar/ftp/cuadros/poblacion/c2_proyecciones_prov_2010_2040.xls"
download.file(url, destfile = "poblacion.xls", mode="wb")
Esto se puede hacer a través de la función excel_sheets de readxl
sheets = readxl::excel_sheets("poblacion.xls")
print(sheets)
## [1] "GraphData" "01-TOTAL DEL PAÍS" "02-CABA"
## [4] "06-BUENOS AIRES" "10-CATAMARCA" "14-CÓRDOBA"
## [7] "18-CORRIENTES" "22-CHACO" "26-CHUBUT"
## [10] "30-ENTRE RÍOS" "34-FORMOSA" "38-JUJUY"
## [13] "42-LA PAMPA" "46-LA RIOJA" "50-MENDOZA"
## [16] "54-MISIONES" "58-NEUQUÉN" "62-RÍO NEGRO"
## [19] "66-SALTA" "70-SAN JUAN" "74-SAN LUIS"
## [22] "78-SANTA CRUZ" "82-SANTE FE" "86-SANTIAGO DEL ESTERO"
## [25] "90-TUCUMÁN" "94-TIERRA DEL FUEGO"
Eliminar el nombre de la primera hoja de la planilla en la variable (ya que no contiene información)
sheets = sheets[sheets!="GraphData"]
Todas las hojas presentan cuadros con la información de interés. Cada cuadro ocupa 21 filas y 3 columnas más una vacía utilizada como margen, comenzando el primero en la celda “B8”. En cada cuadro se presenta la información de la proyección (por grupos de edad y sexo) para una jurisdicción seleccionada (la de la hoja) y un año seleccionado (indicado en el encabezado de cada cuadro).
El diseño de cada hoja (que representa a una jurisdicción) se compone de 5 bloques horizontales de 6 cuadros cada uno y un bloque final (el sexto) de un cuadro. Con una secuencia vamos a obtener la columna en la que empieza cada cuadro. Identificamos la letra “B” en el vector LETTERS que incluye R. En este caso, la letra “B” es la segunda del vector. Usando esa posición, iremos agregando los múltiplos de 4 hasta completar las 6 columnas.
orden_letra_b = 2
columnas = LETTERS[seq(orden_letra_b,length(LETTERS), by = 4)][1:6]
print(columnas)
## [1] "B" "F" "J" "N" "R" "V"
Ahora obtendremos las filas donde empiezan los cuadros. Entre la longitud de los cuadros (21 filas) más los espacios y los totales que no utilizaremos, vemos que cada bloque comienza 28 filas debajo del anterior. Multiplicamos hasta obtener las filas iniciales de cada uno.
fila_primer_bloque = 8
fila_ultimo_bloque = 148
filas = seq(fila_primer_bloque,fila_ultimo_bloque, by = 28)
print(filas)
## [1] 8 36 64 92 120 148
Con el comando expand.grid obtendermos todas las combinaciones de fila y columna donde comienzan los cuadros a partir de los dos vectores creados anteriormente.
columnas_y_filas = list(
columnas,
filas
)
columnas_y_filas = expand.grid(columnas_y_filas)
print(columnas_y_filas)
## Var1 Var2
## 1 B 8
## 2 F 8
## 3 J 8
## 4 N 8
## 5 R 8
## 6 V 8
## 7 B 36
## 8 F 36
## 9 J 36
## 10 N 36
## 11 R 36
## 12 V 36
## 13 B 64
## 14 F 64
## 15 J 64
## 16 N 64
## 17 R 64
## 18 V 64
## 19 B 92
## 20 F 92
## 21 J 92
## 22 N 92
## 23 R 92
## 24 V 92
## 25 B 120
## 26 F 120
## 27 J 120
## 28 N 120
## 29 R 120
## 30 V 120
## 31 B 148
## 32 F 148
## 33 J 148
## 34 N 148
## 35 R 148
## 36 V 148
Eliminamos las ultimas 5 combinaciones ya que el último bloque sólo dispone de un cuadro
columnas_y_filas = columnas_y_filas[1:(nrow(columnas_y_filas)-5),]
Pegamos ambas columnas para obtener los identificadores de celdas tal como los usa Excel
celdas = paste0(columnas_y_filas$Var1,columnas_y_filas$Var2)
print(celdas)
## [1] "B8" "F8" "J8" "N8" "R8" "V8" "B36" "F36" "J36" "N36"
## [11] "R36" "V36" "B64" "F64" "J64" "N64" "R64" "V64" "B92" "F92"
## [21] "J92" "N92" "R92" "V92" "B120" "F120" "J120" "N120" "R120" "V120"
## [31] "B148"
Ya tenemos entonces la lista de todas las celdas que representan la primera celda (arriba y a la izquierda) de cada uno de los bloques donde se encuentran las proyecciones para un año y jurisdicción específicos
Sumamos ahora unos pasos que servirán para agregar las columnas de jurisdicción, sexo y edad en la tabla final. Creamos un vector con los grupos de edad que utiliza INDEC en las proyecciones. Podemos hacerlo desde el rango A8:A28 de la primera hoja, por ejemplo. Luego, hacemos lo mismo con la variables sexo desde B4:D4 y finalmente creamos un vector con la secuencia de años de las proyecciones (2010 a 2040).
grupos_de_edad = readxl::read_xls("poblacion.xls", sheet = sheets[1], range = "A8:A28", col_names = F)[[1]] # vector de grupos de edad
sexo = colnames(readxl::read_xls("poblacion.xls", sheet = sheets[1], range = "B4:D4")) # vector de categorías de sexo
anos = 2010:2040 # vector de años
print(grupos_de_edad)
## [1] "0-4" "5-9" "10-14" "15-19" "20-24" "25-29"
## [7] "30-34" "35-39" "40-44" "45-49" "50-54" "55-59"
## [13] "60-64" "65-69" "70-74" "75-79" "80-84" "85-89"
## [19] "90-94" "95-99" "100 y más"
print(sexo)
## [1] "Ambos sexos" "Varones" "Mujeres"
print(anos)
## [1] 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024
## [16] 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
## [31] 2040
Ya tenemos toda la información que necesitamos:
Con esa información podemos recorrer el archivo e ir extrayendo la información para generar un data frame. Ésta será la tarea más compleja.
Vamos a hacer ahora un loop que recorra hoja por hoja el archivo y a medida que pase por cada cuadro lo agregue a un data frame debajo del cuadro anterior, identificando la jurisdicción y el grupo de edad en filas y el sexo en columnas
resultado = data.frame() # creamos en data.frame vacío donde se guardarán los resultados
for (i in sheets) { # recorre las hojas del archivo original
for (j in celdas) { # recorre cada una de las celdas donde comienza un bloque de datos
ano=anos[which(celdas==j)] # identifica el año del bloque que está capturando
rango = c(j,
paste0(LETTERS[which(LETTERS==substring(j,1,1))+2],as.numeric(substring(j,2,4))+20))
rango = paste(rango,collapse = ":") # obtiene el rango completo del bloque
cuadro = readxl::read_xls("poblacion.xls", sheet = i, range = rango, col_names = F) # lee el bloque
colnames(cuadro) = sexo # pone nombre de columnas a los datos obtenidos
cuadro$ano = ano # agrega el año de los datos
cuadro$juri = i # agrega la jurisdicción a los datos
cuadro$edad = grupos_de_edad # agrega las etiquetas de los grupos de edad
resultado = rbind(
resultado,
cuadro[,c(4,5,6,1,2,3)]
) # une los datos obtenidos al data frame donde se almacenarán todos (resultado)
}
}
Ahora sí, contamos con un data frame que contiene los datos en un formato amigable:
print(resultado)
## # A tibble: 16,275 × 6
## ano juri edad `Ambos sexos` Varones Mujeres
## <int> <chr> <chr> <dbl> <dbl> <dbl>
## 1 2010 01-TOTAL DEL PAÍS 0-4 3571540 1838771 1732769
## 2 2010 01-TOTAL DEL PAÍS 5-9 3507135 1794531 1712604
## 3 2010 01-TOTAL DEL PAÍS 10-14 3541954 1801750 1740204
## 4 2010 01-TOTAL DEL PAÍS 15-19 3559813 1797164 1762649
## 5 2010 01-TOTAL DEL PAÍS 20-24 3346483 1674870 1671613
## 6 2010 01-TOTAL DEL PAÍS 25-29 3166874 1575889 1590985
## 7 2010 01-TOTAL DEL PAÍS 30-34 3112375 1540513 1571862
## 8 2010 01-TOTAL DEL PAÍS 35-39 2711144 1334655 1376489
## 9 2010 01-TOTAL DEL PAÍS 40-44 2347809 1149610 1198199
## 10 2010 01-TOTAL DEL PAÍS 45-49 2212137 1076990 1135147
## # ℹ 16,265 more rows
Podemos mejorar la presentación de los datos.
# separamos los códigos de jurisdicción de los nombres
resultado$juri_nombre = substring(resultado$juri,4,max(nchar(resultado$juri)))
resultado$juri = substring(resultado$juri,1,2)
También podemos usar tidyr (Wickham, Vaughan, and Girlich 2023) para pasar la variable sexo a las filas y DT (Xie, Cheng, and Tan 2023).
if ("tidyr" %in% installed.packages()[,"Package"]) {library(tidyr)} else {install.packages("tidyr");library(tidyr)}
if ("DT" %in% installed.packages()[,"Package"]) {library(DT)} else {install.packages("DT");library(DT)}
resultado = resultado %>% pivot_longer(cols = 4:6,
names_to = "sexo_nombre",
values_to = "poblacion") # pasa sexo a filas
# codifica sexo
resultado$sexo_codigo = ""
resultado$sexo_codigo[resultado$sexo_nombre=="Ambos sexos"] = "0"
resultado$sexo_codigo[resultado$sexo_nombre=="Varones"] = "1"
resultado$sexo_codigo[resultado$sexo_nombre=="Mujeres"] = "2"
resultado = resultado[,c(1,2,4,7,5,3,6)] # ordena columnas
DT::datatable(resultado)
Podemos observar que generamos un data frame de 48.825 filas. Si tenemos en cuenta que tabulamos información de 31 años, en 25 jurisdicciones (incluyendo “total país”), para 3 categorías de sexo (incluyendo “ambos sexos”) y 21 grupos de edad, podemos comprobar si nuestro trabajo fue correcto:
filas_data_frame = 48825
n_anos = 31
n_jurisdicciones = 25
n_categorias_sexo = 3
n_grupos_de_edad = 21
filas_esperadas = n_anos * n_jurisdicciones * n_categorias_sexo * n_grupos_de_edad
print(filas_data_frame == filas_esperadas)
## [1] TRUE
Finalmente, podemos hacer una visualización sencilla de los datos usando los paquetes shiny (Chang et al. 2022), highcharter (Kunst 2022) y htmlwidgets (Vaidyanathan et al. 2023).
library(shiny)
library(dplyr)
library(highcharter)
library(shinyWidgets)
ui <- fluidPage(
column(3,
br(),
selectizeInput(inputId = "ano",
label = "Seleccionar año:",
choices = unique(resultado$ano),
selected = substring(Sys.Date(),1,4)),
selectizeInput(inputId = "juri",
label = "Seleccionar jurisdicción:",
choices = unique(resultado$juri_nombre))
),
column(6,
br(),
highchartOutput("grafico")),
column(3)
)
# definimos la lógica para elaborar el gráfico de pirámides a partir de la información ingresada en la ui
server <- function(input, output, session) {
output$grafico = renderHighchart({
datos_grafico = resultado[
resultado$ano==input$ano &
resultado$sexo_codigo!="0" &
resultado$juri_nombre==input$juri,]
highchart() %>%
hc_chart(type = "bar") %>%
hc_title(text = paste("Pirámide de población", "-", input$juri, "-", input$ano)) %>%
hc_xAxis(categories = rev(unique(datos_grafico$edad))) %>%
hc_yAxis(title = list(text = "Población"),
labels = list(formatter = JS(
"function() {
return Math.abs(this.value);
}"
)),
max = max(datos_grafico$poblacion)*1.1,
min = max(datos_grafico$poblacion)*1.1*-1) %>%
hc_plotOptions(series = list(stacking = "normal",
groupPadding = 0,
pointPadding = 0,
borderWidth = .1)) %>%
hc_add_series(name = "Varones", data = rev(datos_grafico$poblacion[datos_grafico$sexo_codigo=="1"])*-1, color = "#d8b365") %>%
hc_add_series(name = "Mujeres", data = rev(datos_grafico$poblacion[datos_grafico$sexo_codigo=="2"]), color = "#5ab4ac") %>%
hc_legend(align = "right", verticalAlign = "top", reversed = TRUE) %>%
hc_tooltip(formatter = JS("function () {
if (this.series.name === 'Varones') {
return `<b>${this.series.name}</b></br>${this.y*-1}`
} else if (this.series.name === 'Mujeres') {
return `<b>${this.series.name}</b></br>${this.y}`}}")) %>%
hc_exporting(enabled = TRUE)
})
}
# mostramos la aplicación en el servidor local
shinyApp(ui, server)
Los formatos en los cuales se puede acceder a la información sobre proyecciones de población de Argentina resultan poco amigables para los procesamientos con herramientas informáticas. Este documento muestra una metodología sencilla para dar a esos datos un formato acorde a la ciencia de datos que aumenta las posibilidades de uso de esta información en casos que requieren procesamientos complejos (series temporales, cálculo de indicadores en lote, desarrollo de visualizaciones, etc.). El software estadístico R (R Core Team 2019) se presenta como una alternativa eficiente para esta tarea.