Thanks for the awesome and helpful package! I get a lot of value out of frequencies_to_stop_times, but I've noticed that it's quite slow when working at scale (i.e., it takes several hours on my reasonably powerful desktop to go from 37k stop times and 1k trips to 4.4mil stop times and 140k trips for recent Mexico City freq-based GTFS feeds). This is almost assuredly due to the use of rbind here to append row by row onto the eventually quite huge stop_times data.table which is used to store and eventually output results:
stop_times <- rbind (stop_times, stop_times_trip_i)
I propose three alternatives, which I've tried to implement locally on my machine but I've not worked with rcpp before and so I couldn't make work. I can't quite tell if stop_times_trip_i is always 1 row long or not. If so:
- Preallocate
stop_times with some upper bound number of rows (perhaps user set), and then simply replace the above with:
stop_times[i, ] <- stop_times_trip_i
Else (if it's number of rows vary), here are two other ideas:
- Preallocate
stop_times with some upper bound number of rows (perhaps user set), and replace a group of rows, as above
- Accumulate results in smaller data.tables and then
rbind, there's an opportunity for a couple of levels of recursion. Specifically, the for trips block could be (sorry for the stupid names):
for (trip in trips) {
# n is to be added to trip_id
n <- 1
# order frequencies table by trip_id and start_time
frequencies_trip <-
gtfs_cp$frequencies [order (trip_id, start_time)] [trip_id == trip]
# in case end_time of the previous period and start_time of the next are
# equal:
frequencies_trip [end_time ==
data.table::shift (start_time, 1,
type = "lead"),
end_time := end_time - 1]
stop_times_trip <-
gtfs_cp$stop_times [order (stop_sequence)] [trip_id == trip]
# in case of the first arrival time > 0, then reset them:
if (stop_times_trip [1] [["arrival_time"]] > 0) {
stop_times_trip [, c ("arrival_time", "departure_time") :=
list(
arrival_time - stop_times_trip [1] [["arrival_time"]],
departure_time - stop_times_trip [1] [["arrival_time"]]
)]
}
start_t <- min (frequencies_trip$start_time)
headway <- headway_old <- frequencies_trip [1] [["headway_secs"]]
## NEW LINE ##
stop_times_small <- stop_times
for (i in row (frequencies_trip)) {
end_t <- frequencies_trip [i] [["end_time"]]
headway <- frequencies_trip [i] [["headway_secs"]]
# in order to ensure a 'smooth' transition between frequency periods
ifelse (
headway_old - start_t +
frequencies_trip [i] [["start_time"]] < headway,
start_t <- start_t - headway_old + headway,
start_t <- frequencies_trip [i] [["start_time"]]
)
## NEW LINE ##
stop_times_small_small <- stop_times
# multiply stop_times for all trips based on a given frequency
while (start_t < end_t) {
stop_times_trip_i <-
data.table::copy (stop_times_trip) [, c ("arrival_time",
"departure_time",
"trip_id_f") :=
list ((arrival_time + start_t),
(departure_time + start_t),
paste (trip_id, n, sep = "_"))]
n <- n + 1
# EDITED BELOW #
stop_times_small_small <- rbind (stop_times_small_small, stop_times_trip_i)
start_t <- start_t + headway
}
# EDITED BELOW #
stop_times_small <- rbind (stop_times_small, stop_times_small_small)
headway_old <- headway
}
# EDITED BELOW #
stop_times <- rbind (stop_times, stop_times_small_small)
}
This last option, while not totally efficient, will probably substantially increase performance without needing to explicitly deal with preallocation. Again, thanks for the great package!
Thanks for the awesome and helpful package! I get a lot of value out of
frequencies_to_stop_times, but I've noticed that it's quite slow when working at scale (i.e., it takes several hours on my reasonably powerful desktop to go from 37k stop times and 1k trips to 4.4mil stop times and 140k trips for recent Mexico City freq-based GTFS feeds). This is almost assuredly due to the use ofrbindhere to append row by row onto the eventually quite hugestop_timesdata.table which is used to store and eventually output results:I propose three alternatives, which I've tried to implement locally on my machine but I've not worked with
rcppbefore and so I couldn't make work. I can't quite tell ifstop_times_trip_iis always 1 row long or not. If so:stop_timeswith some upper bound number of rows (perhaps user set), and then simply replace the above with:Else (if it's number of rows vary), here are two other ideas:
stop_timeswith some upper bound number of rows (perhaps user set), and replace a group of rows, as aboverbind, there's an opportunity for a couple of levels of recursion. Specifically, thefor tripsblock could be (sorry for the stupid names):This last option, while not totally efficient, will probably substantially increase performance without needing to explicitly deal with preallocation. Again, thanks for the great package!