Operations analytics
Driver Supply and Demand Analysis
The whole brief is open to read. This lab's datasets come with Premium.
- Company
Uber- Job positions
- Data AnalystBusiness AnalystProduct Analyst
- Topics
- Marketplace operationsJoinsTime patternsBudget allocationSQL
The scenario
Uber: Measure the ride requests that go unmet for lack of a driver, and recommend where to spend the incentive budget.
Uber riders only get a ride if a driver is nearby when they open the app, and a request with no driver is a ride lost rather than delayed. Each city team has a weekly budget to pay drivers to be online at chosen places and times. You're an analyst supporting three city teams, who want that budget spent where it buys back the most rides.
Your task
Measure unmet demand by zone, day of the week and hour of the day across four weeks, estimate the fare it cost, and allocate a $60,000 weekly incentive budget.
Instructions
- 1Load the two hourly feeds and the zone and weather tables. Check the row counts against the number of zone-hours four weeks should contain before you join anything.
- 2Clean the data: handle repeated rows, and decide what to do about zone-hours one feed covers and the other does not. Explain your choice, because it changes the answer.
- 3Build a table at the level the city teams plan on, which is zone by day of the week by hour of the day. Save it as unmet_demand.csv with the columns zone_id, day_of_week, hour_of_day, requests, unmet_requests, unmet_rate and est_lost_fare_usd.
- 4Say which kinds of zone run short and when, and check whether the ranking changes when you order by share of requests unmet rather than by fare lost.
- 5Bring in the weather and measure what rain does to requests and to drivers online.
- 6Allocate the budget. A driver hour costs $9 and serves about 1.6 rides, and a zone-hour cannot give back more rides than went unmet there. Save incentive_plan.csv with the columns zone_id, day_of_week, hour_of_day, driver_hours_funded, incentive_cost_usd and est_rides_recovered.
- 7Write a short recommendation with the blocks you would fund first and what you expect back.
Datasets
All files come in one download.
ride_requests_hourly.csv
Rider demand per zone per hour, from September 1 to September 28, 2025.
12,060 rows · 9 columns · 568 KB
| Column | Type | Description |
|---|---|---|
| zone_id | text | The pickup zone. |
| hour_start | datetime | Start of the hour, in local city time. |
| requests | integer | Riders who asked for a ride in that zone that hour. |
| completed_rides | integer | Requests that ended in a completed ride. |
| no_driver_requests | integer | Requests where no driver was available at all. |
| rider_canceled | integer | Requests where a driver was found and the rider gave up before pickup. |
| avg_wait_seconds | integer | Average wait from request to pickup. Empty when no ride completed that hour. |
| avg_surge_multiplier | decimal | Average price multiplier applied that hour. |
| gross_fare_usd | decimal | Total fare from the completed rides. |
Preview the first 5 rowsHide preview
| zone_id | hour_start | requests | completed_rides | no_driver_requests | rider_canceled | avg_wait_seconds | avg_surge_multiplier | gross_fare_usd |
|---|---|---|---|---|---|---|---|---|
| SF01 | 2025-09-01 00:00:00 | 9 | 9 | 0 | 0 | 165 | 1 | 185.23 |
| SF01 | 2025-09-01 01:00:00 | 6 | 6 | 0 | 0 | 180 | 1 | 109.59 |
| SF01 | 2025-09-01 02:00:00 | 5 | 5 | 0 | 0 | 136 | 1 | 89.94 |
| SF01 | 2025-09-01 03:00:00 | 3 | 3 | 0 | 0 | 131 | 1 | 60.29 |
| SF01 | 2025-09-01 04:00:00 | 3 | 3 | 0 | 0 | 65 | 1 | 61.22 |
driver_supply_hourly.csv
Driver availability per zone per hour over the same four weeks, recorded by a separate system.
11,850 rows · 4 columns · 370 KB
| Column | Type | Description |
|---|---|---|
| zone_id | text | The zone the driver was available in. |
| hour_start | datetime | Start of the hour, in local city time. |
| drivers_online | integer | Drivers who were online in the zone at some point that hour. |
| driver_hours | decimal | Driver hours available in the zone that hour. |
Preview the first 5 rowsHide preview
| zone_id | hour_start | drivers_online | driver_hours |
|---|---|---|---|
| SF01 | 2025-09-01 00:00:00 | 7 | 6.7 |
| SF01 | 2025-09-01 01:00:00 | 5 | 4.7 |
| SF01 | 2025-09-01 02:00:00 | 4 | 4 |
| SF01 | 2025-09-01 03:00:00 | 2 | 2.3 |
| SF01 | 2025-09-01 04:00:00 | 3 | 3.3 |
zones.csv
The eighteen pickup zones across three cities.
18 rows · 4 columns · 1 KB
| Column | Type | Description |
|---|---|---|
| zone_id | text | Unique ID of the zone. |
| zone_name | text | Name the city team uses for the zone. |
| city | text | City the zone belongs to. |
| zone_type | text | downtown, nightlife, airport, residential or suburban. |
Preview the first 5 rowsHide preview
| zone_id | zone_name | city | zone_type |
|---|---|---|---|
| SF01 | Financial District | San Francisco | downtown |
| SF02 | Mission District | San Francisco | nightlife |
| SF03 | SoMa | San Francisco | nightlife |
| SF04 | SFO Terminals | San Francisco | airport |
| SF05 | Sunset | San Francisco | residential |
city_weather.csv
Hourly weather for each city over the same four weeks.
2,016 rows · 4 columns · 71 KB
| Column | Type | Description |
|---|---|---|
| city | text | The city. |
| hour_start | datetime | Start of the hour, in local city time. |
| precipitation_mm | decimal | Rain in the hour, in millimeters. Zero when it was dry. |
| temperature_c | decimal | Temperature in degrees Celsius. |
Preview the first 5 rowsHide preview
| city | hour_start | precipitation_mm | temperature_c |
|---|---|---|---|
| San Francisco | 2025-09-01 00:00:00 | 0 | 16.8 |
| San Francisco | 2025-09-01 01:00:00 | 0 | 15.5 |
| San Francisco | 2025-09-01 02:00:00 | 0 | 16.5 |
| San Francisco | 2025-09-01 03:00:00 | 0 | 13.5 |
| San Francisco | 2025-09-01 04:00:00 | 0 | 17.2 |
Hints
Keeping every demand row when you bring availability in leaves the gaps visible. Dropping them hides the hours you most need to look at, and reading them as no drivers invents a shortage.
merged = requests.merge(supply, on=["zone_id", "hour_start"], how="left") merged["driver_hours"].isna().sum()
The budget runs out long before the shortage does, so what matters is what each block gives back per dollar rather than which blocks are busiest.
Deliverable
A public GitHub repo with your analysis (a notebook, scripts or SQL), unmet_demand.csv and incentive_plan.csv, and a README with your data checks, the patterns you found and the reasoning behind the budget.
When you're done, post your repo in the Solutions tab to share it with other learners.
What grading checks
Use this checklist to review your own work before you post and share it.
- Submitted GitHub repo is public and reachable.
- Repo contains at least one notebook, script or SQL file.
- unmet_demand.csv has a row for every zone, day of the week and hour, with counts matching the expected values.
- incentive_plan.csv stays inside the $60,000 weekly budget and claims no more recovered rides than went unmet in each block.
- The zone-hours one feed covers and the other does not are found and handled, with the choice explained rather than read as no drivers.
- The write-up names the zone types and times that run short, and notes that the two ways of ranking disagree.
- Rain is measured against both requests and drivers online, and the allocation is justified by what each block gives back per dollar.