Introducción

Este ejercicio consiste en mostrar espacialmente, los patrones de movilidad en la Ciudad de Cali, a partir la base de datos suministrada denominada “EncuestaOrigenDestino”, con el popósito de identificar cuáles son las comunas de mayor cantidad de viajes de origen y destino, y los modos de transporte más utilizados (bicicleta, moto y automóvil).

Preparación Entorno de trabajo

A continuación se garantizaron los paquetes y librerías necesarias para el desarrollo del ejericicio, se organizaron las entradas y salidas de la información y se realizó con código de limpieza de los resultados y configuración global del documento.

# Paquetes necesarios
req <- c("sf","dplyr","readxl","janitor","stringr","tmap","lwgeom","units","rlang")
inst <- req[!req %in% installed.packages()[,1]]
if(length(inst)) install.packages(inst)
invisible(lapply(req, require, character.only = TRUE))
# Librerias necesarias
library(dplyr)
library(ggplot2)
library(scales)
library(tmap)
tmap_mode("plot")
# Rutas del caso 

base_dir <- "C:/DOCUMENTOS/CIENCIA DE DATOS/ANALISIS_ESPACIAL/Casos"
cali_dir <- file.path(base_dir, "cali")
path_eod <- file.path(base_dir, "EncuestaOrigenDestino.xlsx")
path_out <- file.path(base_dir, "output")
dir.create(path_out, showWarnings = FALSE, recursive = TRUE)

Funciones Auxiliares

Se optó por crear una sección de funciones auxiliareas, ya que permiten ser usadas sin tener que repetir el código varias veces, y se deja al inicio del documento para garantizar que se encuentren creadas al momento de su utilización.

Función Contar Viajes

A Continuación se define una una función personalizada llamada contar_viajes(), que se usa después para calcular cuántos viajes se originan o terminan en cada comuna.

Esta función permite contar el número de viajes por comuna, ya sea de origen o de destino, y opcionalmente filtrar por tipo de vehículo (Bicicleta, Moto o Automóvil). En otras palabras: Recibe los datos y la columna de comuna,Filtra por tipo de vehículo si se requiere Y cuenta viajes válidos por comuna.

# FUNCIONES AUXILIARES

# Crea función contar_viajes
contar_viajes <- function(df, var_comuna, tipo = NULL){
  var <- rlang::ensym(var_comuna)  # tidy-eval del nombre de columna
  tmp <- df
  if (!is.null(tipo)) {
    tmp <- dplyr::filter(tmp, .data$tipo_simple == tipo)
  }
  tmp %>% 
    dplyr::filter(!is.na(!!var)) %>% 
    dplyr::count(!!var, name = "n_viajes") %>% 
    dplyr::rename(comuna = !!var) %>% 
    dplyr::mutate(comuna = as.integer(comuna))
}

Función make_map

Este bloque define la función make_map para producir todos los mapas con el mismo estilo y exporta los mapas en formato.png en la carpeta del proyecto 2000 × 1500 px, lo cual es aproximadamente 200 dpi. que es una buena resolución .

# ---- Creación de la función make_map --------------------------


make_map <- function(shape, variable, titulo, archivo,
                     palette = "YlOrRd") {

  # Validaciones básicas
  stopifnot(variable %in% names(shape))
  shp <- shape[!is.na(shape[[variable]]), ]
  if (nrow(shp) == 0) stop("El shape quedó vacío para '", variable, "'")

  # Quita el texto 'Comuna' del nombre para etiquetas más limpias
  shp$nombre_sin <- gsub("Comuna\\s*", "", shp$nombre)

  # Construcción del mapa
  m <- tm_shape(shp) +
    tm_polygons(
      col = variable,
  palette = c("#1a9850", "#fee08b", "#d73027"), # verde–amarillo–rojo
    style = "quantile",
  border.col = "grey40",
  border.alpha = 0.6,
  border.lwd = 0.8,
  title = "# de viajes"
) +
    tm_borders("grey30", lwd = 0.7) +
    tm_text("nombre_sin",
            size = 0.6, col = "black", fontface = "bold",
            bg.color = "white", bg.alpha = 0.5,
            just = "center", auto.placement = TRUE) +
    tm_scale_bar(
      breaks = c(0, 2, 4),
      text.size = 0.6,
      color.dark = "grey20",
      position = c("left", "bottom")
    ) +
    tm_compass(
      type = "8star", size = 2,
      position = c("right", "top"),
      color.light = "white", color.dark = "grey30"
    ) +
    tm_credits("Fuente: Encuesta Origen–Destino Cali 2025",
               position = c("left", "bottom"),
               size = 0.6, col = "grey35") +
    tm_layout(
      main.title = titulo,
      main.title.size = 1.3,
      main.title.position = c("center", "top"),
      frame = FALSE,
      legend.outside = TRUE,
      legend.outside.position = "right",
      legend.title.size = 1.0,
      legend.text.size = 0.8,
      inner.margins = c(0.02, 0.02, 0.02, 0.02),
      bg.color = "white"
    )

  # Guardar imagen con buena resolución
  tmap_save(m, filename = archivo,
            width = 2000, height = 1500, units = "px", dpi = 200)

  # Devuelve el objeto tmap
  m
}

Función join_count

Esta función cumple un papel fundamental en el flujo de este análisis. sirve para unir la tabla de conteos de viajes (por comuna) con la cartografía de las comunas (shapefile).

#----Función para unir el conteo con el shapefile----
join_cont <- function(cont){
  dplyr::left_join(comunas, cont, by = "comuna") |>
    dplyr::mutate(n_viajes = tidyr::replace_na(n_viajes, 0L))
}

Carga de archivo shapefile de comunas

En esta sección se carga el shapefile de comunas, se verifica la correcta geometría del archivo, la correcta nomenclatura del nombre de los atributos y el sistema de referencia espacial.

comunas <- sf::st_read(file.path(cali_dir, "comunas.shp"), quiet = TRUE) %>%
sf::st_make_valid() %>% janitor::clean_names() #Corregir geometrías - evitar errores topológicos
stopifnot("comuna" %in% names(comunas)) #Verificar existencia del campo comuna
comunas <- comunas %>% mutate(comuna = as.integer(comuna))#Convertir códigos de comuna a número
crs_work <- st_crs(comunas)#Extraer el sistema de referencia espacial

Preparación de la Base de Datos Encuesta Origen Destino

En esta sección se prepara la base de datos suministrada “EncuestaOrigenDestino” para luego poder contar los viajes y mapear. Identificamos las columnas importantes de la base de datos, filtramos solamente por los viajes de la ciudada de Cali y verificamos la cofificación por tipo de vehículo : tipo_codigo = 1 ~ “Bicicleta”,tipo_codigo = 2 ~ “Moto”,tipo_codigo =3 ~ “Automovil”.

Con el fin de obtener una visión más completa de los desplazamientos urbanos, se incorporó la base denominada eod_cali_total, la cual incluye todos los modos de transporte reportados en la encuesta, sin aplicar filtros por tipo de vehículo, para la elaboración de los mapas generales. Esto permite que los mapas “eod_cali_total – Origen (general)” y “eod_cali_total – Destino (general)” reflejen la totalidad de los viajes observados en la encuesta, abarcando tanto medios motorizados como no motorizados, así como viajes a pie, en transporte público, y otros modos registrados.

# Lectura de la hoja1 donde se encuentra la información".
eod_raw <- readxl::read_excel(path_eod, sheet = 1)
eod <- eod_raw %>% janitor::clean_names()


# Identifica automáticamente las columnas importantes del archivo Excel —como municipio, comuna de origen, comuna de destino y tipo de vehículo— aunque los nombres no sean exactamente iguales 

nms <- names(eod)
# municipio
cand_mpio <- nms[grepl("^muni|municipio", nms, ignore.case = TRUE)]
a_mpio <- cand_mpio[1]

# comuna origen/destino
cand_ori <- nms[grepl("comuna.*origen|origen.*comuna|comuna_origen", nms, ignore.case = TRUE)]
cand_des <- nms[grepl("comuna.*destino|destino.*comuna|comuna_destino", nms, ignore.case = TRUE)]
a_ori <- cand_ori[1]
a_des <- cand_des[1]

# tipo de vehículo: nos quedamos con la columna de códigos 1,2,3 en la hoja 1
cand_veh <- nms[grepl("veh", nms, ignore.case = TRUE) & (grepl("tipo", nms, ignore.case = TRUE) | grepl("vehiculo", nms, ignore.case = TRUE))]
a_veh <- if (length(cand_veh)) cand_veh[1] else NA_character_


# Verificación mínima: Si falta alguna columna crítica, el proceso se detiene con un mensaje claro.
needed <- c(a_mpio, a_ori, a_des)
if(any(is.na(needed))) stop("Faltan columnas requeridas (municipio/origen/destino).")


# Filtrar solo ciudad de Cali y preparar variables
eod_cali <- eod %>%
filter(.data[[a_mpio]] %in% c("CALI","Cali","cali")) %>%
mutate(
comuna_origen = as.integer(suppressWarnings(as.numeric(.data[[a_ori]]))),
comuna_destino = as.integer(suppressWarnings(as.numeric(.data[[a_des]])))
) %>%
filter(!is.na(comuna_origen), !is.na(comuna_destino))


# Mapear tipo de vehículo exclusivamente por CÓDIGOS 1,2,3 de la hoja 1.

if (!is.na(a_veh)) {
eod_cali <- eod_cali %>%
mutate(tipo_codigo = suppressWarnings(as.integer(.data[[a_veh]])),
tipo_simple = dplyr::case_when(
tipo_codigo == 1 ~ "Bicicleta",
tipo_codigo == 2 ~ "Moto",
tipo_codigo == 3 ~ "Automovil",
TRUE ~ NA_character_
))
} else {
eod_cali <- eod_cali %>% mutate(tipo_codigo = NA_integer_, tipo_simple = NA_character_) 
}
eod_cali_total <- eod %>% 
  dplyr::filter(.data[[a_mpio]] %in% c("CALI","Cali","cali")) %>% 
  dplyr::mutate(
    comuna_origen  = as.integer(suppressWarnings(as.numeric(.data[[a_ori]]))),
    comuna_destino = as.integer(suppressWarnings(as.numeric(.data[[a_des]])))
  ) %>%
  dplyr::filter(!is.na(comuna_origen), !is.na(comuna_destino))
#eod_cali sería la base que debería utilizar si deseo hacer los "mapas generales" de los tres medios de transporte seleccionados
#eod_cali_total es la base que utilizo para hacer los "mapas generales" incluyendo todos los medios de transporte que hay en la base de datos original

Contar el número de viajes por comuna

Este bloque de código prepara los datos agregados por comuna (origen y destino), y también por tipo de vehículo (Bicicleta, Moto, Automóvil), dejándolos listos para unir con la cartografía y mapear. ,

# usa la función contar_viajes() para contar n° de viajes por comuna. 
ori_gen <- contar_viajes(eod_cali_total, comuna_origen)
des_gen <- contar_viajes(eod_cali_total, comuna_destino)

# Si se requiere el mapa general solo para los medios de transporte 1–2–3 (bici/moto/auto): se reemplaza por estas dos líneas
#ori_gen <- contar_viajes(eod_cali, comuna_origen)
#des_gen <- contar_viajes(eod_cali, comuna_destino)

#La función devuelve dos columnas estándar: comuna y n_viajes.

Seguido de esto, se continúa con toda la preparación de datos necesaria para dibujar los 8 mapas. Toma los conteos por comuna de origen (ori_gen) y destino (des_gen), los une con el shapefile de comunas (join_cont hace un left_join con comunas) y rellena con 0 donde no hubo viajes y genera los sf listos para mapear: com_ori_gen y com_des_gen.

También lista los tipos de vehículos que se van a analizar y realiza un conteo por comuna y por tipo de vehículo para origen y destino, generando como resultado dos tablas de conteo “largas”: ori_tipo y des_tipo.luego se Crean dos listas: una para origen y otra para destino, con una capa por cada vehículo y une con el shapefile con ayuda de la función “join_cont” y rellena ceros donde falten viajes.

#Toma los conteos por comuna de origen (ori_gen) y destino (des_gen).

com_ori_gen <- join_cont(ori_gen) # comunas + viajes de origen
com_des_gen <- join_cont(des_gen) # comunas + viajes de destino

# Las dos capas com_ori_gen y com_des_gen son las que posteriormente se utilizan para la construcción de los mapas generales.

#Definir los tres tipos de vehículos que se van a tener en cuenta en el análisis
  
 #  calculo, para cada tipo de vehículo, los conteos por comuna de origen y destino 
  
veh <- c("Bicicleta","Moto","Automovil")
ori_tipo <- lapply(veh, function(v) contar_viajes(eod_cali, comuna_origen, v) %>% mutate(tipo=v)) %>% dplyr::bind_rows()
des_tipo <- lapply(veh, function(v) contar_viajes(eod_cali, comuna_destino, v) %>% mutate(tipo=v)) %>% dplyr::bind_rows()
#----Armar listas por tipo (para mapear fácilmente)----

#Crea dos listas con tres elementos (uno por tipo). Cada elemento es una capa sf (cartografía + n_viajes), ya unida y con ceros donde no hubo viajes, para que si un tipo no aparece en una comuna, queda con 0.

com_ori_tipo <- setNames(vector("list", length(veh)), veh)
com_des_tipo <- setNames(vector("list", length(veh)), veh)
for (v in veh) {
  com_ori_tipo[[v]] <- join_cont(ori_tipo %>% dplyr::filter(tipo==v) %>% dplyr::select(comuna, n_viajes))
  com_des_tipo[[v]] <- join_cont(des_tipo %>% dplyr::filter(tipo==v) %>% dplyr::select(comuna, n_viajes))
}

Elaboración de Mapas

Con el paso anterior, se dejaron los datos listos para 6 mapas por tipo (3 de origen + 3 de destino) y 2 mapas generales (origen/destino) para pasarlos a la función make_map() y exportarlos y mostrarlos en grillas (tmap_arrange).

En este bloque se automatiza la creación y exportación de los mapas temáticos de la Encuesta Origen–Destino (EOD) para la ciudad de Cali.

El script genera un total de ocho mapas que representan el número de viajes por comuna, diferenciados según el tipo de análisis:

Mapas generales (2): muestran la distribución total de los viajes de origen y destino para toda la ciudad, utilizando la variable n_viajes.

Mapas específicos por tipo de vehículo (6): se generan de forma independiente para bicicleta, moto y automóvil, tanto para el origen como para el destino de los desplazamientos.

Cada mapa es creado mediante la función make_map(), la cual emplea la librería tmap para representar la intensidad de los viajes mediante una escala de colores (paleta tipo semáforo donde Rojo muestra mayor cantidad de viajes y verde menor cantidad de viajes).

# ----Crear y guardar los 8 mapas---------

out_dir <- file.path(getwd(), "output")
if (!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE)

map_ori_gen <- make_map(com_ori_gen, "n_viajes", "EOD Cali – Origen (general)",
                        file.path(out_dir, "01_origen_general.png"))
map_des_gen <- make_map(com_des_gen, "n_viajes", "EOD Cali – Destino (general)",
                        file.path(out_dir, "02_destino_general.png"))

map_ori_bici <- make_map(com_ori_tipo[["Bicicleta"]], "n_viajes", "Origen – Bicicleta",
                         file.path(out_dir, "03_origen_bicicleta.png"))
map_ori_moto <- make_map(com_ori_tipo[["Moto"]], "n_viajes", "Origen – Moto",
                         file.path(out_dir, "04_origen_moto.png"))
map_ori_auto <- make_map(com_ori_tipo[["Automovil"]], "n_viajes", "Origen – Automóvil",
                         file.path(out_dir, "05_origen_automovil.png"))

map_des_bici <- make_map(com_des_tipo[["Bicicleta"]], "n_viajes", "Destino – Bicicleta",
                         file.path(out_dir, "06_destino_bicicleta.png"))
map_des_moto <- make_map(com_des_tipo[["Moto"]], "n_viajes", "Destino – Moto",
                         file.path(out_dir, "07_destino_moto.png"))
map_des_auto <- make_map(com_des_tipo[["Automovil"]], "n_viajes", "Destino – Automóvil",
                         file.path(out_dir, "08_destino_automovil.png"))

Además, el código incluye una verificación que crea automáticamente la carpeta output en el directorio de trabajo si esta no existe, y guarda los resultados en formato PNG con nombres consecutivos (01_origen_general.png, 02_destino_general.png, etc.).

El bloque a continuación, no genera mapas nuevos, sino que muestra en el documento los mapas ya creados en el paso anterior, ordenados en grillas para facilitar su comparación visual.

# Mostrar los 8 mapas ordenados en el HTML usando grillas

tmap_arrange(map_ori_gen, map_des_gen, ncol = 2)

tmap_arrange(map_ori_bici, map_ori_moto, map_ori_auto, ncol = 3)

tmap_arrange(map_des_bici, map_des_moto, map_des_auto, ncol = 3)

Conclusiones

Los mapas temáticos elaborados a partir de la Encuesta Origen–Destino permiten observar de manera espacial la distribución de los flujos de movilidad en la ciudad de Cali.

Mapas generales (todos los modos de transporte):

*Las comunas en rojo intenso (por ejemplo, 19,2,17, 18 y 3) concentran las mayores cantidades de viajes de origen, lo que indica que son zonas altamente emisoras de movilidad.

*Las comunas del norte y nororiente (como la 5, 7, 6) están en verde, lo que significa menor generación de viajes.

  • En cuanto a los destinos las comunas 19,2,17 y 3 al igual que como comunas de origen, también son comunas con alta concentración de destinos. Adicionalmente la columna 22 que es relativamente baja en cantidad de viajes de origen es alta en cantidad de viajes de destino, y la 18 que es alta en realizar viajes desde el origen es baja en viajes de destino.

*Observación Procedimental:Es importante tener claridad si los mapas que se consideran generales se realizan como generales para los tres medios de transporte seleccionados, o generales para todos lo medios de transporte. A lo largo de documento, en la explicación del texto y el código se muestran los cambios que deberían realizarse para una opción u otra. A efectos de este informe se tomaron como Mapas Generales aquellos que incluyen todos los medios de transporte de transporte reportados en la encuesta, sin aplicar filtros.

Modos de Transporte Bicicleta, moto y automovil:

  • En cuanto a viajes de origen en todos los medios de transporte las comunas 2,19,17 son las que presentan mayor cantidad de viajes y la 4, la 9 y la 12 las que menos de cantidad de viajes tienen.

  • En Bicicleta la comuna 22 no tiene tan alto desplazamiento de origen como si lo tiene en moto o en automóvil.

  • la comuna 22 tiene baja cantidad de viajes de origen en todos los medios de transporte.

  • En cuanto a los viajes de destino las comunas del nororiente tienen menor cantidad de viajes de destino, y las que reciben mayor de cantidad de viajes son las del centro y noroccidente en todos los medios de transporte.

  • Las comunas del oriente de Cali son las que menos cantidad de viajes en moto reciben.

A continuación se presentan algunas estadísticas que refuerzan lo observado gráficamente en los mapas:

# ---- Top 3 comunas de origen y destino por tipo de vehículo ----

#  Filtro general para los tres tipos de vehículo
vehiculos_validos <- c("Bicicleta", "Moto", "Automóvil")

# 🔹 Top 3 comunas de ORIGEN por cada tipo de vehículo
top_origen <- eod_cali %>%
  filter(!is.na(tipo_simple) & tipo_simple %in% vehiculos_validos) %>%
  group_by(tipo_simple, comuna_origen) %>%
  summarise(n_viajes = n(), .groups = "drop") %>%
  arrange(tipo_simple, desc(n_viajes)) %>%
  group_by(tipo_simple) %>%
  slice_head(n = 3) %>%
  ungroup() %>%
  left_join(
    comunas %>% st_drop_geometry() %>% select(comuna, nombre),
    by = c("comuna_origen" = "comuna")
  ) %>%
  transmute(
    `Tipo de vehículo` = tipo_simple,
    `Comuna (origen)` = nombre,
    `# de viajes` = n_viajes
  )

# 🔹 Top 3 comunas de DESTINO por cada tipo de vehículo
top_destino <- eod_cali %>%
  filter(!is.na(tipo_simple) & tipo_simple %in% vehiculos_validos) %>%
  group_by(tipo_simple, comuna_destino) %>%
  summarise(n_viajes = n(), .groups = "drop") %>%
  arrange(tipo_simple, desc(n_viajes)) %>%
  group_by(tipo_simple) %>%
  slice_head(n = 3) %>%
  ungroup() %>%
  left_join(
    comunas %>% st_drop_geometry() %>% select(comuna, nombre),
    by = c("comuna_destino" = "comuna")
  ) %>%
  transmute(
    `Tipo de vehículo` = tipo_simple,
    `Comuna (destino)` = nombre,
    `# de viajes` = n_viajes
  )
# ---- Mostrar tablas con formato ----

library(kableExtra)

top_origen %>%
  knitr::kable(caption = "Top 3 comunas de origen por tipo de vehículo") %>%
  kableExtra::kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover", "condensed"),
    position = "center"
  )
Top 3 comunas de origen por tipo de vehículo
Tipo de vehículo Comuna (origen) # de viajes
Bicicleta Comuna 19 58
Bicicleta Comuna 2 53
Bicicleta Comuna 17 50
Moto Comuna 19 46
Moto Comuna 2 43
Moto Comuna 17 41
top_destino %>%
  knitr::kable(caption = "Top 3 comunas de destino por tipo de vehículo") %>%
  kableExtra::kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover", "condensed"),
    position = "center"
  )
Top 3 comunas de destino por tipo de vehículo
Tipo de vehículo Comuna (destino) # de viajes
Bicicleta Comuna 2 101
Bicicleta Comuna 19 84
Bicicleta Comuna 3 65
Moto Comuna 2 76
Moto Comuna 3 60
Moto Comuna 19 46
# Helper para poner nombres de comuna
nom_comuna <- comunas %>% st_drop_geometry() %>% select(comuna, nombre)

# --- Origen TOP 10 ---
top_origen <- eod_cali %>%
  count(comuna_origen, name = "n_viajes") %>%
  mutate(
    pct = round(n_viajes / sum(n_viajes), 3) # redondeo para evitar muchos decimales
  ) %>%
  arrange(desc(n_viajes)) %>%
  left_join(nom_comuna, by = c("comuna_origen" = "comuna"))

# --- Destino TOP 10 ---
top_destino <- eod_cali %>%
  count(comuna_destino, name = "n_viajes") %>%
  mutate(
    pct = round(n_viajes / sum(n_viajes), 3)
  ) %>%
  arrange(desc(n_viajes)) %>%
  left_join(nom_comuna, by = c("comuna_destino" = "comuna"))

# --- Mostrar tablas resumidas (Top 5) ---
library(kableExtra)

top_origen %>%
  slice_head(n = 5) %>%
  transmute(
    Comuna = nombre,
    `# viajes` = n_viajes,
    `%` = percent(pct)
  ) %>%
  knitr::kable(caption = "Top 5 comunas emisoras (origen)") %>%
  kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
Top 5 comunas emisoras (origen)
Comuna # viajes %
Comuna 2 2319 10.0%
Comuna 19 2318 10.0%
Comuna 17 1859 8.1%
Comuna 3 1650 7.1%
Comuna 18 1242 5.4%
top_destino %>%
  slice_head(n = 5) %>%
  transmute(
    Comuna = nombre,
    `# viajes` = n_viajes,
    `%` = percent(pct)
  ) %>%
  knitr::kable(caption = "Top 5 comunas receptoras (destino)") %>%
  kableExtra::kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover"))
Top 5 comunas receptoras (destino)
Comuna # viajes %
Comuna 2 3836 16.6%
Comuna 3 2931 12.7%
Comuna 19 2466 10.7%
Comuna 17 1631 7.1%
Comuna 22 1571 6.8%
# --- Gráficos de barras (Top 10) ---
p_origen <- top_origen %>%
  slice_head(n = 10) %>%
  ggplot(aes(x = reorder(nombre, n_viajes), y = n_viajes)) +
  geom_col(fill = "#2E86C1") +  # color azul institucional
  geom_text(aes(label = scales::percent(pct)),
            hjust = -0.1, size = 3.2) +  # agrega etiqueta con % al final de la barra
  coord_flip() +
  labs(
    x = NULL,
    y = "# viajes",
    title = "Top 10 Origen por comuna"
  ) +
  theme_bw() +
  theme(
    plot.title = element_text(face = "bold", size = 12),
    axis.text.y = element_text(size = 10)
  )

p_destino <- top_destino %>%
  slice_head(n = 10) %>%
  ggplot(aes(x = reorder(nombre, n_viajes), y = n_viajes)) +
  geom_col(fill = "#28B463") +  # color verde institucional
  geom_text(aes(label = scales::percent(pct)),
            hjust = -0.1, size = 3.2) +
  coord_flip() +
  labs(
    x = NULL,
    y = "# viajes",
    title = "Top 10 Destino por comuna"
  ) +
  theme_bw() +
  theme(
    plot.title = element_text(face = "bold", size = 12),
    axis.text.y = element_text(size = 10)
  )

# Mostrar ambos gráficos
p_origen; p_destino