Skip to main content

Report on demand-response service

Demand-response performance is measured against promises made to individual riders: a pickup window, a trip that was booked, a ride that does not take absurdly long. Agencies track denials, missed trips, untimely pickups and trip length because paratransit service criteria turn on them, and because they are what riders complain about.

All of those numbers already exist inside your scheduling and dispatch system. What this page covers is getting them onto one board, computed the same way each month.

What has to be true

There is no demand-response connector here. The source is your dispatch system, reached through a data source pointed at your scheduling vendor's database, or at a replica of it, or at an export you land in your own warehouse.

Getting read access is usually most of the work. Ask for a read-only replica rather than credentials on production, and expect the vendor to have a supported way to provide one. Where they do not, a nightly export landed in a warehouse table is a workable second best. Put historical capture on that warehouse source and the monthly snapshots accumulate in-product, so the trend survives even if the vendor only ever hands you a current-state file.

You also need the definitions written down once, since they are yours rather than the product's. The pickup window is whatever your standards say it is. A denial is a trip request the system could not accommodate, and vendors record that in more than one way. A no-show is attributed to the rider, and a missed trip is attributed to the agency. Those last two get conflated constantly, which attributes an agency failure to the rider.

Before you start

  • Read access to trips, bookings and cancellations, with timestamps.
  • Your service standards' pickup window and trip-length comparison rule.
  • The same mode and type-of-service coding used in the monthly ridership pack, so the two boards agree.

The steps

1. Get to one trip-level table

Whatever the vendor's schema, reduce it to one row per booked trip with these columns. Do it in a single query that everything else reads:

ColumnHolds
trip_id, service_dateIdentity
requested_pickup, promised_pickup, actual_pickupThe three times that matter
actual_dropoffFor trip length
statusCompleted, cancelled, no-show, missed, denied, in the vendor's own vocabulary
distance_milesFor the comparison in step 3

Those three pickup times are easy to mix up. Performance is actual against promised, because the promise is what the agency made. Actual against requested measures something else: how often the requested time was available.

2. Compute the four measures

WITH 0 AS window_early, 1800 AS window_late -- seconds, from your standards
SELECT
date_trunc('month', service_date) AS month,
count(*) FILTER (WHERE status = 'completed') AS completed,
count(*) FILTER (WHERE status = 'denied') AS denials,
count(*) FILTER (WHERE status = 'missed') AS missed_trips,
count(*) FILTER (WHERE status = 'no_show') AS no_shows,
count(*) FILTER (
WHERE status = 'completed'
AND extract(epoch FROM actual_pickup - promised_pickup)
BETWEEN window_early AND window_late
) AS on_time
FROM dr_trips
WHERE service_date >= date_trunc('month', now()) - INTERVAL '13 months'
GROUP BY month
ORDER BY month DESC

Compute the ratios in the visualization or in a results query on top. Keep the counts and the percentages in the same table, so a reader can see what each percentage was calculated over.

3. Add the trip-length comparison

The comparison that matters to a rider is how long the same journey would take on fixed route, so you need a fixed-route travel time from somewhere.

Your own schedule is already queryable. The static GTFS archive holds stop_times, which carries an arrival and departure time per stop per trip, and trips, which links each trip to its route. Between an origin stop and a destination stop on the same trip, the difference of those two times is the scheduled ride. A results query joining the two tables turns that into a lookup you can check an origin-destination pair against. What you get is the schedule rather than what actually ran, which is the right comparison here, since the rider's alternative was the timetable.

That leaves out any trip a rider would have had to make with a transfer, or with a walk at either end. Nothing above computes a path through the network, so an O-D pair with no single trip serving it has no scheduled time here. Closing that gap takes a trip planner or a routing service, which stays an outside dependency.

If you have none of this, report the distribution of actual ride times by distance band instead. That is useful operationally. Label it as a ride-time distribution rather than as a fixed-route comparison.

4. Build the board

  • Counters for the month: completed trips, denials, missed trips, on-time percentage.
  • A line chart by month over thirteen months, so this month sits against the same month last year.
  • A histogram of pickup deviation in minutes. This shows whether late pickups are two minutes late or forty, which the on-time percentage on its own does not.
  • A table of the longest rides in the period, for operations.

5. Make the denominators agree

The demand-response UPT in your monthly pack and the completed trips on this board should either be the same number or differ for a reason you can state. They often do differ, legitimately: a trip with two passengers is one trip and two unlinked passenger trips.

Put both numbers on the board with their definitions in the titles.

How you know it worked

Take one week and walk it with the dispatch supervisor, trip by trip on the exceptions. Denials and missed trips are where vendor vocabulary and agency vocabulary diverge, so use the walk-through to agree on what each status means before the board goes out.

What this does not do

It does not schedule, dispatch, or contact riders, and it does not decide eligibility. It reads what the dispatch system recorded and reports it. If a cancellation was keyed as a no-show, the board reports a no-show, which is what step 5 and the walk-through above are there to catch.