Este informe fue elaborado en el rol de analista de la Unidad de Análisis Hospitalario, con el encargo de dimensionar y caracterizar la actividad hospitalaria asociada a una enfermedad específica dentro de los egresos hospitalarios registrados por el mecanismo de pago Grupos Relacionados por el Diagnóstico (GRD) durante el año 2024. El alcance es descriptivo: no se ajustan modelos predictivos ni se realizan pruebas de hipótesis, y ninguna cifra de este documento debe interpretarse como incidencia, prevalencia poblacional o riesgo de enfermar. Cada fila de la base representa un episodio (una hospitalización completa, desde el ingreso hasta el egreso), no una persona: un mismo paciente puede aparecer varias veces en la base.
La enfermedad elegida es la insuficiencia cardíaca. Es una condición crónica y progresiva, causa frecuente de hospitalización y de reingreso en adultos mayores, y su manejo consume recursos hospitalarios significativos (días de estancia prolongados, unidades de cuidados intensivos, procedimientos cardiovasculares). Analizar su peso dentro de los egresos GRD 2024 permite a la jefatura dimensionar la carga hospitalaria asociada, identificar los territorios y establecimientos donde se concentra esta actividad, y detectar diferencias relevantes en la severidad y en la duración de la estancia entre centros.
Se utilizó Claude (Anthropic, modelo Sonnet 5) como
herramienta de apoyo para: (i) escribir y depurar el código R de
lectura, limpieza, cruce con tablas maestras y visualización; (ii)
redactar el texto interpretativo de cada resultado; y (iii) estructurar
el informe conforme a la pauta de la actividad. La verificación de los
resultados se realizó de las siguientes formas: los conteos de cada
etapa de construcción de la cohorte quedan impresos en el propio
documento y son recalculados en cada ejecución del .Rmd (no
están escritos a mano); los códigos CIE-10 utilizados se contrastaron
contra la tabla oficial CIE-10.xlsx; los cruces de código
de hospital y código GRD se validaron contra las tablas maestras
entregadas por FONASA (Tablas maestras bases GRD.xlsx); y
todas las cifras narradas en el texto citan la tabla o figura donde
pueden verificarse. La responsabilidad técnica y de contenido del
informe es del autor.
GRD_PUBLICO_2024.txt,
descargado por el autor desde el portal de datos abiertos de
FONASA.|,
codificación Latin-1 (ISO-8859-1), 129 columnas.| Tabla | Contenido | Uso en este informe |
|---|---|---|
Tablas maestras bases GRD .xlsx — hoja
Hospitales
|
Código de hospital → nombre del establecimiento (377 registros) | Identificar los establecimientos en la sección territorial |
Tablas maestras bases GRD .xlsx — hoja IR -
GRD |
Código GRD → descripción del grupo relacionado de diagnóstico | Describir los GRD más frecuentes de la cohorte |
Tablas maestras bases GRD .xlsx — hoja Severidad
GRD |
Nivel de severidad (0-3) → etiqueta | Etiquetar el nivel de severidad de los episodios |
Tablas maestras bases GRD .xlsx — hoja Mortalidad
GRD |
Nivel de riesgo de mortalidad (0-3) → etiqueta | Etiquetar el riesgo de mortalidad esperado |
CIE-10.xlsx |
Catálogo CIE-10 versión 2013 | Verificar la descripción oficial de los códigos I50 utilizados |
Adicionalmente, la variable SERVICIO_SALUD, incluida
directamente en la base, se usó para agrupar los episodios por
territorio. Como esta variable no trae la región asociada, se construyó
una tabla de correspondencia Servicio de Salud → Región, basada en la
división administrativa vigente de las 29 direcciones de servicio de
salud de Chile (ver “Distribución territorial e institucional”).
cie10 <- as.data.table(read_excel("CIE-10.xlsx", sheet = "CIE 10"))
cie10_i50 <- cie10[grepl("^I50", Código), .(Código, Descripción, Sección)]
tabla_html(cie10_i50, caption = "Códigos CIE-10 usados para definir la cohorte (fuente: CIE-10.xlsx, catálogo oficial CIE-10 versión 2013)")
| Código | Descripción | Sección |
|---|---|---|
| I50 | Insuficiencia cardíaca | I30-I52 OTRAS FORMAS DE ENFERMEDAD CARDIACA |
| I50.0 | Insuficiencia cardíaca congestiva | I30-I52 OTRAS FORMAS DE ENFERMEDAD CARDIACA |
| I50.1 | Insuficiencia ventricular izquierda | I30-I52 OTRAS FORMAS DE ENFERMEDAD CARDIACA |
| I50.9 | Insuficiencia cardíaca, no especificada | I30-I52 OTRAS FORMAS DE ENFERMEDAD CARDIACA |
Se utilizó el código I50 (Insuficiencia cardíaca) y sus tres subcategorías vigentes en el catálogo chileno: I50.0 (insuficiencia cardíaca congestiva), I50.1 (insuficiencia ventricular izquierda) e I50.9 (insuficiencia cardíaca no especificada). El catálogo no registra subcategorías I50.2 a I50.8 para la versión utilizada en Chile.
cols_diag <- paste0("DIAGNOSTICO", 1:35)
cols_usar <- c("COD_HOSPITAL", "SEXO", "FECHA_NACIMIENTO", "SERVICIO_SALUD", "PREVISION",
"TIPO_INGRESO", "ESPECIALIDAD_MEDICA", "TIPO_ACTIVIDAD", "FECHA_INGRESO",
"FECHAALTA", "TIPOALTA", cols_diag,
"IR_29301_COD_GRD", "IR_29301_PESO", "IR_29301_SEVERIDAD", "IR_29301_MORTALIDAD",
"ID_BENEFICIARIO")
base <- fread("GRD_PUBLICO_2024/GRD_PUBLICO_2024.txt", sep = "|", encoding = "Latin-1",
quote = "", na.strings = "", select = cols_usar,
colClasses = list(character = c("COD_HOSPITAL", "ID_BENEFICIARIO",
"IR_29301_COD_GRD")))
n0 <- nrow(base)
cat("Registros en la base completa GRD 2024:", fnum(n0))
## Registros en la base completa GRD 2024: 1.085.813
tabla_html(base[, .(episodios = .N), by = TIPO_ACTIVIDAD][order(-episodios)],
caption = "Registros por tipo de actividad, base completa")
| TIPO_ACTIVIDAD | episodios |
|---|---|
| HOSPITALIZACIÓN | 873772 |
| CIRUGÍA MAYOR AMBULATORIA (CMA) | 212041 |
La base completa contiene 1.085.813 registros. El
campo TIPO_ACTIVIDAD distingue dos tipos de actividad:
egresos hospitalarios y cirugía mayor ambulatoria (CMA).
Conforme a la pauta, se trabaja solo con egresos hospitalarios; los registros de CMA se excluyen porque corresponden a actividad quirúrgica sin pernoctación, con una dinámica clínica distinta a la hospitalización.
n_cma <- base[TIPO_ACTIVIDAD == "CIRUGÍA MAYOR AMBULATORIA (CMA)", .N]
base_hosp <- base[TIPO_ACTIVIDAD == "HOSPITALIZACIÓN"]
n1 <- nrow(base_hosp)
cat("Registros excluidos por ser CMA:", fnum(n_cma), "\n")
## Registros excluidos por ser CMA: 212.041
cat("Registros de egresos hospitalarios (base de trabajo):", fnum(n1))
## Registros de egresos hospitalarios (base de trabajo): 873.772
La enfermedad puede registrarse en el campo DIAGNOSTICO1
(diagnóstico principal) o en cualquiera de los 34 campos de diagnóstico
secundario (DIAGNOSTICO2 a DIAGNOSTICO35). Se
buscó el patrón ^I50 (es decir, I50, I50.0, I50.1 o I50.9)
en los 35 campos de diagnóstico de cada episodio de la
base de egresos hospitalarios.
mat_i50 <- sapply(cols_diag, function(cc) grepl("^I50", base_hosp[[cc]]))
tiene_i50 <- rowSums(mat_i50) > 0
es_principal <- grepl("^I50", base_hosp$DIAGNOSTICO1)
candidatos <- base_hosp[tiene_i50]
n_cand <- nrow(candidatos)
# cuántos episodios de CMA mencionaban I50 (se excluyen igualmente, se documentan)
base_cma <- base[TIPO_ACTIVIDAD == "CIRUGÍA MAYOR AMBULATORIA (CMA)"]
mat_i50_cma <- sapply(cols_diag, function(cc) grepl("^I50", base_cma[[cc]]))
n_i50_cma <- sum(rowSums(mat_i50_cma) > 0)
cat("Episodios candidatos (I50 en algún campo de diagnóstico, ya sin CMA):", fnum(n_cand), "\n")
## Episodios candidatos (I50 en algún campo de diagnóstico, ya sin CMA): 52.315
cat("De ellos, como diagnóstico principal:", fnum(sum(es_principal[tiene_i50])), "\n")
## De ellos, como diagnóstico principal: 11.331
cat("Registros de CMA con I50 mencionado (excluidos de todas formas por ser CMA):", fnum(n_i50_cma))
## Registros de CMA con I50 mencionado (excluidos de todas formas por ser CMA): 1.546
Sobre los 52.315 episodios candidatos se revisó la validez de las
fechas indispensables para caracterizar el episodio
(FECHA_INGRESO, FECHA_NACIMIENTO,
FECHAALTA). Un número reducido de registros trae el valor
"DESCONOCIDO" en vez de una fecha, lo que impide calcular
edad o estancia; estos registros se excluyen de la cohorte final y se
documentan aquí y en la sección de calidad de los datos.
fecha_valida <- function(x) grepl("^\\d{4}-\\d{2}-\\d{2}$", x)
malas <- !fecha_valida(candidatos$FECHA_INGRESO) |
!fecha_valida(candidatos$FECHA_NACIMIENTO) |
!fecha_valida(candidatos$FECHAALTA)
n_malas <- sum(malas)
cohorte <- candidatos[!malas]
n_final <- nrow(cohorte)
cat("Episodios excluidos por fecha no parseable ('DESCONOCIDO'):", fnum(n_malas), "\n")
## Episodios excluidos por fecha no parseable ('DESCONOCIDO'): 3
cat("Cohorte final:", fnum(n_final))
## Cohorte final: 52.312
No se detectaron duplicados exactos dentro de la cohorte final al comparar la combinación beneficiario + fecha de ingreso + hospital (ver sección de calidad de los datos para el detalle de esta verificación).
trazabilidad <- data.table(
Etapa = c("1 — Base completa GRD 2024",
"2 — Excluye registros de CMA",
"3 — Candidatos con código I50 en algún diagnóstico (dx. 1 a 35)",
"4 — Excluye episodios con fecha no parseable",
"5 — Cohorte final"),
`Episodios` = fnum(c(n0, n1, n_cand, n_final, n_final)),
`Excluidos en la etapa` = c("—", fnum(n_cma), "(subconjunto, no exclusión)", fnum(n_malas), "—")
)
tabla_html(trazabilidad, caption = "Trazabilidad de la construcción de la cohorte, de la base completa a la cohorte final")
| Etapa | Episodios | Excluidos en la etapa |
|---|---|---|
| 1 — Base completa GRD 2024 | 1.085.813 | — |
| 2 — Excluye registros de CMA | 873.772 | 212.041 |
| 3 — Candidatos con código I50 en algún diagnóstico (dx. 1 a 35) | 52.315 | (subconjunto, no exclusión) |
| 4 — Excluye episodios con fecha no parseable | 52.312 | 3 |
| 5 — Cohorte final | 52.312 | — |
es_principal_final <- grepl("^I50", cohorte$DIAGNOSTICO1)
n_principal <- sum(es_principal_final)
n_secundario <- n_final - n_principal
De los 52.312 episodios de la cohorte final, 11.331 (21,7%) tienen la insuficiencia cardíaca como diagnóstico principal, y 40.981 (78,3%) la registran únicamente como diagnóstico secundario (comorbilidad o complicación asociada a otra causa de hospitalización). Esta distinción es relevante: en el primer grupo la insuficiencia cardíaca motivó la hospitalización; en el segundo, acompañó a episodios cuya causa principal fue otra (por ejemplo, neumonía o insuficiencia respiratoria, como se ve en la sección de caracterización).
pct_base_completa <- 100 * n_final / n0
pct_sin_cma <- 100 * n_final / n1
La cohorte de 52.312 episodios representa:
Ambos porcentajes se informan explícitamente porque tienen denominadores distintos y no deben confundirse entre sí.
df_ps <- data.table(tipo = c("Diagnóstico principal", "Diagnóstico secundario"),
n = c(n_principal, n_secundario))
ggplot(df_ps, aes(x = tipo, y = n, fill = tipo)) +
geom_col(width = 0.55, show.legend = FALSE) +
geom_text(aes(label = fnum(n)), vjust = -0.4, size = 4.2) +
scale_fill_manual(values = paleta[c(1,5)]) +
scale_y_continuous(labels = function(x) fnum(x), expand = expansion(mult = c(0, 0.15))) +
labs(title = "Episodios según rol diagnóstico",
x = NULL, y = "Episodios")
Interpretación: la insuficiencia cardíaca aparece con más frecuencia como diagnóstico secundario (78,3% de la cohorte) que como diagnóstico principal (21,7%). Esto sugiere que, dentro de los egresos GRD 2024, la insuficiencia cardíaca actúa mayoritariamente como comorbilidad relevante que acompaña a otras causas de hospitalización, más que como el motivo primario de ingreso.
La distribución temporal se calculó sobre FECHAALTA
(fecha de egreso), ya que es el campo que define la pertenencia del
episodio al año de la base: el 100% de los egresos hospitalarios tiene
FECHAALTA en 2024, mientras que un porcentaje menor de los
ingresos (FECHA_INGRESO) corresponde a fines de 2023
(hospitalizaciones que se prolongaron entre ambos años).
cohorte[, fecha_alta_d := as.Date(FECHAALTA)]
cohorte[, trimestre := paste0("T", as.integer(substr(quarters(fecha_alta_d), 2, 2)))]
tab_trim <- cohorte[, .(episodios = .N), by = trimestre][order(trimestre)]
tab_trim[, var_absoluta := episodios - shift(episodios)]
tab_trim[, var_relativa := round(100 * var_absoluta / shift(episodios), 1)]
tab_trim_show <- copy(tab_trim)
tab_trim_show[, episodios := fnum(episodios)]
tab_trim_show[, var_absoluta := ifelse(is.na(var_absoluta), "—", fnum(var_absoluta))]
tab_trim_show[, var_relativa := ifelse(is.na(var_relativa), "—", paste0(ifelse(var_relativa>0,"+",""), fnum(var_relativa,1), "%"))]
setnames(tab_trim_show, c("Trimestre", "Episodios", "Variación absoluta", "Variación relativa"))
tabla_html(tab_trim_show, caption = "Distribución trimestral de la cohorte y variación entre trimestres consecutivos (según fecha de egreso)")
| Trimestre | Episodios | Variación absoluta | Variación relativa |
|---|---|---|---|
| T1 | 11.517 | — | — |
| T2 | 12.720 | 1.203 | +10,4% |
| T3 | 14.408 | 1.688 | +13,3% |
| T4 | 13.667 | -741 | -5,1% |
ggplot(tab_trim, aes(x = trimestre, y = episodios)) +
geom_col(fill = paleta[1], width = 0.55) +
geom_text(aes(label = fnum(episodios)), vjust = -0.4, size = 4.2) +
scale_y_continuous(labels = fnum, expand = expansion(mult = c(0, 0.15))) +
labs(title = "Episodios por trimestre de egreso (2024)",
x = "Trimestre", y = "Episodios")
Interpretación: el volumen de episodios aumenta de forma sostenida entre el primer y el tercer trimestre (de 11.517 a 14.408, variación de 13,3% respecto del trimestre anterior) y desciende levemente en el cuarto trimestre. El patrón es compatible con la mayor frecuencia de descompensaciones cardíacas asociadas a cuadros respiratorios invernales, aunque este informe no evalúa causalidad.
cohorte[, fecha_ingreso_d := as.Date(FECHA_INGRESO)]
cohorte[, fecha_nac_d := as.Date(FECHA_NACIMIENTO)]
cohorte[, edad := as.numeric(difftime(fecha_ingreso_d, fecha_nac_d, units = "days")) / 365.25]
cohorte[, estancia := as.numeric(difftime(fecha_alta_d, fecha_ingreso_d, units = "days"))]
cohorte[, peso_grd := as.numeric(gsub(",", ".", IR_29301_PESO))]
cohorte[, grupo_etario := cut(edad,
breaks = c(-Inf, 44, 64, 74, 84, Inf),
labels = c("< 45", "45-64", "65-74", "75-84", "85 y más"))]
sev_lab <- c("0" = "0 · Sin gravedad", "1" = "1 · Menor", "2" = "2 · Moderada", "3" = "3 · Mayor")
mort_lab <- c("0" = "0 · Sin gravedad", "1" = "1 · Menor", "2" = "2 · Moderada", "3" = "3 · Mayor")
cohorte[, severidad_lab := factor(ifelse(IR_29301_SEVERIDAD %in% names(sev_lab),
sev_lab[IR_29301_SEVERIDAD], "Sin información"),
levels = c(sev_lab, "Sin información"))]
cohorte[, mortalidad_lab := factor(ifelse(IR_29301_MORTALIDAD %in% names(mort_lab),
mort_lab[IR_29301_MORTALIDAD], "Sin información"),
levels = c(mort_lab, "Sin información"))]
res_edad <- cohorte[, .(Mínimo = fnum(min(edad, na.rm=TRUE),1),
`Percentil 25` = fnum(quantile(edad, .25, na.rm=TRUE),1),
Mediana = fnum(median(edad, na.rm=TRUE),1),
Media = fnum(mean(edad, na.rm=TRUE),1),
`Percentil 75` = fnum(quantile(edad, .75, na.rm=TRUE),1),
Máximo = fnum(max(edad, na.rm=TRUE),1))]
tabla_html(res_edad, caption = "Resumen de la edad (años) en la cohorte final")
| Mínimo | Percentil 25 | Mediana | Media | Percentil 75 | Máximo |
|---|---|---|---|---|---|
| 0,0 | 64,1 | 73,4 | 71,7 | 81,6 | 108,0 |
ggplot(cohorte[!is.na(grupo_etario)], aes(x = grupo_etario)) +
geom_bar(fill = paleta[1]) +
geom_text(stat = "count", aes(label = fnum(after_stat(count))), vjust = -0.4, size = 4) +
scale_y_continuous(labels = fnum, expand = expansion(mult = c(0, 0.15))) +
labs(title = "Episodios por grupo etario",
x = "Grupo etario", y = "Episodios")
Interpretación: la edad mediana de la cohorte es de 73,4 años (rango intercuartílico 64,1 a 81,6), y más de la mitad de los episodios ocurre en personas de 65 años o más, consistente con el perfil clínico esperado de la insuficiencia cardíaca como enfermedad predominantemente del adulto mayor.
tab_sexo <- cohorte[, .(episodios = .N), by = SEXO][order(-episodios)]
tabla_html(copy(tab_sexo)[, episodios := fnum(episodios)], caption = "Episodios por sexo registrado")
| SEXO | episodios |
|---|---|
| HOMBRE | 26.694 |
| MUJER | 25.617 |
| DESCONOCIDO | 1 |
ggplot(tab_sexo, aes(x = reorder(SEXO, -episodios), y = episodios, fill = SEXO)) +
geom_col(width = 0.5, show.legend = FALSE) +
geom_text(aes(label = fnum(episodios)), vjust = -0.4, size = 4.2) +
scale_fill_manual(values = paleta) +
scale_y_continuous(labels = fnum, expand = expansion(mult = c(0, 0.15))) +
labs(title = "Episodios por sexo", x = NULL, y = "Episodios")
Interpretación: la distribución por sexo es
relativamente equilibrada (51,0% hombres, 49,0% mujeres), con una leve
mayoría masculina y un registro sin información
(SEXO = "DESCONOCIDO") marginal.
La estancia es una variable con distribución asimétrica hacia la derecha (algunos episodios muy prolongados), por lo que se reporta mediana y rango intercuartílico en vez del promedio, conforme al criterio estadístico de la pauta.
res_est <- cohorte[, .(Mínimo = fnum(min(estancia, na.rm=TRUE)),
`Percentil 25` = fnum(quantile(estancia, .25, na.rm=TRUE)),
Mediana = fnum(median(estancia, na.rm=TRUE)),
Media = fnum(mean(estancia, na.rm=TRUE),1),
`Percentil 75` = fnum(quantile(estancia, .75, na.rm=TRUE)),
Máximo = fnum(max(estancia, na.rm=TRUE)))]
tabla_html(res_est, caption = "Resumen de la estancia (días) en la cohorte final")
| Mínimo | Percentil 25 | Mediana | Media | Percentil 75 | Máximo |
|---|---|---|---|---|---|
| 0 | 3 | 7 | 11,1 | 13 | 645 |
ggplot(cohorte[estancia <= quantile(estancia, .99, na.rm=TRUE)], aes(x = estancia)) +
geom_histogram(binwidth = 2, fill = paleta[1], boundary = 0) +
geom_vline(xintercept = median(cohorte$estancia, na.rm=TRUE), linetype = "dashed", color = paleta[7]) +
labs(title = "Distribución de la estancia hospitalaria",
subtitle = paste0("Recortada al percentil 99 para visualización · mediana = ",
fnum(median(cohorte$estancia, na.rm=TRUE)), " días"),
x = "Estancia (días)", y = "Episodios")
Interpretación: la estancia mediana de la cohorte es de 7 días (RIC 3 a 13), muy superior a la media, lo que confirma la asimetría de la variable: un grupo minoritario de episodios con estancias muy prolongadas arrastra el promedio (11,1 días) muy por encima de la mediana.
tab_alta <- cohorte[, .(episodios = .N), by = TIPOALTA][order(-episodios)]
tab_alta[, pct := fpct(100*episodios/n_final)]
tabla_html(copy(tab_alta)[, episodios := fnum(episodios)],
caption = "Condición de egreso de la cohorte final (% sobre el total de la cohorte)")
| TIPOALTA | episodios | pct |
|---|---|---|
| DOMICILIO | 36.464 | 69,7% |
| HOSPITALIZACIÓN DOMICILIARIA | 5.827 | 11,1% |
| FALLECIDO | 5.231 | 10,0% |
| DERIVACIÓN OTRO HOSPITAL DEL SERVICIO | 2.617 | 5,0% |
| DERIVACIÓN OTRO HOSPITAL DE LA RED NACIONAL | 838 | 1,6% |
| ALTA VOLUNTARIA | 594 | 1,1% |
| DERIVACIÓN INST. PRIVADA (COMPRA DE SERVICIOS | 419 | 0,8% |
| DERIVACIÓN A OTROS CENTROS (CÁRCEL, HOGAR DE | 186 | 0,4% |
| FUGA DEL PACIENTE | 86 | 0,2% |
| DERIVACIÓN INST. PRIVADA (VOLUNTARIO) | 50 | 0,1% |
ggplot(tab_alta, aes(x = reorder(TIPOALTA, episodios), y = episodios)) +
geom_col(fill = paleta[1]) +
coord_flip() +
scale_x_discrete(labels = wrap_lbl) +
scale_y_continuous(labels = fnum, expand = expansion(mult = c(0, 0.12))) +
labs(title = "Condición de egreso de los episodios",
x = NULL, y = "Episodios")
Interpretación: la mayoría de los episodios termina en alta a domicilio (69,7%), pero destaca que 10,0% de los episodios termina en fallecimiento intrahospitalario y otro 11,1% deriva a hospitalización domiciliaria, ambos indicadores compatibles con la severidad clínica habitual de los episodios de insuficiencia cardíaca hospitalizados.
res_peso <- cohorte[, .(Mínimo = fnum(min(peso_grd, na.rm=TRUE),2),
Mediana = fnum(median(peso_grd, na.rm=TRUE),2),
Media = fnum(mean(peso_grd, na.rm=TRUE),2),
`Percentil 75` = fnum(quantile(peso_grd, .75, na.rm=TRUE),2),
Máximo = fnum(max(peso_grd, na.rm=TRUE),2))]
tabla_html(res_peso, caption = "Resumen del peso GRD en la cohorte final")
| Mínimo | Mediana | Media | Percentil 75 | Máximo |
|---|---|---|---|---|
| 0,35 | 1,01 | 1,48 | 1,52 | 17,14 |
tab_sev <- cohorte[, .(episodios = .N), by = severidad_lab][order(severidad_lab)]
tabla_html(copy(tab_sev)[, episodios := fnum(episodios)], caption = "Episodios por nivel de severidad GRD")
| severidad_lab | episodios |
|---|---|
| 1 · Menor | 734 |
| 2 · Moderada | 19.831 |
| 3 · Mayor | 31.745 |
| Sin información | 2 |
ggplot(cohorte, aes(x = severidad_lab, y = peso_grd)) +
geom_boxplot(fill = paleta[2], outlier.alpha = 0.15, outlier.size = 0.6) +
labs(title = "Peso GRD según nivel de severidad",
x = "Nivel de severidad", y = "Peso GRD") +
theme(axis.text.x = element_text(angle = 15, hjust = 1))
Interpretación: el peso GRD mediano de la cohorte es 1,01, levemente superior al peso 1 del episodio promedio de referencia del sistema. Como es esperable, el peso GRD aumenta de forma monótona con el nivel de severidad: los episodios de severidad “Mayor” concentran los pesos más altos y la mayor dispersión, reflejando un consumo de recursos más intenso y heterogéneo.
irgrd <- as.data.table(read_excel("Tablas maestras bases GRD .xlsx", sheet = "IR - GRD"))
setnames(irgrd, c("cod_grd", "descripcion_grd"))
irgrd[, cod_grd := as.character(cod_grd)]
cohorte[, cod_grd_norm := sub("^0+", "", IR_29301_COD_GRD)]
top_grd <- cohorte[, .(episodios = .N), by = cod_grd_norm][order(-episodios)][1:8]
top_grd <- merge(top_grd, irgrd, by.x = "cod_grd_norm", by.y = "cod_grd", all.x = TRUE, sort = FALSE)
top_grd <- top_grd[order(-episodios)]
setnames(top_grd, "cod_grd_norm", "Código GRD")
setnames(top_grd, "descripcion_grd", "Descripción")
tabla_html(copy(top_grd)[, episodios := fnum(episodios)][, .(`Código GRD`, Descripción, episodios)],
caption = "GRD más frecuentes dentro de la cohorte de insuficiencia cardíaca (top 8)")
| Código GRD | Descripción | episodios |
|---|---|---|
| 54123 | MH INSUFICIENCIA CARDIACA W/MCC | 6.946 |
| 44163 | MH NEUMONÍA SIMPLE Y TOS FERINA W/MCC | 2.738 |
| 54122 | MH INSUFICIENCIA CARDIACA W/CC | 2.450 |
| 44173 | MH ENFERMEDAD PULMONAR OBSTRUCTIVA CRÓNICA W/MCC | 1.534 |
| 41023 | PH VENTILACIÓN MECÁNICA PROLONGADA SIN TRAQUEOSTOMÍA W/MCC | 1.431 |
| 41203 | PH PROCEDIMIENTOS NO COMPLEJOS SOBRE APARATO RESPIRATORIO W/MCC | 1.215 |
| 51152 | PH CATETERISMO CARDIACO W/CC | 1.000 |
| 44153 | MH INFECCIONES E INFLAMACIONES RESPIRATORIAS W/MCC | 939 |
Interpretación: los dos GRD más frecuentes corresponden directamente a insuficiencia cardíaca con y sin complicación mayor (MH Insuficiencia Cardíaca W/MCC y W/CC), coherente con los episodios donde la enfermedad es diagnóstico principal. El resto de la lista está dominado por GRD respiratorios (neumonía, EPOC, ventilación mecánica prolongada) y cardiovasculares (cateterismo, procedimientos percutáneos, arritmias), lo que confirma que buena parte de la cohorte corresponde a episodios donde la insuficiencia cardíaca es una comorbilidad relevante de otra causa de ingreso.
ss_region <- c(
"ARICA" = "Arica y Parinacota",
"IQUIQUE" = "Tarapacá",
"ANTOFAGASTA" = "Antofagasta",
"ATACAMA" = "Atacama",
"COQUIMBO" = "Coquimbo",
"VALPARAISO SAN ANTONIO" = "Valparaíso",
"VIÑA DEL MAR QUILLOTA" = "Valparaíso",
"ACONCAGUA" = "Valparaíso",
"METROPOLITANO NORTE" = "Metropolitana",
"METROPOLITANO OCCIDENTE" = "Metropolitana",
"METROPOLITANO CENTRAL" = "Metropolitana",
"METROPOLITANO ORIENTE" = "Metropolitana",
"METROPOLITANO SUR" = "Metropolitana",
"METROPOLITANO SURORIENTE" = "Metropolitana",
"LIBERTADOR B. O HIGGINS" = "O'Higgins",
"DEL MAULE" = "Maule",
"ÑUBLE" = "Ñuble",
"CONCEPCIÓN" = "Biobío",
"TALCAHUANO" = "Biobío",
"BIOBIO" = "Biobío",
"ARAUCO" = "Biobío",
"ARAUCANÍA NORTE" = "Araucanía",
"ARAUCANÍA SUR" = "Araucanía",
"DEL RELONCAVÍ" = "Los Lagos",
"OSORNO" = "Los Lagos",
"CHILOÉ" = "Los Lagos",
"VALDIVIA" = "Los Ríos",
"AYSEN" = "Aysén",
"MAGALLANES" = "Magallanes"
)
cohorte[, region := ifelse(SERVICIO_SALUD %in% names(ss_region), ss_region[SERVICIO_SALUD], "Sin información")]
tab_ss <- cohorte[, .(episodios = .N), by = SERVICIO_SALUD][order(-episodios)][1:10]
tabla_html(copy(tab_ss)[, episodios := fnum(episodios)],
caption = "Servicios de salud con mayor número de episodios (top 10)")
| SERVICIO_SALUD | episodios |
|---|---|
| METROPOLITANO SURORIENTE | 5.078 |
| DEL MAULE | 4.384 |
| METROPOLITANO SUR | 3.619 |
| METROPOLITANO OCCIDENTE | 3.293 |
| METROPOLITANO CENTRAL | 2.918 |
| CONCEPCIÓN | 2.674 |
| ARAUCANÍA SUR | 2.619 |
| LIBERTADOR B. O HIGGINS | 2.218 |
| METROPOLITANO ORIENTE | 2.184 |
| ÑUBLE | 2.127 |
tab_region <- cohorte[, .(episodios = .N), by = region][order(-episodios)]
tabla_html(copy(tab_region)[, episodios := fnum(episodios)],
caption = "Episodios por región (agregación de servicios de salud)")
| region | episodios |
|---|---|
| Metropolitana | 18.455 |
| Biobío | 6.831 |
| Valparaíso | 5.024 |
| Maule | 4.384 |
| Araucanía | 3.361 |
| Los Lagos | 3.334 |
| O’Higgins | 2.218 |
| Ñuble | 2.127 |
| Coquimbo | 1.778 |
| Los Ríos | 1.115 |
| Antofagasta | 862 |
| Atacama | 741 |
| Magallanes | 624 |
| Tarapacá | 602 |
| Arica y Parinacota | 500 |
| Aysén | 335 |
| Sin información | 21 |
ggplot(tab_ss, aes(x = reorder(SERVICIO_SALUD, episodios), y = episodios)) +
geom_col(fill = paleta[1]) +
coord_flip() +
scale_y_continuous(labels = fnum, expand = expansion(mult = c(0, 0.12))) +
labs(title = "Servicios de salud con mayor número de episodios",
x = NULL, y = "Episodios")
Interpretación: el Servicio de Salud Metropolitano Suroriente concentra el mayor número absoluto de episodios (5.078), seguido por Del Maule y Metropolitano Sur. A nivel de región, la Región Metropolitana concentra la mayor cantidad de episodios en términos absolutos —esperable por su mayor población y oferta hospitalaria—, pero esto no implica necesariamente una mayor concentración relativa de la enfermedad, como se examina a continuación con los establecimientos.
hosp <- as.data.table(read_excel("Tablas maestras bases GRD .xlsx", sheet = "Hospitales"))
setnames(hosp, c("cod_hospital", "nombre_hospital"))
hosp[, cod_hospital := as.character(as.integer(cod_hospital))]
top_hosp <- cohorte[, .(episodios = .N), by = COD_HOSPITAL][order(-episodios)][1:10]
top_hosp <- merge(top_hosp, hosp, by.x = "COD_HOSPITAL", by.y = "cod_hospital", all.x = TRUE, sort = FALSE)
top_hosp[is.na(nombre_hospital), nombre_hospital := paste0("Cód. ", COD_HOSPITAL, " (sin nombre en tabla maestra)")]
top_hosp <- top_hosp[order(-episodios)]
tabla_html(copy(top_hosp)[, episodios := fnum(episodios)][, .(Establecimiento = nombre_hospital, `Código` = COD_HOSPITAL, episodios)],
caption = "Establecimientos con mayor número absoluto de episodios (top 10)")
| Establecimiento | Código | episodios |
|---|---|---|
| Complejo Hospitalario Dr. Sótero del Río (Santiago, Puente Alto) | 114101 | 3.138 |
| Hospital Clínico Regional Dr. Guillermo Grant Benavente (Concepción) | 118100 | 2.078 |
| Hospital Barros Luco Trudeau (Santiago, San Miguel) | 113100 | 1.987 |
| Hospital Regional de Rancagua | 115100 | 1.611 |
| Complejo Asistencial Dr. Víctor Ríos Ruiz (Los Ángeles) | 120101 | 1.606 |
| Hospital Dr. Félix Bulnes Cerda (Santiago, Quinta Normal) | 110120 | 1.492 |
| Hospital de Puerto Montt | 124105 | 1.475 |
| Hospital Dr. César Garavagno Burotto (Talca) | 116105 | 1.452 |
| Hospital Las Higueras (Talcahuano) | 119100 | 1.386 |
| Hospital Clínico Herminda Martín (Chillán) | 117101 | 1.382 |
ggplot(top_hosp, aes(x = reorder(nombre_hospital, episodios), y = episodios)) +
geom_col(fill = paleta[1]) +
coord_flip() +
scale_x_discrete(labels = wrap_lbl) +
scale_y_continuous(labels = fnum, expand = expansion(mult = c(0, 0.12))) +
labs(title = "Establecimientos con mayor número de episodios",
x = NULL, y = "Episodios")
Interpretación: el Complejo Hospitalario Dr. Sótero del Río concentra el mayor volumen absoluto de episodios (3.138), seguido por el Hospital Regional de Concepción y el Hospital Barros Luco Trudeau. Son, en general, grandes hospitales de alta complejidad con población asignada numerosa, por lo que su volumen alto es esperable y no implica por sí solo una mayor concentración relativa de la enfermedad dentro de su propia actividad.
Para evitar el sesgo de establecimientos con pocos casos totales (donde un puñado de episodios puede generar porcentajes muy inestables), este análisis se restringe a establecimientos con al menos 30 episodios de insuficiencia cardíaca en la cohorte.
tot_hosp <- base_hosp[, .(total_egresos = .N), by = COD_HOSPITAL]
prop_hosp <- cohorte[, .(episodios_i50 = .N), by = COD_HOSPITAL]
prop_hosp <- merge(prop_hosp, tot_hosp, by = "COD_HOSPITAL")
prop_hosp[, pct := 100 * episodios_i50 / total_egresos]
prop_hosp <- merge(prop_hosp, hosp, by.x = "COD_HOSPITAL", by.y = "cod_hospital", all.x = TRUE, sort = FALSE)
prop_hosp[is.na(nombre_hospital), nombre_hospital := paste0("Cód. ", COD_HOSPITAL)]
top_prop <- prop_hosp[episodios_i50 >= 30][order(-pct)][1:10]
tabla_html(copy(top_prop)[, `:=`(episodios_i50 = fnum(episodios_i50),
total_egresos = fnum(total_egresos),
pct = fpct(pct))][, .(Establecimiento = nombre_hospital,
`Episodios I50` = episodios_i50,
`Total egresos del establecimiento` = total_egresos,
`% de su propia actividad` = pct)],
caption = "Establecimientos donde la insuficiencia cardíaca representa mayor proporción de su propia actividad (mínimo 30 episodios I50)")
| Establecimiento | Episodios I50 | Total egresos del establecimiento | % de su propia actividad |
|---|---|---|---|
| Instituto Nacional de Enfermedades Respiratorias y Cirugía Torácica | 717 | 3.951 | 18,1% |
| Hospital de Constitución | 369 | 2.796 | 13,2% |
| Hospital de Tomé | 375 | 3.210 | 11,7% |
| Hospital de Pitrufquén | 311 | 2.904 | 10,7% |
| Hospital de Urgencia Asistencia Pública Dr. Alejandro del Río | 1.237 | 11.565 | 10,7% |
| Hospital Penco - Lirquén | 293 | 2.751 | 10,7% |
| Hospital de Lota | 406 | 4.113 | 9,9% |
| Hospital de San Carlos | 695 | 7.112 | 9,8% |
| Hospital Intercultural de Nueva Imperial | 396 | 4.133 | 9,6% |
| Hospital Dr. Abraham Godoy (Lautaro) | 255 | 2.724 | 9,4% |
Interpretación: el establecimiento con mayor peso relativo es el Instituto Nacional de Enfermedades Respiratorias y Cirugía Torácica, donde la insuficiencia cardíaca explica 18,1% de sus egresos hospitalarios totales, muy por encima de hospitales con volumen absoluto mucho mayor pero actividad más diversificada (como el Complejo Sótero del Río, donde la enfermedad representa una fracción menor de su actividad total). Esto confirma que volumen absoluto y peso relativo son dimensiones distintas del problema y deben observarse por separado.
var_hosp <- cohorte[, .(episodios = .N,
mediana_estancia = median(estancia, na.rm = TRUE),
ric_estancia = paste0(fnum(quantile(estancia,.25,na.rm=TRUE)), "-", fnum(quantile(estancia,.75,na.rm=TRUE))),
mediana_peso = round(median(peso_grd, na.rm = TRUE), 2)),
by = COD_HOSPITAL][episodios >= 30]
var_hosp <- merge(var_hosp, hosp, by.x = "COD_HOSPITAL", by.y = "cod_hospital", all.x = TRUE, sort = FALSE)
var_hosp[is.na(nombre_hospital), nombre_hospital := paste0("Cód. ", COD_HOSPITAL)]
var_hosp <- var_hosp[order(-episodios)][1:10]
tabla_html(copy(var_hosp)[, episodios := fnum(episodios)][, .(Establecimiento = nombre_hospital, episodios,
`Mediana estancia (días)` = mediana_estancia,
`RIC estancia` = ric_estancia,
`Mediana peso GRD` = mediana_peso)],
caption = "Estancia y peso GRD por establecimiento, top 10 establecimientos por volumen (mínimo 30 episodios)")
| Establecimiento | episodios | Mediana estancia (días) | RIC estancia | Mediana peso GRD |
|---|---|---|---|---|
| Complejo Hospitalario Dr. Sótero del Río (Santiago, Puente Alto) | 3.138 | 4 | 1-10 | 1.07 |
| Hospital Clínico Regional Dr. Guillermo Grant Benavente (Concepción) | 2.078 | 6 | 3-12 | 1.10 |
| Hospital Barros Luco Trudeau (Santiago, San Miguel) | 1.987 | 7 | 3-15 | 1.17 |
| Hospital Regional de Rancagua | 1.611 | 5 | 3-10 | 1.01 |
| Complejo Asistencial Dr. Víctor Ríos Ruiz (Los Ángeles) | 1.606 | 6 | 4-11 | 1.07 |
| Hospital Dr. Félix Bulnes Cerda (Santiago, Quinta Normal) | 1.492 | 7 | 3-12 | 1.01 |
| Hospital de Puerto Montt | 1.475 | 7 | 4-13 | 1.01 |
| Hospital Dr. César Garavagno Burotto (Talca) | 1.452 | 8 | 4-14 | 1.03 |
| Hospital Las Higueras (Talcahuano) | 1.386 | 6 | 3-11 | 1.01 |
| Hospital Clínico Herminda Martín (Chillán) | 1.382 | 6 | 3-11 | 1.01 |
Interpretación: entre los establecimientos de mayor volumen, la mediana de estancia varía entre 4 y 8 días, y la mediana del peso GRD entre 1,01 y 1,17. Esta variabilidad puede reflejar diferencias reales en la complejidad de los casos atendidos, en la disponibilidad de cupos de hospitalización domiciliaria o de camas de continuidad de cuidados, y no debe interpretarse como una medida directa de calidad de atención sin un ajuste por case-mix, que excede el alcance descriptivo de este informe.
n_sin_info <- c(
sum(cohorte$SEXO == "DESCONOCIDO"),
sum(cohorte$SERVICIO_SALUD == "DESCONOCIDO"),
sum(cohorte$TIPO_INGRESO == "DESCONOCIDO"),
sum(cohorte$IR_29301_SEVERIDAD == "DESCONOCIDO"),
sum(cohorte$IR_29301_MORTALIDAD == "DESCONOCIDO"),
sum(!fecha_valida(candidatos$FECHA_INGRESO)),
sum(!fecha_valida(candidatos$FECHA_NACIMIENTO))
)
denom_sin_info <- c(n_final, n_final, n_final, n_final, n_final, n_cand, n_cand)
calidad <- data.table(
Variable = c("SEXO", "SERVICIO_SALUD", "TIPO_INGRESO", "IR_29301_SEVERIDAD",
"IR_29301_MORTALIDAD", "FECHA_INGRESO (previo a depuración)",
"FECHA_NACIMIENTO (previo a depuración)"),
`Base de cálculo` = c(rep("Cohorte final", 5), rep("Candidatos (pre-depuración)", 2)),
`Sin información / DESCONOCIDO` = fnum(n_sin_info),
`%` = fpct(100 * n_sin_info / denom_sin_info, 2)
)
tabla_html(calidad, caption = "Valores sin información por variable utilizada en el análisis (% calculado sobre la base indicada en cada fila)")
| Variable | Base de cálculo | Sin información / DESCONOCIDO | % |
|---|---|---|---|
| SEXO | Cohorte final | 1 | 0,00% |
| SERVICIO_SALUD | Cohorte final | 21 | 0,04% |
| TIPO_INGRESO | Cohorte final | 3 | 0,01% |
| IR_29301_SEVERIDAD | Cohorte final | 2 | 0,00% |
| IR_29301_MORTALIDAD | Cohorte final | 2 | 0,00% |
| FECHA_INGRESO (previo a depuración) | Candidatos (pre-depuración) | 2 | 0,00% |
| FECHA_NACIMIENTO (previo a depuración) | Candidatos (pre-depuración) | 1 | 0,00% |
n_grd_desconocido <- sum(cohorte$IR_29301_COD_GRD == "DESCONOCIDO")
n_hosp_sin_tabla <- sum(!(cohorte$COD_HOSPITAL %in% hosp$cod_hospital))
cat("Episodios con código GRD 'DESCONOCIDO' (sin agrupador asignado):", fnum(n_grd_desconocido), "\n")
## Episodios con código GRD 'DESCONOCIDO' (sin agrupador asignado): 2
cat("Episodios cuyo código de hospital no aparece en la tabla maestra de establecimientos:", fnum(n_hosp_sin_tabla))
## Episodios cuyo código de hospital no aparece en la tabla maestra de establecimientos: 514
Los episodios sin código GRD asignado (2) y aquellos cuyo hospital no figura en la tabla maestra (514, correspondiente al código 200717) se mantienen en la cohorte —porque el diagnóstico I50 sí está identificado— pero se excluyen o se marcan como “sin información” en las tablas y figuras que dependen específicamente de esas variables (GRD y nombre de establecimiento, respectivamente).
dup_key_base <- base_hosp[, .N, by = .(ID_BENEFICIARIO, FECHA_INGRESO, COD_HOSPITAL)]
n_grupos_dup <- sum(dup_key_base$N > 1)
n_filas_dup <- sum(dup_key_base$N[dup_key_base$N > 1])
dup_key_cohorte <- cohorte[, .N, by = .(ID_BENEFICIARIO, FECHA_INGRESO, COD_HOSPITAL)]
n_dup_cohorte <- sum(dup_key_cohorte$N > 1)
cat("Base de egresos hospitalarios: combinaciones beneficiario+ingreso+hospital repetidas:",
fnum(n_grupos_dup), "(", fnum(n_filas_dup), "filas involucradas )\n")
## Base de egresos hospitalarios: combinaciones beneficiario+ingreso+hospital repetidas: 415 ( 874 filas involucradas )
cat("Dentro de la cohorte de insuficiencia cardíaca: combinaciones repetidas:", fnum(n_dup_cohorte))
## Dentro de la cohorte de insuficiencia cardíaca: combinaciones repetidas: 0
Usando como identificador la combinación beneficiario encriptado + fecha de ingreso + hospital, se detectan 415 combinaciones repetidas en el total de egresos hospitalarios (874 filas), que probablemente corresponden a reingresos el mismo día o a registros duplicados de administración. Ninguna de estas combinaciones cae dentro de la cohorte final de insuficiencia cardíaca, por lo que no fue necesario eliminar duplicados de la cohorte.
n_est_cero <- sum(cohorte$estancia == 0, na.rm = TRUE)
n_est_larga <- sum(cohorte$estancia > 100, na.rm = TRUE)
n_edad_extrema <- sum(cohorte$edad < 1 | cohorte$edad > 100, na.rm = TRUE)
n_edad_neg <- sum(cohorte$edad < 0, na.rm = TRUE)
n_est_neg <- sum(cohorte$estancia < 0, na.rm = TRUE)
cat("Episodios con estancia = 0 días (alta el mismo día del ingreso):", fnum(n_est_cero), "\n")
## Episodios con estancia = 0 días (alta el mismo día del ingreso): 1.594
cat("Episodios con estancia > 100 días:", fnum(n_est_larga), "\n")
## Episodios con estancia > 100 días: 221
cat("Episodios con edad < 1 año o > 100 años:", fnum(n_edad_extrema), "\n")
## Episodios con edad < 1 año o > 100 años: 289
cat("Episodios con edad o estancia negativas (incompatibles, ninguno esperado):",
fnum(n_edad_neg + n_est_neg))
## Episodios con edad o estancia negativas (incompatibles, ninguno esperado): 0
No se detectaron edades ni estancias negativas (valores incompatibles). Se observan 1.594 episodios con estancia de 0 días —compatibles con ingresos y altas administrativas el mismo día o fallecimientos tempranos— y 221 episodios con estancias superiores a 100 días, que son extremos pero clínicamente plausibles en pacientes con insuficiencia cardíaca compleja y prolongada. Ninguno de estos valores se excluyó de la cohorte, ya que corresponden a valores extremos posibles y no a errores de registro identificables.
En total se descartaron 3 episodios de los 52.315
candidatos iniciales (0,006%), exclusivamente por traer fecha de ingreso
o de nacimiento no parseable ("DESCONOCIDO"). El efecto
sobre el tamaño final de la cohorte es marginal: de 52.315 a 52.312
episodios.
Con la misma base de datos es posible construir un análisis
descriptivo de reingresos hospitalarios por insuficiencia
cardíaca dentro de una ventana de tiempo definida (por ejemplo, 30
días), utilizando ID_BENEFICIARIO para identificar
al mismo beneficiario y las fechas de FECHAALTA de un
episodio y FECHA_INGRESO del siguiente episodio con
diagnóstico I50. Este análisis permitiría dimensionar la frecuencia de
reingresos tempranos y cómo se distribuyen por establecimiento, sin
requerir datos adicionales a los ya utilizados en este informe.
GRD_PUBLICO_2024.txt, extraído el 21 de agosto de
2026.
Tablas maestras bases GRD.xlsx.
CIE-10.xlsx.