CARGA DE LA BASE YA LIMPIA (sin re-aplicar limpieza)

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).

RESUMEN DE VALORES FALTANTES (NA) POR VARIABLE - df_obj1 (N=3582)

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

OBJETIVO 4 - PASOS 3 y 4: Modelo crudo y ajustado

# 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.