La indigencia es uno de los problemas sociales más grandes de Colombia; personas que han sido afectadas por factores externos como los desplazamientos, desempleo, drogadicción, entre otras, en su mayoría no pueden disfrutar de sus derechos fundamentales, ya que no están al alcance. Esto demuestra la incapacidad del Estado para formular políticas sociales que respondan a esta realidad. Actualmente, este conjunto de personas denominado HDC (Habitante De Calle) es definido en Colombia, como aquel sujeto que, independiente de su género, condición étnica o edad, hace de la calle su lugar de habitación, ya sea de manera permanente o transitoria (Ley 1641, 2013) . A diferencia de estos, una persona en situación de calle hace del espacio público el escenario propio para su supervivencia, aunque su estancia es temporal, dado que cuenta con un lugar privado donde estar y pernoctar (Ministerio de Salud y Protección Social, 2018).
library(leaflet)
library(sf)
library(rnaturalearth)
library(rnaturalearthdata)
# --- 1. Cargar mapa base de Colombia ---
colombia <- ne_countries(scale = "medium", country = "Colombia", returnclass = "sf")
# --- 2. Coordenadas de las ciudades ---
ciudades <- data.frame(
Ciudad = c("Bogotá", "Medellín", "Cali"),
Lat = c(4.7110, 6.2442, 3.4516),
Lon = c(-74.0721, -75.5812, -76.5320),
Color = c("red", "blue", "green")
)
# --- 3. Crear el mapa interactivo ---
mapa <- leaflet() %>%
addProviderTiles(providers$CartoDB.Positron, group = "Mapa Claro") %>% # vista clara
addProviderTiles(providers$Esri.WorldImagery, group = "Satelital") %>% # vista satelital
addPolygons(
data = colombia,
fillColor = "lightyellow",
color = "darkgreen",
weight = 2,
fillOpacity = 0.3,
label = "Colombia"
) %>%
addCircleMarkers(
data = ciudades,
lng = ~Lon, lat = ~Lat,
radius = 9,
color = ~Color,
stroke = TRUE,
fillOpacity = 0.9,
label = ~Ciudad,
popup = ~paste0("<b>", Ciudad, "</b><br>Latitud: ", Lat, "<br>Longitud: ", Lon)
) %>%
addLegend(
position = "bottomright",
colors = ciudades$Color,
labels = ciudades$Ciudad,
title = "Ciudades resaltadas"
) %>%
addLayersControl(
baseGroups = c("Mapa Claro", "Satelital"),
options = layersControlOptions(collapsed = FALSE)
) %>%
setView(lng = -74.3, lat = 4.5, zoom = 6)
# --- 4. Mostrar el mapa ---
mapa
Teniendo en cuenta este contexto social, la presente investigación parte del reconocimiento de los HDC y de las circunstancias adversas que afectan su estado de salud, ya sea a causa de accidentes o enfermedades. Para ello, se utiliza un conjunto de datos obtenido a partir de una encuesta realizada por el Departamento Administrativo Nacional de Estadística (DANE) en Colombia, la cual recopila información de diferentes casos que permiten analizar y predecir si una persona ha presentado o no algún tipo de problema de salud. En este estudio, dicha condición se representa mediante la variable dependiente P17. Los datos censales utilizados corresponden a registros recolectados en las tres principales ciudades del país: Bogotá, Medellín y Cali.
El objetivo de este trabajo es analizar los factores que pueden influir en que una persona habitante de calle haya presentado o no algún problema de salud, utilizando técnicas de clasificación basadas en modelos de aprendizaje automático. Para ello, se desarrollará un análisis predictivo que permitirá identificar patrones y relaciones entre distintas variables sociales, de consumo y de actividad económica, con el fin de comprender los factores que inciden en la salud de esta población vulnerable.
La metodología adoptada para esta investigación se desarrolló en varias etapas. En primer lugar, se llevó a cabo un proceso de depuración y selección de variables con base en criterios de relevancia analítica y significado dentro del estudio. Se descartaron aquellas variables que no presentaban una relación significativa con el estado de salud, como el sexo o la edad, para focalizar el análisis en aspectos sociales, económicos y de consumo que pudieran incidir directamente en la aparición de enfermedades o accidentes.
Posteriormente, se realizó un análisis exploratorio y descriptivo de los datos, que permitió identificar tendencias, distribuciones, correlaciones y posibles inconsistencias en la información. Esta fase fue fundamental para comprender la estructura interna del conjunto de datos y para garantizar la calidad de la información utilizada en los modelos predictivos. Además, se aplicaron técnicas de balanceo de clases con el fin de corregir posibles desequilibrios en la variable dependiente (presencia o ausencia de problemas de salud), asegurando así un desempeño más robusto y confiable de los modelos
La información fue obtenida a partir de los microdatos del Departamento Administrativo Nacional de Estadística (DANE), recolectados mediante entrevistas personales en papel aplicadas por personal capacitado en las tres principales ciudades del país: Bogotá, Medellín y Cali. El operativo combinó estrategias como barridos calle a calle, puntos fijos y brigadas de atención, en coordinación con las alcaldías locales.
El proceso contó con supervisión continua para garantizar la calidad y cobertura, usando indicadores de avance y consistencia. Además, se empleó cartografía censal y georreferenciación para planificar los recorridos y asegurar una recolección completa y confiable.
P23S1R: La variable P23S1R corresponde a la pregunta “¿Cuánto tiempo lleva viviendo en la calle?” y se registra en años, con un rango que va de 0 a 60, donde 0 indica que la persona lleva solo meses y aún no ha cumplido un año en situación de calle. Esta variable se puede relacionar con la P17, que identifica si la persona ha tenido : accidentes o problemas de salud en los últimos 30 días, ya que se puede suponer que a mayor tiempo viviendo en la calle, mayor es la exposición a condiciones adversas como el clima, la falta de acceso a servicios de salud, la alimentación inadecuada o el riesgo de violencia, lo cual incrementa la probabilidad de sufrir problemas de salud o accidentes recientes. Por lo tanto, establecer esta relación permite explorar cómo el deterioro asociado al tiempo en calle impacta en el bienestar físico de las personas en situación de calle.
P29: La variable P29, corresponde a “Principalmente, ¿cómo consigue usted dinero?”, cuyas respuestas son:
Se seleccionó esta variable debido a que el tipo de actividad económica puede influir en la exposición a riesgos; los oficios y el reciclaje tienden a implicar más accidentes laborales, mientras que las actividades de calle se asocian a problemas de salud derivados de la interacción constante con el entorno urbano.
P31:La variable P31 corresponde a la pregunta: ¿Usted sabe si la alcaldía tiene programas donde se atiende a los habitantes de la calle? esta se incorpora con el propósito de analizar si el conocimiento de programas sociales municipales influye o se relaciona con el estado de salud reciente de la persona (haber estado o no enfermo en los últimos 30 días). El conocimiento de estos programas puede reflejar mayor integración social, acceso a información pública y cercanía a servicios de salud, factores que podrían reducir la probabilidad de enfermedad.Por el contrario, el desconocimiento de tales programas podría asociarse con menor acceso a apoyo institucional o información preventiva, aumentando el riesgo de afectaciones en la salud. Asi mismo, esta variable aporta valor al modelo al permitir evaluar si existen diferencias significativas en la probabilidad de haber estado enfermo según el nivel de conocimiento institucional, contribuyendo así a un análisis más integral de la relación entre factores sociales y condiciones de salud en la población encuestada.
P30:La variable P30 corresponde a la pregunta: “¿Qué droga consume principalmente?”, cuyas opciones de respuesta son: 1.Cigarrillo 2.Alcohol 3.Marihuana 4.Cocaína 6.Basuco 7.Heroína 8.Pepas
Esta variable resulta especialmente relevante, ya que el consumo de sustancias psicoactivas puede incidir directamente en el estado de salud y en la probabilidad de sufrir accidentes; en efecto, el uso de este tipo de sustancias puede alterar la percepción, la coordinación motora, el juicio y la toma de decisiones, incrementando el riesgo de sufrir accidentes o enfermedades. De este modo, el análisis en conjunto con la variable objetivo permite investigar cómo el tipo de sustancia predominante en el consumo de un individuo actúa como factor de riesgo de estos incidentes.
library(readxl)
library(tidyverse)
library(caret)
library(class)
library(ROCR)
set.seed(28)
# ----------------------------------------------------
# Cargar y preparar los datos
# ----------------------------------------------------
ENCUESTA <- read_excel("ENCUESTA.xlsx")
View(ENCUESTA)
ENCUESTA$grupo_P29 <- ifelse(ENCUESTA$P29 %in% c(3, 5), "GRUPO 1",
ifelse(ENCUESTA$P29 %in% c(1, 2, 4), "GRUPO 2", NA))
ENCUESTA$grupo_P30 <- ifelse(ENCUESTA$P30 %in% c(1,2,3,4), "LEGALES",
ifelse(ENCUESTA$P30 %in% c(5,6,7,8), "ILEGALES", NA))
ENCUESTA <- ENCUESTA %>%
mutate(
P17 = factor(P17, levels = c(1, 2), labels = c("Si", "No")),
P31 = factor(P31, levels = c(1, 2), labels = c("Si", "No")),
P13 = factor(P13, levels = c(1, 2, 3), labels = c("CALLE", "DORMITORIO", "INSTITUCION")),
grupo_P29 = factor(grupo_P29, levels = c("GRUPO 1", "GRUPO 2")),
grupo_P30 = factor(grupo_P30, levels = c("LEGALES", "ILEGALES"))
) %>%
select(P17, P13, P23S1R, P31, grupo_P29, grupo_P30)
Inicialmente, se realizó un proceso de limpieza y organización de los datos, eliminando registros incompletos o inconsistentes de las variables categóricas para facilitar el análisis. Posteriormente, se llevó a cabo un análisis exploratorio con el fin de identificar las variables que podrían tener mayor relevancia en la explicación de los resultados de la encuesta.
Durante este proceso, se observó que la variable P17 presentaba un desequilibrio entre las categorías “Sí” y “No”, lo cual podría afectar la precisión de los análisis. Por esta razón, se aplicó un balance de la variable dependiente, ajustando la proporción de casos para lograr una distribución más equitativa entre ambas respuestas. Este procedimiento permitió obtener una base de datos más precisa y representativa, reduciendo el sesgo y mejorando la calidad de los resultados. Quedando el grupo de “Sí” con 523 y el grupo de “No” con 494.
Por otro lado, la variable P29, fue reclasificada para reducir la dispersión de respuestas y permitir comparaciones más equilibradas, agrupando las categorías originales en dos grupos con sentido analítico y coherencia conceptual:
Se implementó el mismo procedimiento con la variable P30, correspondiente al tipo de sustancia alucinógena que se consume principalmente; fue reclasificada con el objetivo de reducir la distancia de respuesta entre los perfiles de comparación y facilitar la interpretación de los datos. Inicialmente, esta variable presentaba una diversidad amplia de categorías que dificulta la identificación de patrones claros de consumo; por esa razón, se optó por agruparlas en dos subgrupos:
Se realizó el mismo procedimiento con la variable P13, cuyo propósito es identificar el espacio principal en el que una persona habitante de calle pasa la noche o descansa de manera recurrente, con la diferencia de que inicialmente estaba codificada numéricamente con las opciones (1.Calle, 2. Dormitorio transitorio, 3. Instituto), donde las categorías numéricas fueron transformadas a etiquetas nominales descriptivas, quedando de la siguiente manera: 1. CALLE 2. DORMITORIO 3. INSTITUTO
Esta recodificación permite que las categorías sean identificadas de manera directa y significativa, evitando la ambigüedad de los códigos numéricos y contribuyendo a una representación más clara de las condiciones de las personas habitantes de calle; además, favorece el uso de la variable en modelos de clasificación y otros procedimientos analíticos, al proporcionar etiquetas más interpretables y coherentes con el contexto del estudio.
En la fase de modelamiento, se implementaron dos técnicas de clasificación supervisada:
K-Nearest Neighbors (kNN), un modelo basado en la similitud entre observaciones que permite predecir la clase de un individuo a partir de sus vecinos más cercanos en el espacio de características.
Regresión Logística (Logit), un modelo probabilístico que estima la relación entre la variable dependiente dicotómica y un conjunto de variables independientes mediante la función logística.
Ambos modelos fueron entrenados y validados utilizando particiones de los datos de entrenamiento y prueba, y posteriormente evaluados a través de métricas de desempeño como la exactitud, la precisión, la sensibilidad y el área bajo la curva ROC (AUC).
El propósito final de este enfoque metodológico es determinar la capacidad predictiva de cada modelo y evaluar la influencia de los factores sociales y de subsistencia en la salud de los habitantes de calle durante los últimos 30 días, generando evidencia empírica que contribuya a la comprensión de este fenómeno desde una perspectiva cuantitativa y aplicada.
A continuación, se realiza un análisis descriptivo con el fin de conocer cómo se comportan las variables del estudio, que ofrece una visión general de las características de la población encuestada y de los principales factores sociales asociados a la condición de habitante de calle.
A través de estadísticas descriptivas (frecuencias, porcentajes y gráficos), se busca identificar patrones, tendencias y relaciones preliminares entre las variables sociales, económicas y de salud. Esto permite comprender el contexto en el que se encuentran los participantes antes de realizar el análisis predictivo o inferencial.
#Tabla de descriptivas
tabla_P23S1R <- ENCUESTA |> summarise(
media = mean(P23S1R, na.rm = TRUE),
mediana = median(P23S1R, na.rm = TRUE),
minimo = min(P23S1R, na.rm = TRUE),
maximo = max(P23S1R, na.rm = TRUE),
desviacion = sd(P23S1R, na.rm = TRUE),
varianza = var(P23S1R, na.rm = TRUE))
#Mostrar
library(knitr)
library(kableExtra)
kbl(tabla_P23S1R, caption = "P23S1R - Tiempo que lleva viviendo en la calle en años", align = "c") |>
kable_classic(full_width = FALSE, html_font = "Cambria") |>
kable_styling(latex_options = "striped", position = "center")
| media | mediana | minimo | maximo | desviacion | varianza |
|---|---|---|---|---|---|
| 10.49754 | 6 | 0 | 60 | 11.29352 | 127.5435 |
En la Tabla 1, los datos revelan que, en promedio, el tiempo que llevan viviendo en la calle es de aproximadamente 10.5 años, sin embargo, la mediana es de 6 años, lo que indica que la mitad de las personas han estado en esta situación durante 6 años o menos. Esta diferencia, nos sugiere la presencia de valores atípicos que elevan el promedio. Esto se confirma con un rango muy amplio entre 0 a 60 años, y una alta desviación de 11.29, que muestra una gran dispersión o desigualdad en las experiencias, es decir, mientra una parte significativa de la población ha vivido en la calle durante muy poco tiempo, otro pequeño porcentaje de la población lleva décadas en esta situación, distorsionando la media hacia arriba.
En la Tabla 2, los resultados son notablemente equilibrados, se observa que 523 personas, que representan el 51.4% del total de la muestra, declararon haber sufrido accidentes o problemas de salud en el último mes; este dato es muy significativo, ya que indica que más de la mitad de la población estudiada se vio afectada por una condición de salud en un periodo de 30 días , esto nos permite inferir una alta morbilidad dentro de esta población, posiblemente asociada a las condiciones de vida extremas que llevan. Por otro lado, 494 personas, que representan el 48,6% indicaron no haber tenido ningún problema de salud durante este lapso de tiempo; la cercanía al 50% resalta la vulnerabilidad sanitaria de la población, donde presentar un problema de salud o accidentarse es una probabilidad casi tan alta como no hacerlo en cualquier mes dado.
library(readxl)
library(dplyr)
library(knitr)
library(kableExtra)
ENCUESTA <- read_excel("ENCUESTA.xlsx")
ENCUESTA$grupo_P29 <- ifelse(ENCUESTA$P29 %in% c(3, 5), "GRUPO 1",
ifelse(ENCUESTA$P29 %in% c(1, 2, 4), "GRUPO 2", NA))
ENCUESTA$grupo_P30 <- ifelse(ENCUESTA$P30 %in% c(1,2,3,4), "LEGALES",
ifelse(ENCUESTA$P30 %in% c(5,6,7,8), "ILEGALES", NA))
tabla_P17 <- ENCUESTA %>%
count(P17) %>%
mutate(prop = round(n / sum(n) * 100, 1))
kbl(tabla_P17, caption = "P17 - Accidentes o problemas de salud en los últimos 30 días", align = "c") |>
kable_classic(full_width = FALSE, html_font = "Cambria") |>
kable_styling(latex_options = "striped", position = "center")
| P17 | n | prop |
|---|---|---|
| 1 | 523 | 51.4 |
| 2 | 494 | 48.6 |
library(readxl)
library(dplyr)
library(ggplot2)
library(knitr)
library(kableExtra)
# Cargar datos
ENCUESTA <- read_excel("ENCUESTA.xlsx")
# Crear variables agrupadas
ENCUESTA$grupo_P29 <- ifelse(ENCUESTA$P29 %in% c(3, 5), "GRUPO 1",
ifelse(ENCUESTA$P29 %in% c(1, 2, 4), "GRUPO 2", NA))
ENCUESTA$grupo_P30 <- ifelse(ENCUESTA$P30 %in% c(1,2,3,4), "LEGALES",
ifelse(ENCUESTA$P30 %in% c(5,6,7,8), "ILEGALES", NA))
# Tabla
tabla_P13 <- ENCUESTA %>%
count(P13) %>%
mutate(prop = round(n/sum(n)*100, 1))
kbl(tabla_P13, caption = "P13 - ¿Dónde duerme habitualmente?", align = "c") %>%
kable_classic(full_width = FALSE, html_font = "Cambria") %>%
kable_styling(latex_options = "striped", position = "center")
| P13 | n | prop |
|---|---|---|
| 1 | 810 | 79.6 |
| 2 | 82 | 8.1 |
| 3 | 125 | 12.3 |
# Gráfico
ENCUESTA$P13 <- factor(ENCUESTA$P13,
levels = c(1, 2, 3),
labels = c("Calle", "Hogar o albergue", "Otro lugar"))
ENCUESTA$P17 <- factor(ENCUESTA$P17,
levels = c(1, 2),
labels = c("Sí", "No"))
ggplot(ENCUESTA, aes(x = P13, fill = P17)) +
geom_bar(position = "dodge", width = 0.7, color = "white") +
scale_fill_manual(values = c("Sí" = "#3B82F6", "No" = "#EF4444")) +
labs(
title = "Lugar donde duerme vs. enfermedad reciente",
x = "Lugar donde duerme habitualmente (P13)",
y = "Número de personas",
fill = "¿Se enfermó en los últimos 30 días? (P17)"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 15),
axis.text.x = element_text(size = 12),
legend.position = "top"
)
De la Tabla 3, los datos revelan una distribución muy clara, donde la opción mayoritaria, con 810 personas, que representan el 79.6%, es dormir en la calle. Esto indica que la gran mayoría de la población encuestada vive en exposición y vulnerabilidad extrema. En contraste, solo un 8.1% accede a un dormitorio transitorio y un 12.3% en una institución. Esto indica que menos de una quinta parte de la población cuenta con un techo y/o alojamiento formal, destacando una cobertura insuficiente de espacios seguros estables para descansar. En el diagrama se aprecia con mayor claridad que la mayoría de las personas encuestadas duermen en la calle, y dentro de este grupo se concentran tanto quienes se enfermaron como quienes no lo hicieron en los últimos 30 días.
library(readxl)
library(dplyr)
library(knitr)
library(kableExtra)
ENCUESTA <- read_excel("ENCUESTA.xlsx")
ENCUESTA$grupo_P29 <- ifelse(ENCUESTA$P29 %in% c(3, 5), "GRUPO 1",
ifelse(ENCUESTA$P29 %in% c(1, 2, 4), "GRUPO 2", NA))
ENCUESTA$grupo_P30 <- ifelse(ENCUESTA$P30 %in% c(1,2,3,4), "LEGALES",
ifelse(ENCUESTA$P30 %in% c(5,6,7,8), "ILEGALES", NA))
tabla_grupo_P29 <- ENCUESTA %>% count(grupo_P29) %>% mutate(prop = round(n/sum(n)*100, 1))
kbl(tabla_grupo_P29, caption = "P29 - Principalmente, ¿Como consigue usted dinero?", align = "c") |>
kable_classic(full_width = FALSE, html_font = "Cambria") |>
kable_styling(latex_options = "striped", position = "center")
| grupo_P29 | n | prop |
|---|---|---|
| GRUPO 1 | 511 | 50.2 |
| GRUPO 2 | 506 | 49.8 |
library(dplyr)
library(ggplot2)
# Suponemos que ENCUESTA$P29 contiene los códigos originales (1,2,3,4,5,...)
ENCUESTA <- ENCUESTA %>%
mutate(
P29_orig = P29, # conservar original por si se necesita
P29_group = case_when(
P29 %in% c(3, 5) ~ "Oficios y reciclaje", # Grupo 1: códigos 3 y 5
P29 %in% c(1, 2, 4) ~ "Actividades informales de calle", # Grupo 2: el resto (1,2,4)
TRUE ~ NA_character_
),
# Asegurar P17 como factor con etiquetas claras
P17 = case_when(
P17 == 1 ~ "Si",
P17 == 2 ~ "No",
TRUE ~ NA_character_
)
)
# Convertir a factores (orden y etiquetas)
ENCUESTA$P29_group <- factor(ENCUESTA$P29_group, levels = c("Oficios y reciclaje", "Actividades informales de calle"))
ENCUESTA$P17 <- factor(ENCUESTA$P17, levels = c("Si", "No"))
# --- Tabla resumen de conteos para verificar ---
tabla_resumen <- ENCUESTA %>%
filter(!is.na(P29_group) & !is.na(P17)) %>%
count(P29_group, P17) %>%
tidyr::pivot_wider(names_from = P17, values_from = n, values_fill = 0) %>%
mutate(Total = rowSums(across(where(is.numeric))))
print(tabla_resumen)
# También el total por grupo (por si quieres confirmar 511 / 506)
totales_por_grupo <- ENCUESTA %>%
filter(!is.na(P29_group)) %>%
count(P29_group)
print(totales_por_grupo)
# --- Gráfico de barras (dos barras por grupo: Sí / No) ---
ggplot(ENCUESTA %>% filter(!is.na(P29_group) & !is.na(P17)),
aes(x = P29_group, fill = P17)) +
geom_bar(position = "dodge", width = 0.65, color = "white") +
scale_fill_manual(values = c("Si" = "#3B82F6", "No" = "#EF4444")) +
labs(
title = "Tipo de actividad (P29) y presencia de enfermedad en los últimos 30 días (P17)",
x = "Tipo de actividad (P29)",
y = "Número de personas",
fill = "Se enfermó?"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 14),
axis.text.x = element_text(size = 11),
legend.position = "top"
)
En la tabla 4, se observa que el GRUPO 1 que representa el 50,2% de la población encuestada,logra ingresos mediante actividades como pequeños oficios y el reciclaje, mientras que el GRUPO 2 que representa el 49.8%, depende directamente de lo que obtiene en la calle,como de limosna. Esta división evidencia que, ante la imposibilidad de acceder a un empleo estable, toda su economía se basa en la informalidad y la ingeniosidad diaria para cubrir sus necesidades más básicas. En el diagrama de barras se aprecia con mayor claridad la relación, esto podría indicar que las actividades informales de calle, por desarrollarse en condiciones más expuestas y sin medidas de protección, podrían implicar un mayor riesgo para la salud pero la diferencia es mínima.
library(readxl)
library(dplyr)
library(knitr)
library(kableExtra)
ENCUESTA <- read_excel("ENCUESTA.xlsx")
ENCUESTA$grupo_P29 <- ifelse(ENCUESTA$P29 %in% c(3, 5), "GRUPO 1",
ifelse(ENCUESTA$P29 %in% c(1, 2, 4), "GRUPO 2", NA))
ENCUESTA$grupo_P30 <- ifelse(ENCUESTA$P30 %in% c(1,2,3,4), "LEGALES",
ifelse(ENCUESTA$P30 %in% c(5,6,7,8), "ILEGALES", NA))
tabla_grupo_P30 <- ENCUESTA %>% count(grupo_P30) %>% mutate(prop = round(n/sum(n)*100, 1))
kbl(tabla_grupo_P30, caption = "P30 - Sustancias alucinógenas que consumen principalmente", align = "c") |>
kable_classic(full_width = FALSE, html_font = "Cambria") |>
kable_styling(latex_options = "striped", position = "center")
| grupo_P30 | n | prop |
|---|---|---|
| ILEGALES | 517 | 50.8 |
| LEGALES | 500 | 49.2 |
library(ggplot2)
library(dplyr)
# Clasificar P30 y limpiar P17
ENCUESTA <- ENCUESTA %>%
mutate(
P30_group = case_when(
P30 %in% c(1, 2, 3, 4) ~ "Sustancias legales",
P30 %in% c(5, 6, 7, 8) ~ "Sustancias ilegales",
TRUE ~ NA_character_
),
P17 = case_when(
P17 %in% c(1, "1", "Si", "Si", "1. Si") ~ "Si",
P17 %in% c(2, "2", "No", "no", "2. No") ~ "No",
TRUE ~ NA_character_
)
)
# Filtrar y graficar
ggplot(ENCUESTA %>% filter(!is.na(P30_group), !is.na(P17)),
aes(x = P30_group, fill = P17)) +
geom_bar(position = "dodge", color = "white") +
scale_fill_manual(values = c("Si" = "#4C9AFF", "No" = "#FF9E9E")) +
labs(
title = "Distribución del tipo de sustancia consumida",
x = "Tipo de sustancia",
y = "Frecuencia",
fill = "Respuesta P17"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 15, hjust = 0.5),
legend.position = "top"
)
En esta tabla, el análisis de consumo de sustancias, muestra una distribución muy equilibrada entres los dos tipos de sustancias. El 50.8% de las personas indicó consumir sustancias ilegales, mientras que el 49.2% indicó consumir sustancias legales. Esta cercanía en los porcentajes muestra que no existe una diferencia marcada en los patrones de consumo, ya que prácticamente la mitad de la población recurre tanto a sustancias legales como ilegales. En conjunto, los datos sugieren que el consumo de sustancias es un comportamiento generalizado dentro del grupo estudiado, sin una predominancia clara hacia un tipo específico. El gráfico permite visualizar que el consumo de sustancias legales e ilegales es muy similar. Además, muestra que el estado de salud reciente no varía de forma significativa entre ambos grupos, lo que sugiere que el tipo de sustancia consumida no marca una diferencia clara en la presencia de enfermedad.
tabla_P31 <- ENCUESTA %>% count(P31) %>% mutate(prop = round(n/sum(n)*100, 1))
kbl(tabla_P31, caption = "P31 - Conocimiento sobre programas de la alcaldía", align = "c") |>
kable_classic(full_width = FALSE, html_font = "Cambria") |>
kable_styling(latex_options = "striped", position = "center")
| P31 | n | prop |
|---|---|---|
| 1 | 285 | 28 |
| 2 | 732 | 72 |
library(ggplot2)
library(dplyr)
# Limpiar y reclasificar variables
ENCUESTA <- ENCUESTA %>%
mutate(
P31 = case_when(
P31 %in% c(1, "1", "Si", "si", "1. Si") ~ "Si conoce programas",
P31 %in% c(2, "2", "No", "no", "2. No") ~ "No conoce programas",
TRUE ~ NA_character_
),
P17 = case_when(
P17 %in% c(1, "1", "Si", "si", "1. Si") ~ "Si estuvo enfermo",
P17 %in% c(2, "2", "No", "no", "2. No") ~ "No estuvo enfermo",
TRUE ~ NA_character_
)
)
# Filtrar datos válidos
datos_filtrados <- ENCUESTA %>%
filter(!is.na(P31), !is.na(P17))
# Gráfico de barras agrupadas
ggplot(datos_filtrados, aes(x = P31, fill = P17)) +
geom_bar(position = "dodge", color = "white") +
scale_fill_manual(values = c("Si estuvo enfermo" = "#4C9AFF",
"No estuvo enfermo" = "#FF9E9E")) +
labs(
title = "Conocimiento de programas municipales y estado de salud reciente",
x = "Conocimiento de programas de la alcaldía",
y = "Frecuencia",
fill = "Estado de salud (P17)"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "top"
)
De la tabla, observamos que el 72% de los encuestados (732 personas) declaró no tener conocimiento sobre los programas ofrecidos por la alcaldía, frente a sólo un 28% (285 personas) que sí afirmó conocerlos. Esta mayoría que desconoce la oferta de apoyo público sugiere una grave falla en los canales de difusión de estos programas. Las consecuencias son directas: si las personas no saben que existen estos recursos, es imposible que accedan a ellos. Esto puede prolongar su situación de vulnerabilidad, ya que potenciales ayudas en materia de alojamiento, alimentación, salud o reinserción laboral no están llegando a la mayoría de la población que más lo necesita. Esto señala la urgente necesidad de implementar estrategias de comunicación más efectivas y descentralizadas que logren adentrarse en el entorno de la calle.
El gráfico refuerza lo observado en la tabla, mostrando de manera visual el predominio de personas que no están informadas sobre los programas de la alcaldía. Este grupo no solo es mayoritario, sino que también presenta una mayor frecuencia de casos de enfermedad reciente, lo que puede reflejar la falta de acceso a servicios preventivos o de atención básica.
library(readxl)
library(ggplot2)
ENCUESTA <- read_excel("ENCUESTA.xlsx")
# Convertir P17 en variable categórica con etiquetas
ENCUESTA$P17 <- factor(ENCUESTA$P17,
levels = c(1, 2),
labels = c("Sí", "No"))
# Crear el boxplot (Sí = azul moderado, No = rojo fuerte)
ggplot(ENCUESTA, aes(x = P17, y = P23S1R, fill = P17)) +
geom_boxplot(color = "black", alpha = 0.9) +
scale_fill_manual(values = c("Sí" = "#1E90FF", "No" = "#FF0000")) +
labs(
title = "Tiempo viviendo en la calle",
x = "¿Se enfermó en los últimos 30 días? (P17)",
y = "Años viviendo en la calle (P23S1R)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(hjust = 0.5, face = "bold"),
panel.background = element_rect(fill = "white", color = NA),
plot.background = element_rect(fill = "white", color = NA),
legend.position = "none"
)
El boxplot muestra la distribución del tiempo que las personas llevan viviendo en la calle según si se enferman, accidentan o presentan otros problemas de salud o no en los últimos 30 días. En general, se observa que ambas distribuciones son bastante similares: tanto quienes se enfermaron (color azul) como quienes no (color rojo) tienen medianas cercanas a los 4 o 5 años viviendo en la calle. No obstante, el grupo que manifestó haberse enfermado presenta una mayor dispersión, evidenciando casos con tiempos de permanencia considerablemente más prolongados. En contraste, el grupo que no se enfermó muestra una distribución más concentrada en valores bajos. En términos generales, el gráfico sugiere que no existe una diferencia marcada en el tiempo de permanencia en la calle entre los dos grupos, aunque se percibe una leve tendencia a que quienes han permanecido más tiempo en esta condición presenten una mayor probabilidad de haber enfermado recientemente.
En esta sección, presentamos los resultados obtenidos del entrenamiento y la evaluación de los modelos de clasificación K-Nearest Neighbors (kNN) y el logit. El objetivo es evaluar su capacidad predictiva en el conjunto de pruebas de las condiciones sociales y de subsistencia en las personas en situación de calle y ver si inciden de manera determinante en su estado de salud en los últimos 30 días.
library(caret)
library(dplyr)
library(knitr)
library(kableExtra)
library(class)
ENCUESTA <- read_excel("ENCUESTA.xlsx")
ENCUESTA <- ENCUESTA %>%
mutate_if(is.character, as.factor)
# Si algunas variables son numéricas pero representan categorías (como 1=Sí, 2=No)
ENCUESTA$P17 <- as.factor(ENCUESTA$P17)
ENCUESTA$P31 <- as.factor(ENCUESTA$P31)
ENCUESTA$P13 <- as.factor(ENCUESTA$P13)
ENCUESTA$grupo_P29 <- ifelse(ENCUESTA$P29 %in% c(3, 5), "GRUPO 1",
ifelse(ENCUESTA$P29 %in% c(1, 2, 4), "GRUPO 2", NA))
ENCUESTA$grupo_P30 <- ifelse(ENCUESTA$P30 %in% c(1,2,3,4), "LEGALES",
ifelse(ENCUESTA$P30 %in% c(5,6,7,8), "ILEGALES", NA))
indx_entrena <- createDataPartition(y = ENCUESTA$P17, p = 0.7, list = FALSE)
entrena <- ENCUESTA[indx_entrena, ]
test <- ENCUESTA[-indx_entrena, ]
entrena_mat <- model.matrix(~ P23S1R + P13 + P31 + grupo_P29 + grupo_P30 - 1, data = entrena)
test_mat <- model.matrix(~ P23S1R + P13 + P31 + grupo_P29 + grupo_P30 - 1, data = test)
preproc <- preProcess(entrena_mat, method = c("center", "scale"))
entrena_norm <- predict(preproc, entrena_mat)
test_norm <- predict(preproc, test_mat)
k_values <- 1:100
resultados <- data.frame(k = k_values, precision = NA)
for (i in k_values) {
pred <- knn(train = entrena_norm, test = test_norm, cl = entrena$P17, k = i)
resultados$precision[i] <- mean(pred == test$P17)
}
Se obtuvo un gráfico donde la precisión de los 100 primeros “k” es bastante irregular, entre los valores k=1 hasta k=18 podemos observar un incremento por cada k que se aumenta en la mayoría de casos, después se obtiene un comportamiento oscilante o inestable donde el valor máximo de 0.5921053 en k=18.
Una vez identificado el valor óptimo de k = 18, se procedió a entrenar el modelo final utilizando el conjunto de entrenamiento previamente normalizado (entrena_norm) y posteriormente se evaluó su capacidad predictiva sobre el conjunto de prueba (test_norm).
ggplot(resultados, aes(x = k, y = precision)) +
geom_line(color = "steelblue") +
geom_point(color = "darkred") +
labs(title = "Precisión del modelo KNN según K",
x = "Valor de K",
y = "Precisión") +
theme_minimal(base_size = 13)
Se construyó la matriz de confusión, que permite comparar las predicciones realizadas por el modelo frente a los valores reales de la variable dependiente, identificando los aciertos y errores de clasificación.
# Asegurar que P17 esté correctamente definida y sin NAs
ENCUESTA <- ENCUESTA %>%
mutate(P17 = case_when(
P17 %in% c(1, "1", "Si", "si", "1. Si") ~ "Si",
P17 %in% c(2, "2", "No", "no", "2. No") ~ "No",
TRUE ~ NA_character_
)) %>%
filter(!is.na(P17))
ENCUESTA$P17 <- factor(ENCUESTA$P17, levels = c("Si", "No"))
# Dividir nuevamente en entrenamiento y prueba
set.seed(28)
indx_entrena <- createDataPartition(y = ENCUESTA$P17, p = 0.7, list = FALSE)
entrena <- ENCUESTA[indx_entrena, ]
test <- ENCUESTA[-indx_entrena, ]
# Verificar niveles en ambos conjuntos
levels(entrena$P17)
## [1] "Si" "No"
levels(test$P17)
## [1] "Si" "No"
table(entrena$P17)
##
## Si No
## 367 346
table(test$P17)
##
## Si No
## 156 148
# Luego correr de nuevo tu knn y matriz de confusión
entrena_mat <- model.matrix(~ P23S1R + P13 + P31 + grupo_P29 + grupo_P30 - 1, data = entrena)
test_mat <- model.matrix(~ P23S1R + P13 + P31 + grupo_P29 + grupo_P30 - 1, data = test)
preproc <- preProcess(entrena_mat, method = c("center", "scale"))
entrena_norm <- predict(preproc, entrena_mat)
test_norm <- predict(preproc, test_mat)
pred_final <- knn(train = entrena_norm, test = test_norm, cl = entrena$P17, k = 18)
matriz_conf <- confusionMatrix(
data = pred_final,
reference = test$P17,
positive = "Si"
)
matriz_conf
## Confusion Matrix and Statistics
##
## Reference
## Prediction Si No
## Si 95 64
## No 61 84
##
## Accuracy : 0.5888
## 95% CI : (0.5312, 0.6447)
## No Information Rate : 0.5132
## P-Value [Acc > NIR] : 0.004816
##
## Kappa : 0.1766
##
## Mcnemar's Test P-Value : 0.858028
##
## Sensitivity : 0.6090
## Specificity : 0.5676
## Pos Pred Value : 0.5975
## Neg Pred Value : 0.5793
## Prevalence : 0.5132
## Detection Rate : 0.3125
## Detection Prevalence : 0.5230
## Balanced Accuracy : 0.5883
##
## 'Positive' Class : Si
##
La evaluación del modelo kNN en el conjunto de prueba, reflejada en la matriz de confusión, muestra un rendimiento general con un Accuracy de 0.5888. Esto significa que el modelo clasificó correctamente aproximadamente al 58.88% de los HDC en el conjunto de prueba. Analizando la matriz con más detalle:
Clasificó correctamente a 95 habitantes de calle con problemas de salud en los últimos 30 días (“Si”) (Verdaderos Positivos).
Clasificó correctamente a 84 habitantes de calle sin problemas de salud en los últimos 30 días (“No”) (Verdaderos Negativos).
Clasificó erróneamente a 64 habitantes de calle sin problemas de salud en los últimos 30 días (“No”) como habitantes de calle con problemas de salud en los últimos 30 días (“Si”) (Falsos Positivos).
Clasificó erróneamente a 61 habitantes de calle con problemas de salud en los últimos 30 días (“Si”) como habitantes de calle sin problemas de salud en los últimos 30 días (“No”) (Falsos Negativos).
En términos de métricas clave :
La Sensibilidad fue de 0.6090 nos indica que el modelo detecta algo de señal, pero deja escapar muchos casos. Falla 4 de cada 10 habitantes de calle que sí han tenido problemas en los últimos 30 días.
La Especificidad fue de 0.5676, muestra que el modelo confunde con frecuencia a los que no tienen problemas de salud con las variables estipuladas.
El valor de Kappa fue de 0.1766, revela un bajo nivel de concordancia entre las predicciones del modelo y los valores reales después de descontar el azar.
La exactitud balanceada (Balanced Accuracy = 0.5883) promedio entre sensibilidad y especificidad; refleja que el modelo tiene un rendimiento moderado, sin un sesgo marcado hacia ninguna de las dos clases.
En resumen, el modelo kNN logra un desempeño apenas superior al azar, descartando nuestra hipótesis inicial lo que sugiere que las variables utilizadas (tiempo viviendo en la calle, lugar donde duerme, forma de obtener dinero, tipo de consumo y conocimiento de programas sociales) no explican con suficiente fuerza la probabilidad de presentar problemas de salud en los últimos 30 días.
Esto podría deberse a que la salud en población habitante de calle está influida por factores adicionales no considerados en este modelo, como la atención médica previa, la red de apoyo social o el estado nutricional. En consecuencia, el modelo al limitarse a factores más estructurales con las variables escogidas no logra capturar toda la complejidad del fenómeno, reduciendo su capacidad predictiva.
Para evaluar el rendimiento general del modelo kNN, se construyó la curva ROC (Receiver Operating Characteristic) a partir de las probabilidades predichas de pertenecer a la clase positiva (“Sí”). Esta curva representa gráficamente la relación entre la tasa de verdaderos positivos (sensibilidad) y la tasa de falsos positivos (1 - especificidad), permitiendo analizar el equilibrio entre ambos errores para distintos umbrales de decisión.
En el gráfico, la línea diagonal punteada representa un modelo completamente aleatorio (sin poder de clasificación), mientras que la curva naranja muestra el desempeño real del modelo.
El Área Bajo la Curva (AUC) obtenida fue de 0.559 con una curva ROC muy próxima a la diagonal, esto confirma que el modelo no posee una capacidad predictiva sólida., este resultado confirma que el modelo no logra separar de manera efectiva a las personas con y sin problemas de salud recientes; y que las variables escogidas no explican suficientemente las diferencias en salud percibida dentro de esta población.
library(ROCR)
library(ggplot2)
# Predicciones "probabilísticas" del modelo KNN
# (atributo prob=TRUE devuelve las proporciones)
pred_prob <- knn(
train = entrena_norm,
test = test_norm,
cl = entrena$P17,
k = 18,
prob = TRUE
)
# Extraer las probabilidades asociadas a la clase positiva ("Si")
# knn() guarda las probabilidades del voto mayoritario; si la predicción fue "No",
# prob indica la proporción de votos "No", así que debemos ajustar:
prob_values <- ifelse(pred_prob == "Si", attr(pred_prob, "prob"), 1 - attr(pred_prob, "prob"))
# Crear el objeto ROC
roc_pred <- prediction(prob_values, test$P17)
roc_perf <- performance(roc_pred, "tpr", "fpr")
# Calcular el área bajo la curva (AUC)
auc_value <- performance(roc_pred, "auc")@y.values[[1]]
auc_value
## [1] 0.5588617
# Graficar la curva ROC
plot(roc_perf, col = "#1E90FF", lwd = 3,
main = paste("Curva ROC - Modelo kNN (k = 18)\nAUC =", round(auc_value, 3)))
abline(a = 0, b = 1, lty = 2, col = "gray")
El segundo modelo implementado corresponde a una regresión logística binaria (modelo Logit), cuyo objetivo fue estimar la probabilidad de que una persona HDC haya presentado algún problema de salud en los últimos 30 días (variable P17), en función de variables asociadas a sus condiciones de vida y características sociales: tiempo viviendo en la calle (P23S1R), lugar donde duerme habitualmente (P13), fuente principal de ingresos (grupo_P29), tipo de consumo predominante (grupo_P30) y conocimiento sobre programas de ayuda (P31).
A diferencia del modelo KNN, que clasifica en función de similitudes entre individuos, el modelo Logit permite interpretar directamente la influencia de cada variable sobre la probabilidad de presentar problemas de salud, ofreciendo así una perspectiva más explicativa que predictiva.
library(tidyverse)
library(caret)
library(pROC) # para ROC y AUC
set.seed(28)
# ----------------------------------------------------
# 2️⃣ Partición de datos (70% entrenamiento / 30% prueba)
# ----------------------------------------------------
indx_entrena <- createDataPartition(y = ENCUESTA$P17, p = 0.7, list = FALSE)
entrena <- ENCUESTA[indx_entrena, ]
test <- ENCUESTA[-indx_entrena, ]
# ----------------------------------------------------
# 3️⃣ Ajustar el modelo logit
# ----------------------------------------------------
logit_mod <- glm(P17 ~ P23S1R + P13 + grupo_P29 + grupo_P30 + P31,
data = entrena,
family = binomial(link = "logit"))
summary(logit_mod)
Los coeficientes estimados indican que la mayoría de las variables no son estadísticamente significativas (p > 0.05), excepto P31, cuyo efecto sí resulta relevante con un valor p = 0.0377. Esto no indica que las personas HDC que no conocen los programas institucionales de apoyo tienen mayor probabilidad de reportar problemas de salud, lo cual podría interpretarse como una consecuencia de la desconexión institucional y la falta de acceso a servicios básicos.
Respecto a las demás variables, el modelo muestra efectos débiles y no significativos. En particular, el tiempo viviendo en la calle presenta un coeficiente negativo (-0,0025), indicando una ligera reducción en la probabilidad de enfermedad a medida que aumentan los años en calle, aunque este efecto no es estadísticamente confiable, por lo que no se puede afirmar la existencia de una relación real.
El valor del R² de McFadden (0.0089) indica que el modelo explica menos del 1% de la variabilidad observada en la variable dependiente, y el AIC (993.03) refuerza la idea de que el ajuste global es bajo. Por lo tanto, desde un punto de vista estadístico, el modelo no logra capturar adecuadamente las relaciones entre las variables explicativas y la salud reportada.
# 4️⃣ Odds ratios y intervalos de confianza
# ----------------------------------------------------
exp_coef <- exp(coef(logit_mod))
ci <- exp(confint.default(logit_mod))
odds_table <- data.frame(
Estimate = coef(logit_mod),
OR = exp_coef,
CI_low = ci[,1],
CI_high = ci[,2]
)
print(odds_table)
## Estimate OR CI_low CI_high
## (Intercept) -0.221249460 0.8015167 0.5356485 1.199348
## P23S1R -0.002556945 0.9974463 0.9847057 1.010352
## P132 0.390201739 1.4772788 0.8380817 2.603986
## P133 0.205056916 1.2275949 0.7570543 1.990596
## grupo_P29GRUPO 2 -0.020392203 0.9798143 0.7281861 1.318394
## grupo_P30LEGALES -0.239313325 0.7871682 0.5795715 1.069124
## P312 0.365831126 1.4417118 1.0210592 2.035663
# ----------------------------------------------------
# 5️⃣ Indicadores de ajuste del modelo
# ----------------------------------------------------
ll_null <- logLik(update(logit_mod, . ~ 1))
ll_mod <- logLik(logit_mod)
mcfadden_r2 <- 1 - as.numeric(ll_mod / ll_null)
cat("McFadden R2:", round(mcfadden_r2, 4), "\n")
## McFadden R2: 0.0089
cat("AIC:", AIC(logit_mod), "\n")
## AIC: 993.035
# ----------------------------------------------------
# 6️⃣ Diagnóstico básico
# ----------------------------------------------------
par(mfrow = c(2,2))
plot(logit_mod)
par(mfrow = c(1,1))
# VIF manual (si no tienes 'car')
vif_manual <- function(model){
mm <- model.matrix(model)
vifs <- rep(NA, ncol(mm)-1)
names(vifs) <- colnames(mm)[-1]
for(i in 2:ncol(mm)){
z <- lm(mm[,i] ~ mm[,-i])
vifs[i-1] <- 1/(1 - summary(z)$r.squared)
}
return(vifs)
}
vif_vals <- vif_manual(logit_mod)
print(vif_vals)
## P23S1R P132 P133 grupo_P29GRUPO 2
## 1.010666 1.036818 1.129734 1.008229
## grupo_P30LEGALES P312
## 1.071778 1.080080
Al analizar los odds ratios, se observa que la variable P31 (NO conoce programas) presenta un OR = 1.44, lo que significa que las personas sin conocimiento de programas tienen 44% más probabilidades de presentar problemas de salud frente a quienes sí los conocen.
El resto de variables tienen OR cercanos a 1, evidenciando un efecto prácticamente nulo sobre la probabilidad de enfermar o accidentarse.Dado que todos los VIF son cercanos a 1, se concluye que no existe multicolinealidad significativa entre los predictores, esto implica que las variables son independientes entre sí y que los problemas de bajo ajuste del modelo no se deben a redundancia entre ellas, sino a una falta de capacidad real para explicar la variable dependiente (problemas de salud).
# 7️⃣ Predicción y evaluación
# ----------------------------------------------------
prob_test <- predict(logit_mod, newdata = test, type = "response")
pred_05 <- factor(ifelse(prob_test >= 0.5, "Si", "No"), levels = c("Si", "No"))
confusion_05 <- confusionMatrix(pred_05, test$P17, positive = "Si")
print(confusion_05)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Si No
## Si 66 80
## No 90 68
##
## Accuracy : 0.4408
## 95% CI : (0.3842, 0.4986)
## No Information Rate : 0.5132
## P-Value [Acc > NIR] : 0.9951
##
## Kappa : -0.1173
##
## Mcnemar's Test P-Value : 0.4900
##
## Sensitivity : 0.4231
## Specificity : 0.4595
## Pos Pred Value : 0.4521
## Neg Pred Value : 0.4304
## Prevalence : 0.5132
## Detection Rate : 0.2171
## Detection Prevalence : 0.4803
## Balanced Accuracy : 0.4413
##
## 'Positive' Class : Si
##
# ----------------------------------------------------
# 8️⃣ Umbral óptimo (Youden)
# ----------------------------------------------------
roc_obj <- roc(response = test$P17, predictor = prob_test, levels = c("Si", "No"))
best_coords <- coords(roc_obj, "best", best.method = "youden",
ret = c("threshold","sensitivity","specificity"))
best_coords <- as.list(best_coords)
best_coords$threshold <- as.numeric(best_coords$threshold)
cat("Umbral óptimo (Youden):", best_coords$threshold, "\n")
## Umbral óptimo (Youden): 0.4690215
pred_opt <- factor(ifelse(prob_test >= best_coords$threshold, "Si", "No"), levels = c("Si", "No"))
confusion_opt <- confusionMatrix(pred_opt, test$P17, positive = "Si")
print(confusion_opt)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Si No
## Si 84 108
## No 72 40
##
## Accuracy : 0.4079
## 95% CI : (0.3521, 0.4655)
## No Information Rate : 0.5132
## P-Value [Acc > NIR] : 0.999907
##
## Kappa : -0.1925
##
## Mcnemar's Test P-Value : 0.009087
##
## Sensitivity : 0.5385
## Specificity : 0.2703
## Pos Pred Value : 0.4375
## Neg Pred Value : 0.3571
## Prevalence : 0.5132
## Detection Rate : 0.2763
## Detection Prevalence : 0.6316
## Balanced Accuracy : 0.4044
##
## 'Positive' Class : Si
##
# ----------------------------------------------------
# 9️⃣ Curva ROC y AUC
# ----------------------------------------------------
plot(roc_obj, col = "#2E86DE", lwd = 3,
main = sprintf("Curva ROC - Modelo Logit | AUC = %.3f", auc(roc_obj)))
abline(a = 0, b = 1, lty = 2, col = "gray")
# ----------------------------------------------------
# 🔟 Efecto marginal aproximado
# ----------------------------------------------------
med_p23 <- median(entrena$P23S1R, na.rm = TRUE)
new1 <- entrena; new2 <- entrena
new1$P23S1R <- med_p23 - 1
new2$P23S1R <- med_p23 + 1
prob1 <- predict(logit_mod, newdata = new1, type = "response")
prob2 <- predict(logit_mod, newdata = new2, type = "response")
effect_p23_approx <- mean(prob2 - prob1, na.rm = TRUE)
cat("Efecto aproximado: +1 unidad en P23S1R cambia la probabilidad promedio en:",
round(effect_p23_approx, 4), "\n")
## Efecto aproximado: +1 unidad en P23S1R cambia la probabilidad promedio en: -0.0013
El desempeño del modelo sobre el conjunto de prueba fue bajo ya que obtuvo una precisión total (accuracy) de tan solo 44%, con una sensibilidad del 42.3% y una especificidad del 45.95%. Mostrandonos una matriz con estas caracteristicas:
Clasificó correctamente a 66 habitantes de calle con problemas de salud en los últimos 30 días (“Si”) (Verdaderos Positivos).
Clasificó correctamente a 68 habitantes de calle sin problemas de salud en los últimos 30 días (“No”) (Verdaderos Negativos).
Clasificó erróneamente a 80 habitantes de calle sin problemas de salud en los últimos 30 días (“No”) como habitantes de calle con problemas de salud en los últimos 30 días (“Si”) (Falsos Positivos).
Clasificó erróneamente a 90 habitantes de calle con problemas de salud en los últimos 30 días (“Si”) como habitantes de calle sin problemas de salud en los últimos 30 días (“No”) (Falsos Negativos).
Obteniendo peores resultados de aprendizaje, aca inferimos que con este modelo que detalla más las variables, obtenemos valores más alejados de una buena predicción.
El umbral óptimo de decisión (0.469) obtenido mediante el criterio de Youden es muy similar al convencional 0.5, lo cual evidencia que ajustar el punto de corte no mejoraría el rendimiento.
Aunque, queriamos observar que tanto cambiaban los resultados, decidimos ajustar el umbral óptimo (Youden = 0.469), el modelo logra apenas un 40.7 % de precisión, con sensibilidad de 0.53 y especificidad de 0.27. Esto significa que el modelo tiende a clasificar mal tanto los casos positivos (personas con problemas de salud) como los negativos, siendo poco útil para predecir correctamente.
El estadístico Kappa (-0.19) confirma que no existe un acuerdo real entre las predicciones y los valores observados, e incluso el modelo rinde peor que una predicción aleatoria.
El p-valor del test de McNemar (0.009) en la matriz de umbral optimo señala además que el modelo se equivoca de forma asimétrica, mostrando una tendencia a sobreestimar la clase positiva.
Finalmente, obteniendo el área bajo la curva ROC (AUC = 0.609) indica un poder discriminante muy débil,igual al del modelo kNN, ligeramente superior al azar (AUC = 0.5).Esto confirma que el modelo no es capaz de distinguir con precisión entre individuos que sí y no presentan problemas de salud, reduciendo su utilidad práctica como herramienta de predicción.
El KNN muestra un desempeño levemente mejor en términos de clasificación, el modelo Logit aporta mayor valor interpretativo, al evidenciar explícitamente que ninguna de las variables analizadas explica de manera sustancial la probabilidad de presentar problemas de salud. Ambos enfoques, por lo tanto, convergen en un mismo hallazgo; la realidad sanitaria de la población habitante de calle no puede modelarse eficazmente solo con variables observables básicas, sino que requiere un análisis más profundo que integre factores como la edad y sexo de la persona HDC, historial clínico o el lugar de nacimiento.
Al comparar los resultados de ambos modelos, se observa que tanto el KNN como el Logit presentan desempeños modestos, aunque el KNN logra un comportamiento ligeramente superior en términos de clasificación, el modelo KNN obtuvo una exactitud del 58.8% y un AUC de 0.56, mientras que el modelo Logit alcanzó una exactitud del 44% y un AUC también cercano a 0.609, lo que indica que ninguno de los dos modelos posee un poder predictivo sólido para distinguir entre quienes sí y no tuvieron problemas de salud.
Se recomienda ampliar el conjunto de variables y aplicar técnicas de selección o modelos alternativos que permitan mejorar el ajuste y la capacidad predictiva del modelo en futuros análisis.