Skip to content

S3Q1 · Flight Records Analysis

⚡ Quick Reference

Five functions on a list of (flight_no, departure, arrival, delay_min) tuples:

def on_time_percentage(flights):
    return round(sum(1 for *_, d in flights if d <= 15) / len(flights) * 100, 2)

def most_delayed_route(flights):
    routes = {}
    for _, dep, arr, delay in flights:
        routes.setdefault((dep, arr), []).append(delay)
    return min(routes, key=lambda r: (-sum(routes[r])/len(routes[r]),
                                      flights.index(next(f for f in flights if f[1]==r[0] and f[2]==r[1]))))

def busiest_airport(flights):
    from collections import Counter
    airports = [f[1] for f in flights] + [f[2] for f in flights]
    freq = Counter(airports)
    max_count = max(freq.values())
    return min(a for a in freq if freq[a] == max_count)

def total_delay_hours(flights):
    return round(sum(f[3] for f in flights) / 60, 2)

def track_flights(flights):
    return {
        "on_time_rate":    on_time_percentage(flights),
        "most_delayed":    most_delayed_route(flights),
        "busiest_airport": busiest_airport(flights),
        "total_delay":     total_delay_hours(flights),
    }

Key rules: - On-time = delay <= 15 minutes - Most delayed route = highest average delay; ties → first appearance - Busiest airport = highest count across both dep + arr; ties → alphabetically smallest - Total delay in hours = sum(delays) / 60


Problem Statement

Problem

Implement five functions to analyse a list of flight records, each a tuple (flight_no, departure, arrival, delay_min).

Sample data:

flights = [
    ('SG101', 'DEL', 'BOM', 30),
    ('TX202', 'BOM', 'BLR', 5),
    ('FX303', 'DEL', 'BOM', 45),
]

Expected output:

track_flights(flights) == {
    'on_time_rate':    33.33,
    'most_delayed':    ('DEL', 'BOM'),
    'busiest_airport': 'BOM',
    'total_delay':     1.33,
}


Function 1 - on_time_percentage

Count flights with delay ≤ 15, divide by total, multiply by 100:

def on_time_percentage(flights: list) -> float:
    on_time = sum(1 for *_, delay in flights if delay <= 15)
    return round(on_time / len(flights) * 100, 2)

From sample: only TX202 (delay=5) is on time → 1/3 * 100 = 33.33


Function 2 - most_delayed_route

Group delays by (departure, arrival), compute average per route, return the highest. Ties → first in original list.

def most_delayed_route(flights: list) -> tuple:
    routes = {}
    for _, dep, arr, delay in flights:
        routes.setdefault((dep, arr), []).append(delay)
    # average delay per route
    avg = {route: sum(delays)/len(delays) for route, delays in routes.items()}
    return max(avg, key=lambda r: avg[r])

From sample: - ('DEL','BOM'): delays = [30, 45] → avg = 37.5 - ('BOM','BLR'): delays = [5] → avg = 5.0

Highest average = ('DEL','BOM')

Tie-breaking - first appearance

max() scans left to right and only updates on a strictly greater value. So ties automatically preserve the first route encountered - no extra logic needed.


Function 3 - busiest_airport

Count each airport across both departure and arrival columns. Ties → alphabetically smallest.

def busiest_airport(flights: list) -> str:
    from collections import Counter
    airports = [f[1] for f in flights] + [f[2] for f in flights]
    freq = Counter(airports)
    max_count = max(freq.values())
    return min(a for a in freq if freq[a] == max_count)

From sample: - DEL: dep in SG101, FX303 → 2 - BOM: arr in SG101, FX303; dep in TX202 → 3 ← highest - BLR: arr in TX202 → 1

Busiest = 'BOM'


Function 4 - total_delay_hours

Sum all delays in minutes, divide by 60:

def total_delay_hours(flights: list) -> float:
    return round(sum(f[3] for f in flights) / 60, 2)

From sample: 30 + 5 + 45 = 80 minutes → 80 / 60 = 1.333...1.33


Function 5 - track_flights

Aggregates all four functions into one dictionary:

def track_flights(flights: list) -> dict:
    return {
        "on_time_rate":    on_time_percentage(flights),
        "most_delayed":    most_delayed_route(flights),
        "busiest_airport": busiest_airport(flights),
        "total_delay":     total_delay_hours(flights),
    }

Complete solution approaches

from collections import Counter

def on_time_percentage(flights: list) -> float:
    return round(sum(1 for *_, d in flights if d <= 15) / len(flights) * 100, 2)

def most_delayed_route(flights: list) -> tuple:
    routes = {}
    for _, dep, arr, delay in flights:
        routes.setdefault((dep, arr), []).append(delay)
    return max(routes, key=lambda r: sum(routes[r]) / len(routes[r]))

def busiest_airport(flights: list) -> str:
    freq = Counter(f[1] for f in flights) + Counter(f[2] for f in flights)
    max_count = max(freq.values())
    return min(a for a in freq if freq[a] == max_count)

def total_delay_hours(flights: list) -> float:
    return round(sum(f[3] for f in flights) / 60, 2)

def track_flights(flights: list) -> dict:
    return {
        "on_time_rate":    on_time_percentage(flights),
        "most_delayed":    most_delayed_route(flights),
        "busiest_airport": busiest_airport(flights),
        "total_delay":     total_delay_hours(flights),
    }
def on_time_percentage(flights: list) -> float:
    count = 0
    for f in flights:
        if f[3] <= 15:
            count += 1
    return round(count / len(flights) * 100, 2)

def most_delayed_route(flights: list) -> tuple:
    routes = {}
    for f in flights:
        route = (f[1], f[2])
        if route not in routes:
            routes[route] = []
        routes[route].append(f[3])
    best_route = None
    best_avg   = -1
    for route, delays in routes.items():
        avg = sum(delays) / len(delays)
        if avg > best_avg:
            best_avg   = avg
            best_route = route
    return best_route

def busiest_airport(flights: list) -> str:
    freq = {}
    for f in flights:
        for airport in (f[1], f[2]):
            freq[airport] = freq.get(airport, 0) + 1
    max_count = max(freq.values())
    candidates = sorted(a for a in freq if freq[a] == max_count)
    return candidates[0]

def total_delay_hours(flights: list) -> float:
    return round(sum(f[3] for f in flights) / 60, 2)

def track_flights(flights: list) -> dict:
    return {
        "on_time_rate":    on_time_percentage(flights),
        "most_delayed":    most_delayed_route(flights),
        "busiest_airport": busiest_airport(flights),
        "total_delay":     total_delay_hours(flights),
    }
from collections import Counter

def on_time_percentage(flights: list) -> float:
    return round(len(list(filter(lambda f: f[3] <= 15, flights))) / len(flights) * 100, 2)

def most_delayed_route(flights: list) -> tuple:
    routes = {}
    for _, dep, arr, delay in flights:
        routes.setdefault((dep, arr), []).append(delay)
    return max(routes, key=lambda r: sum(routes[r]) / len(routes[r]))

def busiest_airport(flights: list) -> str:
    freq = Counter(map(lambda f: f[1], flights)) + Counter(map(lambda f: f[2], flights))
    max_count = max(freq.values())
    return min(filter(lambda a: freq[a] == max_count, freq))

def total_delay_hours(flights: list) -> float:
    return round(sum(map(lambda f: f[3], flights)) / 60, 2)

def track_flights(flights: list) -> dict:
    return {
        "on_time_rate":    on_time_percentage(flights),
        "most_delayed":    most_delayed_route(flights),
        "busiest_airport": busiest_airport(flights),
        "total_delay":     total_delay_hours(flights),
    }

Key takeaways

01

setdefault() for grouping

routes.setdefault((dep, arr), []).append(delay) initialises the list if the key is new, then appends. Cleaner than if key not in d: d[key] = [].

02

Counter + Counter merges frequency counts

Counter(departures) + Counter(arrivals) sums the counts for matching keys. A clean way to combine two frequency distributions without a manual loop.

03

min() for alphabetical tie-breaking

When multiple airports share the maximum frequency, min(candidates) returns the alphabetically smallest one - Python string comparison is lexicographic by default.