El indicador de espera permite valorar la oportunidad percibida por el paciente una vez llega al servicio. Un tiempo promedio bajo sugiere mejor flujo operativo; un valor elevado puede reflejar sobrecarga de agendas, retrasos acumulados o insuficiente capacidad instalada.
Ruta de acción sugerida: establecer seguimiento semanal del tiempo promedio de espera y activar alertas cuando el indicador supere el umbral definido por la institución.
¿Qué evidencia el gráfico? Muestra la evolución mensual del tiempo promedio de espera e identifica el pico crítico del periodo analizado.
¿Por qué es relevante? Permite detectar meses con mayor presión operativa y priorizar análisis de causas: agendas saturadas, retrasos acumulados o aumento de demanda.
¿Qué ruta de acción sugiere? Comparar los meses pico con disponibilidad de talento humano, número de consultorios, duración real de citas y volumen de usuarios citados.
¿Qué evidencia el gráfico? Muestra la participación porcentual de citas asistidas, canceladas y no atendidas.
¿Por qué es relevante? Las cancelaciones e inasistencias reducen productividad, dejan cupos sin usar y afectan la oportunidad de pacientes que requieren atención.
¿Qué ruta de acción sugiere? Implementar recordatorios automatizados, confirmación previa y reprogramación digital para recuperar cupos antes de que se pierdan.
La conversión a gráfico interactivo permite que el usuario directivo explore los meses con mayor tiempo de espera, revise valores específicos mediante tooltips y amplíe periodos críticos con zoom. Esta funcionalidad aporta valor porque transforma una visualización descriptiva en una herramienta de exploración gerencial.
Lectura ejecutiva: la segmentación por edad permite identificar qué grupos concentran mayor carga de citas y cómo se distribuyen los estados de atención. Esta información ayuda a focalizar estrategias de comunicación y seguimiento.
| Hallazgo | Impacto_en_gestion | Accion_sugerida |
|---|---|---|
| Se identifican diferencias en el estado final de las citas. | La inasistencia y cancelación reducen productividad y disponibilidad real. | Confirmación previa y recuperación temprana de cupos. |
| El tiempo de espera presenta variación mensual. | Los picos de espera sugieren congestión operativa o desbalance de agenda. | Monitoreo semanal y ajuste de agendas en meses críticos. |
| Existen grupos etarios con mayor concentración de citas. | La demanda por edad permite focalizar acciones de comunicación y acceso. | Segmentar recordatorios y rutas de atención por grupo poblacional. |
1. Implementar un tablero de seguimiento operativo
semanal.
Se recomienda monitorear tiempo promedio de espera, porcentaje de
asistencia, cancelación e inasistencia. El tablero debe tener semáforos
de alerta para activar acciones correctivas cuando se superen los
umbrales definidos.
2. Fortalecer la estrategia de confirmación y reprogramación
de citas.
Se recomienda automatizar recordatorios, permitir confirmación por
canales digitales y liberar cupos con anticipación cuando el paciente no
pueda asistir. Esta medida puede mejorar la productividad de agenda y
reducir pérdida de capacidad instalada.
La narrativa visual permite que la información operativa deje de ser
únicamente descriptiva y se convierta en un insumo para la toma de
decisiones. El uso de ggplot2, plotly y
flexdashboard facilita construir un informe reproducible,
visualmente claro y orientado a la acción directiva.
---
title: "Narrativa visual con RStudio"
subtitle: "Dashboard ejecutivo para gestión de citas y tiempos de espera"
author: "JOP VANNDERTH MORA"
date: "`r Sys.Date()`"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
source_code: embed
theme: cosmo
---
```{r setup, include=FALSE}
# ============================================================
# CONFIGURACIÓN TÉCNICA
# Actividad Semana 4: Narrativa visual con RStudio.
# Recomendación docente aplicada:
# - Contenido visible redactado como informe ejecutivo.
# - Detalles técnicos conservados dentro del código con comentarios #.
# - Documento diseñado para publicarse en RPubs como HTML.
# ============================================================
options(repos = c(CRAN = "https://cloud.r-project.org"))
if (!requireNamespace("rlang", quietly = TRUE) || packageVersion("rlang") < "1.1.7") {
install.packages("rlang", dependencies = TRUE)
}
paquetes <- c(
"flexdashboard", "readxl", "dplyr", "ggplot2", "plotly",
"tidyr", "ggrepel", "tidyverse", "skimr", "rsample",
"caret", "broom", "yardstick", "htmltools", "bslib",
"bsicons", "janitor", "lubridate", "RColorBrewer",
"knitr", "scales", "tibble"
)
instalar <- paquetes[!(paquetes %in% installed.packages()[, "Package"])]
if (length(instalar) > 0) install.packages(instalar, dependencies = TRUE)
suppressPackageStartupMessages({
library(flexdashboard)
library(readxl)
library(dplyr)
library(ggplot2)
library(plotly)
library(tidyr)
library(ggrepel)
library(tidyverse)
library(skimr)
library(rsample)
library(caret)
library(broom)
library(yardstick)
library(htmltools)
library(bslib)
library(bsicons)
library(janitor)
library(lubridate)
library(RColorBrewer)
library(knitr)
library(scales)
library(tibble)
})
knitr::opts_chunk$set(warning = FALSE, message = FALSE, echo = FALSE)
```
```{r cargar-datos}
# ============================================================
# CARGA Y PREPARACIÓN DE DATOS
# Problema de gestión: seguimiento de oportunidad, ocupación y no asistencia.
# ============================================================
if (file.exists("data/appointments.xlsx")) {
ruta_archivo <- "data/appointments.xlsx"
} else if (file.exists("appointments.xlsx")) {
ruta_archivo <- "appointments.xlsx"
} else {
stop("No se encontró appointments.xlsx. Debe estar en data/ o junto al archivo .Rmd.")
}
datos <- read_excel(ruta_archivo) %>%
clean_names() %>%
mutate(
scheduling_date = as.Date(scheduling_date),
appointment_date = as.Date(appointment_date),
dias_espera = as.numeric(difftime(appointment_date, scheduling_date, units = "days")),
age = as.numeric(age),
waiting_time = as.numeric(waiting_time),
appointment_duration = as.numeric(appointment_duration),
sex = as.factor(sex),
status = as.factor(status),
mes = floor_date(appointment_date, "month")
) %>%
filter(!is.na(appointment_date), !is.na(status))
datos_limpios <- datos %>%
filter(!is.na(dias_espera), dias_espera >= 0, !is.na(waiting_time), waiting_time >= 0)
total_citas <- nrow(datos)
porc_asistencia <- mean(datos$status == "attended", na.rm = TRUE)
porc_no_asiste <- mean(datos$status == "did not attend", na.rm = TRUE)
espera_promedio <- mean(datos_limpios$waiting_time, na.rm = TRUE)
estado_tabla <- datos %>%
count(status) %>%
mutate(porcentaje = n / sum(n))
tendencia_mensual <- datos_limpios %>%
group_by(mes) %>%
summarise(
citas = n(),
espera_promedio = mean(waiting_time, na.rm = TRUE),
dias_espera_promedio = mean(dias_espera, na.rm = TRUE),
no_asistencia = mean(status == "did not attend", na.rm = TRUE),
.groups = "drop"
) %>%
filter(!is.na(mes))
pico_espera <- tendencia_mensual %>%
filter(espera_promedio == max(espera_promedio, na.rm = TRUE)) %>%
slice(1)
estado_edad <- datos_limpios %>%
filter(!is.na(age), age >= 0) %>%
mutate(
grupo_edad = cut(
age,
breaks = c(0, 18, 30, 45, 60, 75, 120),
labels = c("0-18", "19-30", "31-45", "46-60", "61-75", "76+"),
include.lowest = TRUE
)
) %>%
group_by(grupo_edad, status) %>%
summarise(citas = n(), .groups = "drop")
```
Contexto {.sidebar}
=======================================================================
### Definición estratégica y audiencia
**Problema de gestión real:** disminuir la congestión operativa asociada a tiempos de espera, cancelaciones e inasistencias en la programación de citas ambulatorias.
**Audiencia directiva:** gerencia general, coordinación médica, dirección de calidad, líderes de experiencia del usuario y responsables de agenda asistencial.
**Enfoque A³ aplicado:**
- **Atención:** identificar el problema crítico de oportunidad y uso de agenda.
- **Análisis:** visualizar tendencias, estados de cita y grupos con mayor carga operativa.
- **Acción:** proponer decisiones ejecutivas para optimizar capacidad, confirmar citas y reducir tiempos improductivos.
Resumen ejecutivo
=======================================================================
Column {data-width=280}
-----------------------------------------------------------------------
### Citas analizadas
```{r value-total}
valueBox(comma(total_citas), "Total de citas registradas", icon = "fa-calendar-check", color = "primary")
```
### Asistencia
```{r value-asistencia}
valueBox(percent(porc_asistencia, accuracy = 0.1), "Citas asistidas", icon = "fa-user-check", color = "success")
```
### Inasistencia
```{r value-noasiste}
valueBox(percent(porc_no_asiste, accuracy = 0.1), "Citas no atendidas", icon = "fa-user-times", color = ifelse(porc_no_asiste > 0.10, "danger", "warning"))
```
Column {data-width=420}
-----------------------------------------------------------------------
### KPI crítico: tiempo promedio de espera
```{r gauge-espera}
gauge(
round(espera_promedio, 1),
min = 0,
max = 60,
symbol = " min",
gaugeSectors(success = c(0, 15), warning = c(15, 30), danger = c(30, 60))
)
```
### Lectura ejecutiva
El indicador de espera permite valorar la oportunidad percibida por el paciente una vez llega al servicio. Un tiempo promedio bajo sugiere mejor flujo operativo; un valor elevado puede reflejar sobrecarga de agendas, retrasos acumulados o insuficiente capacidad instalada.
**Ruta de acción sugerida:** establecer seguimiento semanal del tiempo promedio de espera y activar alertas cuando el indicador supere el umbral definido por la institución.
Análisis visual
=======================================================================
Column {data-width=500}
-----------------------------------------------------------------------
### Tendencia mensual de citas y espera
```{r tendencia-estatica, fig.height=5}
grafico_tendencia <- ggplot(tendencia_mensual, aes(x = mes, y = espera_promedio)) +
geom_line(linewidth = 1, color = "#2C3E50") +
geom_point(size = 2, color = "#2C3E50") +
geom_point(data = pico_espera, aes(x = mes, y = espera_promedio), color = "#C0392B", size = 4) +
annotate("rect",
xmin = pico_espera$mes - 15, xmax = pico_espera$mes + 15,
ymin = pico_espera$espera_promedio - 2, ymax = pico_espera$espera_promedio + 2,
alpha = 0.12, fill = "#C0392B") +
ggrepel::geom_text_repel(
data = pico_espera,
aes(label = paste0("Pico: ", round(espera_promedio, 1), " min")),
color = "#C0392B", size = 3.5
) +
labs(title = "Evolución mensual del tiempo promedio de espera",
x = "Mes", y = "Tiempo promedio de espera (minutos)") +
theme_minimal()
grafico_tendencia
```
**¿Qué evidencia el gráfico?** Muestra la evolución mensual del tiempo promedio de espera e identifica el pico crítico del periodo analizado.
**¿Por qué es relevante?** Permite detectar meses con mayor presión operativa y priorizar análisis de causas: agendas saturadas, retrasos acumulados o aumento de demanda.
**¿Qué ruta de acción sugiere?** Comparar los meses pico con disponibilidad de talento humano, número de consultorios, duración real de citas y volumen de usuarios citados.
Column {data-width=500}
-----------------------------------------------------------------------
### Estado de las citas
```{r estado-citas, fig.height=5}
grafico_estado <- ggplot(estado_tabla, aes(x = status, y = porcentaje, fill = status)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = percent(porcentaje, accuracy = 0.1)), vjust = -0.3, size = 3.5) +
scale_y_continuous(labels = percent_format()) +
scale_fill_brewer(palette = "Set2") +
labs(title = "Distribución de citas según estado", x = "Estado de la cita", y = "Porcentaje") +
theme_minimal()
grafico_estado
```
**¿Qué evidencia el gráfico?** Muestra la participación porcentual de citas asistidas, canceladas y no atendidas.
**¿Por qué es relevante?** Las cancelaciones e inasistencias reducen productividad, dejan cupos sin usar y afectan la oportunidad de pacientes que requieren atención.
**¿Qué ruta de acción sugiere?** Implementar recordatorios automatizados, confirmación previa y reprogramación digital para recuperar cupos antes de que se pierdan.
Exploración interactiva
=======================================================================
Column {data-width=650}
-----------------------------------------------------------------------
### Exploración interactiva de la tendencia
```{r tendencia-interactiva, fig.height=5}
plotly::ggplotly(grafico_tendencia)
```
### Justificación de la interactividad
La conversión a gráfico interactivo permite que el usuario directivo explore los meses con mayor tiempo de espera, revise valores específicos mediante tooltips y amplíe periodos críticos con zoom. Esta funcionalidad aporta valor porque transforma una visualización descriptiva en una herramienta de exploración gerencial.
Column {data-width=350}
-----------------------------------------------------------------------
### Citas por grupo de edad y estado
```{r edad-estado, fig.height=5}
grafico_edad <- ggplot(estado_edad, aes(x = grupo_edad, y = citas, fill = status)) +
geom_col(position = "dodge") +
scale_fill_brewer(palette = "Set2") +
labs(title = "Citas por grupo de edad y estado",
x = "Grupo de edad", y = "Número de citas", fill = "Estado") +
theme_minimal()
grafico_edad
```
**Lectura ejecutiva:** la segmentación por edad permite identificar qué grupos concentran mayor carga de citas y cómo se distribuyen los estados de atención. Esta información ayuda a focalizar estrategias de comunicación y seguimiento.
Acción directiva
=======================================================================
Column {data-width=500}
-----------------------------------------------------------------------
### Hallazgos críticos
```{r tabla-hallazgos}
hallazgos <- tibble(
Hallazgo = c(
"Se identifican diferencias en el estado final de las citas.",
"El tiempo de espera presenta variación mensual.",
"Existen grupos etarios con mayor concentración de citas."
),
Impacto_en_gestion = c(
"La inasistencia y cancelación reducen productividad y disponibilidad real.",
"Los picos de espera sugieren congestión operativa o desbalance de agenda.",
"La demanda por edad permite focalizar acciones de comunicación y acceso."
),
Accion_sugerida = c(
"Confirmación previa y recuperación temprana de cupos.",
"Monitoreo semanal y ajuste de agendas en meses críticos.",
"Segmentar recordatorios y rutas de atención por grupo poblacional."
)
)
knitr::kable(hallazgos, caption = "Síntesis ejecutiva de hallazgos y acciones")
```
Column {data-width=500}
-----------------------------------------------------------------------
### Recomendaciones ejecutivas
**1. Implementar un tablero de seguimiento operativo semanal.**
Se recomienda monitorear tiempo promedio de espera, porcentaje de asistencia, cancelación e inasistencia. El tablero debe tener semáforos de alerta para activar acciones correctivas cuando se superen los umbrales definidos.
**2. Fortalecer la estrategia de confirmación y reprogramación de citas.**
Se recomienda automatizar recordatorios, permitir confirmación por canales digitales y liberar cupos con anticipación cuando el paciente no pueda asistir. Esta medida puede mejorar la productividad de agenda y reducir pérdida de capacidad instalada.
### Cierre estratégico
La narrativa visual permite que la información operativa deje de ser únicamente descriptiva y se convierta en un insumo para la toma de decisiones. El uso de `ggplot2`, `plotly` y `flexdashboard` facilita construir un informe reproducible, visualmente claro y orientado a la acción directiva.