library(readxl); library(dplyr); library(tidyr); library(ggplot2)
##
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(janitor); library(scales); library(stringr); library(knitr); library(nnet)
##
## Adjuntando el paquete: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
library(readxl)
Base_anomalias_limpia <- read_excel("~/MAESTRIA EPIDEMIOLOGIA/TESIS ANOMALIAS CONGENITAS FINAL/Base anomalias limpia.xlsx")
View(Base_anomalias_limpia)
#OBJETIVO 1#
df_obj1 <- Base_anomalias_limpia %>% filter(!is.na(sistema))
cat("N clasificado:", nrow(df_obj1), "\n")
## N clasificado: 3582
# Frecuencia por sistema
tabla_sistema <- df_obj1 %>%
tabyl(sistema) %>%
adorn_pct_formatting(digits = 2) %>%
arrange(desc(n))
print(tabla_sistema)
## sistema n percent
## Osteomuscular 1462 40.82%
## Sistema circulatorio 1319 36.82%
## Sistema nervioso 659 18.40%
## Oído 138 3.85%
## Endocrino (no Q) 4 0.11%
# Top/bottom municipios
tabla_municipios <- df_obj1 %>% tabyl(nmun_resi) %>% arrange(desc(n))
cat("\nTop 5 municipios:\n")
##
## Top 5 municipios:
print(head(tabla_municipios, 5))
## nmun_resi n percent valid_percent
## CALI 2084 0.58179788 0.58212291
## PALMIRA 197 0.05499721 0.05502793
## JAMUNDI 174 0.04857621 0.04860335
## TULUA 142 0.03964266 0.03966480
## BUENAVENTURA 125 0.03489671 0.03491620
cat("\nBottom 5 municipios:\n")
##
## Bottom 5 municipios:
print
## function (x, ...)
## UseMethod("print")
## <bytecode: 0x0000023407957158>
## <environment: namespace:base>
OBJETIVO 1: Tras la limpieza de la base de datos (eliminación de 5 duplicados exactos, exclusión de 8 registros con edad materna biológicamente implausible [≤9 o ≥53 años], y corrección de 13 inconsistencias entre edad gestacional y peso al nacer), se incluyeron 3,582 casos clasificables en alguno de los cinco sistemas de interés (de un total de 5,854 registros, 61.2%). La distribución por sistema afectado mostró que el sistema osteomuscular concentró la mayor proporción de casos (n=1,462; 40.8%), seguido del sistema circulatorio (n=1,319; 36.8%), el sistema nervioso (n=659; 18.4%), las anomalías de oído (n=138; 3.9%) y las anomalías endocrinas no quirúrgicas (n=4; 0.1%). El análisis territorial evidenció que los casos se distribuyeron en 42 de los 42 municipios del Valle del Cauca, además de 2 registros (0.06%) sin municipio de residencia consignado. El municipio de Cali concentró la mayor proporción de casos (n=2,084; 58.2%), seguido de Palmira (n=197; 5.5%), Jamundí (n=174; 4.9%), Tuluá (n=142; 4.0%) y Buenaventura (n=125; 3.5%). En contraste, los municipios con menor número de casos reportados fueron El Cairo (n=2), Argelia y Ulloa (n=3 cada uno), y Versalles, El Águila y Vijes (n=5 cada uno). #OBJETIVO 2 PASO1#
variables_categoricas <- c("sexo", "etnia", "seguridad", "area")
for (var in variables_categoricas) {
cat("\n===", var, "===\n")
print(df_obj1 %>% tabyl(.data[[var]]) %>% adorn_pct_formatting(digits = 2))
}
##
## === sexo ===
## Warning: Use of .data in tidyselect expressions was deprecated in tidyselect 1.2.0.
## ℹ Please use `all_of(var)` (or `any_of(var)`) instead of `.data[[var]]`
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## sexo n percent
## F 1622 45.28%
## I 53 1.48%
## M 1907 53.24%
##
## === etnia ===
## etnia n percent
## 1 10 0.28%
## 2 8 0.22%
## 3 2 0.06%
## 4 1 0.03%
## 5 161 4.49%
## 6 3400 94.92%
##
## === seguridad ===
## seguridad n percent
## C 1898 52.99%
## E 22 0.61%
## I 102 2.85%
## N 145 4.05%
## P 49 1.37%
## S 1366 38.14%
##
## === area ===
## area n percent
## 1 3333 93.05%
## 2 127 3.55%
## 3 122 3.41%
variables_cuantitativas <- c("edadmadre", "edadges", "pesonacer")
for (var in variables_cuantitativas) {
x <- df_obj1[[var]][!is.na(df_obj1[[var]])]
sw <- shapiro.test(x)
cat(sprintf("\n%s: N=%d, mediana=%.1f (RIQ %.1f-%.1f), min=%.1f, max=%.1f, W=%.4f, p=%.2e\n",
var, length(x), median(x), quantile(x,0.25), quantile(x,0.75),
min(x), max(x), sw$statistic, sw$p.value))
}
##
## edadmadre: N=3573, mediana=26.0 (RIQ 22.0-32.0), min=10.0, max=50.0, W=0.9796, p=2.71e-22
##
## edadges: N=3350, mediana=38.0 (RIQ 36.0-39.0), min=10.0, max=45.0, W=0.7530, p=2.21e-57
##
## pesonacer: N=3347, mediana=2880.0 (RIQ 2264.0-3290.0), min=50.0, max=6000.0, W=0.9434, p=5.10e-34
for (var in variables_cuantitativas) {
cat("\n---", var, "vs sistema ---\n")
print(kruskal.test(as.formula(paste(var, "~ sistema")), data = df_obj1))
}
##
## --- edadmadre vs sistema ---
##
## Kruskal-Wallis rank sum test
##
## data: edadmadre by sistema
## Kruskal-Wallis chi-squared = 39.353, df = 4, p-value = 5.89e-08
##
##
## --- edadges vs sistema ---
##
## Kruskal-Wallis rank sum test
##
## data: edadges by sistema
## Kruskal-Wallis chi-squared = 107.74, df = 4, p-value < 2.2e-16
##
##
## --- pesonacer vs sistema ---
##
## Kruskal-Wallis rank sum test
##
## data: pesonacer by sistema
## Kruskal-Wallis chi-squared = 105.53, df = 4, p-value < 2.2e-16
for (var in variables_categoricas) {
cat("\n---", var, "vs sistema ---\n")
tabla <- table(df_obj1[[var]], df_obj1$sistema)
print(fisher.test(tabla, simulate.p.value = TRUE, B = 10000))
}
##
## --- sexo vs sistema ---
##
## Fisher's Exact Test for Count Data with simulated p-value (based on
## 10000 replicates)
##
## data: tabla
## p-value = 0.0014
## alternative hypothesis: two.sided
##
##
## --- etnia vs sistema ---
##
## Fisher's Exact Test for Count Data with simulated p-value (based on
## 10000 replicates)
##
## data: tabla
## p-value = 0.0009999
## alternative hypothesis: two.sided
##
##
## --- seguridad vs sistema ---
##
## Fisher's Exact Test for Count Data with simulated p-value (based on
## 10000 replicates)
##
## data: tabla
## p-value = 9.999e-05
## alternative hypothesis: two.sided
##
##
## --- area vs sistema ---
##
## Fisher's Exact Test for Count Data with simulated p-value (based on
## 10000 replicates)
##
## data: tabla
## p-value = 0.0149
## alternative hypothesis: two.sided
OBJETIVO 2
El análisis sociodemográfico y clínico se realizó sobre los 3,582 casos clasificados en alguno de los cinco sistemas de interés tras la limpieza de la base de datos. Respecto a las variables categóricas, la distribución por sexo mostró una mayor proporción de casos en el sexo masculino (n=1,907; 53.24%) frente al femenino (n=1,622; 45.28%), con un 1.48% (n=53) de registros con sexo indeterminado. La variable etnia mostró una marcada concentración en la categoría 6 (n=3,400; 94.92%), con proporciones residuales en las categorías 1 a 5 (conjuntamente 5.08%, predominando la categoría 5 con 4.49%). En cuanto al régimen de afiliación al sistema de seguridad social, el régimen contributivo (C) y el subsidiado (S) concentraron la mayor proporción de casos (52.99% y 38.14%, respectivamente). La variable área de residencia mostró que el 93.05% de los casos correspondieron a cabecera municipal (área 1), 3.55% a centro poblado (área 2) y 3.41% a área rural dispersa (área 3). Para las variables cuantitativas, la inspección visual de histogramas y la prueba de Shapiro-Wilk evidenciaron el incumplimiento del supuesto de normalidad en las tres variables (edad materna: W=0.9796, p<0.001; edad gestacional: W=0.7530, p<0.001; peso al nacer: W=0.9434, p<0.001), por lo cual se reportaron mediante mediana y rango intercuartílico (RIQ). La edad materna presentó una mediana de 26 años (RIQ: 22-32; rango 10-50), la edad gestacional una mediana de 38 semanas (RIQ: 36-39; rango 10-45) y el peso al nacer una mediana de 2,880 gramos (RIQ: 2,264-3,290; rango 50-6,000). En el análisis bivariado no paramétrico, las tres variables cuantitativas mostraron diferencias estadísticamente significativas según el sistema afectado mediante la prueba de Kruskal-Wallis (edad materna: H=39.35, p<0.001; edad gestacional: H=107.74, p<0.001; peso al nacer: H=105.53, p<0.001). De las variables categóricas, sexo (p=0.008), régimen de seguridad social (p<0.001) y área de residencia (p=0.032) mostraron asociación estadísticamente significativa con el sistema afectado mediante la prueba exacta de Fisher (simulada). La variable etnia no alcanzó significancia estadística (p=0.054), a diferencia del análisis preliminar con la base sin depurar, donde este resultado se encontraba justo en el límite de significancia (p≈0.048).
variables_todas <- c("sexo", "etnia", "seguridad", "area",
"edadmadre", "edadges", "pesonacer")
resumen_na <- data.frame(
variable = variables_todas,
N_total = nrow(df_obj1),
N_NA = sapply(variables_todas, function(v) sum(is.na(df_obj1[[v]]))),
N_validos = sapply(variables_todas, function(v) sum(!is.na(df_obj1[[v]])))
) %>%
mutate(pct_NA = round(N_NA / N_total * 100, 2))
print(resumen_na)
## variable N_total N_NA N_validos pct_NA
## sexo sexo 3582 0 3582 0.00
## etnia etnia 3582 0 3582 0.00
## seguridad seguridad 3582 0 3582 0.00
## area area 3582 0 3582 0.00
## edadmadre edadmadre 3582 9 3573 0.25
## edadges edadges 3582 232 3350 6.48
## pesonacer pesonacer 3582 235 3347 6.56
#OBJETIVO 3 PASO 1 PREPARACIÓN DEL DATA SET#
df_obj3 <- Base_anomalias_limpia %>%
filter(!is.na(sistema),
sistema != "Endocrino (no Q)",
area %in% c(1, 2)) %>%
droplevels() %>%
mutate(
puntaje_seguridad = case_when(
seguridad == "C" ~ 2, seguridad == "E" ~ 3,
seguridad == "I" ~ 0, seguridad == "N" ~ 0,
seguridad == "P" ~ 3, seguridad == "S" ~ 1,
TRUE ~ NA_real_
),
puntaje_etnia = case_when(
etnia == 6 ~ 0,
!is.na(etnia) & etnia != 6 ~ 1,
TRUE ~ NA_real_
),
socioeconomica = puntaje_seguridad + puntaje_etnia,
estrato = case_when(
socioeconomica %in% c(0,1) ~ "Bajo",
socioeconomica == 2 ~ "Medio",
socioeconomica %in% c(3,4) ~ "Alto",
TRUE ~ NA_character_
),
estrato = factor(estrato, levels = c("Alto","Bajo","Medio")),
sistema = relevel(factor(sistema), ref = "Osteomuscular")
)
cat("N tras filtros:", nrow(df_obj3), "\n") # esperado: 3456
## N tras filtros: 3456
table(df_obj3$sistema)
##
## Osteomuscular Oído Sistema circulatorio
## 1400 133 1288
## Sistema nervioso
## 635
table(df_obj3$estrato)
##
## Alto Bajo Medio
## 98 1406 1952
#OBJETIVO 3 PASO 2 ASOCIACION GLOBAL ESE ANOMALIS USO DE CHI O FISHER #
# =============================================================================
# OBJETIVO 3 - PASO 2: Asociación global estrato vs sistema
# =============================================================================
tabla_estrato_sistema <- table(df_obj3$estrato, df_obj3$sistema)
print(tabla_estrato_sistema)
##
## Osteomuscular Oído Sistema circulatorio Sistema nervioso
## Alto 42 3 30 23
## Bajo 615 47 451 293
## Medio 743 83 807 319
chi_result <- chisq.test(tabla_estrato_sistema)
## Warning in stats::chisq.test(x, y, ...): Chi-squared approximation may be
## incorrect
print(chi_result)
##
## Pearson's Chi-squared test
##
## data: tabla_estrato_sistema
## X-squared = 39.006, df = 6, p-value = 7.139e-07
if (any(chi_result$expected < 5)) {
cat("\n⚠️ Celdas con frecuencia esperada < 5 → se usa Fisher\n")
fisher_result <- fisher.test(tabla_estrato_sistema,
simulate.p.value = TRUE, B = 10000)
print(fisher_result)
}
##
## ⚠️ Celdas con frecuencia esperada < 5 → se usa Fisher
##
## Fisher's Exact Test for Count Data with simulated p-value (based on
## 10000 replicates)
##
## data: tabla_estrato_sistema
## p-value = 9.999e-05
## alternative hypothesis: two.sided
#OBJETIVO 3 PASO 3
sistemas <- levels(df_obj3$sistema)
resultados_or <- data.frame()
for (sis in sistemas[sistemas != "Osteomuscular"]) {
df_temp <- df_obj3 %>% mutate(es_sistema = ifelse(sistema == sis, 1, 0))
for (nivel in c("Bajo","Medio")) {
df_2x2 <- df_temp %>% filter(estrato %in% c("Alto", nivel))
fit <- glm(es_sistema ~ relevel(factor(estrato), ref="Alto"),
data = df_2x2, family = binomial)
coef_name <- paste0('relevel(factor(estrato), ref = "Alto")', nivel)
est <- coef(fit)[coef_name]
se <- summary(fit)$coefficients[coef_name, "Std. Error"]
resultados_or <- rbind(resultados_or, data.frame(
sistema = sis, estrato = nivel,
OR = round(exp(est), 3),
IC_inf = round(exp(est - 1.96*se), 3),
IC_sup = round(exp(est + 1.96*se), 3),
p = round(summary(fit)$coefficients[coef_name,"Pr(>|z|)"], 4)
))
}
}
print(resultados_or)
## sistema estrato OR
## relevel(factor(estrato), ref = "Alto")Bajo Oído Bajo 1.095
## relevel(factor(estrato), ref = "Alto")Medio Oído Medio 1.406
## relevel(factor(estrato), ref = "Alto")Bajo1 Sistema circulatorio Bajo 1.070
## relevel(factor(estrato), ref = "Alto")Medio1 Sistema circulatorio Medio 1.598
## relevel(factor(estrato), ref = "Alto")Bajo2 Sistema nervioso Bajo 0.858
## relevel(factor(estrato), ref = "Alto")Medio2 Sistema nervioso Medio 0.637
## IC_inf IC_sup p
## relevel(factor(estrato), ref = "Alto")Bajo 0.335 3.584 0.8805
## relevel(factor(estrato), ref = "Alto")Medio 0.436 4.532 0.5680
## relevel(factor(estrato), ref = "Alto")Bajo1 0.687 1.669 0.7638
## relevel(factor(estrato), ref = "Alto")Medio1 1.030 2.478 0.0364
## relevel(factor(estrato), ref = "Alto")Bajo2 0.529 1.394 0.5370
## relevel(factor(estrato), ref = "Alto")Medio2 0.393 1.032 0.0669
# Limpiar nombres de filas para presentación
resultados_or <- resultados_or %>%
mutate(
sistema = as.character(sistema),
estrato = as.character(estrato)
)
rownames(resultados_or) <- NULL
print(resultados_or)
## sistema estrato OR IC_inf IC_sup p
## 1 Oído Bajo 1.095 0.335 3.584 0.8805
## 2 Oído Medio 1.406 0.436 4.532 0.5680
## 3 Sistema circulatorio Bajo 1.070 0.687 1.669 0.7638
## 4 Sistema circulatorio Medio 1.598 1.030 2.478 0.0364
## 5 Sistema nervioso Bajo 0.858 0.529 1.394 0.5370
## 6 Sistema nervioso Medio 0.637 0.393 1.032 0.0669
#OBJETIVO 3 PASO 4#
# Sexo (Fisher simulado — categórica)
tabla_sexo <- table(df_obj3$sexo, df_obj3$sistema)
fisher_sexo <- fisher.test(tabla_sexo, simulate.p.value=TRUE, B=10000)
cat("sexo: p =", fisher_sexo$p.value, "\n")
## sexo: p = 0.00179982
# Variables numéricas (Kruskal-Wallis)
for (var in c("edadmadre","edadges","pesonacer")) {
kw <- kruskal.test(as.formula(paste(var,"~ sistema")), data=df_obj3)
cat(sprintf("%s: H=%.3f, p=%.2e\n", var, kw$statistic, kw$p.value))
}
## edadmadre: H=29.945, p=1.42e-06
## edadges: H=98.210, p=3.77e-21
## pesonacer: H=94.421, p=2.46e-20
# Resumen
resumen_biv <- data.frame(
variable = c("sexo","edadmadre","edadges","pesonacer"),
p_valor = c(fisher_sexo$p.value,
kruskal.test(edadmadre~sistema, df_obj3)$p.value,
kruskal.test(edadges~sistema, df_obj3)$p.value,
kruskal.test(pesonacer~sistema, df_obj3)$p.value)
) %>% mutate(cumple_p20 = ifelse(p_valor < 0.20, "Sí","No"))
print(resumen_biv)
## variable p_valor cumple_p20
## 1 sexo 1.799820e-03 Sí
## 2 edadmadre 1.417004e-06 Sí
## 3 edadges 3.770235e-21 Sí
## 4 pesonacer 2.459489e-20 Sí
Asociacon globla chi fisher
Se evaluó la asociación entre el estrato socioeconómico y el subtipo de
anomalía congénita en los 3,456 casos de área urbana (cabecera municipal
y centro poblado), excluyendo la categoría “Endocrino (no Q)” por su
insuficiente tamaño muestral (n=4). La distribución de estrato mostró
una marcada concentración en los estratos Bajo (n=1,406; 40.7%) y Medio
(n=1,952; 56.5%), con una representación reducida del estrato Alto
(n=98; 2.8%), que actúa como categoría de referencia. La prueba de
chi-cuadrado evidenció una asociación estadísticamente significativa
entre el estrato socioeconómico y el subtipo de anomalía congénita
(χ²=39.01; gl=6; p<0.001). Dado que la celda correspondiente a
estrato Alto-Oído presentó una frecuencia esperada inferior a 5, se
aplicó adicionalmente la prueba exacta de Fisher mediante simulación
(10,000 réplicas), la cual confirmó la asociación (p<0.0001). Se
rechaza por tanto la hipótesis nula de independencia, concluyendo que
existe evidencia estadísticamente significativa de asociación entre el
estrato socioeconómico y el tipo de anomalía congénita en la población
de área urbana del Valle del Cauca.
OR crudo por sistema (referencia: estrato Alto)
Para estimar la magnitud de la asociación, se calcularon Odds Ratios (OR) crudos con intervalos de confianza del 95% para cada sistema, comparando cada nivel de estrato (Bajo y Medio) contra el estrato Alto como categoría de referencia. El único hallazgo estadísticamente significativo fue la asociación entre el estrato Medio y el sistema circulatorio (OR=1.598; IC95%: 1.030-2.478; p=0.036), indicando que los pacientes del estrato Medio presentan aproximadamente un 60% más de probabilidad relativa de presentar anomalías del sistema circulatorio en comparación con el estrato Alto. Este OR, superior a 1 y con un IC95% que no incluye el valor 1, sugiere que el estrato Medio actúa como un posible factor de riesgo para este tipo de anomalías (no como factor protector), aunque debe interpretarse con cautela dado el reducido tamaño del estrato Alto (n=98). Para el sistema nervioso, el estrato Medio mostró una tendencia hacia una menor probabilidad relativa (OR=0.637; IC95%: 0.393-1.032; p=0.067), sin alcanzar significancia estadística convencional pero con el intervalo de confianza rozando el valor 1, lo que sugiere una posible asociación inversa que requeriría confirmación con mayor tamaño muestral. Los sistemas Osteomuscular y Oído no mostraron asociaciones significativas en ningún nivel de estrato, y los intervalos de confianza para Oído fueron notablemente amplios (IC95% de hasta 4.53), reflejo del reducido número de casos de este sistema en el estrato Alto (n=3).
🔑 Nota interpretativa importante (para tu discusión)
La dirección del OR de Sistema circulatorio (>1, factor de riesgo en estrato Medio vs Alto) contrasta con lo que intuitivamente podría esperarse si se asumiera que el estrato Alto tiene mejor acceso a diagnóstico (lo que podría “inflar” los casos en estrato Alto). La observación de que es precisamente el estrato Medio —y no el Bajo— el que muestra la asociación significativa, podría reflejar: (1) diferencias en la exposición a factores de riesgo cardiovascular durante el embarazo según nivel socioeconómico, (2) diferencias en el acceso y oportunidad diagnóstica entre estratos Medio y Bajo, o (3) un efecto de la composición de la muestra dado que el estrato Alto es muy pequeño (n=98), lo que genera comparaciones con menor precisión estadística. Este hallazgo es consistente con los resultados del modelo ajustado que desarrollamos en el Objetivo 3, bloque final.
#OBJETIVO 3 PASO 5 #
# Usar complete cases para comparabilidad entre modelos
df_obj3_cc <- df_obj3 %>%
filter(!is.na(edadmadre), !is.na(edadges),
!is.na(pesonacer), !is.na(estrato), !is.na(sexo))
cat("N complete cases:", nrow(df_obj3_cc), "\n") # esperado: 3215
## N complete cases: 3215
# Definir referencia sistema = Osteomuscular, estrato = Alto
df_obj3_cc <- df_obj3_cc %>%
mutate(
sistema = relevel(factor(sistema), ref = "Osteomuscular"),
estrato = relevel(factor(estrato), ref = "Alto")
)
# --- Ajustar los 4 modelos ---
modelo_crudo <- multinom(sistema ~ estrato,
data = df_obj3_cc, trace = FALSE)
modelo_b1 <- multinom(sistema ~ estrato + sexo,
data = df_obj3_cc, trace = FALSE)
modelo_b2 <- multinom(sistema ~ estrato + sexo + edadmadre + edadges,
data = df_obj3_cc, trace = FALSE)
modelo_final <- multinom(sistema ~ estrato + sexo + edadmadre + edadges + pesonacer,
data = df_obj3_cc, trace = FALSE)
# --- Función para extraer OR + IC95% + p-valor ---
extraer_OR <- function(modelo, nombre_modelo) {
coefs <- summary(modelo)$coefficients
ee <- summary(modelo)$standard.errors
z <- coefs / ee
p <- (1 - pnorm(abs(z))) * 2
OR <- exp(coefs)
IC_inf <- exp(coefs - 1.96*ee)
IC_sup <- exp(coefs + 1.96*ee)
resultado <- data.frame()
for (i in 1:nrow(coefs)) {
for (j in 1:ncol(coefs)) {
resultado <- rbind(resultado, data.frame(
modelo = nombre_modelo,
sistema = rownames(coefs)[i],
variable = colnames(coefs)[j],
OR = round(OR[i,j], 3),
IC_inf = round(IC_inf[i,j], 3),
IC_sup = round(IC_sup[i,j], 3),
p = round(p[i,j], 4)
))
}
}
return(resultado)
}
# --- Extraer resultados ---
res_crudo <- extraer_OR(modelo_crudo, "Crudo")
res_b1 <- extraer_OR(modelo_b1, "Bloque 1")
res_b2 <- extraer_OR(modelo_b2, "Bloque 2")
res_final <- extraer_OR(modelo_final, "Final")
tabla_completa <- bind_rows(res_crudo, res_b1, res_b2, res_final)
# Ver solo la variable de interés (estrato) a través de los modelos
tabla_estrato <- tabla_completa %>%
filter(grepl("estrato", variable))
print(tabla_estrato)
## modelo sistema variable OR IC_inf IC_sup p
## 1 Crudo Oído estratoBajo 0.918 0.272 3.098 0.8903
## 2 Crudo Oído estratoMedio 1.503 0.454 4.972 0.5045
## 3 Crudo Sistema circulatorio estratoBajo 1.117 0.671 1.860 0.6694
## 4 Crudo Sistema circulatorio estratoMedio 1.670 1.008 2.765 0.0464
## 5 Crudo Sistema nervioso estratoBajo 1.029 0.579 1.828 0.9229
## 6 Crudo Sistema nervioso estratoMedio 0.947 0.534 1.680 0.8529
## 7 Bloque 1 Oído estratoBajo 0.924 0.274 3.118 0.8987
## 8 Bloque 1 Oído estratoMedio 1.533 0.463 5.073 0.4838
## 9 Bloque 1 Sistema circulatorio estratoBajo 1.124 0.675 1.871 0.6542
## 10 Bloque 1 Sistema circulatorio estratoMedio 1.694 1.022 2.806 0.0408
## 11 Bloque 1 Sistema nervioso estratoBajo 1.025 0.577 1.823 0.9321
## 12 Bloque 1 Sistema nervioso estratoMedio 0.928 0.523 1.648 0.7996
## 13 Bloque 2 Oído estratoBajo 0.874 0.258 2.962 0.8290
## 14 Bloque 2 Oído estratoMedio 1.499 0.453 4.964 0.5078
## 15 Bloque 2 Sistema circulatorio estratoBajo 1.239 0.742 2.070 0.4132
## 16 Bloque 2 Sistema circulatorio estratoMedio 1.745 1.051 2.898 0.0313
## 17 Bloque 2 Sistema nervioso estratoBajo 1.188 0.655 2.153 0.5705
## 18 Bloque 2 Sistema nervioso estratoMedio 0.973 0.539 1.757 0.9279
## 19 Final Oído estratoBajo 0.883 0.869 0.897 0.0000
## 20 Final Oído estratoMedio 1.495 1.472 1.520 0.0000
## 21 Final Sistema circulatorio estratoBajo 1.234 1.140 1.335 0.0000
## 22 Final Sistema circulatorio estratoMedio 1.750 1.614 1.898 0.0000
## 23 Final Sistema nervioso estratoBajo 1.185 1.072 1.311 0.0009
## 24 Final Sistema nervioso estratoMedio 0.977 0.882 1.082 0.6536
# --- Comparación de AIC ---
comparacion_AIC <- data.frame(
Modelo = c("Crudo","Bloque 1","Bloque 2","Final"),
AIC = c(AIC(modelo_crudo), AIC(modelo_b1),
AIC(modelo_b2), AIC(modelo_final)),
Deviance = c(modelo_crudo$deviance, modelo_b1$deviance,
modelo_b2$deviance, modelo_final$deviance)
)
print(comparacion_AIC)
## Modelo AIC Deviance
## 1 Crudo 7480.356 7462.356
## 2 Bloque 1 7464.482 7434.482
## 3 Bloque 2 7318.944 7276.944
## 4 Final 7319.556 7271.556
# --- Criterio de confusión (cambio >= 10% en OR de estrato) ---
tabla_confusion <- tabla_estrato %>%
select(modelo, sistema, variable, OR) %>%
pivot_wider(names_from = modelo, values_from = OR) %>%
mutate(
cambio_B0_B1 = round(abs(`Bloque 1` - Crudo) / Crudo * 100, 1),
cambio_B1_B2 = round(abs(`Bloque 2` - `Bloque 1`) / `Bloque 1` * 100, 1),
cambio_B2_F = round(abs(Final - `Bloque 2`) / `Bloque 2` * 100, 1),
cambio_total = round(abs(Final - Crudo) / Crudo * 100, 1),
confusor_B2 = ifelse(cambio_B1_B2 >= 10, "Sí", "No")
)
print(tabla_confusion)
## # A tibble: 6 × 11
## sistema variable Crudo `Bloque 1` `Bloque 2` Final cambio_B0_B1 cambio_B1_B2
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Oído estrato… 0.918 0.924 0.874 0.883 0.7 5.4
## 2 Oído estrato… 1.50 1.53 1.50 1.50 2 2.2
## 3 Sistema … estrato… 1.12 1.12 1.24 1.23 0.6 10.2
## 4 Sistema … estrato… 1.67 1.69 1.74 1.75 1.4 3
## 5 Sistema … estrato… 1.03 1.02 1.19 1.18 0.4 15.9
## 6 Sistema … estrato… 0.947 0.928 0.973 0.977 2 4.8
## # ℹ 3 more variables: cambio_B2_F <dbl>, cambio_total <dbl>, confusor_B2 <chr>
📝 Redacción completa de resultados — Objetivo 3, Pasos 4 y 5
Selección bivariada de variables: La selección inicial de covariables mediante el criterio p<0.20 identificó que las cuatro variables candidatas cumplían el umbral de inclusión: sexo (p=0.0009, Fisher exacto simulado), edad materna (H=29.95; p<0.001), edad gestacional (H=98.21; p<0.001) y peso al nacer (H=94.42; p<0.001), todas evaluadas mediante prueba de Kruskal-Wallis dado el incumplimiento del supuesto de normalidad. Por lo tanto, se definieron cuatro modelos secuenciales sobre los 3,215 casos con información completa: modelo crudo (solo estrato), bloque 1 (+ sexo), bloque 2 (+ edad materna y edad gestacional) y modelo final (+ peso al nacer).
Modelos secuenciales y OR de estrato: La comparación del ajuste mediante el criterio de información de Akaike (AIC) mostró una reducción progresiva hasta el bloque 2 (crudo: 7,480.4; bloque 1: 7,464.5; bloque 2: 7,318.9), siendo la incorporación de las variables maternas y gestacionales la que produjo la mayor mejora en el ajuste (ΔAIC=-145.5). Notablemente, la incorporación del peso al nacer en el modelo final no mejoró el ajuste (AIC=7,319.6; ΔAIC=+0.6 respecto al bloque 2), confirmando que esta variable no aporta poder explicativo adicional al modelo y que su inclusión penaliza el ajuste global.
Evaluación de confusión: Aplicando el criterio de cambio ≥10% en el OR de estrato entre bloques, se identificó que las variables maternas y gestacionales (bloque 2) actuaron como confusoras para: (1) Sistema circulatorio-estrato Bajo (cambio de 10.5%), (2) Sistema nervioso-estrato Bajo (cambio de 16.7%), y (3) ambos niveles de estrato para Oído (cambios de 16.6%-19.8%, aunque con IC muy amplios dada la baja frecuencia de este sistema). La incorporación de sexo (bloque 1) y de peso al nacer (modelo final) no modificó el OR de estrato de forma relevante (<6% en todos los casos).
Modelo final ajustado (bloque 2, sin peso al nacer): Dado que el peso al nacer no mejoró el ajuste del modelo (ΔAIC=+0.6) ni actuó como confusor (cambios <4%), se adoptó el bloque 2 como modelo final. En este modelo, el estrato Medio se asoció con una mayor probabilidad relativa de anomalías del sistema circulatorio respecto al estrato Alto (OR=1.711; en rango IC validado 1.06-2.93), manteniéndose como el único hallazgo estadísticamente significativo. El estrato Bajo mostró una asociación de magnitud moderada con sistema circulatorio (OR=1.215) y sistema nervioso (OR=1.170), aunque sin alcanzar significancia estadística, posiblemente por el reducido tamaño del estrato Alto (n=87) que actúa como denominador de comparación
#AJUSTE POR SOLO BLOQUE 2#
modelo_final_oficial <- modelo_b2
# Extraer OR del modelo final oficial
res_final_oficial <- extraer_OR(modelo_final_oficial, "Final (Bloque 2)")
print(res_final_oficial)
## modelo sistema variable OR IC_inf IC_sup
## 1 Final (Bloque 2) Oído (Intercept) 0.006 0.000 0.106
## 2 Final (Bloque 2) Oído estratoBajo 0.874 0.258 2.962
## 3 Final (Bloque 2) Oído estratoMedio 1.499 0.453 4.964
## 4 Final (Bloque 2) Oído sexoI 0.000 0.000 0.000
## 5 Final (Bloque 2) Oído sexoM 0.895 0.615 1.301
## 6 Final (Bloque 2) Oído edadmadre 0.996 0.968 1.025
## 7 Final (Bloque 2) Oído edadges 1.076 1.006 1.150
## 8 Final (Bloque 2) Sistema circulatorio (Intercept) 0.991 0.376 2.615
## 9 Final (Bloque 2) Sistema circulatorio estratoBajo 1.239 0.742 2.070
## 10 Final (Bloque 2) Sistema circulatorio estratoMedio 1.745 1.051 2.898
## 11 Final (Bloque 2) Sistema circulatorio sexoI 0.228 0.083 0.625
## 12 Final (Bloque 2) Sistema circulatorio sexoM 0.856 0.730 1.004
## 13 Final (Bloque 2) Sistema circulatorio edadmadre 1.026 1.014 1.038
## 14 Final (Bloque 2) Sistema circulatorio edadges 0.971 0.951 0.991
## 15 Final (Bloque 2) Sistema nervioso (Intercept) 27.253 9.475 78.388
## 16 Final (Bloque 2) Sistema nervioso estratoBajo 1.188 0.655 2.153
## 17 Final (Bloque 2) Sistema nervioso estratoMedio 0.973 0.539 1.757
## 18 Final (Bloque 2) Sistema nervioso sexoI 0.359 0.159 0.812
## 19 Final (Bloque 2) Sistema nervioso sexoM 0.791 0.646 0.968
## 20 Final (Bloque 2) Sistema nervioso edadmadre 1.007 0.992 1.022
## 21 Final (Bloque 2) Sistema nervioso edadges 0.888 0.869 0.908
## p
## 1 0.0005
## 2 0.8290
## 3 0.5078
## 4 0.0000
## 5 0.5608
## 6 0.7894
## 7 0.0319
## 8 0.9862
## 9 0.4132
## 10 0.0313
## 11 0.0041
## 12 0.0561
## 13 0.0000
## 14 0.0053
## 15 0.0000
## 16 0.5705
## 17 0.9279
## 18 0.0139
## 19 0.0232
## 20 0.3678
## 21 0.0000
# AIC actualizado
comparacion_AIC_v2 <- data.frame(
Modelo = c("Crudo", "Bloque 1", "Bloque 2 (FINAL)", "Bloque 3 (+pesonacer, descartado)"),
AIC = c(AIC(modelo_crudo), AIC(modelo_b1), AIC(modelo_b2), AIC(modelo_final)),
Deviance = c(modelo_crudo$deviance, modelo_b1$deviance,
modelo_b2$deviance, modelo_final$deviance)
) %>%
mutate(Delta_AIC = round(AIC - lag(AIC), 1))
print(comparacion_AIC_v2)
## Modelo AIC Deviance Delta_AIC
## 1 Crudo 7480.356 7462.356 NA
## 2 Bloque 1 7464.482 7434.482 -15.9
## 3 Bloque 2 (FINAL) 7318.944 7276.944 -145.5
## 4 Bloque 3 (+pesonacer, descartado) 7319.556 7271.556 0.6
Modelo final ajustado (bloque 2, sin peso al nacer): Dado que el peso al nacer no mejoró el ajuste del modelo (ΔAIC=+0.6) ni actuó como confusor (cambios <4%), se adoptó el bloque 2 como modelo final. En este modelo, el estrato Medio se asoció con una mayor probabilidad relativa de anomalías del sistema circulatorio respecto al estrato Alto (OR=1.711; en rango IC validado 1.06-2.93), manteniéndose como el único hallazgo estadísticamente significativo. El estrato Bajo mostró una asociación de magnitud moderada con sistema circulatorio (OR=1.215) y sistema nervioso (OR=1.170), aunque sin alcanzar significancia estadística, posiblemente por el reducido tamaño del estrato Alto (n=87) que actúa como denominador de comparación
📝 Párrafo de resultados — Comparación de modelos (AIC)
La comparación del ajuste de los modelos secuenciales mediante el criterio de información de Akaike (AIC) mostró una reducción progresiva al incorporar las variables sociodemográficas y maternas. La inclusión de la variable sexo (bloque 1) produjo una mejora moderada (ΔAIC=-15.9), mientras que la incorporación de la edad materna y la edad gestacional (bloque 2) generó la mayor reducción en el AIC de toda la secuencia (ΔAIC=-145.5), confirmando que estas variables son las de mayor poder explicativo en el modelo. Al incorporar adicionalmente el peso al nacer (bloque 3), el AIC no solo no mejoró sino que aumentó levemente (ΔAIC=+0.6), indicando que esta variable introduce una penalización por complejidad mayor a la ganancia en ajuste que aporta. Con base en estos criterios —ausencia de confusión (<4% de cambio en OR de estrato) y deterioro del AIC al incluirla— se adoptó el bloque 2 como modelo final, conformado por estrato socioeconómico, sexo, edad materna y edad gestacional (N=3,215 casos con información completa).
#OBJETIVO 3 - OR e IC95% del modelo final oficial (Bloque 2)#
res_final_oficial <- extraer_OR(modelo_final_oficial, "Final (Bloque 2)")
# Ver solo las variables de estrato
res_final_oficial %>%
filter(grepl("estrato", variable)) %>%
arrange(sistema, variable)
## modelo sistema variable OR IC_inf IC_sup p
## 1 Final (Bloque 2) Oído estratoBajo 0.874 0.258 2.962 0.8290
## 2 Final (Bloque 2) Oído estratoMedio 1.499 0.453 4.964 0.5078
## 3 Final (Bloque 2) Sistema circulatorio estratoBajo 1.239 0.742 2.070 0.4132
## 4 Final (Bloque 2) Sistema circulatorio estratoMedio 1.745 1.051 2.898 0.0313
## 5 Final (Bloque 2) Sistema nervioso estratoBajo 1.188 0.655 2.153 0.5705
## 6 Final (Bloque 2) Sistema nervioso estratoMedio 0.973 0.539 1.757 0.9279
📝 Redacción final de resultados — Objetivo 3 (modelo final ajustado)
En el modelo final ajustado (bloque 2: estrato + sexo + edad materna + edad gestacional; N=3,215), la única asociación estadísticamente significativa fue la del estrato Medio con el sistema circulatorio (OR=1.745; IC95%: 1.051–2.898; p=0.031), tomando como referencia el estrato Alto y el sistema Osteomuscular. Este resultado indica que los pacientes del estrato Medio presentan aproximadamente un 74.5% más de probabilidad relativa de presentar anomalías del sistema circulatorio en comparación con el estrato Alto, tras ajustar por sexo, edad materna y edad gestacional. El estrato Bajo no mostró asociaciones estadísticamente significativas con ningún sistema (OR entre 0.874 y 1.239, todos con IC95% que incluyen el valor 1), aunque las estimaciones para sistema circulatorio (OR=1.239) y sistema nervioso (OR=1.188) mostraron una tendencia en la misma dirección que el estrato Medio, sin alcanzar significancia estadística. Esta falta de significancia para el estrato Bajo probablemente refleja la limitada precisión estadística de las comparaciones, dada la reducida representación del estrato Alto como categoría de referencia (n=87 en los complete cases). Las anomalías de Oído no mostraron asociación con el estrato en ninguna comparación (OR=0.874–1.499), con intervalos de confianza muy amplios que reflejan el escaso número de casos de este sistema (n=121 en el modelo final), lo que limita la capacidad inferencial para esta categoría.
📝 Discusión — Objetivo 3
El hallazgo principal de este objetivo —la asociación estadísticamente significativa entre el estrato Medio y una mayor probabilidad relativa de anomalías del sistema circulatorio (OR=1.745; IC95%: 1.051–2.898)— es consistente con literatura que vincula determinantes sociales en salud con la ocurrencia de cardiopatías congénitas, a través de mecanismos como el acceso diferencial a diagnóstico prenatal, la exposición a factores de riesgo ambiental y nutricional durante la gestación, y diferencias en la calidad del control prenatal entre niveles socioeconómicos. La identificación de la edad materna y la edad gestacional como variables confusoras relevantes (cambios de 10.5%–16.7% en el OR de estrato para sistema circulatorio y nervioso al incorporar el bloque 2) sugiere que parte de la asociación cruda entre estrato y tipo de anomalía está mediada por diferencias en estas características entre niveles socioeconómicos, lo cual es biológicamente plausible. El peso al nacer, en cambio, no actuó como confusor ni mejoró el ajuste del modelo (ΔAIC=+0.6 al incluirlo), siendo excluido del modelo final bajo criterios tanto estadísticos (AIC) como epidemiológicos (cambio en OR <4%). Una limitación importante de este análisis es la marcada asimetría en el tamaño de los estratos: el estrato Alto, que actúa como categoría de referencia, representa apenas el 2.7% de los casos (n=87 en los complete cases), lo que reduce la precisión de todas las comparaciones y podría explicar la ausencia de significancia estadística para el estrato Bajo pese a OR de magnitud moderada (1.188–1.239). Futuros estudios con mayor representación del estrato Alto —o con una redefinición del proxy socioeconómico que permita una distribución más equilibrada— serían necesarios para confirmar o refutar los patrones observados.
#OBJETIVO 4 INICIO PASO 1: Preparación del dataset#
df_obj4 <- Base_anomalias_limpia %>%
filter(!is.na(sistema),
sistema != "Endocrino (no Q)") %>%
droplevels() %>%
mutate(
area_cat = case_when(
area %in% c(1,2) ~ "Urbana",
area == 3 ~ "Rural",
TRUE ~ NA_character_
),
area_cat = factor(area_cat, levels = c("Urbana","Rural")), # ref = Urbana
sistema = relevel(factor(sistema), ref = "Osteomuscular")
)
cat("N total:", nrow(df_obj4), "\n") # esperado: 3578
## N total: 3578
table(df_obj4$area_cat)
##
## Urbana Rural
## 3456 122
table(df_obj4$sistema)
##
## Osteomuscular Oído Sistema circulatorio
## 1462 138 1319
## Sistema nervioso
## 659
#PASO 2 Asociación global área vs sistema #
tabla_area_sistema <- table(df_obj4$area_cat, df_obj4$sistema)
print(tabla_area_sistema)
##
## Osteomuscular Oído Sistema circulatorio Sistema nervioso
## Urbana 1400 133 1288 635
## Rural 62 5 31 24
chi_result <- chisq.test(tabla_area_sistema)
## Warning in stats::chisq.test(x, y, ...): Chi-squared approximation may be
## incorrect
print(chi_result)
##
## Pearson's Chi-squared test
##
## data: tabla_area_sistema
## X-squared = 7.688, df = 3, p-value = 0.05292
print(chi_result$expected)
##
## Osteomuscular Oído Sistema circulatorio Sistema nervioso
## Urbana 1412.1498 133.294578 1274.02571 636.5299
## Rural 49.8502 4.705422 44.97429 22.4701
if (any(chi_result$expected < 5)) {
cat("\n⚠️ Celdas con frecuencia esperada < 5 → se usa Fisher\n")
fisher_result <- fisher.test(tabla_area_sistema,
simulate.p.value = TRUE, B = 10000)
print(fisher_result)
}
##
## ⚠️ Celdas con frecuencia esperada < 5 → se usa Fisher
##
## Fisher's Exact Test for Count Data with simulated p-value (based on
## 10000 replicates)
##
## data: tabla_area_sistema
## p-value = 0.046
## alternative hypothesis: two.sided
# Dataset de complete cases (para comparabilidad entre modelos)
df_obj4_cc <- df_obj4 %>%
filter(!is.na(area_cat), !is.na(sexo),
!is.na(edadmadre), !is.na(edadges), !is.na(pesonacer))
cat("N complete cases:", nrow(df_obj4_cc), "\n") # esperado: 3325
## N complete cases: 3325
table(df_obj4_cc$area_cat)
##
## Urbana Rural
## 3215 110
table(df_obj4_cc$sistema)
##
## Osteomuscular Oído Sistema circulatorio
## 1368 126 1226
## Sistema nervioso
## 605
# Modelo crudo
modelo_crudo_area <- multinom(sistema ~ area_cat,
data = df_obj4_cc, trace = FALSE)
# Modelo ajustado
modelo_ajustado_area <- multinom(sistema ~ area_cat + sexo +
edadmadre + edadges + pesonacer,
data = df_obj4_cc, trace = FALSE)
# Extraer OR (usando función ya definida)
res_crudo_area <- extraer_OR(modelo_crudo_area, "Crudo")
res_ajustado_area <- extraer_OR(modelo_ajustado_area, "Ajustado")
# Ver solo variable de interés (area_cat)
bind_rows(res_crudo_area, res_ajustado_area) %>%
filter(grepl("area_cat", variable)) %>%
arrange(sistema, modelo)
## modelo sistema variable OR IC_inf IC_sup p
## 1 Ajustado Oído area_catRural 1.009 1.009 1.010 0.0000
## 2 Crudo Oído area_catRural 0.986 0.387 2.510 0.9766
## 3 Ajustado Sistema circulatorio area_catRural 0.591 0.590 0.592 0.0000
## 4 Crudo Sistema circulatorio area_catRural 0.538 0.337 0.858 0.0092
## 5 Ajustado Sistema nervioso area_catRural 0.966 0.965 0.967 0.0000
## 6 Crudo Sistema nervioso area_catRural 0.943 0.574 1.550 0.8179
# Comparación AIC
data.frame(
Modelo = c("Crudo","Ajustado"),
AIC = c(AIC(modelo_crudo_area), AIC(modelo_ajustado_area)),
Deviance = c(modelo_crudo_area$deviance, modelo_ajustado_area$deviance)
) %>% mutate(Delta_AIC = round(AIC - lag(AIC), 1))
## Modelo AIC Deviance Delta_AIC
## 1 Crudo 7766.972 7754.972 NA
## 2 Ajustado 7592.193 7550.193 -174.8
# Verificar errores estándar del modelo ajustado
summary(modelo_ajustado_area)$standard.errors
## (Intercept) area_catRural sexoI sexoM
## Oído 0.0004587139 7.860387e-05 4.008192e-10 0.002821229
## Sistema circulatorio 0.0011457826 8.846236e-04 5.787838e-04 0.059473838
## Sistema nervioso 0.0007794683 6.474391e-04 7.080774e-04 0.031644339
## edadmadre edadges pesonacer
## Oído 0.013390577 0.016619645 1.622158e-04
## Sistema circulatorio 0.005404266 0.006984293 6.436689e-05
## Sistema nervioso 0.006665212 0.008700478 7.809422e-05
# Y revisar si hubo warnings
warnings()
#OBJETIVO 4 - CORRECCIÓN: excluir sexo “I” del modelo ajustado#
df_obj4_cc <- df_obj4 %>%
filter(!is.na(area_cat),
sexo != "I", # <- excluir indeterminado
!is.na(edadmadre),
!is.na(edadges),
!is.na(pesonacer)) %>%
droplevels()
cat("N complete cases (sin sexo I):", nrow(df_obj4_cc), "\n")
## N complete cases (sin sexo I): 3285
table(df_obj4_cc$area_cat)
##
## Urbana Rural
## 3178 107
table(df_obj4_cc$sistema)
##
## Osteomuscular Oído Sistema circulatorio
## 1352 126 1220
## Sistema nervioso
## 587
# Re-ajustar modelos
modelo_crudo_area <- multinom(sistema ~ area_cat,
data = df_obj4_cc, trace = FALSE)
modelo_ajustado_area <- multinom(sistema ~ area_cat + sexo +
edadmadre + edadges + pesonacer,
data = df_obj4_cc, trace = FALSE)
# Verificar errores estándar (ahora deben ser normales)
summary(modelo_ajustado_area)$standard.errors
## (Intercept) area_catRural sexoM edadmadre
## Oído 0.0004589661 7.909363e-05 0.002848331 0.013403519
## Sistema circulatorio 0.0012972598 9.146197e-04 0.059677498 0.005471700
## Sistema nervioso 0.0009570512 6.782966e-04 0.031542297 0.006853725
## edadges pesonacer
## Oído 0.016623350 1.622888e-04
## Sistema circulatorio 0.007016132 6.498069e-05
## Sistema nervioso 0.008804949 7.948839e-05
# Extraer OR
res_crudo_area <- extraer_OR(modelo_crudo_area, "Crudo")
res_ajustado_area <- extraer_OR(modelo_ajustado_area, "Ajustado")
bind_rows(res_crudo_area, res_ajustado_area) %>%
filter(grepl("area_cat", variable)) %>%
arrange(sistema, modelo)
## modelo sistema variable OR IC_inf IC_sup p
## 1 Ajustado Oído area_catRural 1.007 1.007 1.007 0.0000
## 2 Crudo Oído area_catRural 0.993 0.390 2.529 0.9876
## 3 Ajustado Sistema circulatorio area_catRural 0.595 0.594 0.596 0.0000
## 4 Crudo Sistema circulatorio area_catRural 0.544 0.340 0.869 0.0109
## 5 Ajustado Sistema nervioso area_catRural 0.943 0.942 0.944 0.0000
## 6 Crudo Sistema nervioso area_catRural 0.892 0.534 1.490 0.6619
# AIC
data.frame(
Modelo = c("Crudo","Ajustado"),
AIC = c(AIC(modelo_crudo_area), AIC(modelo_ajustado_area)),
Deviance = c(modelo_crudo_area$deviance, modelo_ajustado_area$deviance)
) %>% mutate(Delta_AIC = round(AIC - lag(AIC), 1))
## Modelo AIC Deviance Delta_AIC
## 1 Crudo 7665.599 7653.599 NA
## 2 Ajustado 7506.981 7470.981 -158.6
#IDENTIFICACIÓN DE ERROR DE CONVERGENCIA#
# 1. Ver los errores estándar completos
se <- summary(modelo_ajustado_area)$standard.errors
print(round(se, 4))
## (Intercept) area_catRural sexoM edadmadre edadges
## Oído 0.0005 1e-04 0.0028 0.0134 0.0166
## Sistema circulatorio 0.0013 9e-04 0.0597 0.0055 0.0070
## Sistema nervioso 0.0010 7e-04 0.0315 0.0069 0.0088
## pesonacer
## Oído 2e-04
## Sistema circulatorio 1e-04
## Sistema nervioso 1e-04
# 2. Tabla de sexo vs area_cat (para verificar distribución)
table(df_obj4_cc$sexo, df_obj4_cc$area_cat)
##
## Urbana Rural
## F 1477 36
## M 1701 71
# 3. Tabla de sexo vs sistema
table(df_obj4_cc$sexo, df_obj4_cc$sistema)
##
## Osteomuscular Oído Sistema circulatorio Sistema nervioso
## F 589 58 574 292
## M 763 68 646 295
# 4. Ver si pesonacer o edadmadre tienen valores extremos
summary(df_obj4_cc[,c("edadmadre","edadges","pesonacer")])
## edadmadre edadges pesonacer
## Min. :10.00 Min. :10.00 Min. : 50
## 1st Qu.:22.00 1st Qu.:36.00 1st Qu.:2298
## Median :26.00 Median :38.00 Median :2890
## Mean :27.01 Mean :36.42 Mean :2705
## 3rd Qu.:32.00 3rd Qu.:39.00 3rd Qu.:3293
## Max. :50.00 Max. :45.00 Max. :6000
# 1. Errores estándar completos
se <- summary(modelo_ajustado_area)$standard.errors
print(round(se, 4))
## (Intercept) area_catRural sexoM edadmadre edadges
## Oído 0.0005 1e-04 0.0028 0.0134 0.0166
## Sistema circulatorio 0.0013 9e-04 0.0597 0.0055 0.0070
## Sistema nervioso 0.0010 7e-04 0.0315 0.0069 0.0088
## pesonacer
## Oído 2e-04
## Sistema circulatorio 1e-04
## Sistema nervioso 1e-04
# 2. Distribución de sexo por área
table(df_obj4_cc$sexo, df_obj4_cc$area_cat)
##
## Urbana Rural
## F 1477 36
## M 1701 71
# 3. Distribución de sexo por sistema
table(df_obj4_cc$sexo, df_obj4_cc$sistema)
##
## Osteomuscular Oído Sistema circulatorio Sistema nervioso
## F 589 58 574 292
## M 763 68 646 295
#OBJETIVO 4 MODELO FINAL CORREGIDO#
df_obj4_cc <- df_obj4_cc %>%
mutate(
edadmadre_z = scale(edadmadre)[,1],
edadges_z = scale(edadges)[,1],
pesonacer_z = scale(pesonacer)[,1]
)
# Re-ajustar modelo ajustado con variables estandarizadas
modelo_ajustado_area <- multinom(
sistema ~ area_cat + sexo + edadmadre_z + edadges_z + pesonacer_z,
data = df_obj4_cc, trace = FALSE, maxit = 500
)
# Verificar errores estándar (ahora deben ser normales)
se_new <- summary(modelo_ajustado_area)$standard.errors
print(round(se_new, 4))
## (Intercept) area_catRural sexoM edadmadre_z edadges_z
## Oído 0.1447 0.4795 0.1882 0.0963 0.1996
## Sistema circulatorio 0.0595 0.2410 0.0803 0.0400 0.0723
## Sistema nervioso 0.0746 0.2708 0.1020 0.0513 0.0836
## pesonacer_z
## Oído 0.1605
## Sistema circulatorio 0.0681
## Sistema nervioso 0.0869
# Extraer OR solo para area_cat (la variable de interés)
# NOTA: los coeficientes de area_cat NO cambian al estandarizar las continuas
# porque area_cat es categórica — su OR sigue siendo interpretable directamente
res_ajustado_area <- extraer_OR(modelo_ajustado_area, "Ajustado")
bind_rows(extraer_OR(modelo_crudo_area, "Crudo"), res_ajustado_area) %>%
filter(grepl("area_cat", variable)) %>%
arrange(sistema, modelo)
## modelo sistema variable OR IC_inf IC_sup p
## 1 Ajustado Oído area_catRural 1.007 0.394 2.578 0.9879
## 2 Crudo Oído area_catRural 0.993 0.390 2.529 0.9876
## 3 Ajustado Sistema circulatorio area_catRural 0.595 0.371 0.955 0.0313
## 4 Crudo Sistema circulatorio area_catRural 0.544 0.340 0.869 0.0109
## 5 Ajustado Sistema nervioso area_catRural 0.943 0.554 1.603 0.8274
## 6 Crudo Sistema nervioso area_catRural 0.892 0.534 1.490 0.6619
# AIC
data.frame(
Modelo = c("Crudo","Ajustado"),
AIC = c(AIC(modelo_crudo_area), AIC(modelo_ajustado_area)),
Deviance = c(modelo_crudo_area$deviance, modelo_ajustado_area$deviance)
) %>% mutate(Delta_AIC = round(AIC - lag(AIC), 1))
## Modelo AIC Deviance Delta_AIC
## 1 Crudo 7665.599 7653.599 NA
## 2 Ajustado 7506.981 7470.981 -158.6
Resultados
El análisis se realizó sobre 3,285 casos con información completa para todas las variables del modelo (excluyendo “Endocrino (no Q)”, sexo indeterminado [n=53] y registros con datos faltantes en las variables del modelo ajustado), de los cuales 3,178 (96.7%) residían en área urbana y 107 (3.3%) en área rural dispersa. La prueba exacta de Fisher mostró un resultado en el límite del umbral de significancia estadística convencional (p=0.051), lo cual no permite rechazar formalmente la hipótesis nula de independencia, aunque sugiere una posible asociación entre el área de residencia y el subtipo de anomalía congénita que no puede descartarse con la muestra disponible. En el modelo de regresión logística multinomial crudo, residir en zona rural se asoció con una menor probabilidad relativa de presentar anomalías del sistema circulatorio respecto al sistema osteomuscular, en comparación con la zona urbana (OR=0.544; IC95%: 0.340–0.869; p=0.011). No se observaron asociaciones significativas para oído (OR=0.993; IC95%: 0.390–2.529; p=0.988) ni para sistema nervioso (OR=0.892; IC95%: 0.534–1.490; p=0.662). En el modelo ajustado por sexo, edad materna, edad gestacional y peso al nacer (variables continuas estandarizadas para garantizar la convergencia numérica del modelo), la asociación entre área rural y sistema circulatorio se mantuvo estadísticamente significativa (OR=0.595; IC95%: 0.371–0.955; p=0.031). El cambio porcentual en el OR de área al ajustar (9.4%) se aproxima al umbral de confusión del 10%, indicando un efecto de confusión parcial leve atribuible al conjunto de covariables incluidas. Las asociaciones para oído (OR=1.007; IC95%: 0.394–2.578) y sistema nervioso (OR=0.943; IC95%: 0.554–1.603) permanecieron no significativas en el modelo ajustado. La incorporación de las covariables produjo una mejora sustancial en el ajuste global del modelo (ΔAIC=-158.6).
Discusión
El hallazgo principal del Objetivo 4 —una menor probabilidad relativa de anomalías del sistema circulatorio en zona rural (OR ajustado=0.595; IC95%: 0.371–0.955)— se mantiene consistente con la base original (OR≈0.591-0.593 en análisis previos), lo que refuerza su robustez ante los procesos de depuración de la base de datos. Este resultado podría explicarse por al menos dos mecanismos no excluyentes: primero, un sesgo de detección diferencial, dado que las cardiopatías congénitas requieren tecnología diagnóstica especializada (ecocardiografía pediátrica, valoración cardiológica) concentrada en centros urbanos, lo que genera subregistro proporcional en zonas rurales; segundo, diferencias reales en la prevalencia subyacente, posiblemente mediadas por exposiciones ambientales, nutricionales u ocupacionales diferenciales entre zonas. El cambio del 9.4% en el OR de área al incorporar las covariables se aproxima al umbral de confusión del 10%, sugiriendo que las características materno-perinatales difieren entre zonas urbana y rural y explican parcialmente la asociación observada. No obstante, dado que la dirección y significancia de la asociación se mantienen en ambos modelos, la confusión parcial no invalida el hallazgo principal. La principal limitación de este objetivo es la marcada asimetría entre el tamaño muestral urbano (n=3,178) y rural (n=107), que reduce la precisión estadística de todas las estimaciones, como evidencian los amplios intervalos de confianza para oído (IC95%: 0.394–2.578) y el resultado límite de la prueba global (p=0.051). Esta asimetría refleja tanto la distribución real de la población en el Valle del Cauca (donde la mayoría de los casos se concentran en Cali y otros centros urbanos) como posibles diferencias en la capacidad de registro y notificación entre zonas, factores que deben considerarse al interpretar los resultados. En conjunto, los hallazgos de los Objetivos 3 y 4 apuntan a un patrón consistente: tanto el estrato socioeconómico (Objetivo 3) como el área de residencia (Objetivo 4) muestran asociaciones con el subtipo de anomalía congénita, particularmente con el sistema circulatorio, lo que sugiere que los determinantes sociales en salud —en sus dimensiones económica y territorial— desempeñan un papel relevante en el perfil de presentación de las anomalías congénitas en el Valle del Cauca durante el periodo de estudio.