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:
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:
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¶
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] = [].
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.
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.