City Bus Simulation

This problem was proposed by a friend of mine named Aditya. This is how the conversation went.

Aditya: Hey Fermibot, I have a question for you.

Fermibot: Go on.

Aditya: When I used to live in Hyderabad (a city in southern India), I was a frequent passenger of the city’s road transit system. More specifically, I used to travel towards Miyapur. The route numbers 10H and 121 are the ones that I am particularly interested in. They have almost the same route throughout except for the destination. The busses diverge on to different routes for the last segment of their journies. I want to know which one reaches its destination first assuming that both of them travel the same distance

Fermibot: Interesting. We can model this a few ways if we include some randomness. Since the busses would be travelling roughly the same distance, the difference in their travel times would be based on how long the bus halts at each of the stops. This time would be dependent on the number of people boarding or deboarding the bus. Why don’t you gather some data for each of these busses. If you can’t find the data, you can just come up with some. I will make the code general enough to accomodate for any future “real” data.

Aditya: This is what I have compiled. I could not find the details of the transit data but from my experience, I have compiled the following data. You see that for every stop, we have a pair of numbers separated by an “::”. This is simply the average number of people boarding and the average number of people deboarding.

Fermibot: Interesting problem indeed. This is simply a accumulative discrete event simulation. I will get back to you with some simulations. Bye.

Table given by Aditya ↓

Bus Route Number 10 H 121
Number of Stops 20 20
Stop#00 – Secunderabad 10::0 20::0
Stop#01 – Mall Center 5::5 5::5
Stop#02 – Paradise 5::1 0::0
Stop#03 – Begumpet 2::2 6::2
Stop#04 – Ameerpet 3::0 0::0
Stop#05 – Mythrivanam 8::6 6::8
Stop#06 – Yousufguda 2::1 1::2
Stop#07 – Srinagar Colony 1::0 1::0
Stop#08 – Indira Nagar 0::0 0::4
Stop#09 – Jubliee Checkpost 5::9 6::4
Stop#10 – BanjaraHills Rd No.12 2::2 3::1
Stop#11 – Jubliee Hills 32 0::1 1::0
Stop#12 – Jublieehills 36 0::1 0::0
Stop#13 – Cyber Towers 3::9 4::7
Stop#14 – Silparamam 1::0 1::2
Stop#15 – Hitech City 6::3 5::1
Stop#16 – Kondapur 2::4 3::1
Stop#17 – Kondapur X roads 1::6 0::4
Stop#18 – Hafizpet 0::3 1::2
Destination Allwyn X Roads Miyapur


Simulation:
Since we are counting entities here, I would model the boarding and the deboarding count as Poisson Random Variate.

  • The function called PoissonDistribution in wolfram mathematica takes values of λ greater than one and returns a random number when used with an enclosing function called RandomVariate
  • For this problem though we need to add an additional layer over the PoissonDistribution function to make sure that it does not crash when we pass in zero values. The zero values are also the rates because some of the stops at which the bus halts has no people boarding or in some other cases deboarding.
  • We will perform the simulation using the following steps
    1. Simulate and get random data
    2. Use the data for calculations and visualization

 

Passengers Plots:  Shown below are the plots for the number of passengers in the bus through its journey. See how the randomness in the number of passengers boarding and deboarding gives the scribbles. Note that I have smoothed the lines to for a better representations by using the option InterpolationOrder->2. Otherwise we would see straight and overlapping lines.

————————————

 

Times Spent: These are the distributions of the time spent at each stop of the bus.

  • The zero height rectangles mean that zero time was spent there.
  • The darker regions of the rectangle indicate higher density and lighter indicates lower density.
  • The first pair of rectangles having a zero time makes sense since both the busses wait until the passengers get in and before they start at the same time.
  • Green indicates times for boarding and red for deboarding

————————————

 

Mean Times Spent Waiting: This would be the mean time spent waiting (for passengers to board and deboard) by these busses over 1000 trips. Surprisingly enough, both the busses’ total wait time on average is very close

829.94 seconds for 10H & 833.32 seconds for 121


Code: Phew. It was really exhausting to code this even though I have split it in two sessions of 3 and 3 hours.

Note: If you are reading this code on a mobile, the code is horizontally scrollable

ClearAll[dataReformat, poissonDistributionZero, peopleInOut,
  peopleAccumulator, busPlot, ranger];

stopFormat[item_String] :=
 StringReplace[item, {"Stop#" -> "", " - " -> " ", " " -> " "}]
dataReformat[item_String] := ToExpression /@ StringSplit[item, "::"]

poissonDistributionZero[\[Lambda]_] :=
 If[\[Lambda] <= 0, 0, RandomVariate[PoissonDistribution[\[Lambda]]]]

ranger[list_List] :=
 MapThread[{#1, #2} &, {Range[0, Length@list - 1], list}]

peopleInOut[n_, inOut_List] :=
 Clip[n + inOut[[1]] - inOut[[2]], {0, \[Infinity]}]
peopleAccumulator[stopData_List] := Module[{people = 0},
  Table[people = peopleInOut[people, stopData[[r]]], {r, 1,
     Length@stopData}]~Join~{0}]

busPlot[data_, color_: Red] :=
 Module[{plotData = data}, ListLinePlot[peopleAccumulator /@ plotData,
   PlotStyle ->
    Table[{Thickness[0.0001], Opacity[0.2], color}, Length@plotData],
   AspectRatio -> 0.25,
   ImageSize -> 788]]

Module[{data =
   Import[StringReplace[NotebookFileName[], ".nb" -> ".xlsx"]][[1]],
  dataH, dataD, stops, bus10H, bus121,
  bus10HSim, bus121Sim,
  bus10HPassPlot, bus121PassPlot,
  bus10HSimPlotData, bus121SimPlotData,
  boarDeboard10H, boarDeboard121,
  bus10HTime, bus121Time
  },

 dataH = First[data];
 dataD = Transpose[Rest[Transpose[Drop[Rest@Rest@data, -1]]]];
 {bus10H, bus121} = Transpose[dataD];
 {bus10H, bus121} = {dataReformat /@ bus10H, dataReformat /@ bus121};
 stops = stopFormat /@ First[Transpose[Drop[Rest@Rest@data, -1]]];

 {bus10HSim, bus121Sim} =
  ParallelTable[{{0, 0}}~Join~(poissonDistributionZero /@ # & /@ #),
     1000] & /@ {bus10H, bus121};

 {bus10HTime, bus121Time} =
  10 N@Mean@Flatten[Max /@ # & /@ #] & /@ {bus10HSim, bus121Sim};

 {bus10HPassPlot, bus121PassPlot} =
  MapThread[
   Module[{plotData = #1},
     ListLinePlot[ranger /@ peopleAccumulator /@ plotData,
      PlotStyle ->
       Table[{Thickness[0.0001], Opacity[0.25], Lighter@#2},
        Length@plotData],
      ImageSize -> 788,
      PlotLabel -> "Rounte " <> #3, PlotRange -> {{0, 22}, {0, 70}},
      InterpolationOrder -> 2, Frame -> True]] &,
   {{bus10HSim, bus121Sim}, {Red, Blue}, {"10H", "121"}}];


 {boarDeboard10H, boarDeboard121} = MapThread[DistributionChart[
     MapThread[{#1, #2} &, {10 Transpose[
         First /@ # & /@ #1], -10 Transpose[Last /@ # & /@ #1]}],
     ChartStyle -> {Lighter@Green, Lighter@Red}, ImageSize -> 788,
     ChartElementFunction -> "Density",
     FrameLabel -> {"Stops", "Boarding/Deboarding time (sec)"},
     ChartLabels -> {(Rotate[#,
           90 \[Degree]] & /@ ((StringDrop[#, 3] & /@ stops)~
           Join~{#2})), {"", ""}},
     PlotLabel -> "Route " <> #3] &, {{bus10HSim,
     bus121Sim}, {"Allwyn X Roads", "Miyapur"}, {"10H", "121"}}];
 
 MapThread[
  Export[StringReplace[NotebookFileName[],
     ".nb" -> "_" <> ToString[#1] <> ".png"], #2,
    ImageSize -> 788, ImageResolution -> 500] &,
  {{"bus10HPassPlot", "bus121PassPlot", "boarDeboard10H",
    "boarDeboard121"},
   {bus10HPassPlot, bus121PassPlot, boarDeboard10H, boarDeboard121}}];

 Print[{bus10HTime, bus121Time}];

 ]

Code colored and formatted


End of the Post 😴


.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.