Unidad de Análisis HospitalarioProbabilidades y teorema de Bayes · mecanismo GRDCIE-10 I50
Actividad 2 · GRD 2024

Versión de impresión: se muestran todas las secciones en una sola página.

# ---- rutas relativas ----
# La base GRD original no se entrega (es pública y pesa ~570 MB). Para reproducir el informe,
# se deja en la subcarpeta GRD_PUBLICO_2024/ junto a este .Rmd, igual que en la Actividad 1.
# Como respaldo se busca también en la carpeta de la Actividad 1 (../informe-1/).
buscar_archivo <- function(rel) {
  candidatos <- c(rel, file.path("..", "informe-1", rel))
  f <- candidatos[file.exists(candidatos)][1]
  if (is.na(f)) stop("No se encontró el archivo ", rel, " junto al .Rmd")
  f
}
ruta_grd <- buscar_archivo(file.path("GRD_PUBLICO_2024", "GRD_PUBLICO_2024.txt"))

cols_diag <- paste0("DIAGNOSTICO", 1:35)
cols_usar <- c("COD_HOSPITAL", "FECHA_NACIMIENTO", "TIPO_INGRESO", "ESPECIALIDAD_MEDICA",
               "TIPO_ACTIVIDAD", "FECHA_INGRESO", "FECHAALTA", "TIPOALTA", cols_diag,
               "IR_29301_SEVERIDAD")

base <- fread(ruta_grd, sep = "|", encoding = "Latin-1", quote = "", na.strings = "",
              select = cols_usar, colClasses = list(character = "COD_HOSPITAL"))

# ---- etapa 1 y 2: base completa y exclusión de CMA (igual que en la Actividad 1) ----
n0    <- nrow(base)
n_cma <- base[TIPO_ACTIVIDAD == "CIRUGÍA MAYOR AMBULATORIA (CMA)", .N]
hosp_all <- base[TIPO_ACTIVIDAD == "HOSPITALIZACIÓN"]
rm(base); invisible(gc())
n1 <- nrow(hosp_all)

# ---- etapa 3: validez de fechas, ahora aplicada a TODOS los egresos (corrección) ----
fecha_valida <- function(x) grepl("^[0-9]{4}-[0-9]{2}-[0-9]{2}$", x)
ok_fechas <- fecha_valida(hosp_all$FECHA_INGRESO) & fecha_valida(hosp_all$FECHA_NACIMIENTO) &
             fecha_valida(hosp_all$FECHAALTA)

# cohorte tal como se construyó en la Actividad 1 (I50 en algún diagnóstico, luego depuración)
i50_all   <- Reduce(`|`, lapply(cols_diag, function(cc) grepl("^I50", hosp_all[[cc]])))
n_cand_A1 <- sum(i50_all)
n_cohorte_A1 <- sum(i50_all & ok_fechas)
n_malas_omega <- sum(!ok_fechas)
n_malas_E     <- sum(!ok_fechas & i50_all)

omega <- hosp_all[ok_fechas]
rm(hosp_all, i50_all, ok_fechas); invisible(gc())
N <- nrow(omega)
# ---------------- Definición de los eventos sobre Ω ----------------
E_vec <- Reduce(`|`, lapply(cols_diag, function(cc) grepl("^I50", omega[[cc]])))
omega[, E  := E_vec]                                  # I50 en cualquiera de los 35 diagnósticos
omega[, Ep := grepl("^I50", DIAGNOSTICO1)]            # I50 como diagnóstico principal
omega[, Es := E & !Ep]                                # I50 solo como diagnóstico secundario
omega[, (cols_diag) := NULL]; rm(E_vec); invisible(gc())

omega[, Fal := TIPOALTA == "FALLECIDO"]               # evento F (se evita el nombre F, que en R es FALSE)
omega[, U   := TIPO_INGRESO == "URGENCIA"]            # evento U

omega[, estancia := as.numeric(as.Date(FECHAALTA) - as.Date(FECHA_INGRESO))]
corte_L <- unname(quantile(omega$estancia, 0.75))     # percentil 75 de la estancia en todo Ω
omega[, L := estancia > corte_L]                      # evento L: estancia larga

omega[, edad := as.numeric(as.Date(FECHA_INGRESO) - as.Date(FECHA_NACIMIENTO)) / 365.25]
grupos <- c("< 45", "45-64", "65-74", "75-84", "85 y más")
omega[, grupo_etario := cut(edad, breaks = c(-Inf, 45, 65, 75, 85, Inf), right = FALSE,
                            labels = grupos)]         # mismos cortes que en la Actividad 1

sev_tab <- suppressMessages(as.data.table(read_excel("Tablas_maestras_GRD.xlsx", sheet = "Severidad GRD")))
setnames(sev_tab, c("nivel", "etiqueta"))
niveles_sev <- c("S₁", "S₂", "S₃", "Sin severidad registrada")
omega[, sev := factor(fcase(IR_29301_SEVERIDAD == "1", "S₁",
                            IR_29301_SEVERIDAD == "2", "S₂",
                            IR_29301_SEVERIDAD == "3", "S₃",
                            default = "Sin severidad registrada"), levels = niveles_sev)]

nE <- sum(omega$E); pE <- nE / N
nF <- sum(omega$Fal); pF <- nF / N
n_est_neg <- sum(omega$estancia < 0)

1 Introducción

1.1 Contexto y objetivo

Este informe continúa el trabajo de la Actividad 1, en el rol de analista de la Unidad de Análisis Hospitalario. La Actividad 1 dimensionó y caracterizó los episodios con insuficiencia cardíaca (CIE-10 I50) dentro de los egresos hospitalarios GRD 2024. Aquí esas mismas características se expresan como eventos sobre un espacio muestral definido, y se responden preguntas de la jefatura en lenguaje de probabilidad: probabilidades marginales, condicionales y totales, el teorema de Bayes para invertir preguntas, un marcador de registro evaluado con sensibilidad, especificidad y valores predictivos, y una simulación que muestra cómo se comportan las estimaciones cuando se trabaja con muestras de episodios.

El alcance es el cálculo e interpretación de probabilidades sobre la base GRD 2024: no se ajustan modelos predictivos ni se realizan pruebas de hipótesis.

Qué significa una probabilidad en este informe P(A) es la probabilidad de que un episodio elegido al azar entre los egresos hospitalarios de Ω cumpla la condición A. No es la probabilidad de que una persona enferme o fallezca ni una tasa poblacional: cada fila de la base es un episodio de hospitalización, y una misma persona puede aportar varios episodios.

1.2 Declaración de uso de inteligencia artificial

Se utilizó Claude Code (Anthropic, modelo Claude Opus 5.5) como apoyo para: (i) escribir y depurar el código R de construcción de eventos, tablas de contingencia, aplicaciones de Bayes y simulación; (ii) redactar borradores del texto interpretativo; y (iii) revisar el cumplimiento de la pauta y de la retroalimentación de la Actividad 1. Los resultados se verificaron así: (a) ninguna cifra del texto está escrita a mano: todas se insertan desde el código y se recalculan en cada compilación; (b) cada aplicación de la ley de probabilidad total y del teorema de Bayes se contrasta en una tabla con el conteo directo de episodios, con una columna de diferencia que debe ser cero salvo redondeo; (c) la unión se verifica por fórmula y por conteo; (d) la simulación usa semilla fija y se comprobó que dos compilaciones sucesivas entregan los mismos números; y (e) las frases que comparan valores (por ejemplo, qué nivel de severidad aporta más fallecimientos) se generan a partir de los propios valores calculados, para que el texto no pueda contradecir a su tabla. La responsabilidad técnica y de contenido del informe es del autor.

2 Datos y correcciones respecto de la Actividad 1

RESULTADO 01 · PARTE 1

Espacio muestral Ω = egresos hospitalarios GRD 2024, sin CMA y con fechas de ingreso, nacimiento y alta válidas. N = 873.719 episodios. Este N es el denominador de toda probabilidad marginal del informe. La cohorte E (insuficiencia cardíaca en cualquier campo de diagnóstico) tiene n(E) = 52.312 episodios, P(E) = 52.312 / 873.719 = 5,99%.

2.1 Fuente de datos y tablas auxiliares

Se usan la misma base, las mismas tablas auxiliares y la misma definición CIE-10 de la Actividad 1:

  • Base: GRD_PUBLICO_2024.txt, base pública de egresos hospitalarios y cirugía mayor ambulatoria del mecanismo GRD, Fondo Nacional de Salud (FONASA), año 2024. Texto plano delimitado por |, codificación Latin-1. Descargada desde el portal de datos abiertos de FONASA el 21 de agosto de 2026. La base no se entrega junto al informe por su tamaño; para reproducirlo se ubica en la subcarpeta GRD_PUBLICO_2024/.
  • Tablas_maestras_GRD.xlsx (se entrega): hoja Hospitales para el nombre de cada establecimiento y hoja Severidad GRD para las etiquetas de severidad.
  • CIE-10.xlsx (se entrega): catálogo oficial CIE-10, para verificar los códigos I50.
cie10 <- suppressMessages(as.data.table(read_excel("CIE-10.xlsx", sheet = "CIE 10")))
tabla_html(cie10[grepl("^I50", Código), .(Código, Descripción)],
           caption = "Códigos CIE-10 que definen el evento E (fuente: CIE-10.xlsx)",
           label = "cie10")
Tabla 1. Códigos CIE-10 que definen el evento E (fuente: CIE-10.xlsx)
Código Descripción
I50 Insuficiencia cardíaca
I50.0 Insuficiencia cardíaca congestiva
I50.1 Insuficiencia ventricular izquierda
I50.9 Insuficiencia cardíaca, no especificada

El evento E se define con el patrón ^I50, que captura I50 y sus subcategorías I50.0, I50.1 e I50.9 (Tabla 1), buscado en los 35 campos de diagnóstico de cada episodio.

2.2 Cambios aplicados a partir de la retroalimentación

La retroalimentación de la Actividad 1 no pidió corregir la definición de la cohorte; sus observaciones se refieren al lenguaje y a la entrega. Todas se aplicaron en este informe. Se agregó además una corrección metodológica propia, necesaria para el cálculo de probabilidades.

cambios <- data.table(
  `N.º` = 1:6,
  `Observación o problema detectado` = c(
    "(Corrección propia) En la Actividad 1 la depuración de fechas no válidas se aplicó solo a los episodios candidatos con I50, no al total de egresos.",
    "Retroalimentación: se escribió \"personas de 65 años o más\", cuando la unidad de análisis es el episodio.",
    "Retroalimentación: se escribió \"en promedio\" al comparar medianas de peso GRD.",
    "Retroalimentación: la explicación invernal del alza del tercer trimestre es una hipótesis que conviene dejar fuera.",
    "Retroalimentación: la frase \"Fecha de extracción declarada por el estudiante\" debía reescribirse.",
    "Retroalimentación: la entrega no traía el enlace al HTML publicado ni las tablas auxiliares."),
  `Cambio aplicado en la Actividad 2` = c(
    paste0("La misma regla de fechas válidas se aplica a todo el espacio muestral. Así la estancia (evento L) y la edad (grupos G) quedan definidas para cada episodio de Ω, y todas las probabilidades usan el mismo universo depurado."),
    "Todo el texto habla de episodios. Ninguna probabilidad se presenta como característica de personas.",
    "Se nombra el estadístico que se compara. En este informe se trabaja con proporciones, no con promedios.",
    "No se proponen hipótesis explicativas ni estacionales: las probabilidades condicionales se describen como co-ocurrencia en los registros.",
    "Se reescribió como \"Descargada desde el portal de datos abiertos de FONASA el 21 de agosto de 2026\".",
    "Se entregan Tablas_maestras_GRD.xlsx y CIE-10.xlsx junto al .Rmd, y el enlace al HTML publicado."),
  `Efecto sobre Ω o sobre la cohorte` = c(
    paste0("Ω pasa de ", fnum(n1), " a ", fnum(N), " egresos (", fnum(n_malas_omega),
           " excluidos). La cohorte no cambia: ", fnum(nE), " episodios, igual que en la Actividad 1, porque los ",
           fnum(n_malas_E), " episodios con I50 y fecha no válida ya se habían excluido."),
    "Ninguno (redacción).", "Ninguno (redacción).", "Ninguno (redacción).",
    "Ninguno (redacción).", "Ninguno (entrega).")
)
tabla_html(cambios, caption = "Cambios respecto de la Actividad 1 y su efecto sobre el espacio muestral y la cohorte",
           label = "cambios") |>
  column_spec(2:4, width = "30%") |> kable_styling(full_width = TRUE)
Tabla 2. Cambios respecto de la Actividad 1 y su efecto sobre el espacio muestral y la cohorte
N.º Observación o problema detectado Cambio aplicado en la Actividad 2 Efecto sobre Ω o sobre la cohorte
1 (Corrección propia) En la Actividad 1 la depuración de fechas no válidas se aplicó solo a los episodios candidatos con I50, no al total de egresos. La misma regla de fechas válidas se aplica a todo el espacio muestral. Así la estancia (evento L) y la edad (grupos G) quedan definidas para cada episodio de Ω, y todas las probabilidades usan el mismo universo depurado. Ω pasa de 873.772 a 873.719 egresos (53 excluidos). La cohorte no cambia: 52.312 episodios, igual que en la Actividad 1, porque los 3 episodios con I50 y fecha no válida ya se habían excluido.
2 Retroalimentación: se escribió "personas de 65 años o más", cuando la unidad de análisis es el episodio. Todo el texto habla de episodios. Ninguna probabilidad se presenta como característica de personas. Ninguno (redacción).
3 Retroalimentación: se escribió "en promedio" al comparar medianas de peso GRD. Se nombra el estadístico que se compara. En este informe se trabaja con proporciones, no con promedios. Ninguno (redacción).
4 Retroalimentación: la explicación invernal del alza del tercer trimestre es una hipótesis que conviene dejar fuera. No se proponen hipótesis explicativas ni estacionales: las probabilidades condicionales se describen como co-ocurrencia en los registros. Ninguno (redacción).
5 Retroalimentación: la frase "Fecha de extracción declarada por el estudiante" debía reescribirse. Se reescribió como "Descargada desde el portal de datos abiertos de FONASA el 21 de agosto de 2026". Ninguno (redacción).
6 Retroalimentación: la entrega no traía el enlace al HTML publicado ni las tablas auxiliares. Se entregan Tablas_maestras_GRD.xlsx y CIE-10.xlsx junto al .Rmd, y el enlace al HTML publicado. Ninguno (entrega).

Tamaño final de la cohorte: 52.312 episodios (igual que en la Actividad 1, donde la cohorte final fue de 52.312 episodios). El único cambio que altera un conteo es el 1, que reduce Ω en 53 egresos: P(E) cambia en menos de una centésima de punto porcentual (de 5,9869% a 5,9873%).

2.3 Trazabilidad de Ω y de la cohorte

traz <- data.table(
  Etapa = c("1. Base completa GRD 2024",
            "2. Excluye cirugía mayor ambulatoria (CMA)",
            "3. Excluye egresos con fecha de ingreso, nacimiento o alta no válida",
            "Ω: espacio muestral (egresos hospitalarios depurados)",
            "E: episodios de Ω con I50 en algún diagnóstico (subconjunto, no exclusión)"),
  Episodios = fnum(c(n0, n1, N, N, nE)),
  `Excluidos en la etapa` = c("—", fnum(n_cma), fnum(n_malas_omega), "—", "—"))
tabla_html(traz, caption = "Trazabilidad desde la base completa hasta el espacio muestral Ω y la cohorte E",
           label = "traz")
Tabla 3. Trazabilidad desde la base completa hasta el espacio muestral Ω y la cohorte E
Etapa Episodios Excluidos en la etapa
  1. Base completa GRD 2024
1.085.813 —
  1. Excluye cirugía mayor ambulatoria (CMA)
873.772 212.041
  1. Excluye egresos con fecha de ingreso, nacimiento o alta no válida
873.719 53
Ω: espacio muestral (egresos hospitalarios depurados) 873.719 —
E: episodios de Ω con I50 en algún diagnóstico (subconjunto, no exclusión) 52.312 —

Tabla 3 muestra que Ω conserva el 99,994% de los egresos hospitalarios; los 53 excluidos traen el texto "DESCONOCIDO" en lugar de una fecha, lo que impide calcular su estancia y su edad. No hay estancias negativas en Ω (0 casos), por lo que no se requirió otra exclusión.

3 Espacio muestral, eventos y probabilidades marginales

RESULTADO 01 · PARTE 2

3.1 Tabla de eventos

Cada evento se construye como una columna lógica (TRUE/FALSE) sobre Ω con la regla en R indicada. Todas las probabilidades de esta tabla son marginales: denominador N = 873.719.

ev_fila <- function(nombre, definicion, regla, x) {
  data.table(Evento = nombre, `Definición operacional` = definicion,
             # se escapan comillas y acentos graves para que pandoc no los convierta en tipográficos
             `Regla en R` = paste0("<code>", gsub("`", "&#96;", htmltools::htmlEscape(regla, attribute = TRUE)), "</code>"),
             `n(evento)` = fnum(sum(x)),
             `Probabilidad = n / N` = fpct(mean(x), ifelse(mean(x) < 0.001, 4, 2)))
}
tab_ev <- rbind(
  ev_fila("Ω", "Egresos hospitalarios GRD 2024 sin CMA y con fechas válidas", "nrow(omega)", rep(TRUE, N)),
  ev_fila("E", "I50 registrado en cualquiera de los 35 campos de diagnóstico", 'Reduce("|", lapply(paste0("DIAGNOSTICO", 1:35), function(cc) grepl("^I50", omega[[cc]])))', omega$E),
  ev_fila("Eₚ", "I50 como diagnóstico principal", 'grepl("^I50", DIAGNOSTICO1)', omega$Ep),
  ev_fila("Eₛ = E ∩ Ēₚ", "I50 registrado solo como diagnóstico secundario", "E & !Ep", omega$Es),
  ev_fila("F", "El episodio termina en fallecimiento", 'TIPOALTA == "FALLECIDO"', omega$Fal),
  ev_fila("U", "El episodio ingresa por urgencia", 'TIPO_INGRESO == "URGENCIA"', omega$U),
  ev_fila("L", paste0("Estancia larga: estancia mayor que el percentil 75 de Ω (", fnum(corte_L), " días)"),
          paste0("as.numeric(as.Date(FECHAALTA) - as.Date(FECHA_INGRESO)) > ", corte_L), omega$L),
  ev_fila("S₁", paste0("Severidad GRD 1 (", sev_tab[nivel == 1, etiqueta], ")"), 'IR_29301_SEVERIDAD == "1"', omega$sev == "S₁"),
  ev_fila("S₂", paste0("Severidad GRD 2 (", sev_tab[nivel == 2, etiqueta], ")"), 'IR_29301_SEVERIDAD == "2"', omega$sev == "S₂"),
  ev_fila("S₃", paste0("Severidad GRD 3 (", sev_tab[nivel == 3, etiqueta], ")"), 'IR_29301_SEVERIDAD == "3"', omega$sev == "S₃"),
  ev_fila("S sin registro", "Severidad 0 o \"DESCONOCIDO\"", '!(IR_29301_SEVERIDAD %in% c("1","2","3"))', omega$sev == "Sin severidad registrada"),
  rbindlist(lapply(grupos, function(g)
    ev_fila(paste0("G (", g, ")"), paste0("Grupo etario ", g, " años al ingreso"),
            paste0('grupo_etario == "', g, '"'), omega$grupo_etario == g)))
)
tabla_html(tab_ev, caption = paste0("Eventos definidos sobre Ω, con su regla en R, su conteo y su probabilidad marginal (denominador N = ", fnum(N), ")"),
           label = "eventos", escape = FALSE) |>
  column_spec(1, bold = TRUE) |> column_spec(2, width = "28%")
Tabla 4. Eventos definidos sobre Ω, con su regla en R, su conteo y su probabilidad marginal (denominador N = 873.719)
Evento Definición operacional Regla en R n(evento) Probabilidad = n / N
Ω Egresos hospitalarios GRD 2024 sin CMA y con fechas válidas nrow(omega) 873.719 100,00%
E I50 registrado en cualquiera de los 35 campos de diagnóstico Reduce("|", lapply(paste0("DIAGNOSTICO", 1:35), function(cc) grepl("^I50", omega[[cc]]))) 52.312 5,99%
Eₚ I50 como diagnóstico principal grepl("^I50", DIAGNOSTICO1) 11.331 1,30%
Eₛ = E ∩ Ēₚ I50 registrado solo como diagnóstico secundario E & !Ep 40.981 4,69%
F El episodio termina en fallecimiento TIPOALTA == "FALLECIDO" 26.660 3,05%
U El episodio ingresa por urgencia TIPO_INGRESO == "URGENCIA" 545.614 62,45%
L Estancia larga: estancia mayor que el percentil 75 de Ω (7 días) as.numeric(as.Date(FECHAALTA) - as.Date(FECHA_INGRESO)) > 7 212.172 24,28%
S₁ Severidad GRD 1 (Menor) IR_29301_SEVERIDAD == "1" 387.252 44,32%
S₂ Severidad GRD 2 (Moderada) IR_29301_SEVERIDAD == "2" 268.863 30,77%
S₃ Severidad GRD 3 (Mayor) IR_29301_SEVERIDAD == "3" 217.583 24,90%
S sin registro Severidad 0 o "DESCONOCIDO" !(IR_29301_SEVERIDAD %in% c("1","2","3")) 21 0,0024%
G (< 45) Grupo etario < 45 años al ingreso grupo_etario == "< 45" 442.631 50,66%
G (45-64) Grupo etario 45-64 años al ingreso grupo_etario == "45-64" 188.401 21,56%
G (65-74) Grupo etario 65-74 años al ingreso grupo_etario == "65-74" 118.545 13,57%
G (75-84) Grupo etario 75-84 años al ingreso grupo_etario == "75-84" 88.466 10,13%
G (85 y más) Grupo etario 85 y más años al ingreso grupo_etario == "85 y más" 35.676 4,08%

La edad se calcula como (FECHA_INGRESO − FECHA_NACIMIENTO) / 365,25 y los grupos etarios usan los mismos cortes de la Actividad 1 (cut(..., right = FALSE), de modo que un episodio de 45,0 años queda en 45-64).

Interpretación (Tabla 4): la insuficiencia cardíaca está registrada en 5,99% de los egresos de Ω, y en 1,30% como diagnóstico principal: la mayor parte de E (78,3% de sus episodios) corresponde a I50 solo como diagnóstico secundario. El fallecimiento ocurre en 3,05% de los egresos de Ω y el ingreso por urgencia en 62,45%. Como la estancia se mide en días enteros, el percentil 75 de Ω (7 días) concentra muchos empates; por eso L, definido como estancia mayor que 7 días, abarca 24,28% de Ω y no exactamente el 25%. Los eventos S₁, S₂, S₃ y "S sin registro" forman una partición de Ω (suman 873.719 = N), al igual que los cinco grupos etarios.

3.2 Complemento, intersección y unión

nEF   <- sum(omega$E & omega$Fal)
pEF   <- nEF / N
pE_c  <- 1 - pE                                  # complemento por fórmula
pE_c_conteo <- sum(!omega$E) / N                 # complemento por conteo
pFgE  <- nEF / nE                                # P(F | E)
pUnion_formula <- pE + pF - pEF                  # unión por fórmula
pUnion_conteo  <- sum(omega$E | omega$Fal) / N   # unión por conteo directo
pL  <- mean(omega$L); pEL <- mean(omega$E & omega$L)
pUnion2_formula <- pE + pL - pEL
pUnion2_conteo  <- mean(omega$E | omega$L)

ops <- data.table(
  `Operación` = c("Complemento", "Intersección", "Unión", "Unión"),
  `Evento` = c("Ē (sin I50)", "E ∩ F", "E ∪ F", "E ∪ L"),
  `Cálculo por fórmula` = c(
    paste0("1 − P(E) = 1 − ", f4(pE), " = ", f4(pE_c)),
    paste0("P(F | E) · P(E) = ", f4(pFgE), " × ", f4(pE), " = ", f4(pFgE * pE)),
    paste0("P(E) + P(F) − P(E ∩ F) = ", f4(pE), " + ", f4(pF), " − ", f4(pEF), " = ", f4(pUnion_formula)),
    paste0("P(E) + P(L) − P(E ∩ L) = ", f4(pE), " + ", f4(pL), " − ", f4(pEL), " = ", f4(pUnion2_formula))),
  `Conteo directo sobre Ω` = c(
    paste0(fnum(sum(!omega$E)), " / ", fnum(N), " = ", f4(pE_c_conteo)),
    paste0(fnum(nEF), " / ", fnum(N), " = ", f4(pEF)),
    paste0(fnum(sum(omega$E | omega$Fal)), " / ", fnum(N), " = ", f4(pUnion_conteo)),
    paste0(fnum(sum(omega$E | omega$L)), " / ", fnum(N), " = ", f4(pUnion2_conteo))),
  `Diferencia` = formatC(c(pE_c - pE_c_conteo, pFgE * pE - pEF,
                           pUnion_formula - pUnion_conteo, pUnion2_formula - pUnion2_conteo),
                         format = "e", digits = 1)
)
tabla_html(ops, caption = paste0("Complemento, intersección y unión: cálculo por fórmula y verificación por conteo directo (denominador N = ", fnum(N), ")"),
           label = "ops")
Tabla 5. Complemento, intersección y unión: cálculo por fórmula y verificación por conteo directo (denominador N = 873.719)
Operación Evento Cálculo por fórmula Conteo directo sobre Ω Diferencia
Complemento Ē (sin I50) 1 − P(E) = 1 − 0,0599 = 0,9401 821.407 / 873.719 = 0,9401 0.0e+00
Intersección E ∩ F P(F | E) · P(E) = 0,1000 × 0,0599 = 0,0060 5.231 / 873.719 = 0,0060 0.0e+00
Unión E ∪ F P(E) + P(F) − P(E ∩ F) = 0,0599 + 0,0305 − 0,0060 = 0,0844 73.741 / 873.719 = 0,0844 0.0e+00
Unión E ∪ L P(E) + P(L) − P(E ∩ L) = 0,0599 + 0,2428 − 0,0268 = 0,2759 241.102 / 873.719 = 0,2759 5.6e-17

Interpretación (Tabla 5): 94,01% de los egresos de Ω no registra insuficiencia cardíaca. La intersección E ∩ F reúne 5.231 episodios (0,60% de Ω): episodios con I50 que terminan en fallecimiento. La unión E ∪ F (episodios con I50, o que terminan en fallecimiento, o ambos) abarca 8,44% de Ω, y la fórmula de la unión coincide con el conteo directo: la diferencia es del orden del error de redondeo de la máquina. Lo mismo ocurre con E ∪ L. La intersección se resta en la fórmula porque esos 5.231 episodios están contados dos veces en P(E) + P(F).

4 Probabilidad condicional e independencia

RESULTADO 02

4.1 Tabla de contingencia de E contra F

nEnF  <- sum(omega$E & !omega$Fal)
nEcF  <- sum(!omega$E & omega$Fal)
nEcnF <- sum(!omega$E & !omega$Fal)
cont_n <- data.table(
  ` ` = c("E (con I50)", "Ē (sin I50)", "Total"),
  `F (fallece)` = fnum(c(nEF, nEcF, nF)),
  `F̄ (no fallece)` = fnum(c(nEnF, nEcnF, N - nF)),
  Total = fnum(c(nE, N - nE, N)))
tabla_html(cont_n, caption = "Tabla de contingencia de E contra F: conteos de episodios", label = "contEF_n") |>
  column_spec(1, bold = TRUE) |> row_spec(3, bold = TRUE)
Tabla 6. Tabla de contingencia de E contra F: conteos de episodios
F (fallece) F̄ (no fallece) Total
E (con I50) 5.231 47.081 52.312
Ē (sin I50) 21.429 799.978 821.407
Total 26.660 847.059 873.719
cont_p <- data.table(
  ` ` = c("E (con I50)", "Ē (sin I50)", "Total"),
  `F (fallece)` = fpct(c(nEF, nEcF, nF) / N, 3),
  `F̄ (no fallece)` = fpct(c(nEnF, nEcnF, N - nF) / N, 3),
  Total = fpct(c(nE, N - nE, N) / N, 3))
tabla_html(cont_p, caption = paste0("Tabla de contingencia de E contra F: probabilidades conjuntas y marginales (cada celda dividida por N = ", fnum(N), ")"),
           label = "contEF_p") |>
  column_spec(1, bold = TRUE) |> row_spec(3, bold = TRUE)
Tabla 7. Tabla de contingencia de E contra F: probabilidades conjuntas y marginales (cada celda dividida por N = 873.719)
F (fallece) F̄ (no fallece) Total
E (con I50) 0,599% 5,389% 5,987%
Ē (sin I50) 2,453% 91,560% 94,013%
Total 3,051% 96,949% 100,000%

Tabla 6 entrega los conteos y Tabla 7 las probabilidades conjuntas, todas sobre N. Por ejemplo, P(E ∩ F) = 0,599%: solo una pequeña fracción de los egresos de Ω tiene I50 y termina en fallecimiento. Las celdas de la fila y la columna "Total" son las probabilidades marginales P(E) y P(F). Las probabilidades condicionales se obtienen dividiendo por el total de una fila o de una columna, y se presentan a continuación.

4.2 Fallecimiento condicionado al registro de la enfermedad

pFgEc <- nEcF / (N - nE)
pFgEp <- cond(omega$Fal, omega$Ep)
pFgEs <- cond(omega$Fal, omega$Es)
nEp <- sum(omega$Ep); nEs <- sum(omega$Es)
condF <- data.table(
  Probabilidad = c("P(F)", "P(F | E)", "P(F | Ē)", "P(F | Eₚ)", "P(F | Eₛ)"),
  `Pregunta que responde` = c(
    "Fracción de los egresos de Ω que termina en fallecimiento",
    "Fracción de los episodios con I50 que termina en fallecimiento",
    "Fracción de los episodios sin I50 que termina en fallecimiento",
    "Fracción de los episodios con I50 como diagnóstico principal que termina en fallecimiento",
    "Fracción de los episodios con I50 solo secundario que termina en fallecimiento"),
  `Numerador` = fnum(c(nF, nEF, nEcF, sum(omega$Ep & omega$Fal), sum(omega$Es & omega$Fal))),
  `Denominador (total sobre el que se calcula)` = c(paste0("N = ", fnum(N)), paste0("n(E) = ", fnum(nE)),
                                                    paste0("n(Ē) = ", fnum(N - nE)), paste0("n(Eₚ) = ", fnum(nEp)),
                                                    paste0("n(Eₛ) = ", fnum(nEs))),
  Valor = fpct(c(pF, pFgE, pFgEc, pFgEp, pFgEs)),
  `Diferencia con P(F)` = c("—", fpp(c(pFgE, pFgEc, pFgEp, pFgEs) - pF)))
tabla_html(condF, caption = "Probabilidades condicionales de fallecimiento según el registro de insuficiencia cardíaca",
           label = "condF") |> column_spec(2, width = "34%")
Tabla 8. Probabilidades condicionales de fallecimiento según el registro de insuficiencia cardíaca
Probabilidad Pregunta que responde Numerador Denominador (total sobre el que se calcula) Valor Diferencia con P(F)
P(F) Fracción de los egresos de Ω que termina en fallecimiento 26.660 N = 873.719 3,05% —
P(F | E) Fracción de los episodios con I50 que termina en fallecimiento 5.231 n(E) = 52.312 10,00% +6,95 p.p.
P(F | Ē) Fracción de los episodios sin I50 que termina en fallecimiento 21.429 n(Ē) = 821.407 2,61% -0,44 p.p.
P(F | Eₚ) Fracción de los episodios con I50 como diagnóstico principal que termina en fallecimiento 808 n(Eₚ) = 11.331 7,13% +4,08 p.p.
P(F | Eₛ) Fracción de los episodios con I50 solo secundario que termina en fallecimiento 4.423 n(Eₛ) = 40.981 10,79% +7,74 p.p.

Interpretación (Tabla 8): 10,0% de los episodios con I50 termina en fallecimiento, frente a 2,6% de los episodios sin I50: P(F | E) es 3,8 veces P(F | Ē). Dentro de la cohorte, la proporción es mayor cuando I50 está registrada solo como diagnóstico secundario (10,8%) que cuando es el diagnóstico principal (7,1%), lo mismo que se observó en la Actividad 1. Ambas condicionales se calculan sobre más de 11.000 episodios, de modo que no dependen de unos pocos casos. P(F | E) es el promedio ponderado de las dos: P(F | Eₚ)·P(Eₚ | E) + P(F | Eₛ)·P(Eₛ | E) = 0,0713 × 0,2166 + 0,1079 × 0,7834 = 0,1000, igual a P(F | E) = 0,1000. Estos valores describen con qué frecuencia coinciden ambos registros en los episodios de la base; no indican que la insuficiencia cardíaca produzca el fallecimiento.

No confundir la condicional con su inversa P(F | E) = 10,00% responde qué fracción de los episodios con I50 termina en fallecimiento. P(E | F) = 19,62% responde qué fracción de los fallecimientos de Ω tiene I50 registrada (se calcula con Bayes en la sección del teorema de Bayes). Comparten el numerador n(E ∩ F) = 5.231, pero sus denominadores son n(E) = 52.312 y n(F) = 26.660.

4.3 Probabilidad de la enfermedad por grupo etario

tab_g <- omega[, .(nG = .N, nEG = sum(E)), keyby = grupo_etario]
tab_g[, pEgG := nEG / nG]
tab_g_show <- tab_g[, .(`Grupo etario (G)` = as.character(grupo_etario),
                        `n(G): denominador` = fnum(nG),
                        `n(E ∩ G)` = fnum(nEG),
                        `P(E | G)` = fpct(pEgG),
                        `Diferencia con P(E)` = fpp(pEgG - pE),
                        `P(E | G) / P(E)` = fnum(pEgG / pE, 2),
                        Advertencia = aviso(nEG))]
tabla_html(tab_g_show, caption = paste0("Probabilidad de registrar insuficiencia cardíaca dentro de cada grupo etario, P(E | G) = n(E ∩ G) / n(G). Referencia: P(E) = ", fpct(pE)),
           label = "EgG")
Tabla 9. Probabilidad de registrar insuficiencia cardíaca dentro de cada grupo etario, P(E | G) = n(E ∩ G) / n(G). Referencia: P(E) = 5,99%
Grupo etario (G) n(G): denominador n(E ∩ G) P(E | G) Diferencia con P(E) P(E | G) / P(E) Advertencia
< 45 442.631 2.152 0,49% -5,50 p.p. 0,08
45-64 188.401 11.950 6,34% +0,36 p.p. 1,06
65-74 118.545 14.496 12,23% +6,24 p.p. 2,04
75-84 88.466 15.640 17,68% +11,69 p.p. 2,95
85 y más 35.676 8.074 22,63% +16,64 p.p. 3,78
g_max <- tab_g[which.max(pEgG)]; g_min <- tab_g[which.min(pEgG)]
ggplot(tab_g, aes(x = grupo_etario, y = pEgG)) +
  geom_col(fill = paleta[1], width = 0.6) +
  geom_hline(yintercept = pE, linetype = "dashed", color = paleta[2], linewidth = 0.9) +
  annotate("text", x = 0.55, y = pE, label = paste0("P(E) = ", fpct(pE)), vjust = -0.6, hjust = 0,
           color = paleta[2], size = 3.6) +
  geom_text(aes(label = fpct(pEgG, 1)), vjust = -0.4, size = 3.8) +
  scale_y_continuous(labels = function(x) fpct(x, 0), expand = expansion(mult = c(0, 0.15))) +
  labs(title = "Probabilidad de registrar insuficiencia cardíaca según grupo etario",
       subtitle = "Denominador de cada barra: episodios de Ω en ese grupo etario",
       x = "Grupo etario (años al ingreso)", y = "P(E | G)")
Figura 1. P(E | G) por grupo etario; la línea punteada marca P(E) en todo Ω.

Figura 1. P(E | G) por grupo etario; la línea punteada marca P(E) en todo Ω.

Interpretación (Tabla 9 y Figura 1): la probabilidad de que un episodio registre I50 crece con la edad del grupo: va de 0,49% en el grupo < 45 a 22,63% en el grupo 85 y más, es decir, 3,8 veces P(E). Los grupos de 65 años o más superan P(E) y los menores de 65 quedan por debajo. Ninguna celda tiene menos de 30 episodios con I50. Esta tabla responde P(E | G), la fracción de episodios de cada grupo que tiene I50; no debe leerse como la distribución de la cohorte por edad, que sería P(G | E).

4.4 Estancia larga según tipo de ingreso, dentro de la cohorte

coh <- omega[E == TRUE]
pLgEU  <- cond(coh$L, coh$U)
pLgEUc <- cond(coh$L, !coh$U)
pLgE   <- mean(coh$L)
tab_LU <- data.table(
  Probabilidad = c("P(L)", "P(L | E)", "P(L | E ∩ U)", "P(L | E ∩ Ū)"),
  `Subconjunto (denominador)` = c(paste0("Ω: N = ", fnum(N)), paste0("E: ", fnum(nE)),
                                  paste0("E ∩ U: ", fnum(sum(coh$U))), paste0("E ∩ Ū: ", fnum(sum(!coh$U)))),
  `Episodios con estancia larga` = fnum(c(sum(omega$L), sum(coh$L), sum(coh$L & coh$U), sum(coh$L & !coh$U))),
  Valor = fpct(c(pL, pLgE, pLgEU, pLgEUc)),
  `Diferencia con P(L | E)` = c("—", "—", fpp(pLgEU - pLgE), fpp(pLgEUc - pLgE)))
tabla_html(tab_LU, caption = paste0("Estancia larga (más de ", corte_L, " días) dentro de la cohorte según el tipo de ingreso"),
           label = "LU")
Tabla 10. Estancia larga (más de 7 días) dentro de la cohorte según el tipo de ingreso
Probabilidad Subconjunto (denominador) Episodios con estancia larga Valor Diferencia con P(L | E)
P(L) Ω: N = 873.719 212.172 24,28% —
P(L | E) E: 52.312 23.382 44,70% —
P(L | E ∩ U) E ∩ U: 46.876 21.693 46,28% +1,58 p.p.
P(L | E ∩ Ū) E ∩ Ū: 5.436 1.689 31,07% -13,63 p.p.
comp_Uc <- coh[U == FALSE, .N, by = TIPO_INGRESO][order(-N)]
comp_Uc[, etiqueta := fcase(TIPO_INGRESO == "PROGRAMADA", "ingresos programados",
                            TIPO_INGRESO == "OBSTETRICA", "ingresos obstétricos",
                            TIPO_INGRESO == "DESCONOCIDO", "tipo de ingreso desconocido",
                            default = tolower(TIPO_INGRESO))]

Interpretación (Tabla 10): dentro de la cohorte, 46,3% de los episodios que ingresan por urgencia tiene estancia larga, frente a 31,1% de los que no ingresan por urgencia: una diferencia de 15,2 puntos porcentuales. Ambas superan P(L) = 24,3% en todo Ω, lo que indica que la estancia larga es más frecuente en la cohorte, cualquiera sea el tipo de ingreso. Ū agrupa ingresos programados (5.378), ingresos obstétricos (55), tipo de ingreso desconocido (3); los ingresos por urgencia son la gran mayoría de la cohorte (89,6%), por lo que P(L | E) queda muy cerca de P(L | E ∩ U).

4.5 Comparación con independencia

Dos eventos A y B serían independientes si P(A ∩ B) = P(A) · P(B). Se comparan ambos valores para tres pares de eventos sobre Ω, sin prueba de hipótesis: se muestran los dos valores, su diferencia y su razón.

indep <- function(a, b, nombre_a, nombre_b) {
  pa <- mean(a); pb <- mean(b); pab <- mean(a & b)
  data.table(`Par (A, B)` = paste0(nombre_a, ", ", nombre_b),
             `P(A)` = fpct(pa), `P(B)` = fpct(pb),
             `P(A) · P(B)` = fpct(pa * pb, 3), `P(A ∩ B)` = fpct(pab, 3),
             `Diferencia P(A ∩ B) − P(A)·P(B)` = fpp(pab - pa * pb, 3),
             `Razón P(A ∩ B) / [P(A)·P(B)]` = fnum(pab / (pa * pb), 2),
             `P(A | B) frente a P(A)` = paste0(fpct(pab / pb), " vs ", fpct(pa)),
             `n(A ∩ B)` = fnum(sum(a & b)),
             raz = pab / (pa * pb))
}
tab_ind <- rbind(indep(omega$E, omega$Fal, "E", "F"),
                 indep(omega$E, omega$L, "E", "L"),
                 indep(omega$E, omega$U, "E", "U"))
raz_ind <- tab_ind$raz; tab_ind[, raz := NULL]
tabla_html(tab_ind, caption = paste0("Comparación entre P(A ∩ B) y P(A)·P(B) para tres pares de eventos (todas sobre N = ", fnum(N), ")"),
           label = "indep")
Tabla 11. Comparación entre P(A ∩ B) y P(A)·P(B) para tres pares de eventos (todas sobre N = 873.719)
Par (A, B) P(A) P(B) P(A) · P(B) P(A ∩ B) Diferencia P(A ∩ B) − P(A)·P(B) Razón P(A ∩ B) / [P(A)·P(B)] P(A | B) frente a P(A) n(A ∩ B)
E, F 5,99% 3,05% 0,183% 0,599% +0,416 p.p. 3,28 19,62% vs 5,99% 5.231
E, L 5,99% 24,28% 1,454% 2,676% +1,222 p.p. 1,84 11,02% vs 5,99% 23.382
E, U 5,99% 62,45% 3,739% 5,365% +1,626 p.p. 1,43 8,59% vs 5,99% 46.876

Interpretación (Tabla 11):

  • E y F: P(E ∩ F) es 3,28 veces lo que se esperaría si ambos eventos fueran independientes. Equivalentemente, P(E | F) = 19,62% frente a P(E) = 5,99%. Con 5.231 episodios en la intersección, la diferencia no se explica por unos pocos casos: en esta base, el registro de I50 y el fallecimiento no se comportan como independientes y coinciden más de lo esperado bajo independencia.
  • E y L: la razón es 1,84: la estancia larga coincide con el registro de I50 más de lo esperado bajo independencia (P(E | L) = 11,02% frente a P(E) = 5,99%).
  • E y U: la razón es 1,43, lejos de 1: P(E | U) = 8,59% frente a P(E) = 5,99%. Los episodios con I50 ingresan por urgencia con más frecuencia que el conjunto de Ω, lo que es coherente con que Ω incluya muchos ingresos programados y obstétricos donde I50 casi no aparece.

En los tres pares la desviación respecto de la independencia es descriptiva: indica que los eventos aparecen juntos en los registros con una frecuencia distinta a la del producto de sus marginales, no que uno explique al otro.

5 Ley de probabilidad total

RESULTADO 03

Dentro de la cohorte se usan los niveles de severidad como partición y se descompone la probabilidad de fallecimiento:

P(F | E) = Σₖ P(F | E ∩ Sₖ) · P(Sₖ | E)

n_sin_sev_E <- sum(coh$sev == "Sin severidad registrada")
detalle_sin_sev <- coh[sev == "Sin severidad registrada", .N, by = IR_29301_SEVERIDAD]

tp <- coh[, .(n_k = .N, f_k = sum(Fal)), keyby = sev]
tp <- merge(data.table(sev = factor(niveles_sev, levels = niveles_sev)), tp, by = "sev", all.x = TRUE)
tp[is.na(n_k), `:=`(n_k = 0L, f_k = 0L)]
tp[, `:=`(p_s = n_k / nE, p_f_s = ifelse(n_k > 0, f_k / n_k, 0))]
tp[, producto := p_s * p_f_s]
tp[, aporte := f_k / sum(f_k)]
suma_prod <- sum(tp$producto)

tp_show <- tp[, .(`Nivel (Sₖ)` = as.character(sev),
                  `n(E ∩ Sₖ)` = fnum(n_k),
                  `P(Sₖ | E)` = f4(p_s),
                  `n(F ∩ E ∩ Sₖ)` = fnum(f_k),
                  `P(F | E ∩ Sₖ)` = f4(p_f_s),
                  `Producto P(F | E ∩ Sₖ)·P(Sₖ | E)` = f4(producto),
                  `% de los fallecimientos de E` = fpct(aporte, 1),
                  Advertencia = aviso(n_k))]
tp_show <- rbind(tp_show, data.table(`Nivel (Sₖ)` = "Suma", `n(E ∩ Sₖ)` = fnum(sum(tp$n_k)),
                                     `P(Sₖ | E)` = f4(sum(tp$p_s)), `n(F ∩ E ∩ Sₖ)` = fnum(sum(tp$f_k)),
                                     `P(F | E ∩ Sₖ)` = "—", `Producto P(F | E ∩ Sₖ)·P(Sₖ | E)` = f4(suma_prod),
                                     `% de los fallecimientos de E` = fpct(1, 1), Advertencia = ""))
tabla_html(tp_show, caption = paste0("Ley de probabilidad total: descomposición de P(F | E) por nivel de severidad (P(Sₖ | E) sobre n(E) = ",
                                     fnum(nE), "; P(F | E ∩ Sₖ) sobre n(E ∩ Sₖ))"),
           label = "ptotal") |> row_spec(nrow(tp_show), bold = TRUE)
Tabla 12. Ley de probabilidad total: descomposición de P(F | E) por nivel de severidad (P(Sₖ | E) sobre n(E) = 52.312; P(F | E ∩ Sₖ) sobre n(E ∩ Sₖ))
Nivel (Sₖ) n(E ∩ Sₖ) P(Sₖ | E) n(F ∩ E ∩ Sₖ) P(F | E ∩ Sₖ) Producto P(F | E ∩ Sₖ)·P(Sₖ | E) % de los fallecimientos de E Advertencia
S₁ 734 0,0140 9 0,0123 0,0002 0,2%
S₂ 19.831 0,3791 403 0,0203 0,0077 7,7%
S₃ 31.745 0,6068 4.817 0,1517 0,0921 92,1%
Sin severidad registrada 2 0,0000 2 1,0000 0,0000 0,0% Pocos episodios (n = 2)
Suma 52.312 1,0000 5.231 — 0,1000 100,0%
verif_tp <- data.table(
  `Cálculo` = c("Suma de los productos (ley de probabilidad total)", "P(F | E) directa = n(E ∩ F) / n(E)", "Diferencia"),
  Valor = c(f4(suma_prod), paste0(fnum(nEF), " / ", fnum(nE), " = ", f4(pFgE)),
            formatC(suma_prod - pFgE, format = "e", digits = 1)))
tabla_html(verif_tp, caption = "Verificación de la ley de probabilidad total contra el cálculo directo", label = "ptotal_verif")
Tabla 13. Verificación de la ley de probabilidad total contra el cálculo directo
Cálculo Valor
Suma de los productos (ley de probabilidad total) 0,1000
P(F | E) directa = n(E ∩ F) / n(E) 5.231 / 52.312 = 0,1000
Diferencia 0.0e+00
# niveles con al menos 30 episodios para comparar probabilidades (el resto se comenta aparte)
tp_ok <- tp[n_k >= 30]
k_aporte <- as.character(tp[which.max(f_k), sev])
k_prob   <- as.character(tp_ok[which.max(p_f_s), sev])
fig_tp <- rbind(tp[n_k >= 30, .(sev, valor = p_f_s, medida = "P(F | E ∩ Sₖ): fallecimientos dentro del nivel")],
                tp[n_k >= 30, .(sev, valor = aporte, medida = "% de los fallecimientos de E que aporta el nivel")])
ggplot(fig_tp, aes(x = sev, y = valor, fill = medida)) +
  geom_col(position = position_dodge(width = 0.75), width = 0.7) +
  geom_text(aes(label = fpct(valor, 1)), position = position_dodge(width = 0.75), vjust = -0.4, size = 3.5) +
  scale_fill_manual(values = paleta[c(1, 2)], name = NULL) +
  scale_y_continuous(labels = function(x) fpct(x, 0), expand = expansion(mult = c(0, 0.12))) +
  labs(title = "Probabilidad de fallecimiento y aporte de fallecimientos por nivel de severidad",
       subtitle = paste0("Cohorte E; se omite la categoría sin severidad registrada (", n_sin_sev_E, " episodios)"),
       x = "Nivel de severidad GRD", y = NULL) +
  theme(legend.position = "bottom")
Figura 2. Para cada nivel de severidad de la cohorte: probabilidad de fallecimiento dentro del nivel y participación en el total de fallecimientos de E.

Figura 2. Para cada nivel de severidad de la cohorte: probabilidad de fallecimiento dentro del nivel y participación en el total de fallecimientos de E.

Interpretación (Tabla 12, Tabla 13 y Figura 2):

  • La partición cubre toda la cohorte. En E hay 2 episodios sin severidad registrada (IR_29301_SEVERIDAD = "DESCONOCIDO": 2). Se tratan como una cuarta categoría, "Sin severidad registrada", para que S₁, S₂, S₃ y esa categoría sumen n(E) = 52.312 y P(Sₖ | E) sume 1. No se eliminaron ni se imputaron a otro nivel. Su P(F | E ∩ Sₖ) = 100% se calcula sobre solo 2 episodios y no debe interpretarse: su aporte a la suma es de 0,0000.
  • Verificación. La suma de los productos (0,1000) coincide con P(F | E) calculada directamente (0,1000); la diferencia es cero salvo error de redondeo de la máquina.
  • Nivel que aporta más fallecimientos frente a nivel con mayor probabilidad. El nivel que aporta más fallecimientos a la cohorte es S₃, con 92,1% de los 5.231 fallecimientos de E. Entre los niveles con al menos 30 episodios, el de mayor probabilidad de fallecimiento es S₃, con P(F | E ∩ S₃) = 15,2%. En esta cohorte ambos criterios coinciden en S₃, porque ese nivel combina la mayor probabilidad de fallecimiento con la mayor cantidad de episodios (P(S₃ | E) = 60,7%). Que coincidan no es obligatorio: el aporte de un nivel es el producto de ambos factores, y un nivel con baja probabilidad de fallecimiento pero muchos episodios podría aportar más que uno con alta probabilidad y pocos episodios. En esta cohorte, S₃ es a la vez el nivel más frecuente y el de mayor probabilidad, por lo que domina la suma.
  • Pocos episodios en S₁. El nivel S₁ tiene 734 episodios y solo 9 fallecimientos, por lo que P(F | E ∩ S₁) = 1,23% descansa sobre muy pocos fallecimientos y es poco estable.

6 Teorema de Bayes

RESULTADO 04

En cada aplicación se calcula la probabilidad a posteriori con la fórmula de Bayes y se verifica con el conteo directo de episodios.

6.1 Aplicación 1: invertir la condicional, P(E | F)

P(E | F) = P(F | E) · P(E) / P(F), con P(F) = P(F | E)·P(E) + P(F | Ē)·P(Ē)

pF_total  <- pFgE * pE + pFgEc * (1 - pE)        # denominador por probabilidad total
pEgF_bayes  <- pFgE * pE / pF
pEgF_conteo <- nEF / nF
b1 <- data.table(
  Componente = c("P(F | E)", "P(E) (a priori)", "P(F | Ē)", "P(F) directa",
                 "P(F) por probabilidad total", "P(E | F) por Bayes (a posteriori)",
                 "P(E | F) por conteo directo", "Diferencia Bayes − conteo"),
  `Cálculo` = c(paste0(fnum(nEF), " / ", fnum(nE)), paste0(fnum(nE), " / ", fnum(N)),
                paste0(fnum(nEcF), " / ", fnum(N - nE)), paste0(fnum(nF), " / ", fnum(N)),
                paste0(f4(pFgE), " × ", f4(pE), " + ", f4(pFgEc), " × ", f4(1 - pE)),
                paste0(f4(pFgE), " × ", f4(pE), " / ", f4(pF)),
                paste0("n(E ∩ F) / n(F) = ", fnum(nEF), " / ", fnum(nF)), "—"),
  Valor = c(fpct(c(pFgE, pE, pFgEc, pF, pF_total, pEgF_bayes, pEgF_conteo)),
            formatC(pEgF_bayes - pEgF_conteo, format = "e", digits = 1)))
tabla_html(b1, caption = "Teorema de Bayes, aplicación 1: fracción de los fallecimientos de Ω que tiene I50 registrada",
           label = "bayes1") |> row_spec(6:7, bold = TRUE)
Tabla 14. Teorema de Bayes, aplicación 1: fracción de los fallecimientos de Ω que tiene I50 registrada
Componente Cálculo Valor
P(F | E) 5.231 / 52.312 10,00%
P(E) (a priori) 52.312 / 873.719 5,99%
P(F | Ē) 21.429 / 821.407 2,61%
P(F) directa 26.660 / 873.719 3,05%
P(F) por probabilidad total 0,1000 × 0,0599 + 0,0261 × 0,9401 3,05%
P(E | F) por Bayes (a posteriori) 0,1000 × 0,0599 / 0,0305 19,62%
P(E | F) por conteo directo n(E ∩ F) / n(F) = 5.231 / 26.660 19,62%
Diferencia Bayes − conteo — -2.8e-17

Interpretación (Tabla 14): la insuficiencia cardíaca está registrada en 6,0% de los egresos de Ω (a priori), pero en 19,6% de los egresos que terminan en fallecimiento (a posteriori). El conteo directo da 5.231 / 26.660 = 19,6%, igual al resultado de Bayes. Al conocer la evidencia "el episodio terminó en fallecimiento", la probabilidad sube de 6,0% a 19,6%, porque el fallecimiento es mucho más frecuente entre los episodios con I50 que en el total de Ω: la razón P(F | E) / P(F) = 3,28 es exactamente el factor por el que se multiplica la probabilidad a priori. Dicho de otro modo, aproximadamente 2 de cada 10 fallecimientos de Ω tienen I50 registrada, aunque solo 6 de cada 100 egresos la registran.

6.2 Aplicación 2: severidad a posteriori, P(Sₖ | F ∩ E)

P(Sₖ | F ∩ E) = P(F | E ∩ Sₖ) · P(Sₖ | E) / Σᵢ P(F | E ∩ Sᵢ) · P(Sᵢ | E)

tp[, post_bayes  := producto / sum(producto)]
tp[, post_conteo := f_k / sum(f_k)]
b2 <- tp[, .(`Nivel (Sₖ)` = as.character(sev),
             `A priori P(Sₖ | E)` = fpct(p_s),
             `Verosimilitud P(F | E ∩ Sₖ)` = fpct(p_f_s),
             `A posteriori por Bayes` = fpct(post_bayes),
             `A posteriori por conteo n(Sₖ ∩ F ∩ E) / n(F ∩ E)` = paste0(fnum(f_k), " / ", fnum(nEF), " = ", fpct(post_conteo)),
             `Diferencia Bayes − conteo` = formatC(post_bayes - post_conteo, format = "e", digits = 1),
             `Cambio (posteriori − priori)` = fpp(post_bayes - p_s),
             Advertencia = aviso(n_k))]
tabla_html(b2, caption = paste0("Teorema de Bayes, aplicación 2: severidad a priori (sobre n(E) = ", fnum(nE),
                                ") y a posteriori dado el fallecimiento (sobre n(F ∩ E) = ", fnum(nEF), ")"),
           label = "bayes2")
Tabla 15. Teorema de Bayes, aplicación 2: severidad a priori (sobre n(E) = 52.312) y a posteriori dado el fallecimiento (sobre n(F ∩ E) = 5.231)
Nivel (Sₖ) A priori P(Sₖ | E) Verosimilitud P(F | E ∩ Sₖ) A posteriori por Bayes A posteriori por conteo n(Sₖ ∩ F ∩ E) / n(F ∩ E) Diferencia Bayes − conteo Cambio (posteriori − priori) Advertencia
S₁ 1,40% 1,23% 0,17% 9 / 5.231 = 0,17% 2.2e-19 -1,23 p.p.
S₂ 37,91% 2,03% 7,70% 403 / 5.231 = 7,70% 0.0e+00 -30,21 p.p.
S₃ 60,68% 15,17% 92,09% 4.817 / 5.231 = 92,09% 0.0e+00 +31,40 p.p.
Sin severidad registrada 0,00% 100,00% 0,04% 2 / 5.231 = 0,04% 0.0e+00 +0,03 p.p. Pocos episodios (n = 2)
k_sube <- as.character(tp[post_bayes > p_s & n_k >= 30, sev])
k_baja <- as.character(tp[post_bayes < p_s & n_k >= 30, sev])
fig_b2 <- rbind(tp[, .(sev, valor = p_s, tipo = "A priori: P(Sₖ | E)")],
                tp[, .(sev, valor = post_bayes, tipo = "A posteriori: P(Sₖ | F ∩ E)")])
fig_b2[, tipo := factor(tipo, levels = c("A priori: P(Sₖ | E)", "A posteriori: P(Sₖ | F ∩ E)"))]
ggplot(fig_b2, aes(x = sev, y = valor, fill = tipo)) +
  geom_col(position = position_dodge(width = 0.75), width = 0.7) +
  geom_text(aes(label = fpct(valor, 1)), position = position_dodge(width = 0.75), vjust = -0.4, size = 3.4) +
  scale_fill_manual(values = paleta[c(4, 3)], name = NULL) +
  scale_x_discrete(labels = function(x) wrap_lbl(x, 14)) +
  scale_y_continuous(labels = function(x) fpct(x, 0), expand = expansion(mult = c(0, 0.12))) +
  labs(title = "Severidad a priori y a posteriori dentro de la cohorte",
       x = "Nivel de severidad GRD", y = "Probabilidad") +
  theme(legend.position = "bottom")
Figura 3. Distribución de la severidad en la cohorte antes (a priori) y después (a posteriori) de saber que el episodio terminó en fallecimiento.

Figura 3. Distribución de la severidad en la cohorte antes (a priori) y después (a posteriori) de saber que el episodio terminó en fallecimiento.

Interpretación (Tabla 15 y Figura 3): antes de conocer el desenlace, un episodio de la cohorte tiene severidad S₃ con probabilidad 60,7%; si se sabe que terminó en fallecimiento, esa probabilidad sube a 92,1%. La probabilidad baja en S₁ y S₂ porque en esos niveles la verosimilitud P(F | E ∩ Sₖ) es menor que P(F | E) = 10,0%, y sube en S₃ porque allí es mayor. El fallecimiento es la evidencia: desplaza la probabilidad hacia los niveles donde ese desenlace es más frecuente. El conteo directo de episodios (5.231 fallecimientos de la cohorte repartidos por nivel) coincide con la fórmula en todos los niveles. La categoría sin severidad registrada (2 episodios) y el nivel S₁ se basan en muy pocos fallecimientos (2 y 9, respectivamente) y no permiten conclusiones.

6.3 Aplicación 3: rol diagnóstico dada una estancia larga, P(Eₚ | L ∩ E)

P(Eₚ | L ∩ E) = P(L | Eₚ) · P(Eₚ | E) / [P(L | Eₚ)·P(Eₚ | E) + P(L | Eₛ)·P(Eₛ | E)]

pEp_E  <- nEp / nE
pL_Ep  <- cond(coh$L, coh$Ep)
pL_Es  <- cond(coh$L, coh$Es)
pEpgL_bayes  <- pL_Ep * pEp_E / (pL_Ep * pEp_E + pL_Es * (1 - pEp_E))
pEpgL_conteo <- sum(coh$Ep & coh$L) / sum(coh$L)
b3 <- data.table(
  Componente = c("P(Eₚ | E) (a priori)", "P(L | Eₚ)", "P(Eₛ | E)", "P(L | Eₛ)",
                 "P(Eₚ | L ∩ E) por Bayes (a posteriori)", "P(Eₚ | L ∩ E) por conteo directo",
                 "Diferencia Bayes − conteo"),
  `Cálculo` = c(paste0(fnum(nEp), " / ", fnum(nE)), paste0(fnum(sum(coh$Ep & coh$L)), " / ", fnum(nEp)),
                paste0(fnum(nEs), " / ", fnum(nE)), paste0(fnum(sum(coh$Es & coh$L)), " / ", fnum(nEs)),
                paste0(f4(pL_Ep), " × ", f4(pEp_E), " / (", f4(pL_Ep), " × ", f4(pEp_E), " + ", f4(pL_Es), " × ", f4(1 - pEp_E), ")"),
                paste0("n(Eₚ ∩ L) / n(L ∩ E) = ", fnum(sum(coh$Ep & coh$L)), " / ", fnum(sum(coh$L))), "—"),
  Valor = c(fpct(c(pEp_E, pL_Ep, 1 - pEp_E, pL_Es, pEpgL_bayes, pEpgL_conteo)),
            formatC(pEpgL_bayes - pEpgL_conteo, format = "e", digits = 1)))
tabla_html(b3, caption = paste0("Teorema de Bayes, aplicación 3: probabilidad de que I50 sea el diagnóstico principal dado que el episodio de la cohorte tuvo estancia larga (más de ", corte_L, " días)"),
           label = "bayes3") |> row_spec(5:6, bold = TRUE)
Tabla 16. Teorema de Bayes, aplicación 3: probabilidad de que I50 sea el diagnóstico principal dado que el episodio de la cohorte tuvo estancia larga (más de 7 días)
Componente Cálculo Valor
P(Eₚ | E) (a priori) 11.331 / 52.312 21,66%
P(L | Eₚ) 4.220 / 11.331 37,24%
P(Eₛ | E) 40.981 / 52.312 78,34%
P(L | Eₛ) 19.162 / 40.981 46,76%
P(Eₚ | L ∩ E) por Bayes (a posteriori) 0,3724 × 0,2166 / (0,3724 × 0,2166 + 0,4676 × 0,7834) 18,05%
P(Eₚ | L ∩ E) por conteo directo n(Eₚ ∩ L) / n(L ∩ E) = 4.220 / 23.382 18,05%
Diferencia Bayes − conteo — 2.8e-17

Interpretación (Tabla 16): en la cohorte, I50 es el diagnóstico principal en 21,7% de los episodios (a priori). Entre los episodios de la cohorte con estancia larga, esa probabilidad baja a 18,0%, y el conteo directo confirma el valor. La evidencia "estancia larga" es menos frecuente cuando I50 es el diagnóstico principal (P(L | Eₚ) = 37,2%) que cuando es solo secundario (P(L | Eₛ) = 46,8%); por eso, al observar una estancia larga, la probabilidad se desplaza hacia el rol secundario. La razón de verosimilitudes P(L | Eₚ) / P(L | Eₛ) = 0,80 resume cuánto pesa esa evidencia. Es coherente con la Actividad 1, donde la estancia mediana era mayor cuando I50 era secundaria.

6.4 Resumen de verificaciones

res_b <- data.table(
  `Aplicación` = c("1. P(E | F)", paste0("2. P(", niveles_sev, " | F ∩ E)"), "3. P(Eₚ | L ∩ E)"),
  `A priori` = fpct(c(pE, tp$p_s, pEp_E)),
  `A posteriori (Bayes)` = fpct(c(pEgF_bayes, tp$post_bayes, pEpgL_bayes)),
  `A posteriori (conteo)` = fpct(c(pEgF_conteo, tp$post_conteo, pEpgL_conteo)),
  `|Diferencia|` = formatC(abs(c(pEgF_bayes - pEgF_conteo, tp$post_bayes - tp$post_conteo,
                                 pEpgL_bayes - pEpgL_conteo)), format = "e", digits = 1),
  `Dirección` = sube_baja(c(pEgF_bayes, tp$post_bayes, pEpgL_bayes), c(pE, tp$p_s, pEp_E)))
tabla_html(res_b, caption = "Resumen de las tres aplicaciones de Bayes: a priori, a posteriori por fórmula y por conteo directo",
           label = "bayes_res")
Tabla 17. Resumen de las tres aplicaciones de Bayes: a priori, a posteriori por fórmula y por conteo directo
Aplicación A priori A posteriori (Bayes) A posteriori (conteo) |Diferencia| Dirección
  1. P(E | F)
5,99% 19,62% 19,62% 2.8e-17 sube
  1. P(S₁ | F ∩ E)
1,40% 0,17% 0,17% 2.2e-19 baja
  1. P(S₂ | F ∩ E)
37,91% 7,70% 7,70% 0.0e+00 baja
  1. P(S₃ | F ∩ E)
60,68% 92,09% 92,09% 0.0e+00 sube
  1. P(Sin severidad registrada | F ∩ E)
0,00% 0,04% 0,04% 0.0e+00 sube
  1. P(Eₚ | L ∩ E)
21,66% 18,05% 18,05% 2.8e-17 baja

Tabla 17 reúne las tres aplicaciones: en todas, la fórmula de Bayes y el conteo directo coinciden hasta el error de redondeo de la máquina.

7 Bayes aplicado a un marcador de registro

RESULTADO 05

7.1 Definición del marcador M

esp_M <- c("MEDICINA INTERNA", "CARDIOLOGÍA", "GERIATRÍA")
omega[, M := ESPECIALIDAD_MEDICA %in% esp_M & edad >= 65]

# candidatos evaluados antes de elegir M (todos con variables que no son diagnósticos)
eval_marcador <- function(m, nombre) {
  se <- cond(m, omega$E); sp <- cond(!m, !omega$E)
  data.table(Marcador = nombre, `n(M)` = fnum(sum(m)), Sensibilidad = fpct(se, 1),
             Especificidad = fpct(sp, 1), VPP = fpct(cond(omega$E, m), 1))
}
cand_M <- rbind(
  eval_marcador(omega$ESPECIALIDAD_MEDICA == "CARDIOLOGÍA", "Especialidad = Cardiología"),
  eval_marcador(omega$ESPECIALIDAD_MEDICA %in% c("MEDICINA INTERNA", "CARDIOLOGÍA"), "Especialidad ∈ {Medicina interna, Cardiología}"),
  eval_marcador(omega$edad >= 65, "Edad ≥ 65 años"),
  eval_marcador(omega$M, "M elegido: especialidad ∈ {Medicina interna, Cardiología, Geriatría} y edad ≥ 65"))
tabla_html(cand_M, caption = paste0("Marcadores candidatos evaluados sobre Ω (sensibilidad sobre n(E) = ", fnum(nE),
                                    "; especificidad sobre n(Ē) = ", fnum(N - nE), "; VPP sobre n(M))"),
           label = "candM") |> row_spec(4, bold = TRUE)
Tabla 18. Marcadores candidatos evaluados sobre Ω (sensibilidad sobre n(E) = 52.312; especificidad sobre n(Ē) = 821.407; VPP sobre n(M))
Marcador n(M) Sensibilidad Especificidad VPP
Especialidad = Cardiología 24.571 14,5% 97,9% 30,8%
Especialidad ∈ {Medicina interna, Cardiología} 179.764 66,7% 82,4% 19,4%
Edad ≥ 65 años 242.687 73,0% 75,1% 15,7%
M elegido: especialidad ∈ {Medicina interna, Cardiología, Geriatría} y edad ≥ 65 98.239 49,9% 91,2% 26,6%

M = el episodio está a cargo de la especialidad médica Medicina interna, Cardiología o Geriatría (ESPECIALIDAD_MEDICA) y la edad al ingreso es de 65 años o más.

Se eligió por tres razones:

  1. No es circular. ESPECIALIDAD_MEDICA es un campo administrativo del episodio y la edad se calcula con las fechas de nacimiento e ingreso. Ninguno se construye a partir de los campos de diagnóstico que definen E, a diferencia del GRD o de la categoría diagnóstica mayor.
  2. Tiene respaldo en los resultados previos. En la Actividad 1 la cohorte se concentraba en edades altas, y en este informe P(E | G) supera a P(E) solo desde los 65 años (Tabla 9). Medicina interna y Cardiología son las especialidades que más episodios con I50 reúnen, y Geriatría tiene una de las mayores proporciones de I50 entre sus episodios.
  3. Equilibra los candidatos de Tabla 18. Cardiología sola es muy específica, pero detecta una fracción pequeña de la cohorte. La edad sola detecta más episodios, pero con baja especificidad. M combina ambos criterios y obtiene el mayor VPP entre los candidatos que detectan al menos la mitad de la cohorte.

M se eligió mirando estos mismos datos, por lo que su desempeño aquí es una descripción optimista. Esto se retoma en las limitaciones.

7.2 Tabla 2 × 2 de M contra E

nME   <- sum(omega$M & omega$E);  nMEc  <- sum(omega$M & !omega$E)
nMcE  <- sum(!omega$M & omega$E); nMcEc <- sum(!omega$M & !omega$E)
nM <- nME + nMEc
t22 <- data.table(` ` = c("M (marcador positivo)", "M̄ (marcador negativo)", "Total"),
                  `E (con I50)` = fnum(c(nME, nMcE, nE)),
                  `Ē (sin I50)` = fnum(c(nMEc, nMcEc, N - nE)),
                  Total = fnum(c(nM, N - nM, N)))
tabla_html(t22, caption = paste0("Tabla 2 × 2 del marcador M contra el evento E, sobre Ω (N = ", fnum(N), ")"),
           label = "t22") |> column_spec(1, bold = TRUE) |> row_spec(3, bold = TRUE)
Tabla 19. Tabla 2 × 2 del marcador M contra el evento E, sobre Ω (N = 873.719)
E (con I50) Ē (sin I50) Total
M (marcador positivo) 26.127 72.112 98.239
M̄ (marcador negativo) 26.185 749.295 775.480
Total 52.312 821.407 873.719

7.3 Sensibilidad, especificidad y valores predictivos

sens  <- nME / nE                 # P(M | E)
espec <- nMcEc / (N - nE)         # P(M̄ | Ē)
vpp_fun <- function(p, se, sp) se * p / (se * p + (1 - sp) * (1 - p))
vpn_fun <- function(p, se, sp) sp * (1 - p) / (sp * (1 - p) + (1 - se) * p)
vpp_bayes <- vpp_fun(pE, sens, espec); vpp_tabla <- nME / nM
vpn_bayes <- vpn_fun(pE, sens, espec); vpn_tabla <- nMcEc / (N - nM)

met <- data.table(
  Medida = c("Sensibilidad P(M | E)", "Especificidad P(M̄ | Ē)", "Proporción de la enfermedad en Ω, P(E)",
             "VPP P(E | M)", "VPN P(Ē | M̄)"),
  `Por Bayes` = c("—", "—", "—",
                  paste0(f4(sens), " × ", f4(pE), " / (", f4(sens), " × ", f4(pE), " + ", f4(1 - espec), " × ", f4(1 - pE), ") = ", fpct(vpp_bayes)),
                  paste0(f4(espec), " × ", f4(1 - pE), " / (", f4(espec), " × ", f4(1 - pE), " + ", f4(1 - sens), " × ", f4(pE), ") = ", fpct(vpn_bayes))),
  `Por conteo en la tabla 2 × 2` = c(paste0(fnum(nME), " / ", fnum(nE), " = ", fpct(sens)),
                                     paste0(fnum(nMcEc), " / ", fnum(N - nE), " = ", fpct(espec)),
                                     paste0(fnum(nE), " / ", fnum(N), " = ", fpct(pE)),
                                     paste0(fnum(nME), " / ", fnum(nM), " = ", fpct(vpp_tabla)),
                                     paste0(fnum(nMcEc), " / ", fnum(N - nM), " = ", fpct(vpn_tabla))),
  `Diferencia` = c("—", "—", "—", formatC(vpp_bayes - vpp_tabla, format = "e", digits = 1),
                   formatC(vpn_bayes - vpn_tabla, format = "e", digits = 1)))
tabla_html(met, caption = "Sensibilidad, especificidad y valores predictivos del marcador M, por Bayes y por conteo en la tabla 2 × 2",
           label = "metM") |> column_spec(2, width = "38%")
Tabla 20. Sensibilidad, especificidad y valores predictivos del marcador M, por Bayes y por conteo en la tabla 2 × 2
Medida Por Bayes Por conteo en la tabla 2 × 2 Diferencia
Sensibilidad P(M | E) — 26.127 / 52.312 = 49,94% —
Especificidad P(M̄ | Ē) — 749.295 / 821.407 = 91,22% —
Proporción de la enfermedad en Ω, P(E) — 52.312 / 873.719 = 5,99% —
VPP P(E | M) 0,4994 × 0,0599 / (0,4994 × 0,0599 + 0,0878 × 0,9401) = 26,60% 26.127 / 98.239 = 26,60% 1.1e-16
VPN P(Ē | M̄) 0,9122 × 0,9401 / (0,9122 × 0,9401 + 0,5006 × 0,0599) = 96,62% 749.295 / 775.480 = 96,62% 0.0e+00

Interpretación (Tabla 19 y Tabla 20): M marca a 49,9% de los episodios con I50 (sensibilidad) y deja sin marcar a 91,2% de los episodios sin I50 (especificidad). Aun así, de los 98.239 episodios marcados por M solo 26,6% tiene I50 registrada (VPP): de cada 4 episodios marcados, aproximadamente 3 no tienen la enfermedad. Esto ocurre porque E es poco frecuente en Ω (P(E) = 6,0%): aunque solo 8,8% de los episodios sin I50 queda marcado, esos episodios son tantos (72.112) que superan a los verdaderos positivos (26.127). El VPN es alto (96,6%), pero se acerca a 1 − P(E) = 94,0%, el valor que se obtendría sin usar ningún marcador. Bayes y el conteo en la tabla coinciden en ambos valores predictivos.

7.4 Valor predictivo positivo según la proporción de la enfermedad

El VPP depende de P(E). Si la sensibilidad y la especificidad de M se mantuvieran fijas, el mismo marcador tendría un VPP distinto en cada establecimiento. Se marcan los establecimientos con mayor y menor peso relativo de la insuficiencia cardíaca en su actividad, con el mismo criterio de la Actividad 1: al menos 500 egresos hospitalarios y al menos un episodio con I50.

hosp <- suppressMessages(as.data.table(read_excel("Tablas_maestras_GRD.xlsx", sheet = "Hospitales")))
setnames(hosp, c("cod_hospital", "nombre_hospital"))
hosp[, cod_hospital := as.character(as.integer(cod_hospital))]

peso_hosp <- omega[, .(total = .N, nE_h = sum(E), nM_h = sum(M), nEM_h = sum(E & M)), by = COD_HOSPITAL]
peso_hosp <- peso_hosp[total >= 500 & nE_h > 0]
peso_hosp[, pE_h := nE_h / total]
peso_hosp <- merge(peso_hosp, hosp, by.x = "COD_HOSPITAL", by.y = "cod_hospital", all.x = TRUE)
peso_hosp[is.na(nombre_hospital), nombre_hospital := paste0("Cód. ", COD_HOSPITAL)]
h_max <- peso_hosp[which.max(pE_h)]; h_min <- peso_hosp[which.min(pE_h)]

puntos <- data.table(
  Referencia = c(paste0("Mayor peso relativo: ", h_max$nombre_hospital),
                 "Ω completo (todos los establecimientos)",
                 paste0("Menor peso relativo: ", h_min$nombre_hospital)),
  corto = c("Mayor peso relativo", "Ω completo", "Menor peso relativo"),
  egresos = c(h_max$total, N, h_min$total),
  p = c(h_max$pE_h, pE, h_min$pE_h),
  nM_ref = c(h_max$nM_h, nM, h_min$nM_h),
  nEM_ref = c(h_max$nEM_h, nME, h_min$nEM_h))
puntos[, vpp_curva := vpp_fun(p, sens, espec)]
puntos[, vpp_obs := ifelse(nM_ref > 0, nEM_ref / nM_ref, NA_real_)]

tabla_html(puntos[, .(Referencia, `Egresos (denominador)` = fnum(egresos), `P(E) en la referencia` = fpct(p),
                      `VPP con sensibilidad y especificidad de Ω` = fpct(vpp_curva, 1),
                      `VPP observado en la referencia` = ifelse(is.na(vpp_obs), "—", paste0(fpct(vpp_obs, 1), " (", fnum(nEM_ref), " / ", fnum(nM_ref), ")")))],
           caption = paste0("VPP de M en los establecimientos de mayor y menor peso relativo de I50 (mínimo 500 egresos, ", nrow(peso_hosp), " establecimientos comparados)"),
           label = "vpp_hosp")
Tabla 21. VPP de M en los establecimientos de mayor y menor peso relativo de I50 (mínimo 500 egresos, 72 establecimientos comparados)
Referencia Egresos (denominador) P(E) en la referencia VPP con sensibilidad y especificidad de Ω VPP observado en la referencia
Mayor peso relativo: Instituto Nacional de Enfermedades Respiratorias y Cirugía Torácica 3.951 18,15% 55,8% 30,6% (196 / 641)
Ω completo (todos los establecimientos) 873.719 5,99% 26,6% 26,6% (26.127 / 98.239)
Menor peso relativo: Hospital Dr. Exequiel González Cortés (Santiago, San Miguel) 8.011 0,32% 1,8% —
curva <- data.table(p = seq(0.001, max(0.30, h_max$pE_h * 1.4), length.out = 500))
curva[, vpp := vpp_fun(p, sens, espec)]
ggplot(curva, aes(x = p, y = vpp)) +
  geom_line(color = paleta[3], linewidth = 1.1) +
  geom_segment(data = puntos, aes(x = p, xend = p, y = 0, yend = vpp_curva), linetype = "dotted", color = "grey45") +
  geom_point(data = puntos, aes(y = vpp_curva, color = corto), size = 3.6) +
  geom_point(data = puntos[!is.na(vpp_obs)], aes(y = vpp_obs, color = corto), shape = 5, size = 3.4, stroke = 1.2) +
  geom_text(data = puntos[corto != "Menor peso relativo"],
            aes(y = vpp_curva, label = paste0(corto, "\nP(E) = ", fpct(p, 1), " · VPP = ", fpct(vpp_curva, 1)), color = corto),
            hjust = -0.06, vjust = 1.15, size = 3.2, lineheight = 0.95, show.legend = FALSE) +
  geom_text(data = puntos[corto == "Menor peso relativo"],
            aes(y = vpp_curva, label = paste0(corto, ": P(E) = ", fpct(p, 2), " · VPP = ", fpct(vpp_curva, 1)), color = corto),
            hjust = -0.08, vjust = 0.5, nudge_x = 0.004, size = 3.2, show.legend = FALSE) +
  scale_color_manual(values = c("Mayor peso relativo" = paleta[2], "Ω completo" = paleta[1], "Menor peso relativo" = paleta[6]), name = NULL) +
  scale_x_continuous(labels = function(x) fpct(x, 0)) +
  scale_y_continuous(labels = function(x) fpct(x, 0), limits = c(0, 1)) +
  labs(title = "Valor predictivo positivo del marcador M según la proporción de la enfermedad",
       subtitle = paste0("Sensibilidad = ", fpct(sens, 1), " y especificidad = ", fpct(espec, 1), " fijas (valores de Ω)"),
       x = "P(E): proporción de episodios con I50 en el conjunto de referencia", y = "VPP = P(E | M)") +
  theme(legend.position = "bottom")
Figura 4. VPP del marcador M en función de P(E), con la sensibilidad y la especificidad de Ω fijas. Los puntos llenos están sobre la curva; los rombos son el VPP observado en cada referencia.

Figura 4. VPP del marcador M en función de P(E), con la sensibilidad y la especificidad de Ω fijas. Los puntos llenos están sobre la curva; los rombos son el VPP observado en cada referencia.

Interpretación (Tabla 21 y Figura 4): con la sensibilidad y la especificidad fijas, el VPP crece con P(E). En Instituto Nacional de Enfermedades Respiratorias y Cirugía Torácica, donde I50 aparece en 18,1% de los egresos, el marcador tendría un VPP de 55,8%. En Hospital Dr. Exequiel González Cortés (Santiago, San Miguel), donde aparece en solo 0,32%, el VPP caería a 1,8%. El mismo marcador, con el mismo desempeño frente a E, resulta mucho menos útil donde la enfermedad tiene poco peso.

Los VPP observados (rombos) muestran además que la sensibilidad y la especificidad no son iguales en todos los establecimientos, porque la composición por especialidad y edad de cada uno es distinta. En el establecimiento de mayor peso relativo el VPP observado es 30,6% (196 de 641 episodios marcados), menor que el 55,8% que indica la curva. En el de menor peso relativo el VPP observado no se puede calcular: ninguno de sus 8.011 egresos cumple M (n(M) = 0), lo que es esperable en un establecimiento cuya actividad casi no incluye episodios de 65 años o más. Sus 26 episodios con I50 quedan todos sin marcar, es decir, allí la sensibilidad de M es 0%. La curva sirve para mostrar el efecto de P(E) sobre el VPP, pero no predice con exactitud el VPP de cada establecimiento.

8 Verificación por simulación

RESULTADO 06

La jefatura trabaja a veces con muestras de episodios. Se simula esa situación: para cada tamaño n = 100, 500, 1.000, 5.000 y 20.000 se extraen B = 1.000 muestras aleatorias sin reposición de Ω y en cada una se estima P(E | F) con Bayes, usando solo las frecuencias de esa muestra: P̂(E | F) = P̂(F | E) · P̂(E) / P̂(F). La estimación no se puede calcular cuando la muestra no tiene fallecimientos (P̂(F) = 0). Si la muestra no tiene episodios con I50, P̂(F | E) no está definida, pero el numerador P̂(F | E) · P̂(E) = P̂(E ∩ F) vale 0 y la estimación es 0.

set.seed(20261009)                          # semilla fija: mismos resultados en cada compilación
B <- 1000
tamanos <- c(100, 500, 1000, 5000, 20000)
vE <- omega$E; vF <- omega$Fal

sim <- rbindlist(lapply(tamanos, function(n) {
  r <- t(replicate(B, {
    idx <- sample.int(N, n)                 # muestra sin reposición de Ω
    c(nE = sum(vE[idx]), nF = sum(vF[idx]), nEF = sum(vE[idx] & vF[idx]))
  }))
  data.table(n = n, b = seq_len(B), r)
}))
sim[, `:=`(pE_m = nE / n, pF_m = nF / n, pFgE_m = ifelse(nE > 0, nEF / nE, NA_real_))]
sim[, est := fifelse(nF == 0, NA_real_,                         # sin fallecimientos: no calculable
             fifelse(nE == 0, 0, pFgE_m * pE_m / pF_m))]        # Bayes con frecuencias de la muestra

valor_omega <- pEgF_conteo
res_sim <- sim[, .(fallec_prom = mean(nF),
                   pct_na = mean(is.na(est)),
                   mediana = median(est, na.rm = TRUE),
                   q1 = quantile(est, 0.25, na.rm = TRUE),
                   q3 = quantile(est, 0.75, na.rm = TRUE),
                   p05 = quantile(est, 0.05, na.rm = TRUE),
                   p95 = quantile(est, 0.95, na.rm = TRUE),
                   cerca = mean(abs(est - valor_omega) <= 0.05, na.rm = TRUE)), by = n]
res_sim[, ric := q3 - q1]
# fracción de estimaciones exactamente 0 con n = 100 (ningún fallecimiento de la muestra con I50)
cero_100 <- sim[n == 100 & !is.na(est), mean(est == 0)]

tabla_html(res_sim[, .(`Tamaño de muestra n` = fnum(n),
                       `Fallecimientos por muestra (promedio)` = fnum(fallec_prom, 1),
                       `% de muestras sin fallecimientos (no calculable)` = fpct(pct_na, 1),
                       `Mediana de P̂(E | F)` = fpct(mediana, 1),
                       `Percentil 25` = fpct(q1, 1), `Percentil 75` = fpct(q3, 1),
                       `Rango intercuartílico` = fpp_abs(ric, 1),
                       `Rango 5%-95%` = paste0(fpct(p05, 1), " – ", fpct(p95, 1)),
                       `% de estimaciones a ±5 p.p. del valor de Ω` = fpct(cerca, 1))],
           caption = paste0("Resumen de las B = 1.000 estimaciones de P(E | F) por tamaño de muestra. Valor sobre toda Ω: ", fpct(valor_omega),
                            ". Mediana, cuartiles y % a ±5 p.p. se calculan sobre las muestras donde la estimación es calculable."),
           label = "sim")
Tabla 22. Resumen de las B = 1.000 estimaciones de P(E | F) por tamaño de muestra. Valor sobre toda Ω: 19,62%. Mediana, cuartiles y % a ±5 p.p. se calculan sobre las muestras donde la estimación es calculable.
Tamaño de muestra n Fallecimientos por muestra (promedio) % de muestras sin fallecimientos (no calculable) Mediana de P̂(E | F) Percentil 25 Percentil 75 Rango intercuartílico Rango 5%-95% % de estimaciones a ±5 p.p. del valor de Ω
100 3,0 5,5% 0,0% 0,0% 33,3% 33,3 p.p. 0,0% – 75,0% 6,5%
500 15,4 0,0% 20,0% 12,5% 26,3% 13,8 p.p. 0,0% – 38,9% 34,3%
1.000 30,5 0,0% 18,8% 13,8% 24,0% 10,2 p.p. 7,9% – 31,3% 50,2%
5.000 152,6 0,0% 19,5% 17,5% 21,7% 4,2 p.p. 14,6% – 24,8% 88,9%
20.000 609,7 0,0% 19,6% 18,5% 20,7% 2,2 p.p. 17,0% – 22,3% 100,0%
sim_ok <- sim[!is.na(est)]
sim_ok[, n_f := factor(fnum(n), levels = fnum(tamanos))]
ggplot(sim_ok, aes(x = n_f, y = est)) +
  geom_violin(fill = paleta[4], color = NA, alpha = 0.6, scale = "width", adjust = 1.2) +
  geom_boxplot(width = 0.16, outlier.size = 0.5, outlier.alpha = 0.3, fill = "white", color = paleta[3]) +
  geom_hline(yintercept = valor_omega, linetype = "dashed", color = paleta[2], linewidth = 1) +
  annotate("text", x = 5.45, y = valor_omega, label = paste0("Ω: ", fpct(valor_omega, 1)),
           vjust = -0.6, hjust = 1, color = paleta[2], size = 3.7) +
  scale_y_continuous(labels = function(x) fpct(x, 0), breaks = seq(0, 1, 0.1)) +
  labs(title = "Estimaciones de P(E | F) por Bayes en muestras de episodios",
       subtitle = "1.000 muestras sin reposición de Ω por cada tamaño n; se omiten las muestras sin fallecimientos",
       x = "Tamaño de muestra n (episodios)", y = "Estimación de P(E | F)")
Figura 5. Distribución de las 1.000 estimaciones de P(E | F) para cada tamaño de muestra; la línea discontinua marca el valor calculado sobre toda Ω.

Figura 5. Distribución de las 1.000 estimaciones de P(E | F) para cada tamaño de muestra; la línea discontinua marca el valor calculado sobre toda Ω.

Interpretación (Tabla 22 y Figura 5):

  • La dispersión disminuye al crecer n. El rango intercuartílico de las estimaciones baja de 33,3 p.p. con n = 100 a 10,2 p.p. con n = 1.000 y a 2,2 p.p. con n = 20.000 y se reduce en cada aumento de n (Tabla 22). Con n = 20.000, 100% de las estimaciones queda a no más de 5 puntos porcentuales del valor de Ω, frente a 6% con n = 100.
  • Con n = 100 la mediana no representa el valor de Ω. La mediana de las estimaciones es 0,0% con n = 100: en 54,3% de las muestras calculables ninguno de los fallecimientos de la muestra tiene I50 registrada y la estimación es exactamente 0. Desde n = 500 la mediana queda a menos de 2 puntos porcentuales del valor de Ω (19,6%).
  • Lo que determina la precisión es el número de fallecimientos, no solo n. P(E | F) se estima en la práctica sobre los fallecimientos de la muestra: con n = 100 hay en promedio 3,0 fallecimientos, por lo que la estimación solo puede tomar unos pocos valores (0, 1/3, 1/2, 2/3, 1...). Eso explica la forma escalonada de la distribución para n = 100 en Figura 5. Además, 5,5% de esas muestras no tiene ningún fallecimiento y la estimación no se puede calcular. Desde n = 500 ya no ocurre.
  • Implicancia para la jefatura. Con muestras pequeñas, una estimación aislada de P(E | F) puede quedar muy lejos del valor de Ω (el rango 5%-95% con n = 100 va de 0% a 75%). Si la enfermedad o el desenlace condicionante fueran aún menos frecuentes, el problema sería mayor: muchas muestras tendrían cero casos y las estimaciones serían casi siempre 0 o no calculables. Antes de usar una muestra conviene verificar cuántos episodios del evento condicionante contiene, y no solo su tamaño total.

9 Síntesis para la jefatura

RESULTADO 07

9.1 Cinco hallazgos principales

s3_prior <- tp[sev == "S₃", p_s]; s3_post <- tp[sev == "S₃", post_bayes]
  1. La insuficiencia cardíaca está registrada en 6,0% de los egresos de Ω, pero en 19,6% de los egresos que terminan en fallecimiento. P(E) = 52.312 / 873.719 y P(E | F) = P(F | E)·P(E) / P(F) = 5.231 / 26.660. Verificable en Tabla 14.
  2. 10,0% de los episodios con I50 termina en fallecimiento, frente a 2,6% de los episodios sin I50, y la proporción es mayor cuando I50 es solo diagnóstico secundario (P(F | Eₛ) = 10,8%) que cuando es principal (P(F | Eₚ) = 7,1%). P(E ∩ F) es 3,28 veces P(E)·P(F). Verificable en Tabla 8 y Tabla 11.
  3. El nivel de severidad S₃ aporta 92,1% de los fallecimientos de la cohorte y tiene la mayor probabilidad de fallecimiento (P(F | E ∩ S₃) = 15,2%). Dado un fallecimiento, la probabilidad de S₃ sube de 60,7% a 92,1%. Verificable en Tabla 12 y Tabla 15.
  4. Dentro de la cohorte, la estancia larga es más probable en los episodios que ingresan por urgencia (P(L | E ∩ U) = 46,3% frente a P(L | E ∩ Ū) = 31,1%), y dada una estancia larga la probabilidad de que I50 sea el diagnóstico principal baja de 21,7% a 18,0%. Verificable en Tabla 10 y Tabla 16.
  5. Con muestras de 100 episodios, P(E | F) estimada por Bayes no es confiable: hay en promedio 3,0 fallecimientos por muestra, 5,5% de las muestras no permite el cálculo, la mediana de las estimaciones es 0,0% y el rango intercuartílico es 33,3 p.p., frente a 2,2 p.p. con 20.000 episodios. Verificable en Tabla 22 y Figura 5.

9.2 Limitaciones

  1. Las probabilidades son frecuencias relativas de episodios de la base GRD 2024. La base cubre solo los establecimientos públicos adscritos al mecanismo GRD, y un mismo beneficiario puede aportar varios episodios. Por eso ninguna cifra describe a personas ni a la población del país, y los eventos que coinciden en los registros (por ejemplo, E y F) no permiten atribuir un desenlace a la enfermedad.
  2. E depende de cómo cada establecimiento codifica los diagnósticos secundarios. La mayor parte de la cohorte corresponde a I50 registrada como diagnóstico secundario, cuya exhaustividad varía entre establecimientos. Por eso la sensibilidad y la especificidad del marcador M no son constantes entre establecimientos (Tabla 21), y la curva de VPP de Figura 4 es solo una aproximación.
  3. Algunas probabilidades descansan sobre pocos episodios, y el marcador se eligió con los mismos datos con que se evalúa. P(F | E ∩ S₁) se basa en 9 fallecimientos y la categoría sin severidad registrada en 2 episodios. Además, M se seleccionó comparando candidatos sobre Ω (Tabla 18), por lo que su desempeño en otro año o en otra base probablemente sería algo menor. Por último, el corte de L (7 días) depende de la distribución de estancias de todo Ω y de los empates en días enteros.

9.3 ¿Sirve el marcador M para identificar episodios de insuficiencia cardíaca?

No, no sirve para identificar episodios uno a uno. Su valor predictivo positivo es 26,6% (Tabla 20): de cada 100 episodios que M marca, solo unos 27 tienen I50 registrada, y los demás 73 no. Además, M deja sin marcar a 50,1% de los episodios con I50. En los establecimientos donde la enfermedad tiene poco peso el VPP cae aún más: según la curva de Figura 4, sería 1,8% en Hospital Dr. Exequiel González Cortés (Santiago, San Miguel). M puede servir como filtro previo para priorizar qué episodios revisar, porque concentra la enfermedad (VPP 4,4 veces P(E)). Para identificar episodios con insuficiencia cardíaca sigue siendo necesario revisar los campos de diagnóstico.

10 Referencias

  • Fondo Nacional de Salud (FONASA), Chile. Base de datos pública de egresos hospitalarios y cirugía mayor ambulatoria bajo el mecanismo GRD, año 2024. Portal de datos abiertos de FONASA. Archivo GRD_PUBLICO_2024.txt, descargado el 21 de agosto de 2026.
  • FONASA. Tablas maestras GRD (hospitales y severidad), archivo Tablas_maestras_GRD.xlsx.
  • Organización Mundial de la Salud. Clasificación Internacional de Enfermedades, décima revisión (CIE-10), archivo CIE-10.xlsx.
  • Carvajal, A. (2026). Caracterización estadística de la insuficiencia cardíaca en la base GRD 2024. Actividad 1, Estadística Computacional I, USACH.