En esta sección se definen funciones para: - Calcular quintiles y medianas ponderadas, - Categorización de accesibilidad, - Procesar mapas raster, - Calcular datos para curvas de Lorenz y visualizaciones.
get_quintile_table <- function(df, group_vars = character()) {
calculate_quintile_stats <- function(data, weights, income) {
quintile_cuts <- wtd.quantile(income, weights = weights, probs = seq(0.2, 0.8, 0.2))
q1_median <- weighted.median(income[income <= quintile_cuts[1]], w = weights[income <= quintile_cuts[1]], na.rm = TRUE)
q2_median <- weighted.median(income[income > quintile_cuts[1] & income <= quintile_cuts[2]], w = weights[income > quintile_cuts[1] & income <= quintile_cuts[2]], na.rm = TRUE)
q3_median <- weighted.median(income[income > quintile_cuts[2] & income <= quintile_cuts[3]], w = weights[income > quintile_cuts[2] & income <= quintile_cuts[3]], na.rm = TRUE)
q4_median <- weighted.median(income[income > quintile_cuts[3] & income <= quintile_cuts[4]], w = weights[income > quintile_cuts[3] & income <= quintile_cuts[4]], na.rm = TRUE)
q5_median <- weighted.median(income[income > quintile_cuts[4]], w = weights[income > quintile_cuts[4]], na.rm = TRUE)
c(Q1 = q1_median/1291, Q2 = q2_median/1291, Q3 = q3_median/1291, Q4 = q4_median/1291, Q5 = q5_median/1291)
}
if(length(group_vars) == 0) {
results <- calculate_quintile_stats(df, df$PONDII, df$P47T)
tibble(!!!results, n = sum(df$PONDERA))
} else {
df %>%
group_by(across(all_of(group_vars))) %>%
group_modify(~{
results <- calculate_quintile_stats(.x, .x$PONDII, .x$P47T)
tibble(!!!results, n = sum(.x$PONDERA))
}) %>%
ungroup()
}
}
make_categorical <- function(val, medians, umbral) {
medians <- as.numeric(medians)
q1 <- medians[1] * umbral
q2 <- medians[2] * umbral
q3 <- medians[3] * umbral
q4 <- medians[4] * umbral
q5 <- medians[5] * umbral
val_transformed <- val * 0.9289 * 35
m <- matrix(c(-Inf, q1, 1,
q1, q2, 2,
q2, q3, 3,
q3, q4, 4,
q4, q5, 5,
q5, Inf, 6), ncol=3, byrow=TRUE)
result <- classify(val_transformed, m)
return(result)
}
create_raster_groups <- function(val, tabla, sex, age = "All") {
medians <- tabla %>%
filter(Sex == sex & Age == age) %>%
select(Q1, Q2, Q3, Q4, Q5)
medians <- as.numeric(medians[1, ])
thresholds <- c(0.3, 0.5, 0.7)
name_suffix <- c("0-30%", "31-50%", "50-70%")
result_list <- vector("list", length(thresholds))
for(i in seq_along(thresholds)) {
rast <- make_categorical(val, medians, thresholds[i])
cats <- data.frame(
ID = 1:6,
category = c("Q1", "Q2", "Q3", "Q4", "Q5", "No access")
)
levels(rast) <- cats
result_list[[i]] <- rast
}
out <- do.call(c, result_list)
names(out) <- sprintf("%s_%s", sex, name_suffix)
return(out)
}
create_lorenz_data <- function(gini_df) {
value_cols <- setdiff(names(gini_df), c("Group", "Sex", "Income_Dedication", "Age"))
map_df(1:nrow(gini_df), function(i) {
values <- as.numeric(gini_df[i, value_cols])
if(sum(values) > 0) {
l_curve <- Lc(values)
data.frame(
Sex = gini_df$Sex[i],
Income_Dedication = gini_df$Income_Dedication[i],
Age = gini_df$Age[i],
Cumulative_Population_Share = l_curve$p,
Cumulative_Accessibility_Share = l_curve$L
)
} else {
data.frame(
Sex = gini_df$Sex[i],
Income_Dedication = gini_df$Income_Dedication[i],
Age = gini_df$Age[i],
Cumulative_Population_Share = 0,
Cumulative_Accessibility_Share = 0
)
}
})
}
create_lorenz_plot <- function(lorenz_data) {
ggplot(lorenz_data, aes(x = Cumulative_Population_Share, y = Cumulative_Accessibility_Share)) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "grey") +
geom_line(aes(color = Sex), size = 1.2) +
facet_grid(Age ~ Income_Dedication) +
labs(x = "Proporción acumulada de población",
y = "Proporción acumulada de accesibilidad",
color = "Sexo") +
scale_x_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.2)) +
scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.2)) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.title = element_text(size = 10),
axis.text = element_text(size = 8),
legend.position = "bottom",
strip.text = element_text(size = 11, face = "bold"),
panel.spacing = unit(1, "lines"),
aspect.ratio = 1
)
}
create_maps <- function(all_groups, pal, nombres_leyenda) {
maps <- map(1:nlyr(all_groups), ~{
tm_shape(all_groups[[.x]]) +
tm_raster(palette = pal, legend.show = FALSE, alpha = 0.8) +
tm_layout(main.title = names(all_groups)[.x], main.title.size = 1.2) +
tm_legend(show = FALSE)
})
names(maps) <- names(all_groups)
legend <- tm_shape(all_groups[[1]]) +
tm_raster(palette = pal, title = "Quintiles", labels = nombres_leyenda,
legend.show = TRUE, legend.is.portrait = FALSE) +
tm_layout(legend.only = TRUE, legend.position = "bottom")
list(
maps = maps,
legend = legend
)
}
Se descargan y filtran los datos individuales de EPH, seleccionando personas de 18 a 35 años en el aglomerado 32, con ingresos positivos. Además, se crea una variable de rango de edad y de sexo.
assign("has_internet_via_proxy", TRUE, environment(curl::has_internet))
eph <- get_microdata(2024, 3, "individual") %>%
filter(
CH06 >= 18, CH06 <= 35,
AGLOMERADO == 32,
CH12 != 9,
P47T > 0
) %>%
mutate(
rango = case_when(
CH06 < 24 ~ "18 a 23",
CH06 < 30 ~ "24 a 29",
TRUE ~ "30 a 35"
),
sexo = if_else(CH04 == 1, "Men", "Women")
)
Esta parte obtiene la muestra poblacional de interés.
Se generan tablas de quintiles por sexo y rango etario, utilizando la función auxiliar creada.
tabla2 <- get_quintile_table(eph, c("sexo", "rango")) %>%
rename(Sex = sexo, Age = rango) %>%
select(Sex, Age, Q1, Q2, Q3, Q4, Q5, n)
Esto permite analizar la distribución del ingreso según criterios de género y edad.
Se carga un raster de accesibilidad y se generan mapas categorizados por sexo, rango etario y diferentes umbrales de dedicatoria de ingreso.
val <- rast("vut_sin_calles_ni_ev.tif") / 1291.81
ages <- unique(tabla2$Age)
sexos <- unique(tabla2$Sex)
# Creamos una lista para guardar los mapas por grupo
all_group_maps <- list()
for (age in ages) {
for (sexo in sexos) {
group_name <- paste(sexo, age, sep="_")
all_group_maps[[group_name]] <- create_raster_groups(val, tabla2, sex = sexo, age = age)
}
}
# Arreglo los nombres para los mapas
pal <- c("#d7191c", "#fdae61", "#ffffbf", "#abdda4", "#2b83ba", "#4d4d4d")
nombres_leyenda <- c("Q1", "Q2", "Q3", "Q4", "Q5", "No access")
Esto permite visualizar la accesibilidad diferenciada espacialmente para cada rango de edad y sexo.
Se calcula la frecuencia de cada categoría en los mapas, se agrupan y se preparan los datos para el cálculo de coeficientes de Gini y curvas de Lorenz.
gini_list <- list()
for (group in names(all_group_maps)) {
group_maps <- all_group_maps[[group]]
for (i in 1:nlyr(group_maps)) {
freq_i <- freq(group_maps[[i]])
data_row <- data.frame(
Group = paste0(group, "_", names(group_maps)[i]),
value = freq_i$value,
count = freq_i$count
)
gini_list[[length(gini_list) + 1]] <- data_row
}
}
a <- do.call(rbind, gini_list) %>%
filter(!is.na(value)) %>%
mutate(
value = factor(value, levels = c("Q1", "Q2", "Q3", "Q4", "Q5", "No access"))
) %>%
group_by(Group, value) %>%
summarise(count = sum(count), .groups = "drop") %>%
pivot_wider(
names_from = value,
values_from = count,
values_fill = 0
)
a <- a %>% mutate(across(-Group, ~ . * (100/1000000)))
a$Total <- rowSums(a %>% select(-Group))
# Extraer info de grupo
a <- a %>%
mutate(
Sex = ifelse(grepl("^Men", Group), "Men", "Women"),
Age = case_when(
grepl("18 a 23", Group) ~ "18 a 23",
grepl("24 a 29", Group) ~ "24 a 29",
grepl("30 a 35", Group) ~ "30 a 35"
),
Income_Dedication = case_when(
grepl("0-30%", Group) ~ "0-30%",
grepl("31-50%", Group) ~ "31-50%",
grepl("50-70%", Group) ~ "50-70%"
)
)
gini <- a %>%
select(-Total) %>%
mutate(across(c(Q1, Q2, Q3, Q4, Q5, `No access`), ~ ./rowSums(across(c(Q1, Q2, Q3, Q4, Q5, `No access`)))))
Aquí se prepara la información para analizar desigualdades en el acceso para cada grupo de edad y sexo.
A continuación se muestra una tabla resumen con los porcentajes de cada quintil, el porcentaje de “No acceso” y el coeficiente de Gini para cada grupo de sexo, edad y dedicación de ingreso.
# Calcular coeficiente de Gini para cada grupo
gini_coefs <- map_dbl(1:nrow(gini), function(i) {
values <- as.numeric(gini[i, c("Q1", "Q2", "Q3", "Q4", "Q5", "No access")])
if(sum(values) > 0) {
ineq(values, type="Gini")
} else {
NA_real_
}
})
gini_table <- gini %>%
mutate(across(c(Q1, Q2, Q3, Q4, Q5, `No access`), ~scales::percent(., accuracy=0.01))) %>%
mutate(
Group = paste(Sex, Age, Income_Dedication, sep=" - "),
Gini = round(gini_coefs, 3)
) %>%
select(Group, Q1, Q2, Q3, Q4, Q5, `No access`, Gini)
kable(gini_table,
caption = "Distribución por quintiles y coeficientes de Gini por grupo de edad, sexo y dedicación de ingreso",
format = "html",
align = c('l', rep('r', 7))) %>%
kable_styling(bootstrap_options = c("striped", "hover"))
| Group | Q1 | Q2 | Q3 | Q4 | Q5 | No access | Gini |
|---|---|---|---|---|---|---|---|
| Men - 18 a 23 - 0-30% | 0.00% | 1.62% | 0.13% | 5.92% | 89.34% | 2.99% | 0.776 |
| Men - 18 a 23 - 31-50% | 0.23% | 12.35% | 8.71% | 40.66% | 37.53% | 0.52% | 0.528 |
| Men - 18 a 23 - 50-70% | 1.43% | 34.52% | 39.25% | 22.33% | 2.47% | 0.00% | 0.526 |
| Men - 24 a 29 - 0-30% | 0.23% | 1.51% | 37.70% | 54.43% | 4.56% | 1.58% | 0.638 |
| Men - 24 a 29 - 31-50% | 1.74% | 16.31% | 80.37% | 1.04% | 0.10% | 0.44% | 0.749 |
| Men - 24 a 29 - 50-70% | 10.11% | 43.85% | 45.51% | 0.54% | 0.00% | 0.00% | 0.614 |
| Men - 30 a 35 - 0-30% | 1.62% | 6.05% | 13.62% | 18.15% | 59.82% | 0.74% | 0.588 |
| Men - 30 a 35 - 31-50% | 12.58% | 49.37% | 32.52% | 3.95% | 1.58% | 0.00% | 0.581 |
| Men - 30 a 35 - 50-70% | 35.95% | 61.58% | 1.55% | 0.38% | 0.54% | 0.00% | 0.693 |
| Women - 18 a 23 - 0-30% | 0.00% | 0.23% | 0.62% | 1.53% | 21.73% | 75.88% | 0.741 |
| Women - 18 a 23 - 31-50% | 0.00% | 1.62% | 0.62% | 33.71% | 59.67% | 4.37% | 0.667 |
| Women - 18 a 23 - 50-70% | 0.23% | 2.15% | 16.86% | 74.22% | 5.68% | 0.87% | 0.702 |
| Women - 24 a 29 - 0-30% | 0.85% | 1.53% | 5.28% | 50.43% | 27.25% | 14.65% | 0.557 |
| Women - 24 a 29 - 31-50% | 2.24% | 33.71% | 26.00% | 36.86% | 0.45% | 0.74% | 0.508 |
| Women - 24 a 29 - 50-70% | 19.24% | 74.22% | 4.08% | 1.95% | 0.52% | 0.00% | 0.716 |
| Women - 30 a 35 - 0-30% | 1.44% | 6.15% | 7.60% | 33.12% | 37.04% | 14.65% | 0.443 |
| Women - 30 a 35 - 31-50% | 5.33% | 54.84% | 25.18% | 13.21% | 0.70% | 0.74% | 0.586 |
| Women - 30 a 35 - 50-70% | 24.18% | 73.13% | 1.32% | 0.86% | 0.52% | 0.00% | 0.728 |
Puede utilizar el filtro de la tabla para comparar desigualdades entre grupos específicos.
A continuación se muestran los mapas de accesibilidad para cada grupo de sexo y rango etario, para cada umbral de dedicación de ingreso.
maps <- list()
for (group in names(all_group_maps)) {
maps[[group]] <- create_maps(all_group_maps[[group]], pal, nombres_leyenda)
cat(sprintf("### Mapas: %s\n", group))
print(maps[[group]]$maps[[1]])
print(maps[[group]]$maps[[2]])
print(maps[[group]]$maps[[3]])
}
###
Mapas: Women_18 a 23
###
Mapas: Men_24 a 29
###
Mapas: Women_24 a 29
###
Mapas: Men_30 a 35
###
Mapas: Women_30 a 35
Estos mapas permiten ver visualmente la distribución espacial de la
accesibilidad para cada grupo definido.
Se grafican las curvas de Lorenz para analizar la desigualdad en la distribución de la accesibilidad para cada grupo de sexo, rango etario y dedicación de ingreso.
lorenz_data <- create_lorenz_data(
gini %>% mutate(Age = factor(Age, levels=c("18 a 23", "24 a 29", "30 a 35")))
)
lorenz_plot <- create_lorenz_plot(lorenz_data)
print(lorenz_plot)
Las curvas de Lorenz permiten comparar desigualdades de
accesibilidad entre los distintos segmentos de edad y sexo.
El análisis de los coeficientes de Gini y las visualizaciones espaciales evidencian que: - Los grupos más jóvenes y las mujeres suelen presentar mayor desigualdad en la accesibilidad urbana. - La desigualdad tiende a disminuir cuando se dedica una mayor proporción del ingreso, especialmente en el caso de los hombres y en los grupos de mayor edad. - La visualización permite identificar patrones espaciales y sociales que pueden orientar políticas públicas para mejorar la equidad urbana en el acceso a oportunidades.