Data & policies & politics

Este post presenta una simulación del funcionamiento del sistema de transporte público Puma Katari, en La Paz Bolivia. La simulación seleccionó una de las 7 rutas,  estimó los tiempos de salida y llegada de los buses en sus 21 paradas y  las distancias recorridas. Con ello se construyó un funcionamiento numérico de laboratorio de los buses para 1 mes. Los intervalos de partida se basaron en datos oficiales.

¿Para qué simular?

  • Para demostrar que si se generaran los datos, no en laboratorio sino de verdad, podría medirse la eficiencia del sistema de transporte en tiempo real.
  • Para mostrar que las aplicaciones de conectar datos y buses  son numerosas; vincular  google maps con el servicio, por ejemplo.
  • Para apoyar al sistema de transporte público de mi ciudad.

Antecedentes de Los Puma Katari

Para más información: La Paz Bus

Estructura de la simulación

La simulación se basó en 4 momentos y se realizó en R, cuyo código se libera aquí:

1. Elegir la ruta y la dirección

La Paz Bus, opera en 7 rutas. De estas se eligió la ruta Chasquipampa. De esta ruta se eligió la dirección que parte de la parada de nombre "Mercado Camacho" y termina en la parada de nombre "Calle 63" de la zona de Chasquipampa. La ruta completa es de 13 apróximadamente kilómetros  y tiene 21 paradas. El siguiente gráfico muestra la ruta, paradas y dirección seleccionada.

2. Recopilar la frecuencia de salidas

La frecuencia de salida de esta ruta está disponible en la página de La Paz Bus. Entre el 3 de abril de 2017 y el 1 de mayo del mismo año, periodo de la simulación, las frecuencias de salida derivaron en 167 recorridos a la ruta por día y 4676 recorridos por mes.

Esta fue la primera traducción a una función en R que se concatenó con otras para construir la base de datos. La función siguiente permitió automatizar la frecuencia de partidas.

intervalos_partida <- function(x) {
    if (hour(x) >= 7 & hour(x) < 11) incremento <- 10 * 60
    if (hour(x) >= 11 & hour(x) <= 13) incremento <- 5 * 60
    if (hour(x) >= 13 & hour(x) <= 17) incremento <- 10 * 60
    if (hour(x) >= 17 & hour(x) <= 23) incremento <- 5 * 60
    if (hour(x) >= 23) incremento <- 30 * 60
    if (hour(x) >= 0 & hour(x) <= 4) incremento <- 30 * 60
    if (hour(x) >= 4 & hour(x) < 6) incremento <- 10 * 60
    if (hour(x) ==  6) incremento <- 60 * 60
    
    incremento
 }

prueba....

intervalos_partida <- function(x) {
    if (hour(x) >= 7 & hour(x) < 11) incremento <- 10 * 60
    if (hour(x) >= 11 & hour(x) <= 13) incremento <- 5 * 60
    if (hour(x) >= 13 & hour(x) <= 17) incremento <- 10 * 60
    if (hour(x) >= 17 & hour(x) <= 23) incremento <- 5 * 60
    if (hour(x) >= 23) incremento <- 30 * 60
    if (hour(x) >= 0 & hour(x) <= 4) incremento <- 30 * 60
    if (hour(x) >= 4 & hour(x) < 6) incremento <- 10 * 60
    if (hour(x) ==  6) incremento <- 60 * 60
    
    incremento
 }

Aquí la prueba de funcionamiento de la función

(hora_actual <- as.POSIXct("2018-08-07 20:06:25"))
[1] "2018-08-07 20:06:25 GMT"
intervalos_partida(hora_atual) # prueba de la función
[1] 300 # 300 segundos

Cómo se ve en la fórmula desde las 17:00 a las 23:00 los buses parten cada 5 minutos (300 segundos)

3. Tomar los tiempos de recorrido de los buses en "condiciones normales"

Las condiciones normales de funcionamiento de los buses suceden por una combinación de horas que no son pico, mañanas entre 7 y  8 por ejemplo, y  días sin manifestaciones sociales que interrumpan el tráfico y los tiempos  de recorrido de los buses.

Estos tiempos en "condiciones normales" se tomaron utilizando los buses 5 veces en días diferentes para luego tener un promedio. Por tanto, estos tiempos no son simulados, son verdaderos bajo una selección arbitraria de horas y días que se suponen normales.

Los tiempos que se computaron fueron:

  • Entre parada y parada, es decir, tiempo en ruta.
  • Entre llegada a una parada y salida de la misma, es decir, tiempo para recoger pasajeros.

El siguiente gráfico muestra los tiempos recolectados

Estos tiempos se tradujeron a una segunda función, resumida para fines explicativos:

temp <- list()
tiempos.paradas <- function(x) {
  if (x == 1) {
    temp[[1]] <- 0
    temp[[2]] <- 0
  }  
  if (x == 2) {
    temp[[1]] <- hms("00:02:56") *# de la parada 1 a 2*
    temp[[2]] <- hms("00:00:24") *# tiempo en la parada 2 para recoger pasajeros*
  }
  if (x == 3) {
    temp[[1]] <- hms("00:02:29")
    temp[[2]] <- hms("00:00:19")
  }
 } # cierro el corchete aquí para el ejemplo

Prueba de la función de tiempos "normales"

tiempos.paradas(3)
[[1]]
[1] "2M 29S"
[[2]]
[1] "19S"

4. Realizar variaciones a los tiempos normales

Una primera variación consistió en introducir dos conflictos sociales en el mes, que duren dos días cada uno y  atrasen a los buses entre 45 y 120 minutos en ciertas horas y paradas, es decir, la alteración dependió del día hora y en qué parte de la ciudad/ruta se desarrollan los conflictos. Las variaciones se hicieron con una distribución uniforme. La selección de los días y horas para introducir a los conflictos simulados responden a la experiencia de vivir en "la ciudad de las marchas". La tercera función que simula los atrasos por protestas es la siguiente:

conflicto <- function(x, y) {
  marcha <- 0

  if((day(x) == 3|day(x) == 4|day(x) == 13|day(x) == 14) &
     hour(x) >= 11 & hour(x) <= 12 &
     y == 3) {
    marcha <- runif(1, 45*60, 120*60) *# atraso de 45 minutos a 2 horas*
  }

  marcha
}

Ahora la prueba de la función: a una hora de navidad.

fecha_hora <- as.POSIXct("2018-12-24 23:00:00")
parada <- 3
conflicto(fecha_hora, parada)
[1] 0 # Resultado: no hubieron conflictos en vísperas de navidad de este año

Ahora la prueba de la función cuando está programado el conflicto

fecha_hora <- as.POSIXct("2018-04-3 11:00:00")
parada <- 3
conflicto(fecha_hora, parada)
[1] 6319.477 # 6319 segundos o 105 minutos de atraso

La segunda variación de los tiempos normales se produce  por una combinación de horas pico y paradas concurridas. Las horas pico afectan a los tiempos entre paradas por la densidad del tráfico y también al tiempo de recojo de pasajeros en lugares donde la actividad comercial y privada es más intensa. Por ejemplo, el tiempo de recorrido será más lento entre las 7 y 8 de la mañana en tramos cercanos al centro de la ciudad, asi como el tiempo de recojo de personas (estudiantes).

Sobre los tiempos normales, se trabajaron alteraciones en horas, días y lugares que se consideran más congestionados. La función que simula este tipo de atrasos se resume a continuación

variacion_tiempos <-  function(x, y) {
  if(hour(x) >= 23 & hour(x) < 24 & y == 2 | 
     y == 3 | y == 4 | y == 5 | y == 6 |
     y == 7 | y == 8 | y == 9 | y == 10 | 
     y == 11 | y == 12 | y == 13 | y == 14 |
     y == 15 | y == 16 | y == 17 | y == 18 |
     y == 19 | y == 20 | y == 21) {
    temp1[[1]] <- runif(1, 0,  10) # tiempo entre paradas varía entre 0 y 10 segundos entre las 23 y 24 horas en las paradas de los valores "y"
    temp1[[2]] <- runif(1, 0,  5) # tiempo de parada varía entre 0 y 5 segundos entre las 23 y 24 horas en las paradas de los valores "y"
  }
  if(hour(x) >= 0 & hour(x) < 7 & y == 2 |
     y == 3 | y == 4 | y == 5 | y == 6 |
     y == 7 | y == 8 | y == 9 | y == 10 | 
     y == 11 | y == 12 | y == 13 | y == 14 |
     y == 15 | y == 16 | y == 17 | y == 18 |
     y == 19 | y == 20 | y == 21) {
    temp1[[1]] <- runif(1, -30,  5) # tiempo entre paradas se adelanta a lo programado en la madrugada
    temp1[[2]] <- runif(1, -7,  2) # # tiempo en las paradas se adelanta a lo programado en la madrugada
  } 

Ahora la prueba de la función a una hora normal.

fecha_hora <- as.POSIXct("2018-12-24 23:00:00")
parada <- 3
conflicto(fecha_hora, parada)
[1] 0 # Resultado: no hubieron conflictos en vísperas de navidad de este año

Ahora la prueba de la función en horas picos y paradas concurridas

fecha_hora <- as.POSIXct("2018-04-3 11:00:00")
parada <- 3
conflicto(fecha_hora, parada)
[1] 6319.477 # 6319 segundos o 105 minutos de atraso

El siguiente cuadro resume los tiempos "normales y las alteraciones" simuladas