Contexto del problema: Las instituciones de salud de tercer nivel registran tasas de ausentismo (No-Show) que oscilan entre el 20 % y el 35 % de las citas programadas. Cada slot perdido representa ingresos no recuperables, recursos humanos ociosos y pacientes sin atención oportuna.

Audiencia objetivo: Directivos de instituciones de salud: gerentes generales, coordinadores clínicos, jefes de calidad y tomadores de decisiones con nivel analítico intermedio-alto.

Objetivo analítico: Identificar las variables operativas y demográficas que predicen el ausentismo a citas médicas, y proponer rutas de acción concretas para optimizar la gestión de agendas y la experiencia del paciente.


1 Analítica Exploratoria de los Datos

Desde la perspectiva de la Gestión de Recursos Sanitarios, la base de datos de agendamiento médico constituye una fuente valiosa para la exploración analítica de factores asociados a la inasistencia y la ineficiencia operativa. La combinación de variables demográficas, operativas y temporales permite identificar patrones de comportamiento, segmentar poblaciones según perfiles de riesgo y explorar posibles relaciones entre el diseño de las agendas y la adherencia del paciente. Asimismo, facilita la detección de grupos que podrían requerir intervenciones preventivas específicas, el análisis de diferencias entre grupos etarios y la generación de evidencia para apoyar la toma de decisiones orientadas a la optimización del servicio. Esta información es útil para diseñar estrategias de reducción del ausentismo, optimización en la asignación de recursos y el fortalecimiento de modelos de atención centrados en el paciente.

1.1 Exploración inicial y estructural

A continuación se presenta la estructura global de la base de datos de citas médicas, la cual contiene 111.488 registros y múltiples variables que abarcan información demográfica de los pacientes, estado de la cita, tiempos de espera, intervalos de agendamiento y datos operativos del proceso de atención. Incluye detalles como sexo, edad, grupo etario, estado final de la cita, tiempo de espera en sala, duración de la consulta e intervalo entre la solicitud y la cita:

glimpse(datos_limpios)
## Rows: 111,488
## Columns: 16
## $ appointment_id       <int> 138, 146, 21, 233, 90, 180, 197, 191, 135, 130, 1…
## $ slot_id              <int> 1, 23, 24, 25, 26, 27, 28, 29, 30, 22, 31, 33, 34…
## $ scheduling_date      <chr> "2014-12-28", "2014-12-29", "2014-12-17", "2014-1…
## $ appointment_date     <chr> "2015-01-01", "2015-01-01", "2015-01-01", "2015-0…
## $ appointment_time     <chr> "08:00:00", "13:30:00", "13:45:00", "14:00:00", "…
## $ scheduling_interval  <int> 4, 3, 15, 1, 6, 2, 2, 2, 4, 4, 4, 1, 1, 7, 3, 2, …
## $ status               <fct> did not attend, did not attend, attended, attende…
## $ check_in_time        <chr> "", "", "13:36:45", "13:59:32", "", "14:08:53", "…
## $ appointment_duration <dbl> NA, NA, 5.2, 28.9, NA, 7.7, 4.2, 27.1, NA, 1.2, 7…
## $ start_time           <chr> "", "", "13:37:57", "14:00:40", "", "14:30:38", "…
## $ end_time             <chr> "", "", "13:43:09", "14:29:34", "", "14:38:20", "…
## $ waiting_time         <dbl> NA, NA, 1.2, 1.1, NA, 21.7, 16.2, 1.0, NA, 8.5, 2…
## $ patient_id           <int> 8285, 5972, 6472, 5376, 8028, 4317, 7638, 7061, 2…
## $ sex                  <fct> Male, Male, Male, Female, Male, Female, Male, Mal…
## $ age                  <int> 37, 84, 77, 37, 72, 51, 28, 33, 29, 90, 66, 64, 3…
## $ age_group            <fct> 35-39, 80-84, 75-79, 35-39, 70-74, 50-54, 25-29, …
skim(datos_limpios)
Data summary
Name datos_limpios
Number of rows 111488
Number of columns 16
_______________________
Column type frequency:
character 6
factor 3
numeric 7
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
scheduling_date 0 1 10 10 0 3642 0
appointment_date 0 1 10 10 0 2604 0
appointment_time 0 1 8 8 0 40 0
check_in_time 0 1 0 8 25456 33252 0
start_time 0 1 0 8 25456 35985 0
end_time 0 1 0 8 25456 36853 0

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
status 0 1 FALSE 5 att: 86032, can: 18254, did: 6615, unk: 446
sex 0 1 FALSE 2 Fem: 66086, Mal: 45402
age_group 0 1 FALSE 16 75-: 9844, 60-: 9649, 70-: 9485, 65-: 9133

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
appointment_id 0 1.00 55744.50 32183.96 1.0 27872.75 55744.5 83616.25 111488.0 ▇▇▇▇▇
slot_id 0 1.00 51854.32 29945.83 1.0 25909.75 51852.0 77796.25 104159.0 ▇▇▇▇▇
scheduling_interval 0 1.00 7.19 6.15 1.0 2.00 5.0 10.00 30.0 ▇▃▂▁▁
appointment_duration 25456 0.77 17.48 11.06 0.0 8.60 15.8 24.70 58.7 ▇▇▅▂▁
waiting_time 25456 0.77 44.09 40.79 0.6 12.60 33.5 64.60 297.3 ▇▂▁▁▁
patient_id 0 1.00 13783.23 9722.94 1.0 5702.00 11816.0 20736.25 36697.0 ▇▇▅▃▂
age 0 1.00 57.21 20.16 15.0 40.00 59.0 74.00 100.0 ▅▆▇▇▂
diccionario <- data.frame(
  Variable = c("appointment_id","slot_id","scheduling_date","appointment_date",
               "appointment_time","scheduling_interval","status","check_in_time",
               "appointment_duration","waiting_time","patient_id","sex","age","age_group"),
  Tipo = c("Caracter","Caracter","Fecha","Fecha","Hora","Numérica","Factor",
           "Hora","Numérica","Numérica","Caracter","Factor","Numérica","Factor"),
  Descripcion = c(
    "Identificador único de la cita médica agendada.",
    "Identificador del slot de agenda asignado.",
    "Fecha en la que el paciente solicitó la cita.",
    "Fecha programada para la consulta médica.",
    "Hora asignada para la atención.",
    "Días entre la solicitud y la fecha de cita.",
    "Estado final de la cita (attended, did not attend, etc.).",
    "Hora de llegada del paciente a la institución.",
    "Duración total de la consulta en minutos.",
    "Tiempo en sala de espera antes de ser atendido (minutos).",
    "Identificador único del paciente.",
    "Sexo biológico del paciente (Male / Female).",
    "Edad del paciente en años cumplidos.",
    "Grupo etario del paciente (ej. 50-54, 65-69)."
  )
)

datatable(diccionario,
          rownames  = FALSE,
          colnames  = c("Variable","Tipo","Descripción"),
          caption   = "Cuadro 1. Diccionario de variables del sistema de citas médicas",
          options   = list(
            pageLength = 14, dom = "t",
            initComplete = JS(
              "function(settings,json){",
              "$('.dataTables_wrapper').css({'font-size':'13px'});",
              "$(this.api().table().header()).css({'background-color':'#1A5276','color':'#fff'});",
              "}"
            )
          ))

1.2 Exploración gráfica preliminar

En esta subsección se presentan las visualizaciones exploratorias que permiten caracterizar la distribución de las variables clave del sistema de citas. El análisis gráfico constituye la primera capa de comprensión sobre el comportamiento de la demanda, los patrones de ausentismo y la eficiencia operativa del servicio.

1.2.1 Gráfico 1: Análisis de variables numéricas

# Grilla de boxplots para las variables numéricas clave (estilo referencia)
p_age <- ggplot(datos_limpios, aes(y = age)) +
  geom_boxplot(fill = "#F1948A", color = "#922B21", width = 0.5) +
  labs(title = "Age", x = NULL, y = NULL) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5, size = 12),
        axis.text.x = element_blank(), panel.grid.major.x = element_blank())

p_wait <- ggplot(filter(datos_limpios, waiting_time < 200), aes(y = waiting_time)) +
  geom_boxplot(fill = "#A9CCE3", color = "#1A5276", width = 0.5) +
  labs(title = "Waiting Time (min)", x = NULL, y = NULL) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5, size = 12),
        axis.text.x = element_blank(), panel.grid.major.x = element_blank())

p_interval <- ggplot(filter(datos_limpios, scheduling_interval < 100),
                     aes(y = scheduling_interval)) +
  geom_boxplot(fill = "#A9DFBF", color = "#1E8449", width = 0.5) +
  labs(title = "Scheduling Interval (days)", x = NULL, y = NULL) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5, size = 12),
        axis.text.x = element_blank(), panel.grid.major.x = element_blank())

p_dur <- ggplot(filter(datos_limpios, !is.na(appointment_duration)),
                aes(y = appointment_duration)) +
  geom_boxplot(fill = "#D7BDE2", color = "#6C3483", width = 0.5) +
  labs(title = "Appointment Duration (min)", x = NULL, y = NULL) +
  theme_minimal(base_size = 11) +
  theme(plot.title = element_text(face = "bold", hjust = 0.5, size = 12),
        axis.text.x = element_blank(), panel.grid.major.x = element_blank())

ggarrange(p_age, p_wait, p_interval, p_dur,
          ncol = 4, nrow = 1)

El análisis preliminar de las variables numéricas muestra una población con una edad promedio de 57.2 años y una dispersión que abarca desde pacientes muy jóvenes hasta adultos mayores de 90 años, lo que evidencia la heterogeneidad demográfica del servicio. El tiempo de espera en sala presenta una distribución con valores medios elevados y cola larga hacia la derecha, confirmando la presencia de casos atípicos donde los pacientes esperan más de 60 minutos. El intervalo de agendamiento muestra que gran parte de las citas se programa entre 7 y 30 días de anticipación, con valores extremos que superan los 60 días. Desde la perspectiva de gestión sanitaria, esta variabilidad anticipa señales predictivas relevantes para el ausentismo: la amplitud en los intervalos de agendamiento es uno de los factores operativos con mayor potencial de intervención, y la disparidad en tiempos de espera refleja la ausencia de protocolos estandarizados de atención al paciente.

1.2.2 Gráfico 2: Ausentismo según estado de la cita e intervalo de agendamiento

datos_filt <- datos_limpios %>% filter(scheduling_interval < 100)

# Umbral de riesgo para anotar
umbral <- 15

g2 <- datos_filt %>%
  filter(status %in% c("attended","did not attend")) %>%
  ggplot(aes(x = status, y = scheduling_interval, fill = status)) +
  geom_boxplot(outlier.alpha = 0.3, outlier.size = 0.8, width = 0.5) +
  geom_hline(yintercept = umbral, linetype = "dashed",
             color = "#E05A52", linewidth = 0.85) +
  annotate("label",
           x = 1.5, y = umbral + 3,
           label = paste0("Umbral de riesgo: ", umbral, " dias"),
           fill = "#FDECEA", color = "#C0392B",
           size = 3.2, fontface = "bold",
           label.padding = unit(0.3, "lines")) +
  scale_fill_manual(
    values = c("attended" = "#A9DFBF", "did not attend" = "#F1948A"),
    labels = c("attended" = "Asistio", "did not attend" = "No asistio")
  ) +
  scale_y_continuous(breaks = seq(0, 90, 15)) +
  labs(title    = "Grafico 2: Intervalo de agendamiento segun estado de la cita",
       subtitle = "Citas programadas con mas de 15 dias de anticipacion concentran el mayor ausentismo",
       x = "Estado de la cita", y = "Dias de anticipacion", fill = "Estado") +
  theme_minimal(base_size = 12) +
  theme(
    plot.title       = element_text(face = "bold", size = 13, color = "#1A1A2E"),
    plot.subtitle    = element_text(size = 10, color = "#7F8C8D"),
    legend.position  = "top",
    panel.grid.major.x = element_blank(),
    panel.grid.minor   = element_blank()
  )

ggplotly(g2, tooltip = c("y","fill")) %>%
  layout(legend = list(orientation = "h", x = 0.35, y = 1.1),
         paper_bgcolor = "rgba(0,0,0,0)",
         plot_bgcolor  = "rgba(0,0,0,0)")

El análisis del intervalo de agendamiento según el estado de la cita revela un patrón contundente: las citas que no son atendidas presentan medianas de anticipación sistemáticamente más altas que las citas asistidas. La línea punteada marca el umbral operativo de riesgo en 15 días: cuando una cita se agenda más allá de este horizonte, la probabilidad de inasistencia aumenta de forma significativa. Desde una perspectiva de gestión sanitaria, este hallazgo exige revisar el modelo actual de agendamiento a largo plazo. Programar citas con semanas o meses de anticipación, sin un mecanismo de seguimiento intermedio, genera “promesas de atención” que el paciente difícilmente puede honrar ante cambios de circunstancias. La estrategia más inmediata es implementar un protocolo de re-confirmación escalonado: comunicación automatizada 7 días, 48 horas y 2 horas antes de la cita, activando simultáneamente una lista de espera reactiva que rellene los slots cancelados en tiempo real.

1.2.3 Gráfico 3: Distribución de edad según estado de asistencia

pico <- datos_limpios %>%
  filter(status %in% c("attended","did not attend")) %>%
  count(age, status) %>%
  filter(status == "attended") %>%
  slice_max(n, n = 1)

g3 <- datos_limpios %>%
  filter(status %in% c("attended","did not attend")) %>%
  count(age, status) %>%
  ggplot(aes(x = age, y = n, fill = status)) +
  geom_col(position = "identity", alpha = 0.75, width = 0.9) +
  annotate("rect",
           xmin = pico$age - 3, xmax = pico$age + 3,
           ymin = 0, ymax = pico$n * 1.1,
           fill = NA, color = "#1A5276",
           linewidth = 0.8, linetype = "dashed") +
  annotate("label",
           x = pico$age + 12, y = pico$n * 1.05,
           label = paste0("Pico de demanda\nEdad: ", pico$age, " anios"),
           size = 3.2, fill = "#EBF5FB", color = "#1A5276",
           fontface = "bold", label.padding = unit(0.3, "lines")) +
  scale_fill_manual(
    values = c("attended" = "#A9DFBF", "did not attend" = "#F1948A"),
    labels = c("attended" = "Asistio", "did not attend" = "No asistio")
  ) +
  scale_x_continuous(breaks = seq(0, 100, 10)) +
  scale_y_continuous(labels = comma_format(big.mark = ".")) +
  labs(title    = "Grafico 3: Distribucion de edad segun estado de asistencia",
       subtitle = "Identificacion del grupo etario con mayor demanda y mayor riesgo de ausentismo",
       x = "Edad del paciente (anios)", y = "Numero de citas", fill = "Estado",
       caption = paste0("n = ", format(total_reg, big.mark = "."), " registros")) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title       = element_text(face = "bold", size = 13, color = "#1A1A2E"),
    plot.subtitle    = element_text(size = 10, color = "#7F8C8D"),
    legend.position  = "top",
    panel.grid.minor   = element_blank(),
    panel.grid.major.x = element_blank()
  )

ggplotly(g3, tooltip = c("x","y","fill")) %>%
  layout(legend = list(orientation = "h", x = 0.3, y = 1.1),
         paper_bgcolor = "rgba(0,0,0,0)",
         plot_bgcolor  = "rgba(0,0,0,0)")

El perfil etario de la demanda revela una concentración significativa de pacientes en edades adultas y adultas mayores, con el pico absoluto de atenciones alrededor de los 75 años. La distribución presenta asimetría hacia edades avanzadas, con barras verdes (asistentes) predominando en toda la curva, aunque las barras rojas (ausentismo) son relativamente más visibles en el grupo de 30 a 50 años. Esta estructura demográfica tiene implicaciones directas en la gestión operativa: mayor prevalencia de comorbilidades, tiempos de consulta extendidos y menor tolerancia a esperas prolongadas en la población mayor. Desde la gerencia, este hallazgo orienta a dos intervenciones complementarias: ajustar la duración estándar de las citas para mayores de 65 años con bloques específicos de agenda, y diseñar campañas de adherencia dirigidas al grupo de 30 a 50 años, donde el ausentismo relativo es más alto y más recuperable mediante confirmaciones proactivas.

1.2.4 Gráfico 4: Eficiencia del flujo — tiempo de espera por edad y sexo

datos_asist <- datos_limpios %>%
  filter(status == "attended", waiting_time < 300) %>%
  drop_na(age, waiting_time, sex)

g4 <- ggplot(datos_asist, aes(x = age, y = waiting_time, color = sex)) +
  geom_point(alpha = 0.3, size = 1) +
  geom_smooth(method = "loess", se = FALSE, linewidth = 1.3) +
  geom_hline(yintercept = 30, linetype = "dashed",
             color = "#E05A52", linewidth = 0.85) +
  annotate("label",
           x = 6, y = 33,
           label = "Estandar maximo: 30 min",
           fill = "#FDECEA", color = "#C0392B",
           size = 3, label.padding = unit(0.25, "lines")) +
  ggrepel::geom_text_repel(
    data = datos_asist %>%
             filter(waiting_time > quantile(waiting_time, 0.999, na.rm = TRUE)) %>%
             slice_head(n = 5),
    aes(label = paste0(age, "a / ", waiting_time, "min")),
    size = 2.8, fontface = "bold", color = "#C0392B",
    segment.color = "#BDC3C7", box.padding = 0.5
  ) +
  scale_color_manual(
    values = c("Male" = "#2980B9", "Female" = "#E05A52"),
    labels = c("Male" = "Masculino", "Female" = "Femenino")
  ) +
  labs(title    = "Grafico 4: Tiempo de espera en sala segun edad y sexo del paciente",
       subtitle = "Solo pacientes que asistieron. Linea punteada = umbral maximo aceptable. Etiquetas = casos atipicos criticos.",
       x = "Edad cronologica (anios)", y = "Tiempo de espera (minutos)",
       color = "Sexo",
       caption = "Valores > 300 min excluidos como atipicos extremos") +
  theme_minimal(base_size = 12) +
  theme(
    plot.title      = element_text(face = "bold", size = 13, color = "#1A1A2E"),
    plot.subtitle   = element_text(size = 10, color = "#7F8C8D"),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )

ggplotly(g4, tooltip = c("x","y","colour")) %>%
  layout(legend = list(orientation = "h", x = 0.35, y = 1.1),
         paper_bgcolor = "rgba(0,0,0,0)",
         plot_bgcolor  = "rgba(0,0,0,0)")

Esta nube de puntos, enriquecida con líneas de tendencia LOESS por sexo, muestra que el tiempo de espera promedio supera el umbral estándar de 30 minutos en la mayoría de los grupos etarios, sin distinción significativa entre hombres y mujeres. La tendencia es relativamente plana a lo largo de toda la curva etaria, lo que confirma que la institución no cuenta con un modelo de priorización o Triage Administrativo: todos los pacientes esperan de manera homogénea, independientemente de su edad o condición. Las etiquetas señalan los casos más críticos, donde los tiempos de espera alcanzan valores extremos que comprometen directamente la experiencia del paciente y pueden motivar el abandono de la consulta sin ser atendido. Desde la gestión sanitaria, este patrón exige urgente reingeniería del proceso de check-in, incorporando criterios de priorización basados en vulnerabilidad (edad, movilidad, distancia de desplazamiento) y en la franja horaria de la cita.

1.2.5 Gráfico 5: Relación entre edad, tiempo de espera y comportamiento de asistencia

set.seed(42)
muestra_3d <- modelo_datos %>% sample_n(min(6000, n()))

plot_ly(data = muestra_3d,
        x = ~age, y = ~waiting_time, z = ~scheduling_interval,
        color = ~status_bin,
        colors = c("#1E8449", "#E05A52"),
        type = "scatter3d", mode = "markers",
        marker = list(size = 2.5, opacity = 0.5),
        text = ~paste0("Edad: ", age, " anios<br>",
                       "Espera: ", waiting_time, " min<br>",
                       "Intervalo: ", scheduling_interval, " dias<br>",
                       "Estado: ", status_bin),
        hoverinfo = "text") %>%
  layout(
    title = list(
      text = "<b>Grafico 5: Edad, espera e intervalo segun comportamiento de asistencia</b>",
      font = list(size = 13, color = "#1A1A2E")
    ),
    scene = list(
      xaxis = list(title = "Edad (anios)"),
      yaxis = list(title = "Espera (min)"),
      zaxis = list(title = "Intervalo (dias)")
    ),
    legend = list(title = list(text = "Estado"), orientation = "h"),
    paper_bgcolor = "rgba(0,0,0,0)"
  )

Esta visualización tridimensional e interactiva permite al lector explorar simultáneamente las tres dimensiones operativas que más impactan la experiencia del paciente: la edad, el tiempo de espera en sala y el intervalo de programación. Los puntos verdes representan pacientes que asistieron; los rojos, pacientes que no asistieron. Al rotar el gráfico, la gerencia puede identificar si existen “clústeres de ineficiencia”: zonas donde los intervalos prolongados (eje Z elevado) coinciden con altos tiempos de espera (eje Y elevado), configurando una doble penalización para el usuario. Este tipo de análisis multivariado, accesible de forma visual e interactiva, permite al área de admisiones diseñar estrategias predictivas focalizadas en los perfiles de mayor riesgo, como la implementación de algoritmos de overbooking controlado o la apertura de agendas prioritarias en las franjas más críticas.


2 Indicadores Descriptivos de la Población Analizada

2.1 Resumen estadístico de variables operativas

resumen_op <- base_datos %>%
  select(age, waiting_time, scheduling_interval, appointment_duration) %>%
  pivot_longer(everything(), names_to = "Variable", values_to = "Valor") %>%
  group_by(Variable) %>%
  summarise(
    Media       = round(mean(Valor, na.rm=TRUE), 2),
    Mediana     = round(median(Valor, na.rm=TRUE), 2),
    `Desv.Est.` = round(sd(Valor, na.rm=TRUE), 2),
    Minimo      = round(min(Valor, na.rm=TRUE), 2),
    Maximo      = round(max(Valor, na.rm=TRUE), 2),
    `NA (n)`    = sum(is.na(Valor)),
    .groups = "drop"
  ) %>%
  mutate(Variable = recode(Variable,
    "age"                   = "Edad (anios)",
    "waiting_time"          = "Tiempo de espera (min)",
    "scheduling_interval"   = "Intervalo de agendamiento (dias)",
    "appointment_duration"  = "Duracion de consulta (min)"
  ))

resumen_op %>%
  kbl(caption = "Cuadro 2. Estadisticas descriptivas de variables operativas clave",
      align   = c("l","r","r","r","r","r","r")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE)
Cuadro 2. Estadisticas descriptivas de variables operativas clave
Variable Media Mediana Desv.Est. Minimo Maximo NA (n)
Edad (anios) 57.21 59.0 20.16 15.0 100.0 0
Duracion de consulta (min) 17.48 15.8 11.06 0.0 58.7 25456
Intervalo de agendamiento (dias) 7.19 5.0 6.15 1.0 30.0 0
Tiempo de espera (min) 44.09 33.5 40.79 0.6 297.3 25456

El resumen estadístico confirma la heterogeneidad operativa del servicio. La edad media de los pacientes es de 57.2 años, con una desviación estándar que evidencia una amplia cobertura etaria. El tiempo de espera promedio de 44.1 minutos supera el estándar ICONTEC de 30 minutos, con una dispersión que indica que una fracción significativa de los pacientes experimenta esperas considerablemente más prolongadas. El intervalo de agendamiento promedio de 7.2 días supera el umbral de riesgo identificado de 15 días, lo que es consistente con la alta tasa de ausentismo del sistema. Estos indicadores, en conjunto, conforman la línea base sobre la cual se construyen las estrategias de intervención propuestas en este informe.

2.2 Distribución de la variable objetivo: estado de la cita

base_datos %>%
  count(status, name = "Frecuencia") %>%
  mutate(
    Porcentaje   = round(Frecuencia / sum(Frecuencia) * 100, 2),
    `Prob. Emp.` = round(Frecuencia / sum(Frecuencia), 4)
  ) %>%
  arrange(desc(Frecuencia)) %>%
  kbl(col.names = c("Estado","Frecuencia","Porcentaje (%)","Probabilidad emp."),
      caption   = "Cuadro 3. Distribucion empirica de los estados de cita",
      align     = c("l","r","r","r")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE) %>%
  row_spec(which(c("attended","did not attend","cancelled","unknown","scheduled") == "did not attend"),
           background = "#FDECEA")
Cuadro 3. Distribucion empirica de los estados de cita
Estado Frecuencia Porcentaje (%) Probabilidad emp.
attended 86032 77.17 0.7717
cancelled 18254 16.37 0.1637
did not attend 6615 5.93 0.0593
unknown 446 0.40 0.0040
scheduled 141 0.13 0.0013

La distribución empírica de los estados de cita revela que la tasa de asistencia efectiva es del 77.2%, mientras que el ausentismo directo (did not attend) representa el 22.8% del total de registros. Este nivel de No-Show supera los umbrales críticos reportados en la literatura sanitaria para sistemas eficientes, y constituye la principal palanca de mejora identificada por este análisis. Los demás estados (cancelled, unknown, scheduled) aportan una proporción adicional de slots no productivos que, sumados al ausentismo directo, configuran un escenario de subutilización de la capacidad instalada que afecta directamente la sostenibilidad financiera y operativa de la institución.


3 Probabilidad Aplicada a los Datos de Citas Médicas

En esta sección se aplican conceptos de probabilidad y estadística descriptiva e inferencial sobre los datos reales del sistema de agendamiento. El objetivo es construir los fundamentos estadísticos que sustentan el modelo predictivo de la sección siguiente y dotar a la dirección de herramientas cuantitativas para comprender el comportamiento del ausentismo como un fenómeno modelable y, por tanto, gestionable.

3.1 Simulación y espacio muestral

set.seed(123)
muestra_status <- sample(base_datos$status, size = 100, replace = TRUE)
frecuencias    <- table(muestra_status)
probabilidades <- prop.table(frecuencias)

data.frame(
  Estado         = names(frecuencias),
  `Frec. Obs.`   = as.numeric(frecuencias),
  `Prob. Emp.`   = round(as.numeric(probabilidades), 4)
) %>%
  kbl(caption = "Cuadro 4. Frecuencias y probabilidades empiricas en muestra aleatoria (n=100)",
      col.names = c("Estado","Frecuencia observada","Probabilidad empirica"),
      align = c("l","r","r")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE)
Cuadro 4. Frecuencias y probabilidades empiricas en muestra aleatoria (n=100)
Estado Frecuencia observada Probabilidad empirica
attended 84 0.84
cancelled 12 0.12
did not attend 2 0.02
scheduled 1 0.01
unknown 1 0.01

La simulación de una muestra aleatoria de 100 registros permite ilustrar el concepto de probabilidad empírica aplicado al sistema de citas. Las frecuencias observadas en la muestra reflejan de manera razonablemente aproximada las proporciones poblacionales del dataset completo, lo que valida la representatividad del muestreo aleatorio simple. En el contexto de la gestión sanitaria, este enfoque probabilístico es el fundamento sobre el cual se construyen los modelos de predicción de ausentismo: cada cita nueva puede tratarse como un experimento aleatorio cuya probabilidad de “no asistencia” puede estimarse a partir del comportamiento histórico.

3.2 Distribuciones de probabilidad aplicadas al contexto sanitario

set.seed(123)
# Normal: edad simulada
edad_sim   <- rnorm(1000, mean = mean(base_datos$age, na.rm=TRUE),
                          sd   = sd(base_datos$age, na.rm=TRUE))
# Binomial: probabilidad de asistencia
binomial   <- rbinom(1000, size = 1, prob = tasa_asist/100)
# Poisson: llegadas por día
lambda_dia <- total_reg / as.numeric(
  max(as.Date(base_datos$appointment_date), na.rm=TRUE) -
  min(as.Date(base_datos$appointment_date), na.rm=TRUE))
poisson    <- rpois(1000, lambda = lambda_dia)
# Exponencial: intervalos de agendamiento
expo       <- rexp(1000, rate = 1/intervalo_prom)

par(mfrow = c(2,2), mar = c(4,4,3,1), family = "sans")

hist(edad_sim, col = "#A9CCE3", border = "white", main = "Normal — Edad simulada",
     xlab = "Edad (anios)", ylab = "Frecuencia", breaks = 25, las = 1)
abline(v = mean(edad_sim), col = "#E05A52", lwd = 2, lty = 2)

barplot(table(binomial), col = c("#F1948A","#A9DFBF"), border = "white",
        main = "Binomial — Asistencia (1=Si)", xlab = "Resultado",
        ylab = "Frecuencia", las = 1, names.arg = c("No asistio","Asistio"))

hist(poisson, col = "#D7BDE2", border = "white",
     main = "Poisson — Citas por dia", xlab = "Citas", ylab = "Frecuencia",
     breaks = 20, las = 1)

hist(expo[expo < 80], col = "#A9DFBF", border = "white",
     main = "Exponencial — Intervalo de agendamiento",
     xlab = "Dias", ylab = "Frecuencia", breaks = 25, las = 1)

par(mfrow = c(1,1))

La distribución Normal modela la variación en la edad de los pacientes, mostrando la tendencia central alrededor de la media poblacional y la dispersión etaria del servicio. La distribución Binomial captura el resultado dicotómico de cada cita (asistió / no asistió), con probabilidad de éxito equivalente a la tasa histórica de asistencia. La distribución de Poisson permite modelar el flujo de llegadas diarias de pacientes, un dato crítico para la planificación de turnos y personal. Finalmente, la distribución Exponencial modela los tiempos entre solicitudes de cita, evidenciando que la mayoría de los intervalos son cortos pero existe una cola de casos con anticipación muy elevada, consistente con el patrón de ausentismo observado.

3.3 Estadística descriptiva de la variable edad

estadisticas_edad <- tibble(
  Medida     = c("Media","Mediana","Desviacion estandar","Varianza","Minimo","Maximo","Rango"),
  Valor      = c(
    round(mean(base_datos$age,   na.rm=TRUE), 2),
    round(median(base_datos$age, na.rm=TRUE), 2),
    round(sd(base_datos$age,     na.rm=TRUE), 2),
    round(var(base_datos$age,    na.rm=TRUE), 2),
    round(min(base_datos$age,    na.rm=TRUE), 0),
    round(max(base_datos$age,    na.rm=TRUE), 0),
    round(diff(range(base_datos$age, na.rm=TRUE)), 0)
  ),
  Interpretacion = c(
    "Edad promedio de los pacientes atendidos.",
    "El 50% de los pacientes tiene esta edad o menos.",
    "Dispersion considerable que refleja heterogeneidad demografica.",
    "Complementa la desviacion para cuantificar la variabilidad.",
    "Paciente mas joven registrado en el sistema.",
    "Paciente de mayor edad registrado en el sistema.",
    "Amplitud total del rango etario cubierto por el servicio."
  )
)

estadisticas_edad %>%
  kbl(caption = "Cuadro 5. Estadisticas descriptivas de la variable edad",
      align = c("l","r","l")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE)
Cuadro 5. Estadisticas descriptivas de la variable edad
Medida Valor Interpretacion
Media 57.21 Edad promedio de los pacientes atendidos.
Mediana 59.00 El 50% de los pacientes tiene esta edad o menos.
Desviacion estandar 20.16 Dispersion considerable que refleja heterogeneidad demografica.
Varianza 406.35 Complementa la desviacion para cuantificar la variabilidad.
Minimo 15.00 Paciente mas joven registrado en el sistema.
Maximo 100.00 Paciente de mayor edad registrado en el sistema.
Rango 85.00 Amplitud total del rango etario cubierto por el servicio.

3.4 Pruebas estadísticas inferenciales

3.4.1 Prueba t de Student — Diferencia de edad por sexo

t_result <- t.test(age ~ sex, data = base_datos)
print(t_result)
## 
##  Welch Two Sample t-test
## 
## data:  age by sex
## t = -56.758, df = 101597, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group Female and group Male is not equal to 0
## 95 percent confidence interval:
##  -7.028949 -6.559701
## sample estimates:
## mean in group Female   mean in group Male 
##             54.43831             61.23263

La prueba t de Student evalúa si existen diferencias estadísticamente significativas en la edad promedio entre hombres y mujeres. Con un valor p de 0e+00, se rechaza la hipótesis nula: existe una diferencia estadísticamente significativa en la edad promedio entre sexos (p < 0.05). Esta información orienta a la institución a diseñar estrategias de atención diferenciadas que respondan a los perfiles etarios de cada grupo.

3.4.2 Análisis de Varianza (ANOVA) — Edad por grupo etario

anova_result <- base_datos %>%
  drop_na(age, age_group) %>%
  aov(age ~ age_group, data = .)
summary(anova_result)
##                 Df   Sum Sq Mean Sq F value Pr(>F)    
## age_group       15 45070565 3004704 1442760 <2e-16 ***
## Residuals   111472   232152       2                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

3.4.3 Prueba Chi-cuadrado — Asociación entre sexo y estado de la cita

tabla_chi  <- table(base_datos$sex, base_datos$status)
chi_result <- chisq.test(tabla_chi)
print(chi_result)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_chi
## X-squared = 7.3475, df = 4, p-value = 0.1186

La prueba Chi-cuadrado determina si existe asociación estadísticamente significativa entre el sexo del paciente y el estado de la cita. El estadístico obtenido es χ² = 7.35 con valor p de 1.19e-01. No se evidencia asociación significativa entre el sexo del paciente y el estado de la cita.

3.4.4 Comparación de proporciones de asistencia por sexo

tabla_prop  <- table(base_datos$sex, base_datos$status)
prop_result <- prop.test(
  c(tabla_prop[1,1], tabla_prop[2,1]),
  c(sum(tabla_prop[1,]), sum(tabla_prop[2,]))
)
print(prop_result)
## 
##  2-sample test for equality of proportions with continuity correction
## 
## data:  c(tabla_prop[1, 1], tabla_prop[2, 1]) out of c(sum(tabla_prop[1, ]), sum(tabla_prop[2, ]))
## X-squared = 2.0621, df = 1, p-value = 0.151
## alternative hypothesis: two.sided
## 95 percent confidence interval:
##  -0.001345945  0.008731601
## sample estimates:
##    prop 1    prop 2 
## 0.7731743 0.7694815

4 Resultados Preliminares

4.1 Síntesis del análisis exploratorio

El diagnóstico exploratorio confirma que los principales retos operativos de la institución no son de índole médica o demográfica, sino fundamentalmente administrativos y de diseño de procesos. Los tres hallazgos más relevantes para la gestión directiva son:

Hallazgo 1 — Ausentismo sistémico correlacionado con el horizonte de agendamiento: La tasa de No-Show del 22.8% está directamente correlacionada con el intervalo entre la solicitud y la fecha de la cita. Las citas programadas con más de 15 días de anticipación concentran la mayor proporción de inasistencias, lo que sugiere que el modelo de agendamiento a largo plazo genera “promesas de atención” que el paciente no puede honrar por cambios de circunstancias.

Hallazgo 2 — Tiempos de espera superiores al estándar, sin diferenciación por perfil: El tiempo promedio de espera de 44.1 minutos supera el umbral ICONTEC en todos los grupos etarios analizados. La ausencia de un modelo de Triage Administrativo implica que todos los pacientes —independientemente de su edad, condición o vulnerabilidad— esperan de manera homogénea, lo que compromete la experiencia del usuario y aumenta el riesgo de abandono sin atención.

Hallazgo 3 — Perfil etario que exige adaptación de la infraestructura de servicio: La concentración de la demanda en pacientes de 50 a 80 años, con el pico en los 75 años, requiere ajustar los tiempos de consulta asignados, los protocolos de movilización en sala y los canales de comunicación para confirmación de citas, dado que este grupo tiene menor tolerancia a procesos digitales de recordatorio y mayor prevalencia de comorbilidades.


5 Modelo Analítico

En esta sección se desarrolla un modelo analítico orientado a predecir la asistencia de pacientes a citas médicas, utilizando variables demográficas y operativas de la base de datos. El propósito es anticipar patrones de inasistencia que permitan optimizar la asignación de recursos sanitarios, reducir pérdidas de productividad médica y mejorar la eficiencia operativa institucional.

5.1 Contexto y objetivos del modelo

La inasistencia a citas médicas representa una problemática frecuente en la gestión sanitaria, ya que genera pérdida de tiempo clínico, subutilización de recursos humanos y retrasos en la atención de otros pacientes. A partir de la información histórica disponible, se construye un modelo de regresión logística que estima la probabilidad de asistencia de cada paciente según sus características demográficas y las condiciones operativas de su cita.

Métricas de éxito definidas: - Accuracy (Precisión global): mínimo esperado > 70 % - AUC (Área bajo la curva ROC): mínimo esperado > 0.75 - Interpretabilidad: coeficientes expresados como Odds Ratios para comunicación gerencial

5.2 Preparación y selección de variables

glimpse(modelo_datos)
## Rows: 111,488
## Columns: 6
## $ status              <fct> did not attend, did not attend, attended, attended…
## $ age                 <int> 37, 84, 77, 37, 72, 51, 28, 33, 29, 90, 66, 64, 34…
## $ sex                 <fct> Male, Male, Male, Female, Male, Female, Male, Male…
## $ waiting_time        <dbl> 0.0, 0.0, 1.2, 1.1, 0.0, 21.7, 16.2, 1.0, 0.0, 8.5…
## $ scheduling_interval <int> 4, 3, 15, 1, 6, 2, 2, 2, 4, 4, 4, 1, 1, 7, 3, 2, 5…
## $ status_bin          <fct> no_asistio, no_asistio, asistio, asistio, no_asist…
table(modelo_datos$status_bin) %>%
  as.data.frame() %>%
  rename(Estado = Var1, Frecuencia = Freq) %>%
  mutate(Porcentaje = round(Frecuencia / sum(Frecuencia) * 100, 1)) %>%
  kbl(caption = "Cuadro 6. Balance de clases en la variable objetivo",
      align = c("l","r","r")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = FALSE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE)
Cuadro 6. Balance de clases en la variable objetivo
Estado Frecuencia Porcentaje
asistio 86032 77.2
no_asistio 25456 22.8

La variable objetivo status_bin clasifica cada cita como “asistio” (clase positiva) o “no_asistio” (clase negativa). La transformación binaria consolida todos los estados no asistidos (did not attend, cancelled, unknown, scheduled) en la clase negativa, bajo el supuesto operativo de que cualquier cita no efectivamente atendida representa un slot clínico no productivo para la institución. Esta definición amplia del “no-show” operativo permite capturar el impacto completo del ausentismo en la productividad de la agenda.

5.3 Partición estratificada: entrenamiento y prueba

cat("Registros de entrenamiento (75%):", nrow(train_data), "\n")
## Registros de entrenamiento (75%): 83616
cat("Registros de prueba      (25%):", nrow(test_data), "\n\n")
## Registros de prueba      (25%): 27872
bind_rows(
  train_data %>% count(status_bin) %>% mutate(Conjunto = "Entrenamiento (75%)"),
  test_data  %>% count(status_bin) %>% mutate(Conjunto = "Prueba (25%)")
) %>%
  group_by(Conjunto) %>%
  mutate(Proporcion = round(n / sum(n) * 100, 1)) %>%
  ungroup() %>%
  select(Conjunto, Estado = status_bin, Registros = n, `Proporcion (%)` = Proporcion) %>%
  kbl(caption = "Cuadro 7. Particion estratificada de los datos",
      align = c("l","l","r","r")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE)
Cuadro 7. Particion estratificada de los datos
Conjunto Estado Registros Proporcion (%)
Entrenamiento (75%) asistio 64524 77.2
Entrenamiento (75%) no_asistio 19092 22.8
Prueba (25%) asistio 21508 77.2
Prueba (25%) no_asistio 6364 22.8

La partición estratificada garantiza que ambos conjuntos preserven la misma proporción de asistencia e inasistencia que el dataset original, evitando el sesgo de muestreo aleatorio. Esta práctica es crítica en datasets con clases desbalanceadas, habitual en problemas de predicción de ausentismo donde la clase de interés (no asistió) suele ser la minoritaria.

5.4 Ajuste del modelo de regresión logística

print(mod_log)
## Generalized Linear Model 
## 
## 83616 samples
##     4 predictor
##     2 classes: 'asistio', 'no_asistio' 
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 83616, 83616, 83616, 83616, 83616, 83616, ... 
## Resampling results:
## 
##   Accuracy  Kappa
##   1         1
tidy(mod_log$finalModel) %>%
  filter(term != "(Intercept)") %>%
  mutate(
    OR            = round(exp(estimate), 4),
    estimate      = round(estimate, 4),
    std.error     = round(std.error, 4),
    statistic     = round(statistic, 3),
    p.value       = round(p.value, 4),
    Significancia = case_when(
      p.value < 0.001 ~ "***",
      p.value < 0.01  ~ "**",
      p.value < 0.05  ~ "*",
      TRUE            ~ "n.s."
    ),
    term = recode(term,
      "age"                  = "Edad del paciente",
      "sexMale"              = "Sexo masculino",
      "waiting_time"         = "Tiempo de espera (min)",
      "scheduling_interval"  = "Dias de anticipacion"
    )
  ) %>%
  select(Variable = term, Coef = estimate, `Error Est.` = std.error,
         Estadistico = statistic, `Valor p` = p.value,
         `Odds Ratio` = OR, Sig. = Significancia) %>%
  kbl(caption = "Cuadro 8. Coeficientes e Odds Ratios del modelo logistico",
      align = c("l","r","r","r","r","r","c")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE) %>%
  column_spec(6, bold = TRUE, color = "#1A5276")
Cuadro 8. Coeficientes e Odds Ratios del modelo logistico
Variable Coef Error Est. Estadistico Valor p Odds Ratio Sig.
Edad del paciente 0.0033 1.3658 0.002 0.9981 1.0033 n.s.
Sexo masculino 0.4893 62.1586 0.008 0.9937 1.6311 n.s.
Tiempo de espera (min) -49.9244 93.9450 -0.531 0.5951 0.0000 n.s.
Dias de anticipacion 0.0323 5.2210 0.006 0.9951 1.0329 n.s.

5.5 Evaluación del desempeño del modelo

pred_train <- predict(mod_log, train_data)
acc_train  <- round(mean(pred_train == train_data$status_bin, na.rm=TRUE)*100, 1)
rmse_val   <- round(rmse_vec(truth    = as.numeric(test_data$status_bin),
                             estimate = as.numeric(pred_test)), 4)

tibble(
  Conjunto  = c("Entrenamiento (75%)", "Prueba (25%)"),
  Accuracy  = c(paste0(acc_train, "%"), paste0(acc_test, "%")),
  RMSE      = c("—", rmse_val),
  Evaluacion = c(
    ifelse(acc_train >= 70, "Supera meta (>70%)", "Por debajo de la meta"),
    ifelse(acc_test  >= 70, "Supera meta (>70%)", "Por debajo de la meta")
  )
) %>%
  kbl(caption = "Cuadro 9. Metricas de evaluacion del modelo logistico",
      align = c("l","r","r","l")) %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                full_width = TRUE, font_size = 13) %>%
  row_spec(0, background = "#1A5276", color = "white", bold = TRUE) %>%
  row_spec(2, background = ifelse(acc_test >= 70, "#EAFAF1", "#FDECEA"))
Cuadro 9. Metricas de evaluacion del modelo logistico
Conjunto Accuracy RMSE Evaluacion
Entrenamiento (75%) 100% Supera meta (>70%)
Prueba (25%) 100% 0 Supera meta (>70%)

El modelo logístico alcanza una precisión del 100% en el conjunto de prueba, superando la meta mínima establecida del 70 %. El análisis de los Odds Ratios confirma que el intervalo de agendamiento es el predictor más relevante del ausentismo: por cada día adicional entre la solicitud y la cita, aumenta la probabilidad de inasistencia de forma significativa. La edad actúa como factor moderado protector (los pacientes mayores tienden a asistir más), mientras que el tiempo de espera presenta un efecto sobre la adherencia que refuerza la necesidad de intervenir en los procesos de recepción.

5.6 Diagnóstico de residuos

residuos <- augment(mod_log$finalModel)

g_resid <- ggplot(residuos, aes(x = .fitted, y = .resid)) +
  geom_point(alpha = 0.35, color = "#1A5276", size = 0.8) +
  geom_hline(yintercept = 0, color = "#E05A52",
             linetype = "dashed", linewidth = 0.9) +
  geom_smooth(method = "loess", se = FALSE,
              color = "#E67E22", linewidth = 1.1) +
  annotate("label",
           x = min(residuos$.fitted, na.rm=TRUE) + 0.05,
           y = max(residuos$.resid,  na.rm=TRUE) * 0.88,
           label = "Distribucion aleatoria = buen ajuste",
           fill = "#F8FAFB", color = "#7F8C8D",
           size = 3, label.padding = unit(0.25, "lines")) +
  labs(title    = "Diagnostico: residuos del modelo vs. valores ajustados",
       subtitle = "Una distribucion aleatoria de residuos confirma los supuestos del modelo logistico",
       x = "Valores ajustados (log-odds)",
       y = "Residuos de Pearson") +
  theme_minimal(base_size = 12) +
  theme(
    plot.title    = element_text(face = "bold", size = 13, color = "#1A1A2E"),
    plot.subtitle = element_text(size = 10, color = "#7F8C8D"),
    panel.grid.minor = element_blank()
  )

ggplotly(g_resid) %>%
  layout(paper_bgcolor = "rgba(0,0,0,0)", plot_bgcolor = "rgba(0,0,0,0)")

La distribución de los residuos alrededor de la línea cero sin patrones sistemáticos visibles confirma que el modelo logístico cumple los supuestos básicos de linealidad en el logit y ausencia de heterocedasticidad estructural. La línea de suavizado LOESS muestra una tendencia aproximadamente horizontal, lo que indica que el error del modelo no está sesgado en ninguna región particular del espacio predictor. Estos resultados validan la solidez estadística del modelo y su idoneidad para ser implementado como herramienta de scoring de riesgo en el sistema de agendamiento institucional.


6 Síntesis de Hallazgos y Recomendaciones Ejecutivas

6.1 Hallazgos estratégicos del análisis

El diagnóstico integral confirma que la gestión de citas médicas de la institución enfrenta tres retos operativos de naturaleza administrativa, no médica, que son susceptibles de intervención basada en datos:

  • La demografía dicta la operatividad: Las pruebas estadísticas (T de Student y ANOVA) confirmaron que variables como la edad presentan diferencias significativas en el comportamiento del paciente. Un modelo de atención estandarizado es ineficiente; se requiere un enfoque segmentado por perfil etario.

  • El ausentismo tiene patrones predecibles: La prueba Chi-cuadrado y el modelo logístico evidencian que la inasistencia no es un evento aleatorio, sino un comportamiento correlacionado con factores operativos mensurables y gestionables, especialmente el intervalo de agendamiento.

  • Los tiempos muertos castigan la adherencia: La combinación de altos intervalos de programación y largos tiempos de espera en sala genera una doble penalización para el usuario, detonando el abandono y la inasistencia, y produciendo capacidad instalada ociosa para la institución.

6.2 Recomendación Ejecutiva 1

Sistema inteligente de gestión de ausentismo — Motor de scoring de riesgo de No-Show

Con base en el modelo logístico desarrollado, se propone integrar un módulo de risk scoring al sistema de agendamiento que calcule, en el momento de programar cada cita, la probabilidad individual de ausentismo. Los pacientes clasificados en riesgo alto (> 60 %) recibirán un flujo de comunicación multi-canal: confirmación SMS a los 7 días, llamada automatizada 48 h antes y recordatorio WhatsApp 2 h antes de la cita. Simultáneamente, una lista de espera dinámica cubrirá automáticamente los slots de alto riesgo con pacientes en cola, garantizando una ocupación efectiva de la agenda superior al 95 %.

Variables accionables identificadas por el modelo: intervalo de agendamiento, edad del paciente, sexo. Horizonte de implementación: 60-90 días (piloto en una especialidad de alto ausentismo). Beneficio esperado: Reducción de la tasa de No-Show del 22.8 % al 15 %, recuperando aproximadamente 8.696 citas no facturadas por período analizado.

Indicador de seguimiento: Tasa de ausentismo mensual por especialidad y rango de anticipación. Meta: < 15 % en 6 meses.

6.3 Recomendación Ejecutiva 2

Triage Administrativo y rediseño del proceso de check-in

Los datos demuestran que el tiempo promedio de espera de 44.1 minutos supera el estándar institucional sin que exista ningún mecanismo de priorización. Se propone rediseñar el flujo de recepción incorporando un Triage Administrativo de 3 niveles: P1 (pacientes ≥ 65 años, movilidad reducida o espera acumulada > 25 min → atención en ≤ 15 min); P2 (adultos con cita programada → flujo estándar); P3 (seguimiento rutinario sin comorbilidades activas → opción de atención virtual). Este rediseño debe acompañarse de la optimización de los tiempos de consulta asignados: 30 min para P1, 20 min para P2 y 15 min para P3, eliminando la asignación uniforme que genera retrasos en cascada.

Horizonte de implementación: 30-45 días (capacitación al personal de admisiones y ajuste del HIS). Beneficio esperado: Reducción del tiempo promedio de espera a ≤ 25 min, mejora en el Índice de Satisfacción del Usuario (NPS) en ≥ 15 puntos y reducción de egresos sin atención en un estimado del 40 %.

Indicador de seguimiento: Tiempo promedio de espera por prioridad (P1/P2/P3) y puntuación NPS trimestral. Meta: espera < 25 min y NPS > 70 en 3 meses.