Bienvenidas y bienvenidos al taller Demografía del parentesco con énfasis en estimaciones de duelo de la Escuela ALAP 2026. Este sitio reúne todo el material del curso: una pestaña por módulo. Los módulos teóricos se acompañan de presentaciones en vivo; los módulos de laboratorio contienen el código en R que ejecutaremos en conjunto.
Todo el material del curso (código y datos) está disponible en el repositorio de GitHub: https://github.com/alburezg/ALAP26_duelo.
A cargo de Diego Alburez-Gutierrez.
Este primer módulo combina una introducción teórica (presentación en vivo) con un laboratorio de preparación técnica. Al terminar, cada participante tendrá su entorno de R listo y habrá localizado todos los datos que usaremos a lo largo del taller.
El taller se desarrolla en dos días (24 y 25 de agosto de 2026, San José, Costa Rica). Este es el programa tentativo; los horarios pueden ajustarse durante las sesiones.
Lunes 24 de agosto
| Horario | Sesión |
|---|---|
| 8:30–9:00 | Bienvenida · Presentación cultural |
| 9:00–10:30 | Módulo 1 · Introducción a la demografía del parentesco + preparación técnica |
| 10:30–11:00 | Merienda |
| 11:00–12:00 | Módulo 2 · Estimación de parentesco con DemoKin |
| 12:00–13:00 | Almuerzo |
| 13:00–15:00 | Módulo 3 · Demografía del duelo: el conflicto colombiano |
| 15:00–15:30 | Pausa para café |
| 15:30–16:30 | Módulo 4 · Ejercicios: replicar el análisis para otro país |
| 17:00 | Salida del bus · Regreso al alojamiento |
Martes 25 de agosto
| Horario | Sesión |
|---|---|
| 8:30–10:00 | Módulo 5 · Microsimulación con SOCSIM |
| 10:00–10:30 | Merienda |
| 10:30–12:00 | Módulo 6 · Pérdida de parientes y conclusiones |
| 12:00–12:30 | Cierre de las Escuelas ALAP · Comparsa |
| 12:30 | Almuerzo |
| 15:00 | Salida del bus · Regreso a San José |
Las pestañas Soluciones y Datos son material de referencia para consultar en cualquier momento.
Contenido de este módulo:
La demografía formal del parentesco permite estimar, a partir de las tasas de fecundidad y mortalidad de una población, cuántos parientes vivos (y fallecidos) puede esperar tener una persona promedio a cada edad. El marco parte del trabajo seminal de Goodman et al. (1974) y fue reformulado en términos matriciales por Caswell (2019), con extensiones posteriores para tasas variables en el tiempo (Caswell and Song 2021) y modelos de dos sexos (Caswell 2022).
Todas estas variantes están implementadas en el paquete de R DemoKin. En la segunda parte del taller usaremos estas estimaciones para cuantificar el duelo a nivel poblacional: cuántas personas pierden a un familiar y cuántas quedan en duelo por cada defunción.
La parte conceptual de este módulo se desarrolla en la presentación. Aquí nos concentramos en dejar el entorno de trabajo listo.
Necesitamos R (≥ 4.2) y RStudio. Instalamos DemoKin directamente desde GitHub (la versión más reciente) y los demás paquetes desde CRAN. Ejecute este bloque una sola vez:
[1.1]
# Instalar DemoKin desde GitHub
install.packages("remotes")
remotes::install_github("IvanWilli/DemoKin")
# Paquete de microsimulación (Módulos 5-6)
install.packages("rsocsim")
# Paquetes de manejo y visualización de datos usados en todos los módulos:
# tidyverse, arrow -> manipulación de datos y lectura de .parquet
# scales -> formato de ejes y etiquetas (Módulo 3)
# ggh4x, data.table -> facetas y manejo de datos de la microsimulación (5-6)
# future -> rsocsim lo usa para correr la simulación en segundo
# plano (Módulo 5). Hay que instalarlo aparte: rsocsim
# lo declara como sugerido, no como dependencia.
install.packages(c("tidyverse", "arrow", "scales", "ggh4x", "data.table", "future"))rsocsim tiene una simulación sencilla incorporada que
puedes ejecutar para ver si el paquete se ha instalado correctamente.
Por ahora, vamos a ejecutar el código sin centrarnos en los detalles
técnicos:
[1.2]
library(rsocsim)
# basedir mantiene la prueba dentro del proyecto (socsim/); si se omite,
# rsocsim escribe en el directorio temporal de la sesión de R.
folder = rsocsim::create_simulation_folder(
basedir = file.path(getwd(), "socsim"),
simdir = "check_instalacion"
)
supfile = rsocsim::create_sup_file(folder)
seed = 300
rsocsim::socsim(folder, supfile, seed)## [1] 1
Con esto quedan instalados todos los paquetes que
usaremos a lo largo del taller. Cada módulo carga con
library() únicamente los que necesita.
Cargamos los paquetes que usaremos en los laboratorios:
[1.3]
## Versión de DemoKin: 1.0.3
Los modelos de parentesco necesitan tres insumos por edad, sexo y
año: supervivencia, fecundidad y
población. Preparamos estos insumos a partir del World Population Prospects
2024 de las Naciones Unidas para 20 países de América Latina y el
Caribe (1950–2023) y los guardamos en el folder
data/wpp_latam_1950_2023, que contiene un archivo
.parquet por país.
## # A tibble: 20 × 2
## iso3 country
## <chr> <chr>
## 1 ARG Argentina
## 2 BOL Bolivia
## 3 BRA Brasil
## 4 CHL Chile
## 5 COL Colombia
## 6 ECU Ecuador
## 7 PRY Paraguay
## 8 PER Perú
## 9 URY Uruguay
## 10 VEN Venezuela
## 11 CRI Costa Rica
## 12 SLV El Salvador
## 13 GTM Guatemala
## 14 HND Honduras
## 15 MEX México
## 16 NIC Nicaragua
## 17 PAN Panamá
## 18 CUB Cuba
## 19 DOM República Dominicana
## 20 HTI Haití
Cada país es un archivo wpp_<ISO3>.parquet. Veamos
el de Colombia:
## [1] 14948 10
## # A tibble: 6 × 10
## iso3 country year sex age px qx mx fx pop
## <chr> <chr> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 COL Colombia 1950 f 0 0.881 0.119 0.129 0 237964
## 2 COL Colombia 1950 f 1 0.969 0.0310 0.0315 0 211187
## 3 COL Colombia 1950 f 2 0.979 0.0211 0.0214 0 200886
## 4 COL Colombia 1950 f 3 0.985 0.0146 0.0147 0 191607
## 5 COL Colombia 1950 f 4 0.990 0.0104 0.0104 0 183025
## 6 COL Colombia 1950 f 5 0.993 0.00745 0.00748 0 175117
La tabla está en formato tidy: una fila por
combinación de year, sex
("f"/"m") y age (0–100), con las
columnas:
px — probabilidad de sobrevivir de la edad x a
x+1;qx, mx — probabilidad y tasa de
mortalidad;fx — tasa de fecundidad específica por edad (mujeres; 0
en hombres);pop — población al 1 de enero.Para el Módulo 3 reproduciremos las estimaciones de duelo del
artículo sobre el conflicto armado colombiano (Acosta et al. 2026). Los
datos están en data/colombia/:
[1.7]
## [1] 6868 4
## # A tibble: 6 × 4
## year sex age dx
## <dbl> <chr> <int> <dbl>
## 1 1985 f 0 33.3
## 2 1985 f 1 17.9
## 3 1985 f 2 9.81
## 4 1985 f 3 5.67
## 5 1985 f 4 3.54
## 6 1985 f 5 2.47
[1.9]
## [1] 618105.1
Solo homicidios. El artículo original también estima el duelo por desapariciones forzadas. En este taller, por simplicidad, consideraremos únicamente los homicidios.
Con el entorno listo y los datos localizados, en el Módulo 2 aprenderemos a generar estimaciones de parentesco con DemoKin.
A cargo de Diego Alburez-Gutierrez.
En este laboratorio aprenderemos a estimar las redes de parentesco de una persona promedio (a quien llamaremos Focal) usando el paquete DemoKin. Nos concentraremos en el modelo más completo que usaremos en el taller: el modelo de dos sexos y variable en el tiempo, con salida por período. Este es exactamente el modelo que sustenta las estimaciones de duelo del Módulo 3.
Contenido de este módulo:
kin2sex()La idea central de los modelos matriciales de parentesco (Caswell 2019) es una recurrencia por edad de Focal: el número esperado de parientes de un tipo dado que Focal tiene a la edad se obtiene proyectando a los parientes que tenía a la edad (que sobreviven y envejecen un año) y sumando los nuevos parientes que aparecen en el intervalo:
Aquí es un vector, no un número: es la distribución por edad de un tipo de pariente (cuántas hijas de 0 años, de 1 año, …, tiene Focal cuando ella tiene la edad ). La ecuación tiene tres ingredientes:
El subsidio : de dónde salen los parientes nuevos. El término solo hace envejecer a los parientes que ya había; por sí solo, la red nunca crecería. El subsidio es el flujo de parientes nuevos, y es lo que acopla unos tipos de pariente con otros. Casi siempre esos parientes nuevos llegan por un nacimiento, así que entran en la clase de edad 0; la clave es quién los tiene. El subsidio de un tipo de pariente se calcula a partir de la abundancia de otro pariente. Por ejemplo:
Este encadenamiento —un pariente “produce” a otro— es justamente lo que resume el diagrama de parentesco de la sección 2.2.
La condición inicial : los parientes al nacer Focal. La recurrencia necesita un punto de partida a la edad 0. Para los descendientes es trivial: al nacer, Focal todavía no tiene hijas ni nietas, así que . Para los ascendientes, en cambio, la condición inicial la fija la estructura de la población. El caso fundamental es la madre: la distribución por edad de la madre en el instante en que nace Focal es la distribución de las edades a la maternidad, es decir, las mujeres en edad fértil ponderadas por su fecundidad y su número . A partir de la madre se derivan, “subiendo” una generación, la abuela, las tías y los hermanos que Focal ya tiene al nacer.
Las condiciones de frontera: el modelo variable en el tiempo. Hasta aquí las tasas eran fijas. En el modelo variable en el tiempo las matrices llevan un índice de año calendario (, ): cuando Focal pasa de la edad a , el calendario también avanza un año, de modo que una cohorte recorre una diagonal en el diagrama de Lexis, atravesando las tasas de años sucesivos. Esto obliga a fijar una condición de frontera temporal: qué tasas regían antes del primer año con datos. DemoKin supone que la población era estable con las tasas de ese primer año. Por eso importa dónde arranca la serie: empezar en 1950 en lugar de 1900 (como hace el artículo del Módulo 3) altera un poco las estimaciones de los parientes más lejanos, cuyas trayectorias comienzan mucho antes del nacimiento de Focal.
Este taller combina dos extensiones del modelo básico:
Período vs. cohorte. Con tasas variables en el tiempo hay dos formas de mirar el parentesco:
- Período: en un año calendario dado (p. ej., 2018), ¿cómo es la red de parientes de las personas de cada edad? (mirada transversal).
- Cohorte: para las personas nacidas en un año dado, ¿cómo evoluciona su red a lo largo de la vida? (mirada longitudinal).
En el Módulo 3 necesitamos la mirada por período (
output_period), porque estimamos el duelo de toda la población en cada año.
El modelo variable en el tiempo aprovecha justamente la estructura Edad-Periodo-Cohorte (APC): una persona y sus parientes atraviesan tasas distintas a lo largo de su vida, siguiendo una diagonal en el diagrama de Lexis (edad × año). Período y cohorte son dos formas de recortar ese plano:
DemoKin identifica cada tipo de pariente con un código corto. La forma más clara de ver cómo se relacionan entre sí —y qué código le corresponde a cada uno— es el diagrama de parentesco de Keyfitz (Keyfitz and Caswell 2005; Caswell 2019): una red centrada en Focal en la que cada nodo es un tipo de pariente y cada arista une a un pariente con el que lo “produce” (la relación de subsidio de la sección anterior). El diagrama siguiente muestra el número esperado de parientes mujeres de una Focal colombiana de 30 años, con el código de DemoKin y el conteo en cada nodo:
La equivalencia entre los códigos de DemoKin y los de Caswell (2019), junto con las
etiquetas legibles, está en la tabla demokin_codes del
paquete. Nótese que los códigos distinguen, por ejemplo, hermanas
mayores (os) de menores (ys), porque el modelo
las genera de forma distinta (véase el diagrama); cuando no interesa esa
distinción, DemoKin ofrece códigos “colapsados” como s
(hermanas/os), a (tías/os), c (primas/os) y
n (sobrinas/os):
## DemoKin Caswell Labels_female Labels_male
## 1 coa t Cousins from older aunts Cousins from older uncles
## 2 cya v Cousins from younger aunts Cousins from younger uncles
## 3 c <NA> Cousins Cousins
## 4 d a Daughters Sons
## 5 gd b Grand-daughters Grand-sons
## 6 ggd c Great-grand-daughters Great-grand-sons
## 7 ggm h Great-grandmothers Great-grandfathers
## 8 gm g Grandmothers Grandfathers
## 9 m d Mother Father
## 10 nos p Nieces from older sisters Nephews from older brothers
## 11 nys q Nieces from younger sisters Nephews from younger brothers
## 12 n <NA> Nieces Nephews
## 13 oa r Aunts older than mother Uncles older than fathers
## 14 ya s Aunts younger than mother Uncles younger than father
## 15 a <NA> Aunts Uncles
## 16 os m Older sisters Older brothers
## 17 ys n Younger sisters Younger brothers
## 18 s <NA> Sisters Brothers
## Labels_2sex
## 1 Cousins from older aunts/uncles
## 2 Cousins from younger aunts/uncles
## 3 Cousins
## 4 Children
## 5 Grand-children
## 6 Great-grand-children
## 7 Great-grandparents
## 8 Grandparents
## 9 Parents
## 10 Niblings from older siblings
## 11 Niblings from younger siblings
## 12 Niblings
## 13 Aunts/Uncles older than parents
## 14 Aunts/Uncles younger than parents
## 15 Aunts/Uncles
## 16 Older siblings
## 17 Younger siblings
## 18 Siblings
Al ajustar un modelo, elegimos los parientes de interés pasando estos
códigos al argumento output_kin (por ejemplo,
c("d", "m", "gm") para hijas/os, madres/padres y
abuelas/os). En este módulo trabajaremos con seis tipos: hijas/os
(d), madres/padres (m), abuelas/os
(gm), hermanas/os (s), primas/os
(c) y tías/os (a).
kin2sex() necesita seis insumos, cada uno como una
matriz con las edades en las filas
(0–100) y los años en las columnas:
| Insumo | Significado |
|---|---|
pf, pm |
probabilidades de supervivencia (mujeres, hombres) |
ff, fm |
tasas de fecundidad por edad (mujeres, hombres) |
nf, nm |
población por edad (mujeres, hombres) |
Cargamos los datos del WPP para Colombia que preparamos en el Módulo 1:
## # A tibble: 6 × 10
## iso3 country year sex age px qx mx fx pop
## <chr> <chr> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 COL Colombia 1950 f 0 0.881 0.119 0.129 0 237964
## 2 COL Colombia 1950 f 1 0.969 0.0310 0.0315 0 211187
## 3 COL Colombia 1950 f 2 0.979 0.0211 0.0214 0 200886
## 4 COL Colombia 1950 f 3 0.985 0.0146 0.0147 0 191607
## 5 COL Colombia 1950 f 4 0.990 0.0104 0.0104 0 183025
## 6 COL Colombia 1950 f 5 0.993 0.00745 0.00748 0 175117
Explorar los insumos. Antes de correr el modelo conviene mirar las tasas que lo alimentan. Empecemos por la mortalidad: la probabilidad de muerte por edad, con una línea por año (escala logarítmica). Se aprecia la caída de la mortalidad a lo largo del tiempo, sobre todo en la infancia:
[2.4]
La fecundidad por edad, con una línea por año. La curva se desplaza y baja: la fecundidad cae fuertemente desde los años 1960, un hecho central para el parentesco en América Latina:
[2.5]
También podemos ver las tasas como superficies de Lexis (edad × año), que es la forma natural de pensar insumos que varían por edad, período y cohorte (APC):
[2.6]
En esta superficie, una cohorte de Focales avanza en diagonal (envejece un año por cada año calendario), atravesando las tasas cambiantes que vimos en la sección 2.1.
Los datos están en formato tidy (una fila por edad-sexo-año). La siguiente función auxiliar los convierte al formato matriz (edad × año) que espera DemoKin:
[2.7]
reshape_wpp <- function(dat, variable, which_sex) {
dat %>%
filter(sex == which_sex) %>%
select(age, year, value = all_of(variable)) %>%
arrange(age, year) %>%
pivot_wider(names_from = year, values_from = value) %>%
select(-age) %>%
as.matrix()
}
# Supervivencia por sexo
pf <- reshape_wpp(col, "px", "f")
pm <- reshape_wpp(col, "px", "m")
# Fecundidad femenina
ff <- reshape_wpp(col, "fx", "f")
# Población por sexo
nf <- reshape_wpp(col, "pop", "f")
nm <- reshape_wpp(col, "pop", "m")
dim(pf) # 101 edades x 74 años## [1] 101 74
## 1950 1951 1952 1953 1954
## [1,] 0.8811348 0.8839967 0.8867910 0.8895860 0.8923623
## [2,] 0.9689702 0.9704595 0.9716280 0.9728043 0.9739992
## [3,] 0.9788570 0.9798508 0.9807750 0.9816684 0.9825186
## [4,] 0.9853639 0.9860392 0.9867598 0.9874350 0.9880403
Fecundidad masculina. El WPP solo publica fecundidad
femenina. Adoptamos el supuesto androgino: la
fecundidad masculina es igual a la femenina (fm = ff). Es
una simplificación razonable para fines didácticos; existen métodos para
desplazar la fecundidad masculina según la edad media a la paternidad,
pero no los usaremos aquí.
kin2sex()Ejecutamos el modelo de dos sexos variable en el tiempo pidiendo la
salida por período para todos los años
1985–2018 (el rango que necesitaremos en el Módulo 3). Con
output_kin elegimos los tipos de pariente de interés:
hijas/os (d), madres/padres (m), hermanas/os
(s), abuelas/os (gm), nietas/os
(gd), tías/os (a), sobrinas/os
(n) y primas/os (c).
Un solo modelo para Colombia. Correr
kin2sex()es el paso costoso (tarda varios minutos); lo que tarda es ajustar el modelo, no el número de años que se piden en la salida. Por eso lo corremos una sola vez aquí, para todos los años, guardamos el resultado en un archivo y lo reutilizamos en el Módulo 3 (así no repetimos el cálculo). Dos atajos más para agilizar el taller:
- Corremos el modelo solo para Focal mujer y copiamos el resultado para Focal hombre. Normalmente correríamos
kin2sex()de nuevo consex_focal = "m", pero la red de parientes es muy parecida entre Focales de uno u otro sexo y así ahorramos la mitad del tiempo.- Guardamos el resultado en un único archivo (
col_kin_1985_2018.parquet) y, si ya existe, simplemente lo leemos (una “caché”). Así cada módulo se puede ejecutar por separado sin volver a ajustar el modelo.
Antes de correrlo, conviene entender cada argumento
de kin2sex():
pf, pm, ff, fm,
nf, nm: los seis insumos matriciales (edad ×
año) que preparamos arriba —supervivencia, fecundidad y población de
mujeres (*f) y hombres (*m).time_invariant = FALSE: usa tasas variables en
el tiempo (una matriz por año), no un solo año fijo. Es lo que
activa el modelo de la sección 2.1.output_period = 1985:2018: pide la salida por
período para esos años (la mirada transversal que necesita el
Módulo 3).output_kin: los tipos de pariente de interés, con los
códigos de la sección 2.2.sex_focal = "f": el sexo de Focal (aquí, mujer).birth_female = 0.5: la proporción de nacimientos que
son niñas (razón de sexo al nacer); DemoKin la usa para repartir los
nacimientos entre hijas e hijos.El código que ajusta el modelo y guarda el archivo es este:
[2.10]
# Correr el modelo UNA vez, para Focal MUJER y todos los años (paso costoso).
kin_f <- kin2sex(
pf = pf, pm = pm, ff = ff, fm = fm, nf = nf, nm = nm,
time_invariant = FALSE,
output_period = 1985:2018,
output_kin = c("d", "m", "s", "gm", "gd", "a", "n", "c"),
sex_focal = "f",
birth_female = 0.5
)
# Reutilizar (copiar) el resultado para Focal HOMBRE en vez de correr un
# segundo modelo. Guardamos ambos sexos en un solo objeto y en un solo archivo.
kin <- bind_rows(
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "f"),
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "m")
) %>%
transmute(
year = as.integer(year), sex_focal,
age_focal = as.integer(age_focal), kin, sex_kin,
age_kin = as.integer(age_kin), living
) %>%
filter(living >= 1e-6) # descartar celdas prácticamente vacías (archivo compacto)
write_parquet(kin, "data/colombia/col_kin_1985_2018.parquet")El objeto kin tiene una fila por año, sexo/edad de Focal
y sexo/edad del pariente, con el número esperado de parientes vivos
(living). La columna sex_kin
("f"/"m") distingue el sexo del pariente:
kin = "d", sex_kin = "m" son los
hijos de Focal; kin = "m",
sex_kin = "m" es el padre, etc.
## [1] 4976986 7
## # A tibble: 6 × 7
## year sex_focal age_focal kin sex_kin age_kin living
## <int> <chr> <int> <chr> <chr> <int> <dbl>
## 1 1985 f 0 a f 0 0.0130
## 2 1986 f 0 a f 0 0.0123
## 3 1987 f 0 a f 0 0.0117
## 4 1988 f 0 a f 0 0.0114
## 5 1989 f 0 a f 0 0.0111
## 6 1990 f 0 a f 0 0.0109
Para poner los resultados en español definimos una pequeña función
que traduce los códigos de pariente a etiquetas legibles (equivale a
rename_kin() de DemoKin, que las devuelve en inglés):
[2.13]
etiquetas_kin <- c(
d = "Hijas e hijos", m = "Madres y padres", s = "Hermanas y hermanos",
gm = "Abuelas y abuelos", gd = "Nietas y nietos", a = "Tías y tíos",
n = "Sobrinas y sobrinos", c = "Primas y primos"
)
renombrar_kin <- function(df) {
df %>% mutate(kin_label = factor(etiquetas_kin[kin], levels = etiquetas_kin))
}Graficamos cuántos parientes de cada tipo y sexo tiene una mujer
colombiana promedio a cada edad, según las tasas de
2018. Sumamos living sobre la edad del
pariente para obtener el total (count_living) por tipo y
sexo del pariente:
[2.14]
kin %>%
filter(sex_focal == "f", year == 2018) %>%
summarise(count_living = sum(living), .by = c(age_focal, kin, sex_kin)) %>%
renombrar_kin() %>%
mutate(sexo = if_else(sex_kin == "f", "Mujer", "Hombre")) %>%
ggplot(aes(x = age_focal, y = count_living, colour = sexo)) +
geom_line(linewidth = 1) +
facet_wrap(~kin_label, scales = "free_y") +
labs(
title = "Parientes vivos de una mujer colombiana (período 2018)",
x = "Edad de Focal",
y = "Número esperado de parientes vivos",
colour = "Sexo del pariente"
) +
theme_bw() +
theme(legend.position = "bottom")Podemos verlo también como composición de la familia a lo largo de la vida. Aquí sumamos los parientes de ambos sexos para obtener el total por tipo de pariente a cada edad de Focal:
[2.15]
kin %>%
filter(sex_focal == "f", year == 2018) %>%
summarise(count_living = sum(living), .by = c(age_focal, kin)) %>%
renombrar_kin() %>%
ggplot(aes(x = age_focal, y = count_living, fill = kin_label)) +
geom_area(colour = "black", linewidth = 0.2, alpha = 0.85) +
labs(
title = "Composición de la red de parientes vivos (período 2018)",
x = "Edad de Focal", y = "Número esperado de parientes vivos",
fill = "Tipo de pariente"
) +
theme_bw()¿Qué vemos aquí? La red no tiene un tamaño fijo: crece y se recompone con la edad de Focal. En la juventud predominan los parientes ascendientes (madres y padres, abuelas y abuelos) y los hermanos; hacia la adultez esos ascendientes van desapareciendo y ganan peso los descendientes (hijas e hijos, y más tarde nietas y nietos). Los parientes extendidos —tías/os y primas/os— son los más numerosos en casi todas las edades: es justamente esa abundancia la que, en el Módulo 3, amplificará el efecto de cada muerte sobre el duelo.
El objeto kin guarda la distribución por
edad de cada tipo de pariente, no solo su total. Comparemos la
distribución por edad de cuatro tipos de pariente de una Focal de 60
años en 2018 —hijas/os, hermanas/os, tías/os y primas/os—, cada uno en
su propio panel (con escala vertical libre) y separado por sexo del
pariente:
[2.16]
kin %>%
filter(
sex_focal == "f", year == 2018, age_focal == 60,
kin %in% c("d", "s", "a", "c")
) %>%
renombrar_kin() %>%
mutate(sexo = if_else(sex_kin == "f", "Mujer", "Hombre")) %>%
ggplot(aes(x = age_kin, y = living, colour = sexo)) +
geom_line(linewidth = 1) +
facet_wrap(~kin_label, scales = "free_y") +
labs(
title = "Distribución por edad de los parientes de una Focal de 60 años (2018)",
x = "Edad del pariente", y = "Número esperado", colour = "Sexo del pariente"
) +
theme_bw() +
theme(legend.position = "bottom")Cada distribución cuenta una historia demográfica distinta: las hijas/os se concentran en edades adultas jóvenes; las hermanas/os, alrededor de la edad de Focal; las tías/os en edades mayores (y son pocas, porque muchas ya han fallecido); y las primas/os se despliegan sobre un rango de edades amplio.
Con esto tenemos todo lo necesario para el Módulo 3, donde
combinaremos esta estructura de parentesco (kin) con las
muertes por el conflicto para estimar el duelo a nivel
poblacional.
A cargo de Enrique Acosta.
Este módulo combina una presentación teórica sobre la demografía del duelo con un laboratorio en el que reproduciremos, paso a paso, las cuatro medidas clave del artículo Weaponizing Kinship (Acosta et al. 2026):
La parte teórica (qué es el duelo a nivel poblacional, por qué importa, cómo se inserta en la tradición de Goodman-Keyfitz-Pullum y Caswell) se desarrolla en la presentación. Aquí nos concentramos en el cómputo.
Consideraremos únicamente los homicidios relacionados con el conflicto (el artículo también analiza las desapariciones forzadas).
Contenido de este módulo:
Usaremos tres insumos, todos en data/colombia/ (su
documentación completa está en la pestaña Datos):
[3.2]
dcol <- "data/colombia"
# (a) Datos demográficos colombianos: son los INSUMOS del modelo de parentesco
# (fecundidad fx, supervivencia px, población nx) más la población y el
# factor ax que usaremos para el duelo, por año, sexo y edad (1950-2018).
demo <- read_parquet(file.path(dcol, "col_demo_1950_2018.parquet"))
pop <- demo %>% select(year, sex, age, pop, ax)
# (b) Muertes por homicidio (conflicto), por año, sexo y edad de la víctima.
homicidios <- read_parquet(file.path(dcol, "col_homicidios_1985_2018.parquet"))
# (c) Proporción de inmigrantes (se excluyen del riesgo de duelo).
inmigrantes <- read_parquet(file.path(dcol, "col_inmigrantes_2018.parquet"))El cuarto insumo es la estructura de parentesco
(kin): cuántos parientes vivos de cada tipo tiene una
persona promedio, por año y por sexo/edad de Focal y del pariente. No es
un dato externo: es la salida del modelo que ya ajustamos en el
Módulo 2 (de dos sexos y variable en el tiempo), con salida por
período 1985–2018 y para Focal de ambos sexos.
Para no volver a correr el modelo —que es el paso costoso— lo reutilizamos: leemos el archivo que guardó el Módulo 2. Si por alguna razón no existiera, el mismo bloque lo regenera, con los datos del WPP para Colombia y los mismos atajos del Módulo 2 (se corre para Focal mujer y se copia para Focal hombre):
Nota sobre los insumos del parentesco. La estructura de parentesco proviene del modelo del Módulo 2, ajustado con las tasas del WPP para Colombia (1950–2023). El artículo original usa tasas colombianas propias que arrancan en 1900. Como DemoKin supone una población estable antes del primer año (véase “condiciones de frontera”, Módulo 2) y las tasas no son idénticas, nuestras estimaciones de parentesco —sobre todo de parientes lejanos— son cercanas pero no idénticas a las publicadas.
[3.3]
archivo_kin <- file.path(dcol, "col_kin_1985_2018.parquet")
if (!file.exists(archivo_kin)) {
# Reconstruir el archivo (normalmente ya lo generó el Módulo 2).
col <- read_parquet("data/wpp_latam_1950_2023/wpp_COL.parquet")
# reshape_wpp(): toma los datos largos del WPP y devuelve UNA matriz con las
# edades en las filas y los años en las columnas (el formato que espera
# DemoKin). Argumentos: dat = los datos; variable = la columna que queremos
# ("px", "fx" o "pop"); which_sex = "f" o "m".
reshape_wpp <- function(dat, variable, which_sex) {
dat %>%
filter(sex == which_sex) %>%
select(age, year, value = all_of(variable)) %>%
arrange(age, year) %>%
pivot_wider(names_from = year, values_from = value) %>%
select(-age) %>%
as.matrix()
}
# Correr el modelo para Focal mujer y copiarlo para Focal hombre (eficiencia).
kin_f <- kin2sex(
pf = reshape_wpp(col, "px", "f"), pm = reshape_wpp(col, "px", "m"),
ff = reshape_wpp(col, "fx", "f"), fm = reshape_wpp(col, "fx", "f"),
nf = reshape_wpp(col, "pop", "f"), nm = reshape_wpp(col, "pop", "m"),
time_invariant = FALSE, output_period = 1985:2018,
output_kin = c("d", "m", "s", "gm", "gd", "a", "n", "c"),
sex_focal = "f", birth_female = 0.5
)
kin <- bind_rows(
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "f"),
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "m")
) %>%
transmute(
year = as.integer(year), sex_focal,
age_focal = as.integer(age_focal), kin, sex_kin,
age_kin = as.integer(age_kin), living
) %>%
filter(living >= 1e-6)
write_parquet(kin, archivo_kin)
}
kin <- read_parquet(archivo_kin)## [1] 4976986 7
## # A tibble: 6 × 7
## year sex_focal age_focal kin sex_kin age_kin living
## <int> <chr> <int> <chr> <chr> <int> <dbl>
## 1 1985 f 0 a f 0 0.0130
## 2 1986 f 0 a f 0 0.0123
## 3 1987 f 0 a f 0 0.0117
## 4 1988 f 0 a f 0 0.0114
## 5 1989 f 0 a f 0 0.0111
## 6 1990 f 0 a f 0 0.0109
Definimos etiquetas legibles y los dos grupos de parentesco que usa el artículo (lo que llamamos familia extendida es lo que el artículo llama distant/lejana):
| Grupo | Parientes que lo componen | Códigos |
|---|---|---|
| Familia cercana () | hijas/os, madres/padres, hermanas/os | d, m, s |
| Familia extendida () | abuelas/os, nietas/os, tías/os, sobrinas/os, primas/os | gm, gd, a, n,
c |
El siguiente diagrama sitúa a cada tipo de pariente respecto de Focal y lo colorea según pertenezca a la familia cercana (rosa) o extendida (azul). Estos son los mismos colores que usaremos en todas las figuras del módulo:
[3.6]
etiquetas_kin <- c(
d = "Hijas e hijos", m = "Madres y padres", s = "Hermanas y hermanos",
gm = "Abuelas y abuelos", gd = "Nietas y nietos", a = "Tías y tíos",
n = "Sobrinas y sobrinos", c = "Primas y primos"
)
parientes_cercanos <- c("d", "m", "s")
parientes_extendidos <- c("gm", "gd", "a", "n", "c")El objetivo es estimar cuántas personas han perdido al menos un pariente de un tipo dado a causa del conflicto. La lógica combina tres ingredientes: la estructura de parentesco (cuántos parientes vivos hay), las muertes por homicidio (con qué probabilidad muere cada pariente) y la población expuesta.
Paso 1 — Probabilidad de muerte de un pariente. A partir del número de homicidios a la edad y sexo en el año , y de la población correspondiente, obtenemos una tasa de mortalidad por homicidio y la convertimos en probabilidad con la relación estándar de tabla de vida:
Paso 2 — Probabilidad de NO perder ningún pariente (anual). Para una persona Focal de sexo y edad en el año , suponiendo independencia entre las muertes de sus parientes, la probabilidad de no perder ningún pariente del tipo es el producto, sobre todas las edades y sexos de los parientes, de la probabilidad de que cada uno sobreviva, elevada al número esperado de parientes en esa celda (Acosta et al. 2026):
La probabilidad de perder al menos un pariente del tipo ese año es su complemento, .
Paso 3 — Prevalencia acumulada. El duelo es un estado absorbente: una vez que se pierde un pariente, la persona queda en duelo para el resto de su vida. La probabilidad de no haber perdido ningún pariente del tipo a lo largo de la vida, hasta la edad en el año , es el producto de las probabilidades anuales a lo largo de la trayectoria de la cohorte :
La probabilidad acumulada de haber perdido al menos un pariente es .
Paso 4 — De probabilidades a personas. Multiplicamos por la población expuesta (excluyendo inmigrantes, que no estuvieron expuestos al conflicto):
Paso 5 — Multiplicador de duelo. El número total de personas en duelo por el pariente dividido entre el número total de homicidios:
Traducimos estos cinco pasos a código. Primero, la probabilidad de muerte por homicidio de un pariente en cada celda de edad-sexo-año (Paso 1). Seguimos el artículo acotando a un máximo de 0.4 para estabilizar estratos con pocos casos:
[3.7]
Ahora la probabilidad anual de no perder ningún pariente (Paso 2). Unimos la estructura de parentesco con estas probabilidades y aplicamos la fórmula del producto:
p0_anual <- kin %>%
left_join(qx_kin, by = c("year", "sex_kin", "age_kin")) %>%
mutate(p0x = (1 - qx_kin)^living) %>%
summarise(
p0 = prod(p0x, na.rm = TRUE),
.by = c(year, sex_focal, age_focal, kin)
)También calculamos la exposición de 2018 (población menos inmigrantes), que usaremos para convertir probabilidades en personas:
[3.8]
Además de los ocho tipos individuales, agrupamos los parientes en familia cercana () y familia extendida () (véase la tabla de arriba). La pregunta “¿perdió al menos un pariente del grupo?” requiere combinar las probabilidades de no perder a ninguno de sus miembros. Suponiendo independencia entre los tipos de pariente, la probabilidad de no perder a nadie de un grupo es el producto de las probabilidades acumuladas de sus miembros (Acosta et al. 2026):
Con estas dos probabilidades podemos clasificar a toda la población en cuatro categorías mutuamente excluyentes, según haya perdido o no parientes cercanos y/o extendidos:
La lógica booleana detrás de estas cuatro categorías se ve mejor con un diagrama de Venn: el círculo izquierdo son quienes han perdido al menos un pariente cercano (el complemento de ); el derecho, quienes han perdido al menos un pariente extendido (complemento de ). Su intersección es la categoría “cercana y extendida”; su unión (toda el área sombreada) es “cercana o extendida”, con probabilidad .
Las cuatro suman 1: cada persona cae en exactamente una. La probabilidad de haber perdido al menos un pariente cualquiera (cercana O extendida) es el complemento de “no en duelo”: . Multiplicando cada probabilidad por la población expuesta y sumando obtenemos el número de personas en cada categoría. Usaremos estas fórmulas en la prevalencia (Medida 3) y en la pirámide (Medida 4).
La incidencia anual responde a la pregunta: ¿cuántas personas pierden al menos un pariente de un tipo dado en un año determinado? Es una medida de flujo: cuenta los duelos nuevos que ocurren cada año, y por eso es la manera más directa de ver cómo la violencia del conflicto se traduce, año a año, en pérdidas familiares.
El punto de partida son las muertes por homicidio que ya cargamos. Conviene mirarlas primero, porque son el motor de todo lo demás: los años con más homicidios serán los años con más personas en duelo.
[3.9]
Para pasar de muertes a personas en duelo, tomamos la probabilidad anual de perder al menos un pariente, (calculada arriba), la multiplicamos por la población y sumamos sobre el sexo y la edad de Focal. Calculamos primero la incidencia por tipo individual de pariente (la reutilizaremos en los multiplicadores):
[3.10]
¿Por qué no apilar los tipos de pariente? Sería tentador apilar la incidencia de cada tipo de pariente en una sola barra, pero eso cuenta doble: una misma persona puede perder, el mismo año, un primo y una tía, y aparecería en ambas categorías. La incidencia de “al menos un pariente” no es aditiva. Por eso mostramos la incidencia por grupo de familia (cercana y extendida), combinando a los miembros del grupo con la fórmula de producto vista arriba.
[3.11]
# incidencia_familia_anual(): cuántas personas quedan en duelo CADA AÑO por un
# grupo de parientes. Argumentos: tipos = qué parientes forman el grupo
# (p. ej. c("d","m","s")); etiqueta = el nombre que llevará el grupo en el
# gráfico. Devuelve una fila por año, con las personas en duelo y la etiqueta.
# Los pasos son: (1) quedarse con esos parientes, (2) combinarlos con la
# fórmula de producto (la probabilidad de no perder a NINGUNO), (3) pegar la
# población de Focal, (4) pasar de probabilidad a personas, (5) sumar por año.
incidencia_familia_anual <- function(tipos, etiqueta) {
p0_anual %>%
filter(kin %in% tipos) %>%
summarise(p0 = prod(p0), .by = c(year, sex_focal, age_focal)) %>%
left_join(pop_focal, by = c("year", "sex_focal", "age_focal")) %>%
mutate(en_duelo = (1 - p0) * pop) %>%
summarise(en_duelo = sum(en_duelo), .by = year) %>%
mutate(grupo = etiqueta)
}
incidencia_familia <- bind_rows(
incidencia_familia_anual(parientes_cercanos, "Familia cercana"),
incidencia_familia_anual(parientes_extendidos, "Familia extendida")
)Graficamos la incidencia anual de cada grupo con barras agrupadas (como en el estudio sobre Gaza). Los picos coinciden con los años más violentos del conflicto (compárese con la gráfica de homicidios de arriba):
[3.12]
incidencia_familia %>%
ggplot(aes(x = year, y = en_duelo / 1000, fill = grupo)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
scale_fill_manual(values = c(
"Familia cercana" = "#b56576",
"Familia extendida" = "#355070"
)) +
scale_y_continuous(labels = scales::comma) +
labs(
title = "Incidencia anual del duelo por homicidio (Colombia, 1985-2018)",
x = "Año", y = "Personas en duelo (miles)", fill = "Grupo de familia"
) +
theme_bw()Interpretación. Cada año, el conflicto deja en duelo a decenas de miles de personas, muchas más a través de la familia extendida que de la cercana: aunque la probabilidad de perder a un primo específico es baja, cada persona tiene muchos parientes extendidos, de modo que la probabilidad de perder al menos uno es alta. Esta es la intuición central de la demografía del duelo: el tamaño de la red de parentesco amplifica el efecto de cada muerte.
El multiplicador de duelo resume, en un solo número, el alcance social de la violencia: ¿cuántas personas quedan en duelo por cada homicidio? Es el cociente entre las personas en duelo y el número de muertes:
Usamos la incidencia que acabamos de calcular como numerador (personas que quedan en duelo, sumadas sobre todo el período 1985–2018) y el total de homicidios como denominador. Es decir, es una medida de incidencia por muerte: cuántos duelos genera, en promedio, cada homicidio.
Para los grupos de familia (cercana, extendida, o cualquiera) combinamos primero las probabilidades anuales de los parientes del grupo (como en la sección de grupos de arriba) y luego calculamos la incidencia:
[3.13]
muertes_total <- sum(homicidios$dx) # total de homicidios 1985-2018
# incidencia_grupo(): igual que incidencia_familia_anual(), pero devuelve UN
# SOLO número: el total de personas en duelo de todo el período (1985-2018), sin
# separar por año. Es el numerador del multiplicador de duelo.
# Argumento: tipos = qué parientes forman el grupo.
incidencia_grupo <- function(tipos) {
p0_anual %>%
filter(kin %in% tipos) %>%
summarise(p0 = prod(p0), .by = c(year, sex_focal, age_focal)) %>%
left_join(pop_focal, by = c("year", "sex_focal", "age_focal")) %>%
mutate(en_duelo = (1 - p0) * pop) %>%
summarise(en_duelo = sum(en_duelo)) %>%
pull(en_duelo)
}
# por tipo de pariente
mult_kin <- incidencia %>%
summarise(personas = sum(en_duelo), .by = kin) %>%
mutate(kin = recode(kin, !!!etiquetas_kin))
# por grupo de familia
mult_grupos <- tibble(
kin = c("Familia cercana", "Familia extendida", "Cercana o extendida"),
personas = c(
incidencia_grupo(parientes_cercanos),
incidencia_grupo(parientes_extendidos),
incidencia_grupo(names(etiquetas_kin))
)
)
# orden de la tabla igual que en el artículo publicado
orden_tabla <- c(
"Hijas e hijos", "Madres y padres", "Hermanas y hermanos",
"Abuelas y abuelos", "Sobrinas y sobrinos", "Nietas y nietos",
"Primas y primos", "Tías y tíos",
"Familia cercana", "Familia extendida", "Cercana o extendida"
)
tabla_multiplicadores <- bind_rows(mult_kin, mult_grupos) %>%
mutate(
muertes = muertes_total,
multiplicador = round(personas / muertes, 1),
personas = round(personas),
kin = factor(kin, levels = orden_tabla)
) %>%
arrange(kin)| Pariente | Personas en duelo (incidencia) | Muertes (homicidios) | Multiplicador |
|---|---|---|---|
| Hijas e hijos | 828,606 | 618,105 | 1.3 |
| Madres y padres | 1,204,717 | 618,105 | 1.9 |
| Hermanas y hermanos | 2,049,100 | 618,105 | 3.3 |
| Abuelas y abuelos | 1,535,099 | 618,105 | 2.5 |
| Sobrinas y sobrinos | 3,682,503 | 618,105 | 6.0 |
| Nietas y nietos | 621,888 | 618,105 | 1.0 |
| Primas y primos | 17,932,037 | 618,105 | 29.0 |
| Tías y tíos | 5,187,009 | 618,105 | 8.4 |
| Familia cercana | 4,078,227 | 618,105 | 6.6 |
| Familia extendida | 28,744,258 | 618,105 | 46.5 |
| Cercana o extendida | 32,705,956 | 618,105 | 52.9 |
Interpretación. El multiplicador es mucho mayor para la familia extendida que para la cercana: cada homicidio deja en duelo a muchas más personas a través de primos, tíos y sobrinos que a través de hijos, padres y hermanos, simplemente porque hay más parientes extendidos. La tercera columna muestra el denominador común (el total de homicidios), para que quede claro que el multiplicador es personas en duelo por muerte.
Mientras que la incidencia es un flujo (duelos nuevos por año), la
prevalencia acumulada es un stock:
cuenta a las personas que han perdido al menos un pariente en
algún momento de su vida, hasta 2018. Como el duelo es un
estado que no se revierte, aplicamos el producto acumulado
(cumprod) de las probabilidades anuales a lo largo de la
trayectoria de cada cohorte
(),
y nos quedamos con el año 2018 (Paso 3):
p0_acum <- p0_anual %>%
mutate(cohorte = year - age_focal) %>%
group_by(sex_focal, cohorte, kin) %>%
arrange(age_focal, .by_group = TRUE) %>%
mutate(p0_acum = cumprod(p0)) %>%
ungroup() %>%
filter(year == 2018)Una función auxiliar calcula el número de personas que, hacia 2018, habían perdido al menos un pariente de un conjunto dado. Combina la probabilidad acumulada de los miembros del conjunto (producto de sus , como en la sección de grupos), la convierte en probabilidad de pérdida y la multiplica por la exposición:
[3.14]
# en_duelo_prev(): cuántas personas VIVAS en 2018 han perdido al menos un
# pariente de un grupo en algún momento. A diferencia de las funciones
# anteriores, parte de p0_acum (probabilidades ACUMULADAS a lo largo de la vida)
# y usa la exposición de 2018, no la población de cada año.
# Argumento: tipos = qué parientes forman el grupo. Devuelve un solo número.
en_duelo_prev <- function(tipos) {
p0_acum %>%
filter(kin %in% tipos) %>%
summarise(p0_acum = prod(p0_acum), .by = c(sex_focal, age_focal)) %>%
left_join(exposicion_2018, by = c("sex_focal", "age_focal")) %>%
mutate(b = (1 - p0_acum) * exposicion) %>%
summarise(personas = sum(b)) %>%
pull(personas)
}Estimamos la prevalencia por tipo individual de pariente y por grupo de familia (cercana, extendida, y cercana-o-extendida), en número de personas y como porcentaje de la población:
[3.15]
poblacion_2018 <- sum(exposicion_2018$exposicion)
prev_kin <- tibble(kin = names(etiquetas_kin)) %>%
mutate(
personas = map_dbl(kin, en_duelo_prev),
etiqueta = recode(kin, !!!etiquetas_kin),
grupo = if_else(kin %in% parientes_cercanos, "Cercana", "Extendida")
)
prev_grupos <- tibble(
etiqueta = c("Familia cercana", "Familia extendida", "Cercana o extendida"),
personas = c(
en_duelo_prev(parientes_cercanos),
en_duelo_prev(parientes_extendidos),
en_duelo_prev(names(etiquetas_kin))
),
grupo = "Grupo"
)
prevalencia <- bind_rows(prev_kin %>% select(etiqueta, personas, grupo), prev_grupos) %>%
mutate(
millones = personas / 1e6,
pct = 100 * personas / poblacion_2018
)Siguiendo el artículo, mostramos los parientes individuales en un panel y los grupos de familia en otro, en el mismo orden que la publicación:
[3.16]
orden_individual <- c(
"Hijas e hijos", "Madres y padres", "Hermanas y hermanos",
"Abuelas y abuelos", "Sobrinas y sobrinos", "Nietas y nietos",
"Primas y primos", "Tías y tíos"
)
orden_grupo <- c("Familia cercana", "Familia extendida", "Cercana o extendida")
prevalencia %>%
mutate(
panel = if_else(grupo == "Grupo", "Grupos de familia", "Parientes individuales"),
panel = factor(panel, levels = c("Parientes individuales", "Grupos de familia")),
etiqueta = factor(etiqueta, levels = rev(c(orden_individual, orden_grupo)))
) %>%
ggplot(aes(x = etiqueta, y = millones, fill = grupo)) +
geom_col() +
geom_text(aes(label = sprintf("%.1f M (%.0f%%)", millones, pct)),
hjust = -0.1, size = 3
) +
coord_flip() +
facet_grid(panel ~ ., scales = "free_y", space = "free_y") +
scale_fill_manual(values = c(
"Cercana" = "#b56576", "Extendida" = "#355070",
"Grupo" = "grey55"
)) +
scale_y_continuous(expand = expansion(mult = c(0, 0.25))) +
labs(
title = "Prevalencia acumulada del duelo por homicidio en 2018",
subtitle = "Personas que han perdido al menos un pariente del tipo/grupo indicado",
x = NULL, y = "Personas en duelo (millones)", fill = "Grupo"
) +
theme_bw()Resultado clave. Hacia 2018, cerca de 19.1 millones de personas (40% de la población) habían perdido al menos un pariente por homicidios del conflicto. La familia extendida domina: por ejemplo, unos 12.4 millones perdieron al menos un primo o prima. Estas cifras son directamente comparables con la Figura 4 del artículo (que reporta ~11.8 millones para primos considerando homicidios y desapariciones). Nuestra cifra difiere un poco por dos motivos que operan en sentidos opuestos: consideramos solo homicidios (lo que la reduce) y el modelo inicia en 1950 (lo que la aumenta; véase la nota del punto 3.1). El resultado global —cerca del 40% de la población en duelo— coincide con el titular del artículo.
La prevalencia total esconde un patrón por edad muy marcado. Para verlo, representamos toda la población de 2018 en una pirámide poblacional, coloreando cada barra según las cuatro categorías mutuamente excluyentes que definimos antes: no en duelo, solo familia cercana, solo familia extendida, o ambas. Es, en el fondo, la distribución por edad y sexo de la prevalencia.
Usamos directamente las fórmulas de producto (no hace falta inclusión-exclusión): para cada edad y sexo calculamos y y de ahí las cuatro categorías, que por construcción suman la población total:
[3.17]
pop_2018 <- pop %>%
filter(year == 2018) %>%
transmute(sex_focal = sex, age_focal = age, pop)
# p0_grupo(): probabilidad acumulada de NO haber perdido a ningún pariente del
# grupo, por sexo y edad de Focal (combinando a los miembros con el producto).
# A diferencia de en_duelo_prev(), no multiplica por la población: devuelve la
# probabilidad, que es lo que necesitamos para la pirámide.
# Argumento: tipos = qué parientes forman el grupo.
p0_grupo <- function(tipos) {
p0_acum %>%
filter(kin %in% tipos) %>%
summarise(p0 = prod(p0_acum), .by = c(sex_focal, age_focal))
}
pC <- p0_grupo(parientes_cercanos) %>% rename(p0_C = p0)
pD <- p0_grupo(parientes_extendidos) %>% rename(p0_D = p0)
piramide <- pC %>%
left_join(pD, by = c("sex_focal", "age_focal")) %>%
left_join(pop_2018, by = c("sex_focal", "age_focal")) %>%
mutate(
`No en duelo` = p0_C * p0_D * pop,
`Solo familia cercana` = (1 - p0_C) * p0_D * pop,
`Solo familia extendida` = p0_C * (1 - p0_D) * pop,
`Familia cercana y extendida` = (1 - p0_C) * (1 - p0_D) * pop
) %>%
pivot_longer(
c(
`No en duelo`, `Solo familia cercana`,
`Solo familia extendida`, `Familia cercana y extendida`
),
names_to = "categoria", values_to = "personas"
) %>%
mutate(
categoria = factor(categoria, levels = c(
"No en duelo", "Solo familia extendida",
"Familia cercana y extendida", "Solo familia cercana"
)),
# mujeres a la izquierda (valores negativos), hombres a la derecha
personas = ifelse(sex_focal == "f", -personas, personas) / 1000
)[3.18]
colores <- c(
"No en duelo" = "grey75",
"Solo familia extendida" = "#355070",
"Familia cercana y extendida" = "#eaac8b",
"Solo familia cercana" = "#b56576"
)
piramide %>%
ggplot(aes(x = age_focal, y = personas, fill = categoria)) +
geom_col(width = 1) +
geom_hline(yintercept = 0, linewidth = 0.2) +
coord_flip() +
scale_fill_manual(values = colores) +
scale_y_continuous(labels = function(x) scales::comma(abs(x))) +
labs(
title = "Población de Colombia en 2018 según duelo por homicidio",
subtitle = "Mujeres a la izquierda, hombres a la derecha",
x = "Edad", y = "Población (miles)", fill = "Situación de duelo"
) +
theme_bw() +
theme(legend.position = "bottom")Interpretación. La pirámide muestra que el duelo por el conflicto está presente en todas las edades, pero se acumula con la edad: las personas mayores han estado expuestas al riesgo durante más años y por eso es más probable que hayan perdido a un familiar. La mayor parte del duelo ocurre a través de la familia extendida (azul), aunque una fracción importante ha perdido también parientes cercanos (naranja y rosa).
Nota sobre las cifras. Como pedimos, usamos la estimación central (número medio de homicidios) en lugar de las 1000 réplicas de la estimación por sistemas múltiples del artículo. Por ello los valores son muy parecidos, pero no idénticos, a los publicados en Acosta et al. (2026).
En este módulo partimos de la estructura de parentesco del Módulo 2 y las muertes por homicidio para reproducir las cuatro medidas clave del artículo: la incidencia anual (duelos nuevos por año), los multiplicadores (personas en duelo por muerte), la prevalencia acumulada a 2018 (el stock de personas en duelo) y su distribución por edad (la pirámide). El hilo conductor fue siempre el mismo: el tamaño de la red de parentesco —sobre todo la familia extendida— amplifica el efecto demográfico de cada muerte.
En el Módulo 4 te toca a ti: repetirás exactamente estos cuatro pasos para el país que elijas, reutilizando el código que acabamos de escribir.
A cargo de todo el equipo.
En este laboratorio pondrá en práctica todo lo aprendido. La tarea es elegir un país de América Latina o el Caribe y replicar las cuatro medidas del Módulo 3 (incidencia anual, multiplicadores, prevalencia acumulada y su distribución por edad), pero para la mortalidad violenta de ese país.
El flujo de trabajo es el mismo del Módulo 3, con dos cambios:
Datos de mortalidad por homicidio. Estas tasas por homicidio fueron calculadas a partir de estimaciones de la Global Burden of Disease 2023 para 20 países de la región.
Importante: este módulo arranca de cero. Todo el sitio se compila en una sola sesión de R, así que los objetos del Módulo 3 (que son de Colombia) siguen en la memoria cuando llegamos aquí. Si reutiliza uno de ellos sin darse cuenta, el código correrá sin error pero le dará resultados de Colombia. Para evitarlo, empezamos borrándolos: así, reutilizar uno por accidente da un error claro en vez de un resultado equivocado.
[4.2]
# Borramos los objetos de Colombia del Módulo 3. intersect() con ls() hace que
# el bloque funcione igual si corre este módulo por separado (no habrá nada que
# borrar y no dará error).
objetos_modulo_3 <- c(
"demo", "pop", "homicidios", "inmigrantes", "kin", "qx_kin",
"p0_anual", "p0_acum", "pop_focal", "exposicion_2018", "poblacion_2018",
"pop_2018", "muertes_total", "etiquetas_kin", "parientes_cercanos",
"parientes_extendidos", "incidencia_familia_anual", "incidencia_grupo",
"en_duelo_prev", "p0_grupo", "archivo_kin", "dcol"
)
rm(list = intersect(objetos_modulo_3, ls()))Contenido de este módulo:
Elegimos un país mediante su código ISO3 (véase
data/countries.csv). Aquí usamos Costa
Rica (CRI) como ejemplo —el mismo país que se
resuelve paso a paso en la pestaña Soluciones—; cambie
el código por el país que prefiera.
Las tres primeras líneas del bloque son los únicos parámetros del análisis: el país y el rango de años. Todo lo que sigue se calcula a partir de ellos, así que para cambiar de país o de año de referencia basta con editarlas (no hay años escritos «a mano» más abajo).
[4.3]
# --- Parámetros del análisis: lo único que hay que cambiar ---
iso <- "CRI" # <-- cambie aquí su país (código ISO3)
anio_inicial <- 2000 # primer año con datos de homicidio (GBD)
anio_final <- 2023 # año de referencia del análisis (último disponible)
# Datos del WPP para el país elegido (supervivencia px, fecundidad fx, población)
pais <- open_dataset("data/wpp_latam_1950_2023/", format = "parquet") |>
filter(iso3 == iso) |>
collect() |>
as_tibble()
dim(pais) # filas y columnas## [1] 14948 10
## # A tibble: 6 × 10
## iso3 country year sex age px qx mx fx pop
## <chr> <chr> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 CRI Costa Rica 1950 f 0 0.905 0.0948 0.101 0 18386
## 2 CRI Costa Rica 1950 f 1 0.987 0.0130 0.0131 0 16952
## 3 CRI Costa Rica 1950 f 2 0.990 0.00988 0.00993 0 15903
## 4 CRI Costa Rica 1950 f 3 0.992 0.00752 0.00755 0 15128
## 5 CRI Costa Rica 1950 f 4 0.994 0.00572 0.00574 0 14265
## 6 CRI Costa Rica 1950 f 5 0.996 0.00435 0.00436 0 13536
Con los datos del país preparamos la población de Focal (para la incidencia anual, que necesita todos los años) y la exposición en el año de referencia (para la prevalencia, que es una foto de un solo año):
[4.5]
# Población de Focal por año, sexo y edad: el denominador de la incidencia anual.
pop_focal <- pais %>% transmute(year, sex_focal = sex, age_focal = age, pop)
# Exposición en el año de referencia: la población que puede estar en duelo.
# (A diferencia del Módulo 3, aquí no descontamos inmigrantes: no tenemos ese
# dato para todos los países.)
exposicion <- pais %>%
filter(year == anio_final) %>%
transmute(sex_focal = sex, age_focal = age, exposicion = pop)
# Población total del país en el año de referencia.
poblacion <- sum(exposicion$exposicion)
# Etiquetas de los tipos de pariente y los dos grupos de familia.
etiquetas_kin <- c(
d = "Hijas e hijos", m = "Madres y padres", s = "Hermanas y hermanos",
gm = "Abuelas y abuelos", gd = "Nietas y nietos", a = "Tías y tíos",
n = "Sobrinas y sobrinos", c = "Primas y primos"
)
parientes_cercanos <- c("d", "m", "s")
parientes_extendidos <- c("gm", "gd", "a", "n", "c")
poblacion## [1] 5092976
Construimos las matrices (edad × año) que pide
kin2sex(). La función es la misma que vimos en el Módulo 2;
la definimos otra vez aquí para que este módulo funcione por sí
solo:
[4.6]
# reshape_wpp(): toma los datos largos del WPP y devuelve UNA matriz con las
# edades en las filas y los años en las columnas, que es el formato que espera
# DemoKin. Argumentos: dat = los datos del país; variable = la columna que
# queremos ("px", "fx" o "pop"); which_sex = "f" o "m".
reshape_wpp <- function(dat, variable, which_sex) {
dat %>%
filter(sex == which_sex) %>%
select(age, year, value = all_of(variable)) %>%
arrange(age, year) %>%
pivot_wider(names_from = year, values_from = value) %>%
select(-age) %>%
as.matrix()
}
pf <- reshape_wpp(pais, "px", "f")
pm <- reshape_wpp(pais, "px", "m")
ff <- reshape_wpp(pais, "fx", "f")
fm <- ff # supuesto androgino
nf <- reshape_wpp(pais, "pop", "f")
nm <- reshape_wpp(pais, "pop", "m")En el Módulo 3, la probabilidad de muerte de un pariente venía de las
muertes por homicidio del conflicto colombiano. Para el
ejercicio usamos un archivo de tasas de mortalidad por
homicidio por país, año, sexo y edad:
data/homicide_mortality_rates.csv (columnas
iso3, year, sex,
age, mx).
Leemos el archivo, lo filtramos para el país y los años elegidos, y
convertimos la tasa mx en probabilidad qx
(suponiendo ax = 0.5, es decir, que las muertes se reparten
de forma uniforme dentro del año):
[4.7]
## # A tibble: 6 × 4
## year sex_kin age_kin qx_kin
## <dbl> <chr> <dbl> <dbl>
## 1 2000 f 0 0.0000132
## 2 2000 f 1 0.00000638
## 3 2000 f 2 0.0000213
## 4 2000 f 3 0.0000409
## 5 2000 f 4 0.0000455
## 6 2000 f 5 0.0000344
Corremos el modelo de parentesco (como en los Módulos 2 y 3) para el
país elegido, con salida por período y para ambos sexos de Focal. Igual
que en el Módulo 2, lo corremos para Focal mujer y
copiamos el resultado para Focal hombre (por
eficiencia; normalmente correríamos kin2sex() de nuevo con
sex_focal = "m").
Atención: este paso tarda varios minutos. Por eso lo calculamos una sola vez, guardamos el resultado en
data/y lo reutilizamos en adelante (esto se llama caché). La primera vez que corra el bloque tendrá que esperar; después será instantáneo. Si cambia de país o de años, el nombre del archivo cambia también, así que el modelo se vuelve a correr solo cuando hace falta.
[4.8]
# El nombre del archivo incluye el país y los años: así, si cambia cualquiera de
# los parámetros, no se reutiliza por error un resultado viejo.
archivo_kin <- file.path(
"data", paste0("kin_", iso, "_", anio_inicial, "_", anio_final, ".parquet")
)
if (!file.exists(archivo_kin)) {
# Corremos el modelo para Focal MUJER y copiamos el resultado para Focal
# hombre. Es un atajo por eficiencia: normalmente correríamos kin2sex() otra
# vez con sex_focal = "m". Acotamos la salida con output_period a los años que
# tienen datos de homicidio, para no calcular años que no vamos a usar.
kin_f <- kin2sex(
pf = pf, pm = pm, ff = ff, fm = fm, nf = nf, nm = nm,
time_invariant = FALSE,
output_period = anio_inicial:anio_final,
output_kin = c("d", "m", "s", "gm", "gd", "a", "n", "c"),
sex_focal = "f",
birth_female = 0.5
)
kin <- bind_rows(
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "f"),
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "m")
) %>%
transmute(year, sex_focal, age_focal, kin, sex_kin, age_kin, living) %>%
filter(living >= 1e-6)
write_parquet(kin, archivo_kin)
}
kin <- read_parquet(archivo_kin)
dim(kin) # filas y columnas## [1] 3478964 7
## # A tibble: 6 × 7
## year sex_focal age_focal kin sex_kin age_kin living
## <int> <chr> <int> <chr> <chr> <int> <dbl>
## 1 2000 f 0 a f 0 0.00874
## 2 2001 f 0 a f 0 0.00854
## 3 2002 f 0 a f 0 0.00809
## 4 2003 f 0 a f 0 0.00766
## 5 2004 f 0 a f 0 0.00713
## 6 2005 f 0 a f 0 0.00666
Su tarea. Reproduzca para su país las cuatro medidas del Módulo 3:
Ya tiene todo lo que necesita. Los bloques anteriores dejaron listos estos objetos, todos referidos a su país:
| Objeto | Qué contiene |
|---|---|
kin |
Estructura de parentesco: parientes vivos por año, sexo y edad de Focal |
qx_kin |
Probabilidad de morir por homicidio, por año, sexo y edad del pariente |
pop_focal |
Población de Focal por año, sexo y edad (denominador de la incidencia) |
exposicion |
Población por sexo y edad en el año de referencia (para la prevalencia) |
poblacion |
Población total en el año de referencia (un solo número) |
anio_inicial, anio_final |
Rango de años del análisis |
etiquetas_kin |
Nombres legibles de los tipos de pariente |
parientes_cercanos,
parientes_extendidos |
Los dos grupos de familia |
Cuidado con los nombres. Puede reutilizar el código del Módulo 3, pero adapte los nombres de los objetos: el Módulo 3 usa
exposicion_2018,poblacion_2018ypop_2018, que aquí se llamanexposicionypoblacion(sin el año, porque el año es un parámetro). Ese código también usafilter(year == 2018); en su análisis debe serfilter(year == anio_final). Y recuerde que necesitará el total de muertes del período como denominador del multiplicador: en el Módulo 3 eramuertes_total, calculado a partir de conteos de muertes; aquí tendrá que obtenerlo de las tasasmxy la población.
Si se atasca, en la pestaña Soluciones (al final) encontrará el código completo y comentado del ejercicio, resuelto paso a paso para Costa Rica.
Preguntas para la discusión.
- ¿Cuántas personas quedan en duelo por cada homicidio en su país (el multiplicador de duelo)? ¿Cómo se compara con Colombia, y qué explica la diferencia: el tamaño de las redes de parentesco (herencia de la fecundidad pasada) o la intensidad de la violencia?
- ¿Qué tipo de pariente aporta más al duelo total por homicidio? ¿Por qué pesa tanto la familia extendida frente a la cercana?
- Los homicidios suelen concentrarse en hombres jóvenes. ¿Cómo se refleja ese patrón por edad y sexo de las víctimas en quiénes quedan en duelo y a través de qué parientes (madres, hermanas, hijas, parejas…)?
- Dado el descenso de la fecundidad en su país, ¿esperaría que el multiplicador de duelo por homicidio aumente o disminuya en las próximas décadas? ¿Por qué?
A cargo de Liliana Calderón. (Adaptado del taller de la Escuela ALAP 2024.)
Hasta ahora estimamos el parentesco con modelos
analíticos (DemoKin). En este módulo lo hacemos con
microsimulación: en lugar de calcular valores esperados
con matrices, simulamos individuo por individuo una población completa
—con sus nacimientos, muertes y matrimonios— y luego reconstruimos las
redes de parentesco a partir de la genealogía resultante. Usamos el
paquete rsocsim, la
interfaz en R del microsimulador SOCSIM (Hammel et al.
1976; Mason 2016).
[5.1]
Contenido de este módulo:
rsocsim ya se instaló desde CRAN en el Módulo 1. Si
necesitas instalarlo aquí, el comando es el mismo:
SOCSIM se desarrolló originalmente para Unix en UC Berkeley (Hammel et al. 1976), donde se ha mantenido durante décadas. La versión actual de rsocsim pretende ser independiente del sistema operativo y, en su mayor parte, es compatible con la distribución original de SOCSIM. Para más información sobre el SOCSIM original, véase https://lab.demog.berkeley.edu/socsim/ y especialmente Mason (2016). La siguiente descripción se ha adaptado de los materiales complementarios de Alburez‐Gutierrez et al. (2021).
SOCSIM es un programa de microsimulación demográfica de código abierto y extensible. Está escrito en el lenguaje de programación C y se basa en gran medida en matrices de listas enlazadas para hacer seguimiento de las relaciones de parentesco y almacenar información sobre los individuos simulados. El simulador toma como datos de entrada los archivos iniciales de población y las tasas mensuales de fecundidad y mortalidad por edades. El individuo es la unidad de análisis del simulador. Cada persona está sujeta a un conjunto de tasas, expresadas como probabilidades mensuales de eventos, dadas ciertas características demográficas, como la edad y el sexo. Cada mes, cada individuo se enfrenta al riesgo de experimentar una serie de eventos, como el nacimiento, la muerte y el matrimonio. La selección del evento y el tiempo de espera hasta que se produzca se determinan estocásticamente mediante un modelo de riesgo competitivo. En el programa de simulación se incluyen algunas otras restricciones con el fin de sortear los eventos sólo para los individuos que reúnen las condiciones para ser elegibles (por ejemplo, para permitir un intervalo mínimo de tiempo entre nacimientos de la misma madre, para evitar tabúes sociales como el incesto, etc.). Cada evento para el cual el individuo está en riesgo se modela como una distribución exponencial por partes. El tiempo de espera hasta que se produzca cada evento se genera aleatoriamente y el siguiente evento a ser ejecutado para cada individuo es aquel con el tiempo de espera más corto.
Al final de la simulación, se obtiene un archivo de
población (.opop), que registra las
características demográficas de cada individuo que vivió en la
simulación y los números de identificación de las principales relaciones
de parentesco (madre, padre y cónyuge), y un archivo de
matrimonios (.omar). Con esa población
sintética podemos estudiar la disponibilidad y la pérdida de
parientes.
Para dar un ejemplo de la utilización de rsocsim,
realizaremos una microsimulación SOCSIM Colombia para
el periodo 1950–2023, usando como insumos las mismas
tasas del WPP 2024 que preparamos para los modelos de DemoKin.
Para correr la simulación, los datos originales de WPP deben
convertirse primero a tasas o probabilidades mensuales en formato
SOCSIM, tal y como se describe en SOCSIM Oversimplified (c.f. Mason 2016
para más detalles). Las tasas correspondientes a cada año se
especifican en el archivo de supervisión
socsim_Colombia.sup, proporcionado en este repositorio, que
se utilizará para ejecutar la simulación.
SOCSIM necesita tasas mensuales de fecundidad y
mortalidad por edad y sexo en un formato de texto propio. Si se dispone
de la información, también se pueden especificar por estado civil o por
«grupos» [c.f. SOCSIM Oversimplified, Mason (2016), pp. 26-28, para más
información.En los archivos de tasas, el orden es siempre evento
demográfico (birth, death, marriage), grupo (1, ..), sexo (F o M) y
estado civil (married, single, divorced, widowed). Para las tasas de
fecundidad, puede ir seguido de un número que indique la paridad. Cada
línea posterior contiene una tasa o probabilidad mensual y el intervalo
de edad sobre el que rige (años y meses del límite superior de edad).
Definimos a continuación dos funciones auxiliares que convierten
nuestros datos del WPP (los .parquet por país; véase el
Módulo 1) a tasas o probabilidades mensuales en el formato adecuado y
las guardan en la carpeta que les indiquemos. La opción
scipen = 9999 evita la notación científica, que SOCSIM no
sabe leer.
[5.3]
options(scipen = 9999)
# Lee los datos del WPP para un país (ISO3) desde el .parquet preparado.
.leer_wpp_pais <- function(iso) {
arrow::read_parquet(file.path("data/wpp_latam_1950_2023", paste0("wpp_", iso, ".parquet")))
}
# --- Tasas de fecundidad mensuales por edad para SOCSIM ---
write_socsim_fertility_rates_WPP <- function(country = "Colombia", iso = "COL", rates_dir = "rates") {
dir.create(rates_dir, recursive = TRUE, showWarnings = FALSE)
# Fecundidad femenina por edad simple [15-49] (tasas por mujer, anuales)
data <- .leer_wpp_pais(iso) %>%
filter(sex == "f", age >= 15, age <= 49) %>%
select(year, age, fx)
# Convertir a tasas mensuales y usar el límite superior de edad que usa SOCSIM
ASFR <-
data %>%
mutate(
Age_up = age + 1,
Month = 0,
fx_mo = fx / 12
) %>%
select(-fx)
# Añadir filas con tasa 0 para las edades 0-15 y 50-101
ASFR <-
ASFR %>%
group_by(year) %>%
group_split() %>%
map_df(~ add_row(.x,
year = unique(.x$year),
age = 0, Age_up = 15, Month = 0, fx_mo = 0.0, .before = 1
)) %>%
group_by(year) %>%
group_split() %>%
map_df(~ add_row(.x,
year = unique(.x$year),
age = 50, Age_up = 101, Month = 0, fx_mo = 0.0, .after = 36
)) %>%
ungroup() %>%
select(-age)
years <- ASFR %>%
pull(year) %>%
unique()
rows_ageF <- ASFR %>%
pull(Age_up) %>%
unique() %>%
seq_along()
for (year in years) {
n <- which(year == years)
n_row <- (n - 1) * 37 + rows_ageF
outfilename <- file(file.path(rates_dir, paste0(country, "fert", year)), "w")
cat(c("** Period (Monthly) Age-Specific Fertility Rates for", country, "in", year, "\n"),
file = outfilename
)
cat(c("* Retrieved from the World Population Prospects 2024", "\n"), file = outfilename)
cat("\n", file = outfilename)
cat("birth", "1", "F", "single", "0", "\n", file = outfilename)
for (i in n_row) cat(c(as.matrix(ASFR)[i, -1], "\n"), file = outfilename)
cat("\n", file = outfilename)
cat("birth", "1", "F", "married", "0", "\n", file = outfilename)
for (i in n_row) cat(c(as.matrix(ASFR)[i, -1], "\n"), file = outfilename)
close(outfilename)
}
}
# --- Probabilidades de muerte mensuales por edad y sexo para SOCSIM ---
write_socsim_mortality_rates_WPP <- function(country = "Colombia", iso = "COL", rates_dir = "rates") {
dir.create(rates_dir, recursive = TRUE, showWarnings = FALSE)
# Probabilidades de muerte (qx) por edad simple [0-100] y sexo
data <- .leer_wpp_pais(iso) %>%
transmute(year,
Sex = factor(if_else(sex == "f", "Female", "Male"),
levels = c("Female", "Male")
),
age, qx
)
# Convertir probabilidades anuales en mensuales (Wachter 2014, p. 53)
# q100 = 1 para limitar la esperanza de vida a 100
ASMP <-
data %>%
mutate(
qx_mo = if_else(age == 100, 1, 1 - (1 - qx)^(1 / 12)),
Age_up = age + 1,
Month = 0
) %>%
select(year, Age_up, Month, Sex, qx_mo) %>%
pivot_wider(names_from = Sex, values_from = qx_mo) # columnas: Female, Male
years <- ASMP %>%
pull(year) %>%
unique()
rows_ageM <- ASMP %>%
pull(Age_up) %>%
unique() %>%
seq_along()
for (year in years) {
n <- which(year == years)
n_row <- (n - 1) * 101 + rows_ageM
outfilename <- file(file.path(rates_dir, paste0(country, "mort", year)), "w")
cat(c("** Period (Monthly) Age-Specific Probabilities of Death for", country, "in", year, "\n"),
file = outfilename
)
cat(c("* Retrieved from the World Population Prospects 2024. Single age life tables", "\n"),
file = outfilename
)
cat(c("** The final age interval is limited to one year [100-101)", "\n"), file = outfilename)
cat("\n", file = outfilename)
# Mujeres solteras (columnas year, Age_up, Month, Female, Male -> quitar year y Male)
cat("death", "1", "F", "single", "\n", file = outfilename)
for (i in n_row) cat(c(as.matrix(ASMP)[i, -c(1, 5)], "\n"), file = outfilename)
cat("\n", file = outfilename)
# Hombres solteros (quitar year y Female)
cat("death", "1", "M", "single", "\n", file = outfilename)
for (i in n_row) cat(c(as.matrix(ASMP)[i, -c(1, 4)], "\n"), file = outfilename)
close(outfilename)
}
}Toda la simulación (tasas, población inicial y resultados de SOCSIM)
se guarda dentro de la carpeta socsim/, para no ensuciar la
raíz del proyecto:
[5.4]
Como datos de entrada de fecundidad, utilizamos todo el rango de edades de las tasas de fecundidad por edad de la madre (para todos los órdenes de nacimiento) para cada año calendario, proporcionadas por el WPP 2024 [15-49]. Además, SOCSIM requiere una categoría de edad final con un límite superior correspondiente a la esperanza de vida máxima. En este caso, la establecemos en el rango [50-100] para ser coherentes con la estructura de las tablas de mortalidad del WPP. En este ejercicio, las tasas son idénticas para todos los estados civiles. Sin embargo, en los archivos de tasas deben especificarse, al menos, para las mujeres solteras y casadas (single, married). Para los demás estados civiles (divorciadas, viudas y convivientes), se aplican las reglas predeterminadas para las tasas en SOCSIM.
Como datos de entrada de mortalidad, SOCSIM requiere probabilidades de muerte (qx) de las tablas de mortalidad de periodo. Utilizamos todo el rango de edades de las tablas de mortalidad por edad simple proporcionadas por WPP 2024 [0-100]. El intervalo final de edad “100” se limita a un año, es decir, [100-101]. Las probabilidades de muerte son idénticas para todos los estados civiles de cada sexo. Sin embargo, para este ejercicio sólo se especifican para mujeres solteras y hombres solteros (single) en los archivos de tasas. Los demás estados civiles seguirán las reglas predeterminadas para las tasas en SOCSIM.
Ejecutemos las siguientes funciones para convertir al formato SOCSIM
(mensuales) los datos de fecundidad y de mortalidad de WPP 2024 (los
.parquet por país).
[5.5]
# Escribir los archivos de tasas para Colombia (ISO3 = "COL") en socsim/rates/
write_socsim_fertility_rates_WPP(country = "Colombia", iso = "COL", rates_dir = rates_dir)
write_socsim_mortality_rates_WPP(country = "Colombia", iso = "COL", rates_dir = rates_dir)
# Ejemplo: los archivos escritos para 1950
list.files(rates_dir, pattern = "1950")## [1] "Colombiafert1950" "Colombiamort1950"
Ahora que tenemos los archivos de tasas, vamos a crear los archivos iniciales de población (.opop) y matrimonio (.opop). Por cuestiones prácticas del taller, usaremos una población inicial pequeña para reducir el tiempo de ejecución de la simulación y de cómputo del código posterior. El archivo .opop inicial tendrá un tamaño de 1000, con sexo y fechas de nacimiento asignados aleatoriamente, y grupo 1 para todos. Las demás columnas se completarán durante la simulación. El archivo .omar inicial será un archivo vacío.
[5.6]
set.seed(250826)
size_opop <- 1000
# Crear la población inicial
presim.opop <- setNames(
data.frame(matrix(0, ncol = 14, nrow = size_opop)),
c(
"pid", "fem", "group", "nev", "dob", "mom", "pop",
"nesibm", "nesibp", "lborn", "marid", "mstat", "dod", "fmult"
)
)
presim.opop$pid <- 1:size_opop
presim.opop$fem <- sample(0:1, size_opop, replace = TRUE)
presim.opop$group <- 1
presim.opop$dob <- sample(500:1000, size_opop, replace = TRUE)
# Guardar la población inicial para la presimulación
write.table(presim.opop, file.path(folder, "presim.opop"), row.names = FALSE, col.names = FALSE)
# Crear un data frame vacío para los matrimonios en la población inicial
write.table(data.frame(), file.path(folder, "presim.omar"), row.names = FALSE, col.names = FALSE)Una vez creados los archivos de tasas de entrada y los archivos
iniciales de población y matrimonios, podemos ejecutar la simulación.
Para utilizar la función socsim, debemos especificar la
carpeta en la que se encuentran los archivos de supervisión y de tasas,
el nombre del archivo de supervisión y un número de semilla para la
simulación.
El archivo de supervisión socsim_Colombia.sup le indica
a SOCSIM qué segmentos simular y con qué tasas. El primer segmento de la
simulación se ejecuta durante 100 años (1200 meses) para producir una
estructura por edad estable basada en las tasas y probabilidades del WPP
para 1950. Cada uno de los segmentos siguientes se ejecuta durante un
año (12 meses) con los archivos de fecundidad y mortalidad
correspondientes.
Tiempo de cómputo. La simulación tarda algunos minutos. La corremos una vez y guardamos los resultados; si ya existen, solo los cargamos.
[5.7]
folder <- file.path(getwd(), "socsim")
supfile <- "socsim_Colombia.sup"
seed <- "250826"
# Correr la microsimulación y leer sus archivos de salida (población y matrimonios).
# Ojo: el "future" de process_method es el NOMBRE DEL MÉTODO con que rsocsim
# corre la simulación en segundo plano (y así puede mostrar el avance mientras
# corre), no el nombre de un argumento de paquete. Eso sí: para que ese método
# funcione hace falta tener instalado el paquete `future` (Módulo 1), porque
# rsocsim lo declara como sugerido y no se instala solo.
rsocsim::socsim(folder, supfile, seed, process_method = "future")
opop <- rsocsim::read_opop(folder = folder, supfile = supfile, seed = seed, suffix = "", fn = NULL)
omar <- rsocsim::read_omar(folder = folder, supfile = supfile, seed = seed, suffix = "", fn = NULL)
# Guardar los resultados para reutilizarlos (la simulación tarda varios minutos)
saveRDS(list(opop = opop, omar = omar), "data/socsim_resultados.rds")Los resultados de la simulación están listos. Podemos utilizar las
funciones read_opop y read_omar del paquete
rsocsim para importar los archivos de población y
matrimonio de salida a R.
El archivo de población (.opop) contiene un registro por
cada individuo que ha estado vivo durante la simulación. Cada registro
(fila) proporciona la siguiente información sobre un individuo
determinado: identificador de persona (pid), sexo
(fem, 1 para mujer y 0 para hombre), identificador de grupo
(group), siguiente evento programado (nev),
fecha de nacimiento (dob), identificador de persona de la
madre (mom), identificador de persona del padre
(pop), identificador de persona del siguiente hermano mayor
por vía materna (nesibm), identificador de persona del
siguiente hermano mayor por vía paterna (nesibp),
identificador de persona del último hijo nacido (lborn),
identificador del matrimonio más reciente en .omar (marid),
estado civil al final de la simulación (mstat), fecha de
defunción (dod, o 0 si está vivo al final de la simulación)
y multiplicador de fertilidad (fmult).
## pid fem group nev dob mom pop nesibm nesibp lborn marid mstat dod fmult
## 1 1 0 1 65 568 0 0 0 0 1063 63 3 1598 0
## 2 2 0 1 65 994 0 0 0 0 4573 468 4 1589 0
## 3 3 0 1 65 500 0 0 0 0 1068 68 4 1258 0
## 4 4 0 1 65 758 0 0 0 0 1192 162 4 1036 0
## 5 5 0 1 65 721 0 0 0 0 2934 49 4 1547 0
## 6 6 0 1 65 500 0 0 0 0 1267 47 4 1371 0
El archivo de matrimonios (.omar) contiene un registro
por cada matrimonio ocurrido durante la simulación. Cada registro de
matrimonio proporciona la siguiente información: número identificador
del matrimonio (mid), identificador de persona de la esposa
(wpid), identificador de persona del esposo
(hpid), fecha de inicio del matrimonio
(dstart), fecha de finalización del matrimonio
(dend, es 0 si estaba vigente al final de la simulación),
razón por la que finalizó el matrimonio (rend),
identificador del siguiente matrimonio anterior más reciente de la
esposa (wprior), identificador del siguiente matrimonio
anterior más reciente del esposo (hprior).
## mid wpid hpid dstart dend rend wprior hprior
## 1 1 300 731 1001 1630 3 0 0
## 2 2 776 422 1001 1548 3 0 0
## 3 3 241 874 1001 1457 3 0 0
## 4 4 696 328 1001 1103 3 0 0
## 5 5 430 35 1001 1430 3 0 0
## 6 6 179 465 1001 1375 3 0 0
Para verificar que la simulación reproduce las tasas de entrada,
estimamos las tasas de fecundidad y mortalidad por edad a partir de los
microdatos simulados, usando las funciones
estimate_fertility_rates() y
estimate_mortality_rates() del paquete.
Para ejecutarlas, necesitamos definir algunos argumentos: el archivo
.opop opop, el año final de la simulación
final_sim_year, los años mínimo y máximo para los que
queremos estimar las tasas, [year_min y
year_max), el tamaño del grupo de años
year_group, la edad mínima y máxima para la fecundidad
[age_min_fert y age_max_fert) o la edad máxima
para la mortalidad age_max_mort) y el tamaño del grupo de
edades age_group. Podemos calcular las tasas para
diferentes años y grupos de edad, pero es importante que los años y
edades mínimos y máximos correspondan con el tamaño de los grupos.
Estas funciones calculan tasas específicas de fecundidad y mortalidad por edad, utilizando el tamaño de la población a mitad de año por sexo y edad como una estimación de los años-persona vividos durante el año. Debido al tamaño limitado de la población en una microsimulación (especialmente, de supervivientes a edades avanzadas), a veces pocos o ningún individuo de una edad específica están vivos en un momento determinado (1 de julio). Por ello, es posible obtener tasas superiores a 1, iguales a 0 (0 eventos/población), infinitas (eventos/0 población) y valores NaN (0 eventos/0 población).
[5.10]
asfr_sim <- rsocsim::estimate_fertility_rates(
opop = opop, final_sim_year = 2023,
year_min = 1950, year_max = 2020, year_group = 5,
age_min_fert = 15, age_max_fert = 50, age_group = 5
)
asmr_sim <- rsocsim::estimate_mortality_rates(
opop = opop, final_sim_year = 2023,
year_min = 1950, year_max = 2020, year_group = 5,
age_max_mort = 100, age_group = 5
)Ahora podemos graficar nuestras estimaciones de fecundidad y mortalidad derivadas de la microsimulación para mujeres en algunos años seleccionados.
[5.11]
yrs_plot <- c("[1950,1955)", "[1980,1985)", "[2015,2020)")
age_levels <- levels(asmr_sim$age)
bind_rows(
asfr_sim %>% mutate(rate = "Fecundidad", sex = "female"),
asmr_sim %>% mutate(rate = "Mortalidad") %>% filter(sex == "female")
) %>%
mutate(age = factor(as.character(age), levels = age_levels)) %>%
filter(socsim != 0 & !is.infinite(socsim) & !is.nan(socsim), year %in% yrs_plot) %>%
ggplot(aes(x = age, y = socsim, group = year, colour = year)) +
geom_line(linewidth = 1) +
facet_wrap(. ~ rate, scales = "free") +
facetted_pos_scales(y = list(
scale_y_continuous(),
scale_y_continuous(trans = "log10")
)) +
scale_x_discrete(guide = guide_axis(angle = 90)) +
scale_color_manual(values = c("#FFCD00", "#003087", "#C8102E")) +
labs(
title = "Tasas estimadas a partir de la simulación SOCSIM (mujeres, Colombia)",
x = "Edad", y = "Estimación", color = "Años"
) +
theme_bw()¿Qué vemos aquí? Las curvas recuperan los patrones demográficos esperados: la fecundidad dibuja la clásica campana concentrada en las edades reproductivas y se va desplazando entre 1950 y 2020, mientras que la mortalidad muestra su forma en “J” (alta en la infancia, mínima en la juventud y creciente en la vejez). Identificar que la simulación ha reproducido estas formas es la primera señal de que ha funcionado. En la sección siguiente, lo verificaremos comparando los resultados con las tasas de entrada del WPP.
Como verificación final, comparamos las tasas específicas de fecundidad y mortalidad por edad obtenidas a partir de los resultados de SOCSIM con los datos originales WPP utilizados como entrada, agregadas a los mismos grupos quinquenales de edad y periodo utilizados en el punto anterior.
[5.12]
col_wpp <- read_parquet("data/wpp_latam_1950_2023/wpp_COL.parquet")
# Cortes de año y edad usados en las estimaciones SOCSIM para fecundidad
yr_breaks_fert <- unique(as.numeric(str_extract_all(asfr_sim$year, "\\d+", simplify = TRUE)))
age_breaks_fert <- unique(as.numeric(str_extract_all(asfr_sim$age, "\\d+", simplify = TRUE)))
yr_range_fert <- min(yr_breaks_fert):max(yr_breaks_fert - 1)
# Fecundidad WPP agregada, para mujeres (5x5)
WPP_fert <- col_wpp %>%
filter(sex == "f", year %in% yr_range_fert, age >= 15, age <= 49) %>%
mutate(
year = cut(year, yr_breaks_fert, right = FALSE),
age = cut(age, age_breaks_fert, right = FALSE)
) %>%
filter(!is.na(age)) %>%
summarise(Estimate = mean(fx, na.rm = TRUE), .by = c(year, age)) %>%
mutate(Source = "WPP", Rate = "Fecundidad")
# Fecundidad SOCSIM
SocsimF <- asfr_sim %>%
transmute(year, age,
Estimate = socsim,
Source = "SOCSIM", Rate = "Fecundidad"
)
# Cortes de año y edad usados en las estimaciones SOCSIM para mortalidad
yr_breaks_mort <- unique(as.numeric(str_extract_all(asmr_sim$year, "\\d+", simplify = T)))
yr_range_mort <- min(yr_breaks_mort):max(yr_breaks_mort-1)
age_breaks_mort <- unique(as.numeric(str_extract_all(asmr_sim$age, "\\d+", simplify = T)))
# Mortalidad WPP agregada para mujeres (5x5)
WPP_mort <- col_wpp %>%
filter(sex == "f", year %in% yr_range_mort) %>%
mutate(
year = cut(year, yr_breaks_mort, right = FALSE),
age = cut(age, age_breaks_mort, right = FALSE)
) %>%
filter(!is.na(age)) %>%
summarise(Estimate = mean(mx, na.rm = TRUE), .by = c(year, age)) %>%
mutate(Source = "WPP", Rate = "Mortalidad")
# Mortalidad SOCSIM
SocsimM <- asmr_sim %>%
filter(sex == "female") %>%
transmute(year, age,
Estimate = socsim,
Source = "SOCSIM", Rate = "Mortalidad"
)Finalmente, podemos graficar las tasas de fecundidad y mortalidad por edad para mujeres en Colombia obtenidas de los resultados SOCSIM y de WPP.
[5.13]
bind_rows(WPP_fert, SocsimF, WPP_mort, SocsimM) %>%
filter(Estimate != 0 & !is.infinite(Estimate) & !is.nan(Estimate)) %>%
mutate(age = factor(as.character(age), levels = age_levels)) %>%
filter(year %in% yrs_plot) %>%
ggplot(aes(x = age, y = Estimate, group = interaction(year, Source)))+
facet_wrap(. ~ Rate, scales = "free") +
geom_line(aes(colour = year, linetype = Source), linewidth = 1)+
scale_linetype_manual(values = c("WPP" = "11","SOCSIM" = "solid")) +
facetted_pos_scales(y = list(ASFR = scale_y_continuous(),
ASMR = scale_y_continuous(trans = "log10")))+
scale_x_discrete(guide = guide_axis(angle = 90)) +
scale_color_manual(values = c("#FFCD00", "#003087", "#C8102E"))+
labs(title = "Tasas específicas de fecundidad y mortalidad por edades quinquenales
\n obtenidas de WPP y una simulación SOCSIM para Colombia (1950-2023)",
x = "Edades", y = "Estimación",
color = "Años", linetype = "Fuente") +
theme_bw()En general, las tasas derivadas de la microsimulación se aproximan a las del WPP usadas como entrada, lo que nos permite considerar los resultados como suficientemente válidos para el analisis. Las diferencias se reducen al aumentar el tamaño de la población inicial. En el Módulo 6 usamos estos resultados para estudiar la pérdida y disponibilidad de parientes.
A cargo de Liliana Calderón. (Adaptado del taller de la Escuela ALAP 2024; sección de pérdida de parientes preparada originalmente por Mallika Snyder.)
Una vez corrida la simulación (Módulo 5), SOCSIM nos proporciona una genealogía sintética de toda la población: sabemos cuándo nace, se casa y muere cada individuo, y quiénes son sus padres y su cónyuge. Con estos datos, podemos reconstruir redes de parentesco extensas para cada individuo, incluso parientes lejanos que son difíciles de captar en un censo o una encuesta. En este taller, mostraremos un ejemplo de cómo han cambiado las tasas de pérdida parientes a lo largo del tiempo. Este enfoque complementa los modelos analíticos de los módulos 2 y 3. Sin embargo, hay muchas más formas de utilizar SOCSIM para estudiar estas y otras cuestiones sobre las dinámicas de parentesco.
Cargamos los resultados de la simulación del Módulo 5:
[6.2]
Contenido de este módulo:
SOCSIM registra el tiempo en meses. Una función auxiliar convierte los meses en años calendario, y con ella calculamos el año de nacimiento y de defunción de cada individuo (a quienes siguen vivos al final les asignamos el último mes simulado como fecha de defunción):
[6.3]
asYr <- function(month, last_month, final_sim_year) {
final_sim_year - trunc((last_month - month) / 12)
}
last_month <- max(opop$dob)
final_sim_year <- 2023
opop <- opop %>%
mutate(
dod = if_else(dod == 0, last_month, dod),
dob_year = asYr(dob, last_month, final_sim_year),
dod_year = asYr(dod, last_month, final_sim_year)
)Para identificar a los parientes en SOCSIM, utilizamos relaciones de
parentesco por consanguinidad directa -como padres e hijos- y afinidad
-como cónyuges-. Este es el principio en el que se basa la función
retrieve_kin() incluida en rsocsim que puede
utilizarse para identificar parientes de diversos tipos (padres, hijos,
abuelos, hermanos, cónyuges, y mucho más). Puedes leer la documentación
completa de esta función escribiendo ?retrieve_kin en la
consola.
Además de permitirnos identificar más fácilmente las redes de parentesco, SOCSIM también nos proporciona información valiosa sobre la cronología de los eventos vitales, como nacimientos, muertes y matrimonios, en estas redes. Esto puede ayudarnos a relacionar los cambios en las tasas demográficas con los cambios en las redes de parentesco disponibles para nuestros individuos simulados, a menudo a un nivel temporal muy preciso (meses o años)
En este ejercicio, usamos como grupo de referencia a las
mujeres de 25 a 39 años que están vivas en un año
determinado (la muestra cambia cada año). La función
getKinLoss() identifica a esas mujeres, recupera a sus
parientes con rsocsim::retrieve_kin() y calcula, para cada
tipo de pariente, qué proporción de la muestra perdió a un familiar ese
año y qué proporción tenía un familiar vivo el año anterior. El código
es una versión simplificada de las funciones utilizadas en un trabajo de
investigación anterior (Snyder et al. 2022).
[6.4]
getKinLoss <- function(year_of_interest, opop, omar) {
pid_data <- opop %>%
filter(
data.table::between(year_of_interest, dob_year, dod_year, incbounds = FALSE),
fem == 1
) %>%
mutate(age = year_of_interest - dob_year) %>%
filter(data.table::between(age, 25, 39, incbounds = TRUE))
pid_vec <- pid_data$pid
kin <- rsocsim::retrieve_kin(
opop = opop, omar = omar, pid = pid_vec,
extra_kintypes = NULL, kin_by_sex = FALSE
)
bind_rows(lapply(seq_along(kin), function(x) {
kin2 <- unlist(kin[[x]], use.names = FALSE)
names(kin2) <- rep(pid_vec, unlist(lapply(kin[[x]], length)))
dob_k <- opop$dob_year[match(kin2, opop$pid)]
dod_k <- opop$dod_year[match(kin2, opop$pid)]
vivo_antes <- if_else(
data.table::between(year_of_interest - 1, dob_k, dod_k, NAbounds = NA, incbounds = FALSE),
TRUE, FALSE
)
muere_ahora <- dod_k %in% year_of_interest
tibble(
kintype = names(kin[x]),
year = year_of_interest,
n_total = length(pid_vec),
n_losekin = length(unique(names(kin2[muere_ahora]))),
n_withkin = length(unique(names(kin2[vivo_antes == TRUE]))),
pc_losekin_total = 100 * n_losekin / n_total,
pc_withkin = 100 * n_withkin / n_total
)
}))
}Ejecutamos la función para recuperar partientes en intervalos de 10 años entre 1950 y 2020, y guardamos el resultado.
[6.5]
Añadimos etiquetas legibles y un orden a los tipos de pariente:
[6.6]
kinloss_data <- kinloss_data %>%
mutate(
kintype = recode(kintype,
ggparents = "Bisabuelos", gparents = "Abuelos", parents = "Padres",
siblings = "Hermanos", spouse = "Cónyuges", children = "Hijos",
gchildren = "Nietos"
),
kintype = fct_relevel(
kintype, "Bisabuelos", "Abuelos", "Padres",
"Hermanos", "Cónyuges", "Hijos", "Nietos"
)
)Pérdida de parientes de las mujeres de 25–39 años a lo largo del tiempo:
[6.7]
Disponibilidad de parientes (proporción con un familiar vivo el año anterior):
[6.8]
Interpretación. Como cabría esperar para este grupo de edad, las tasas más altas de pérdida corresponden a abuelos y padres. El aumento de la disponibilidad de abuelos a lo largo del tiempo refleja la mayor esperanza de vida; la caída en la disponibilidad de hijos y cónyuges refleja el descenso de la fecundidad y la nupcialidad.
En este taller recorrimos dos familias de métodos para la demografía del parentesco y del duelo:
Ambos enfoques son complementarios: los modelos analíticos son eficientes y exactos en la media; la microsimulación captura la variabilidad individual y relaciones de parentesco difíciles de modelar analíticamente. Juntos ofrecen un conjunto de herramientas potente para cuantificar cómo la mortalidad —y en particular la violencia— reverbera a través de las familias.
Más información. Documentación de
rsocsim: https://mpidr.github.io/rsocsim/. Documentación deDemoKin: https://github.com/IvanWilli/DemoKin.
Esta pestaña resuelve, paso a paso y con código comentado, el ejercicio del Módulo 4 para Costa Rica, usando solo datos del WPP y el archivo de tasas de mortalidad por homicidio. Es la plantilla que puede adaptar a su país: basta con cambiar el código ISO3.
Tasas de mortalidad por homicidio. Como referencia de mortalidad violenta usamos las tasas específicas por homicidio del país (
data/homicide_mortality_rates.csv), calculadas a partir de estimaciones de la Global Burden of Disease 2023 para 20 países de la región. Los resultados reflejan entonces el duelo por mortalidad violenta. Estas tasas cubren 2000–2023, por lo que el análisis del duelo se concentra en ese período.
Contenido:
[S.2]
iso <- "CRI" # <- cambie por su país
anio_inicial <- 2000 # primer año con datos de homicidio (GBD)
anio_final <- 2023 # último año del análisis (último año disponible)
# Datos WPP del país (supervivencia, fecundidad, población)
pais <- open_dataset("data/wpp_latam_1950_2023/", format = "parquet") |>
filter(iso3 == iso) |>
collect() |>
as_tibble()
# Población focal y exposición (sin ajuste por inmigrantes en este ejemplo)
pop_focal <- pais %>% transmute(year, sex_focal = sex, age_focal = age, pop)
exposicion <- pais %>%
filter(year == anio_final) %>%
transmute(sex_focal = sex, age_focal = age, exposicion = pop)
poblacion <- sum(exposicion$exposicion)
# Tasas de mortalidad por homicidio (GBD 2023, 2000-2023).
# Convertimos la tasa mx en probabilidad qx (ax = 0.5).
qx_kin <- read_csv("data/homicide_mortality_rates.csv", show_col_types = FALSE) %>%
filter(iso3 == iso) %>%
mutate(qx = mx / (1 + 0.5 * mx)) %>%
transmute(year, sex_kin = sex, age_kin = age, qx_kin = qx)
# Etiquetas y grupos de familia
etiquetas_kin <- c(
d = "Hijas e hijos", m = "Madres y padres", s = "Hermanas y hermanos",
gm = "Abuelas y abuelos", gd = "Nietas y nietos", a = "Tías y tíos",
n = "Sobrinas y sobrinos", c = "Primas y primos"
)
parientes_cercanos <- c("d", "m", "s")
parientes_extendidos <- c("gm", "gd", "a", "n", "c")Corremos el modelo de parentesco para el país: kin2sex
para Focal mujer y copiamos el resultado para Focal
hombre (por eficiencia, como en el Módulo 2). Tarda unos minutos; lo
calculamos una vez, lo guardamos y lo reutilizamos (caché).
[S.3]
# reshape_wpp(): toma los datos largos del WPP y devuelve UNA matriz con las
# edades en las filas y los años en las columnas (el formato que espera
# DemoKin). Argumentos: dat = los datos del país; variable = la columna que
# queremos ("px", "fx" o "pop"); which_sex = "f" o "m".
reshape_wpp <- function(dat, variable, which_sex) {
dat %>%
filter(sex == which_sex) %>%
select(age, year, value = all_of(variable)) %>%
arrange(age, year) %>%
pivot_wider(names_from = year, values_from = value) %>%
select(-age) %>%
as.matrix()
}
archivo_kin <- file.path("data", paste0("kin_", iso, ".parquet"))
if (!file.exists(archivo_kin)) {
pf <- reshape_wpp(pais, "px", "f")
pm <- reshape_wpp(pais, "px", "m")
ff <- reshape_wpp(pais, "fx", "f")
fm <- ff # supuesto androgino
nf <- reshape_wpp(pais, "pop", "f")
nm <- reshape_wpp(pais, "pop", "m")
# Correr el modelo para Focal mujer y copiarlo para Focal hombre.
# Acotamos la salida a los años con datos de homicidio (2000-2023).
kin_f <- kin2sex(
pf = pf, pm = pm, ff = ff, fm = fm, nf = nf, nm = nm,
time_invariant = FALSE, output_period = anio_inicial:anio_final,
output_kin = c("d", "m", "s", "gm", "gd", "a", "n", "c"),
sex_focal = "f", birth_female = 0.5
)
kin <- bind_rows(
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "f"),
as_tibble(kin_f$kin_full) %>% mutate(sex_focal = "m")
) %>%
transmute(year, sex_focal, age_focal, kin, sex_kin, age_kin, living) %>%
filter(living >= 1e-6)
write_parquet(kin, archivo_kin)
}
kin <- read_parquet(archivo_kin)# Paso 2: probabilidad anual de NO perder ningún pariente del tipo k
p0_anual <- kin %>%
left_join(qx_kin, by = c("year", "sex_kin", "age_kin")) %>%
mutate(p0x = (1 - qx_kin)^living) %>%
summarise(p0 = prod(p0x, na.rm = TRUE), .by = c(year, sex_focal, age_focal, kin))
# Paso 3: probabilidad acumulada (producto por cohorte); nos quedamos con anio_final
p0_acum <- p0_anual %>%
group_by(sex_focal, cohorte = year - age_focal, kin) %>%
arrange(age_focal, .by_group = TRUE) %>%
mutate(p0_acum = cumprod(p0)) %>%
ungroup() %>%
filter(year == anio_final)[S.4]
# incidencia_familia_anual(): cuántas personas quedan en duelo CADA AÑO por un
# grupo de parientes. Argumentos: tipos = qué parientes forman el grupo
# (p. ej. c("d","m","s")); etiqueta = el nombre que llevará el grupo en el
# gráfico. Devuelve una fila por año, con las personas en duelo y la etiqueta.
incidencia_familia_anual <- function(tipos, etiqueta) {
p0_anual %>%
filter(kin %in% tipos) %>%
summarise(p0 = prod(p0), .by = c(year, sex_focal, age_focal)) %>%
left_join(pop_focal, by = c("year", "sex_focal", "age_focal")) %>%
mutate(en_duelo = (1 - p0) * pop) %>%
summarise(en_duelo = sum(en_duelo), .by = year) %>%
mutate(grupo = etiqueta)
}
incidencia_familia <- bind_rows(
incidencia_familia_anual(parientes_cercanos, "Familia cercana"),
incidencia_familia_anual(parientes_extendidos, "Familia extendida")
)
incidencia_familia %>%
ggplot(aes(x = year, y = en_duelo / 1000, fill = grupo)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
scale_fill_manual(values = c(
"Familia cercana" = "#b56576", "Familia extendida" = "#355070"
)) +
labs(
title = paste0("Incidencia anual del duelo por homicidio (", iso, ")"),
x = "Año", y = "Personas en duelo (miles)", fill = "Grupo de familia"
) +
theme_bw()[S.5]
# muertes totales por homicidio del período (denominador del multiplicador)
muertes_total <- read_csv("data/homicide_mortality_rates.csv", show_col_types = FALSE) %>%
filter(iso3 == iso, year %in% anio_inicial:anio_final) %>%
left_join(pais %>% select(year, sex, age, pop), by = c("year", "sex", "age")) %>%
summarise(m = sum(mx * pop, na.rm = TRUE)) %>%
pull(m)
# incidencia_grupo(): igual que incidencia_familia_anual(), pero devuelve UN
# SOLO número: el total de personas en duelo de todo el período, sin separar por
# año. Es el numerador del multiplicador de duelo.
incidencia_grupo <- function(tipos) {
p0_anual %>%
filter(kin %in% tipos) %>%
summarise(p0 = prod(p0), .by = c(year, sex_focal, age_focal)) %>%
left_join(pop_focal, by = c("year", "sex_focal", "age_focal")) %>%
mutate(en_duelo = (1 - p0) * pop) %>%
summarise(en_duelo = sum(en_duelo)) %>%
pull(en_duelo)
}
mult_kin <- p0_anual %>%
left_join(pop_focal, by = c("year", "sex_focal", "age_focal")) %>%
mutate(en_duelo = (1 - p0) * pop) %>%
summarise(personas = sum(en_duelo), .by = kin) %>%
mutate(kin = recode(kin, !!!etiquetas_kin))
mult_grupos <- tibble(
kin = c("Familia cercana", "Familia extendida", "Cercana o extendida"),
personas = c(
incidencia_grupo(parientes_cercanos),
incidencia_grupo(parientes_extendidos),
incidencia_grupo(names(etiquetas_kin))
)
)
bind_rows(mult_kin, mult_grupos) %>%
transmute(
Pariente = kin,
`Personas en duelo (incidencia)` = round(personas),
`Muertes` = round(muertes_total),
Multiplicador = round(personas / muertes_total, 1)
) %>%
knitr::kable(format.args = list(big.mark = ","))| Pariente | Personas en duelo (incidencia) | Muertes | Multiplicador |
|---|---|---|---|
| Tías y tíos | 95,107 | 12,237 | 7.8 |
| Primas y primos | 378,049 | 12,237 | 30.9 |
| Hijas e hijos | 18,481 | 12,237 | 1.5 |
| Nietas y nietos | 15,735 | 12,237 | 1.3 |
| Abuelas y abuelos | 23,855 | 12,237 | 1.9 |
| Madres y padres | 22,432 | 12,237 | 1.8 |
| Sobrinas y sobrinos | 84,550 | 12,237 | 6.9 |
| Hermanas y hermanos | 39,581 | 12,237 | 3.2 |
| Familia cercana | 80,477 | 12,237 | 6.6 |
| Familia extendida | 596,337 | 12,237 | 48.7 |
| Cercana o extendida | 676,315 | 12,237 | 55.3 |
[S.6]
# en_duelo_prev(): cuántas personas VIVAS en el año de referencia han perdido al
# menos un pariente del grupo en algún momento. Parte de p0_acum
# (probabilidades ACUMULADAS a lo largo de la vida) y usa la exposición del año
# de referencia. Devuelve un solo número.
en_duelo_prev <- function(tipos) {
p0_acum %>%
filter(kin %in% tipos) %>%
summarise(p0_acum = prod(p0_acum), .by = c(sex_focal, age_focal)) %>%
left_join(exposicion, by = c("sex_focal", "age_focal")) %>%
mutate(b = (1 - p0_acum) * exposicion) %>%
summarise(personas = sum(b)) %>%
pull(personas)
}
prev_kin <- tibble(kin = names(etiquetas_kin)) %>%
mutate(
personas = map_dbl(kin, en_duelo_prev),
etiqueta = recode(kin, !!!etiquetas_kin),
grupo = if_else(kin %in% parientes_cercanos, "Cercana", "Extendida")
)
prev_grupos <- tibble(
etiqueta = c("Familia cercana", "Familia extendida", "Cercana o extendida"),
personas = c(
en_duelo_prev(parientes_cercanos),
en_duelo_prev(parientes_extendidos),
en_duelo_prev(names(etiquetas_kin))
),
grupo = "Grupo"
)
prevalencia <- bind_rows(prev_kin %>% select(etiqueta, personas, grupo), prev_grupos) %>%
mutate(millones = personas / 1e6, pct = 100 * personas / poblacion)
orden_individual <- c(
"Hijas e hijos", "Madres y padres", "Hermanas y hermanos",
"Abuelas y abuelos", "Sobrinas y sobrinos", "Nietas y nietos",
"Primas y primos", "Tías y tíos"
)
orden_grupo <- c("Familia cercana", "Familia extendida", "Cercana o extendida")
prevalencia %>%
mutate(
panel = if_else(grupo == "Grupo", "Grupos de familia", "Parientes individuales"),
panel = factor(panel, levels = c("Parientes individuales", "Grupos de familia")),
etiqueta = factor(etiqueta, levels = rev(c(orden_individual, orden_grupo)))
) %>%
ggplot(aes(x = etiqueta, y = millones, fill = grupo)) +
geom_col() +
geom_text(aes(label = sprintf("%.1f M (%.0f%%)", millones, pct)), hjust = -0.1, size = 3) +
coord_flip() +
facet_grid(panel ~ ., scales = "free_y", space = "free_y") +
scale_fill_manual(values = c(
"Cercana" = "#b56576", "Extendida" = "#355070", "Grupo" = "grey55"
)) +
scale_y_continuous(expand = expansion(mult = c(0, 0.25))) +
labs(
title = paste0("Prevalencia acumulada del duelo en ", anio_final, " (", iso, ")"),
x = NULL, y = "Personas en duelo (millones)", fill = "Grupo"
) +
theme_bw()[S.7]
pop_final <- pais %>%
filter(year == anio_final) %>%
transmute(sex_focal = sex, age_focal = age, pop)
# p0_grupo(): probabilidad acumulada de NO haber perdido a ningún pariente del
# grupo, por sexo y edad de Focal. A diferencia de en_duelo_prev(), no
# multiplica por la población: devuelve la probabilidad, que es lo que necesita
# la pirámide.
p0_grupo <- function(tipos) {
p0_acum %>%
filter(kin %in% tipos) %>%
summarise(p0 = prod(p0_acum), .by = c(sex_focal, age_focal))
}
pC <- p0_grupo(parientes_cercanos) %>% rename(p0_C = p0)
pD <- p0_grupo(parientes_extendidos) %>% rename(p0_D = p0)
piramide <- pC %>%
left_join(pD, by = c("sex_focal", "age_focal")) %>%
left_join(pop_final, by = c("sex_focal", "age_focal")) %>%
mutate(
`No en duelo` = p0_C * p0_D * pop,
`Solo familia cercana` = (1 - p0_C) * p0_D * pop,
`Solo familia extendida` = p0_C * (1 - p0_D) * pop,
`Familia cercana y extendida` = (1 - p0_C) * (1 - p0_D) * pop
) %>%
pivot_longer(c(
`No en duelo`, `Solo familia cercana`,
`Solo familia extendida`, `Familia cercana y extendida`
), names_to = "categoria", values_to = "personas") %>%
mutate(
categoria = factor(categoria, levels = c(
"No en duelo", "Solo familia extendida",
"Familia cercana y extendida", "Solo familia cercana"
)),
personas = ifelse(sex_focal == "f", -personas, personas) / 1000
)
piramide %>%
ggplot(aes(x = age_focal, y = personas, fill = categoria)) +
geom_col(width = 1) +
geom_hline(yintercept = 0, linewidth = 0.2) +
coord_flip() +
scale_fill_manual(values = c(
"No en duelo" = "grey75",
"Solo familia extendida" = "#355070",
"Familia cercana y extendida" = "#eaac8b",
"Solo familia cercana" = "#b56576"
)) +
scale_y_continuous(labels = function(x) scales::comma(abs(x))) +
labs(
title = paste0("Población de ", iso, " en ", anio_final, " según duelo"),
subtitle = "Mujeres a la izquierda, hombres a la derecha",
x = "Edad", y = "Población (miles)", fill = "Situación de duelo"
) +
theme_bw() +
theme(legend.position = "bottom")Esta pestaña documenta todos los insumos que usamos
a lo largo del taller: qué contiene cada archivo, sus columnas, de dónde
viene y cómo citarlo o descargarlo. Todos los archivos de entrada se
distribuyen en la carpeta data/ del repositorio y pueden
descargarse directamente desde los enlaces de abajo.
Convención de columnas comunes: year = año calendario;
sex = sexo ("f" mujeres, "m"
hombres); age = edad en años simples (0–100).
Contenido:
Base demográfica del taller. Son los insumos (supervivencia, fecundidad y población) que alimentan los modelos de parentesco de todos los módulos de laboratorio. Se usan en los Módulos 1, 2, 4, 5, 6 y en las Soluciones.
| Archivo | Descripción |
|---|---|
data/wpp_latam_1950_2023/ |
Carpeta con un archivo .parquet por país
(wpp_<ISO3>.parquet). Cubre 20 países de América
Latina y el Caribe, 1950–2023 (los mismos 20 con datos de homicidio;
véase D.2). |
data/countries.csv |
Lista de países incluidos (iso3,
country). |
Columnas de cada
wpp_<ISO3>.parquet (una fila por
year × sex × age):
| Columna | Significado |
|---|---|
iso3, country |
código ISO3 y nombre del país |
year, sex, age |
año (1950–2023), sexo, edad (0–100) |
px |
probabilidad de sobrevivir de la edad x a x+1 |
qx, mx |
probabilidad y tasa de mortalidad por edad |
fx |
tasa de fecundidad específica por edad (mujeres; 0 en hombres) |
pop |
población al 1 de enero (personas) |
Fuente y referencia. United Nations, Department of Economic and Social Affairs, Population Division (2024). World Population Prospects 2024. Tablas de vida completas por sexo, fecundidad por edad simple y población por edad y sexo. Disponible en https://population.un.org/wpp/. Licencia CC BY 3.0 IGO.
Insumo del ejercicio del Módulo 4 y de las Soluciones: la mortalidad de referencia con la que se estima el duelo por violencia en el país elegido.
| Archivo | Descripción |
|---|---|
data/homicide_mortality_rates.csv |
Tasas de mortalidad por homicidio por país, año, sexo y edad. |
Columnas: iso3, year,
sex, age, mx (tasa de mortalidad
por homicidio). Cubre 20 países de la región y los años
2000–2023.
Fuente y referencia. Tasas específicas por homicidio calculadas a partir de estimaciones del Global Burden of Disease 2023 (GBD 2023) para 20 países de América Latina y el Caribe. Institute for Health Metrics and Evaluation (IHME), Global Burden of Disease Study 2023, https://www.healthdata.org/gbd.
Insumos para reproducir las estimaciones de duelo del conflicto
armado colombiano. Provienen del paquete de replicación del artículo
Weaponizing Kinship (Acosta et al. 2026).
Están en data/colombia/.
| Archivo | Descripción | Columnas |
|---|---|---|
col_demo_1950_2018.parquet |
Tasas demográficas colombianas 1950–2018: insumos del modelo de parentesco y base para el duelo. | year, sex, age,
fx (fecundidad), px (supervivencia),
nx (población usada como insumo del modelo),
pop (población), ax (fracción del último año
de vida, convierte mx→qx) |
col_homicidios_1985_2018.parquet |
Muertes por homicidio relacionadas con el conflicto, por año, sexo y edad de la víctima (1985–2018). Solo homicidios (se excluyen las desapariciones forzadas). Su suma sobre edad y sexo da el total del conflicto (~618 000), denominador de los multiplicadores de duelo. | year, sex, age,
dx (número de muertes) |
col_inmigrantes_2018.parquet |
Proporción de inmigrantes por sexo y edad (censo 2018). Se excluyen del riesgo de duelo porque no estuvieron expuestos al conflicto. | sex, age, imm_r (proporción
inmigrante) |
Fuente y referencia. Acosta, E., Alburez-Gutierrez, D., Gargiulo, M., & Torres, C. (2026). Weaponizing Kinship: A Demographic Analysis of Bereavement in the Colombian Conflict. Population and Development Review. doi:10.1111/padr.70048. Los datos originales del conflicto provienen del Joint Non-State Actor and State Violence dataset del proyecto de estadística de derechos humanos; véase el artículo y su material de replicación para el detalle de fuentes y métodos.
La microsimulación reutiliza las mismas tasas del WPP (D.1), convertidas al formato de texto que exige SOCSIM. El único archivo propio de esta parte es el archivo de supervisión que ya viene en el repositorio; las funciones que escriben las tasas se definen dentro del propio Módulo 5.
| Archivo | Descripción |
|---|---|
socsim/socsim_Colombia.sup |
Archivo de supervisión de SOCSIM: define los segmentos a simular (100 años de calentamiento con tasas de 1950 y luego un segmento por año hasta 2023). |
Fuente y referencia. SOCSIM: Hammel et al. (1976); Mason (2016).
Interfaz en R rsocsim: https://mpidr.github.io/rsocsim/. Las tasas de entrada
provienen del WPP 2024 (D.1). Esta parte está adaptada del taller de la
Escuela ALAP 2024.
Los siguientes archivos no se distribuyen: son
salidas que el propio código crea la primera vez que se ejecuta y luego
reutiliza (caché). Aparecen en el .gitignore.
| Archivo | Se genera en | Descripción |
|---|---|---|
data/colombia/col_kin_1985_2018.parquet |
Módulo 2 (lo reutiliza el Módulo 3) | Estructura de parentesco de Colombia (salida de
kin2sex), ~21 MB. |
data/kin_<ISO3>.parquet |
Soluciones (Módulo 4) | Estructura de parentesco del país elegido. |
data/socsim_resultados.rds |
Módulo 5 | Población (.opop) y matrimonios (.omar)
simulados. |
data/socsim_kinloss.rds |
Módulo 6 | Pérdida y disponibilidad de parientes estimadas de la simulación. |
socsim/rates/, socsim/presim.opop,
socsim/presim.omar, socsim/sim_results_*/ |
Módulos 5–6 | Archivos intermedios de SOCSIM. |
Como estos archivos se regeneran a partir de los insumos documentados arriba, no es necesario descargarlos: se crean solos al correr los laboratorios en orden.
Referencias citadas a lo largo del taller: