Data AnalyticsWorking and learning in Delhi NCR

A Delhi bus open-data project for your analytics portfolio

PK
Pankit Kumar
Sr. Data Scientist at Parexel (a Goldman Sachs–backed company) · 27 September 2026 · 5 min read
In this article (5 sections)

Related programme: the Data Analytics with Generative AI course (3–4 months; ₹90,000 (inclusive of GST) · EMI from ₹4,000/month).

The short answer

Delhi publishes its bus network as open data on the Open Transit Data platform (otd.delhi.gov.in), in the GTFS format used by transit agencies worldwide. This makes a strong local portfolio project: you join a few standard tables to answer a question anyone in Delhi understands, such as how often a bus actually serves a stop in the morning peak. Below you will find the file structure, a runnable Python example on synthetic data shaped like GTFS, and a write-up plan an interviewer can read in two minutes.

Why a transit project reads well in an interview

Most beginner portfolios contain the same few datasets. A project built on your own city's bus network shows three things an interviewer at a Noida or Gurugram analytics team cares about: you can read a data specification, you can join several tables correctly, and you can explain a finding to someone who has never seen the data. You also know the ground truth. If your analysis says a route through Laxmi Nagar has a bus every two minutes at 3 a.m., you will know something went wrong.

What is inside a GTFS feed

GTFS (General Transit Feed Specification) is a set of plain CSV files, usually zipped together. The ones you need first:

FileOne row isKey columns
agency.txtAn operatoragency_id, agency_name
routes.txtA routeroute_id, route_short_name
trips.txtOne journey on a routetrip_id, route_id, service_id
stop_times.txtOne trip arriving at one stoptrip_id, stop_id, arrival_time, stop_sequence
stops.txtA bus stopstop_id, stop_name, stop_lat, stop_lon
calendar.txtWhich days a service_id runsservice_id, monday … sunday

Two details catch almost everyone. First, arrival_time can exceed 24:00:00 for trips running past midnight, so do not parse it as a clock time. Second, stop_times.txt is by far the largest file, so filter it early.

As of September 2026, the Open Transit Data platform offers static and real-time data, and also carries DMRC static data. Its terms and conditions govern how you may use and share the data, so read them before you publish a notebook or a dashboard. The GTFS reference itself is maintained at gtfs.org.

Computing morning-peak frequency at each stop

The question: for each stop, how many bus departures are scheduled between 08:00 and 10:00 on a weekday, and what is the average gap between them? The code below uses a tiny synthetic feed written inline so it runs anywhere. Once it works, replace the strings with the real files.

python
import csv
import io
from collections import defaultdict

# Synthetic GTFS-shaped data for illustration only; not real Delhi routes or timings.
trips_txt = """trip_id,route_id,service_id
T1,R10,WK
T2,R10,WK
T3,R10,WK
T4,R22,WK
T5,R22,WK
T6,R22,SUN
"""
calendar_txt = """service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday
WK,1,1,1,1,1,1,0
SUN,0,0,0,0,0,0,1
"""
stops_txt = """stop_id,stop_name
S1,Stop A (synthetic)
S2,Stop B (synthetic)
S3,Stop C (synthetic)
"""
stop_times_txt = """trip_id,stop_id,arrival_time,stop_sequence
T1,S1,07:55:00,1
T1,S2,08:05:00,2
T2,S1,08:20:00,1
T2,S2,08:31:00,2
T3,S1,09:10:00,1
T3,S2,09:21:00,2
T4,S2,08:12:00,1
T4,S3,08:25:00,2
T5,S2,09:40:00,1
T5,S3,09:52:00,2
T6,S2,08:15:00,1
T6,S3,08:28:00,2
"""

def rows(text):
    return list(csv.DictReader(io.StringIO(text)))

def to_seconds(hms):
    # GTFS times can exceed 24:00:00, so parse as a duration, not a clock time.
    h, m, s = (int(x) for x in hms.split(":"))
    return h * 3600 + m * 60 + s

weekday_services = {r["service_id"] for r in rows(calendar_txt) if r["tuesday"] == "1"}
weekday_trips = {r["trip_id"] for r in rows(trips_txt) if r["service_id"] in weekday_services}
stop_names = {r["stop_id"]: r["stop_name"] for r in rows(stops_txt)}

window_start, window_end = to_seconds("08:00:00"), to_seconds("10:00:00")
times_by_stop = defaultdict(list)
for r in rows(stop_times_txt):
    if r["trip_id"] not in weekday_trips:
        continue
    t = to_seconds(r["arrival_time"])
    if window_start <= t < window_end:
        times_by_stop[r["stop_id"]].append(t)

print(f"{'stop':<22}{'buses':>6}{'avg gap (min)':>15}")
for stop_id in sorted(stop_names):
    times = sorted(times_by_stop.get(stop_id, []))
    gaps = [(b - a) / 60 for a, b in zip(times, times[1:])]
    avg_gap = f"{sum(gaps) / len(gaps):.1f}" if gaps else "n/a"
    print(f"{stop_names[stop_id]:<22}{len(times):>6}{avg_gap:>15}")

On this synthetic data, Stop B shows 5 weekday buses in the window with an average gap of 23.8 minutes, because it is served by both routes. The Sunday-only trip T6 is correctly excluded, and T1's 07:55 arrival at Stop A falls outside the window, which leaves Stop A with 2 buses 50 minutes apart. Check that you can explain each of those numbers by hand before you run the real feed. Hand-checking small cases is the habit interviewers look for.

Taking it to the real feed

When you move to the real files, a few adjustments matter:

  1. 1Pick one weekday service definition. Real feeds often have many service_id values and a calendar_dates.txt with exceptions. State which date you analysed.
  2. 2Count departures, not trips per route. A stop served by several routes gets the combined frequency, which is what a commuter experiences.
  3. 3Watch for duplicate stop names. Stops on opposite sides of a road can share a name with different IDs. Group by stop_id, and label with name plus direction if the feed provides it.
  4. 4Sanity-check against a stop you use. Pick a stop you know. If the schedule says a bus every five minutes and you usually wait twenty, that gap between scheduled and experienced service is itself a finding, and the real-time feed is how you would measure it.

The write-up that turns code into a portfolio piece

Keep the README to one screen:

  • Question: "Which stops within 1 km of a metro station have fewer than N scheduled buses between 08:00 and 10:00 on a weekday?"
  • Data: Open Transit Data static GTFS, downloaded on a stated date, used under its terms.
  • Method: three sentences on the filter, the join and the window.
  • Finding: one table of the top ten and bottom ten stops, and one map if you made one.
  • Limits: scheduled service is not observed service; one weekday is not every weekday; stop coordinates may have errors.

That last section matters more than most learners expect, and it is one of the things the free skill check looks for in how you describe projects. A candidate who states what the analysis cannot show comes across as someone who can be trusted with real data.

The Data Analyst programme runs projects like this in SQL, Python and Power BI over 3–4 months, live online with optional in-person labs at our Noida centre. If you prefer environmental data, the companion AQI project uses the same write-up approach.

Frequently asked questions

Where do I get Delhi's bus data?

The Open Transit Data platform at otd.delhi.gov.in is run by the Transport Department of the Government of NCT of Delhi with IIIT-Delhi. It offers static and real-time transit data. Read and follow its terms and conditions before you use or publish anything built on it.

Do I need to know Python for this project?

The example uses Python's standard library, and the same logic works in SQL or a spreadsheet. If you are learning, start with the stop-frequency question in a spreadsheet, then rebuild it in Python so your notebook shows both.

PK
Pankit Kumar
Lead Instructor, NeuraPath Academy

Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.

This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.

Explore Data Analytics with Generative AI
Counselling is free · no obligation

Not sure which programme fits?

Tell us your background and we will map it to the right entry point — including saying so when a cheaper programme is the better fit. A counsellor replies within one working day.