Main quest · delay prediction model · Aug 2026
Knowing a flight will leave late, hours before it does.
Unlike US airports, Istanbul Airport publishes no historical delay data. So I collected the dataset myself: every day since May 2026 — schedules, ADS-B movements and METARs. This page is about the model built on it: how it was built, why it was built that way, and where it stops.
Why
A model starts with an opponent worth beating.
"I used machine learning" is not a result. A simple table of how often a flight has been late in the past is already surprisingly good — and most projects never measure their model against one. So I built that table first, then required the model to beat it. If it hadn't, that would have been the finding worth publishing.
Step 1 · The barA shrunken lookup ladder
Starts at the airport average and narrows: destination → airline+destination → +hour → flight number. Each rung shrinks toward its parent, so a route flown three times reports something sane instead of 0% or 100%. That is the bar to beat: 0.711 AUC.
Step 2 · The hybridThe table's answer as one feature
A gradient-boosted tree (HistGradientBoosting) takes the table's estimate as a single feature and learns when to disagree with it. The trap this avoids: the table has already seen the training rows' answers. So training rows are scored out of fold — split into five day-groups, each scored by a table fitted on the other four.
Step 3 · The previous legWhere the difference actually is
Every aircraft is tracked by its airframe hex (icao24) to compute two things: how long it sat on the ground, and how late the flight that brought it in was. 46% of European delay minutes come from that chain. A schedule-only model is structurally blind to it.
Honest measurement
Train on May–June, test blind on July.
The split is by date, not random. A random split scatters two flights from the same day across train and test; that day's weather and congestion leak from one to the other and the result looks better than it is. Every number below is from 22,675 July flights the model never saw.
What each feature added
| Model | AUC | Brier | Lift |
|---|---|---|---|
| Lookup table (the bar) | 0,711 | 0,1060 | 2,98 |
| Hybrid tree | 0,716 | 0,1054 | 3,03 |
| + turnaround time | 0,739 | 0,1003 | 3,44 |
| + inbound flight's delay | 0,742 | 0,0996 | 3,43 |
The tree on its own gains little: 0.711 → 0.716. The real jump arrives with the aircraft's previous leg. The gain comes from measuring the right mechanism, not from "machine learning".
What the model actually sees
| Turnaround | Flights | Really late | Table says | Model says |
|---|---|---|---|---|
| under 60 min | 579 | %56,6 | %20,7 | %48,8 |
| 60–90 dk | 1.557 | %14,5 | %12,3 | %11,9 |
| 90 min and over | 6.841 | %9,7 | %8,3 | %6,9 |
One table tells the whole story. 56.6% of flights whose aircraft had under 60 minutes on the ground really do leave late — the lookup table says 20.7%, because it looks at that flight's history, not at that day's aircraft. The model reaches 48.8%. Still under-calling it, but pointed the right way.
Two real flights
TK1045 → Bucharest · 25 July, 18:00
The lookup table called this one safe: 5.3%. That flight number had almost never been late. But the airframe due to fly it landed in Istanbul 57 minutes late, with only 24 minutes scheduled on the ground.
The model said 84.6%. It left 56 minutes late.
A46070 → Mineralnye Vody · 19 July, 21:00
The other direction. This route's history is bad, so the lookup table raised the alarm at 43.8%. But that evening's aircraft had landed on time and had 92 minutes on the ground.
The model said 9.6%. It left on time.
Both show the same thing: the lookup table knows a flight's reputation, the model knows that day's aircraft. Most delay comes not from reputation but from a chain that broke that morning.
Honest limitations
Where the model does not work.
- A day ahead, this model is no better. Measured: a turnaround becomes known a median of 2.3 hours before departure, and 0% of them 24 hours ahead. Since the model's entire advantage comes from those two features, scoring tomorrow's board leaves 0.716 — barely above the bar's 0.711. That is why the live board still uses the lookup table. The fix is not to put the model on the board, but to re-score the board through the day.
- The inbound delay sits at the noise floor. A single run shows +0.003; measured across four random seeds it averages +0.002 and goes negative on one of the four. The baseline's own seed spread (0.0027) is larger than the effect. I kept the feature because it is the right mechanism — but it cannot be presented as a gain.
- I can only identify 34% of the aircraft. Turnaround exists only for flights whose airframe I catch on ADS-B. OpenSky sees ~554 departure movements a day against ~877 scheduled departures, so even perfect matching stops at 63%.
- Weather is not learnable yet. The training months contain five thunderstorm hours in total, and in those the delay rate ran normal. A model cannot learn an effect its sample does not contain. At IST winter brings 10x the fog and 10x the sub-3km visibility — February is the worst month, not December. So the weather terms wait for winter.
- It says whether, not how long. Delay in minutes exists on only 28% of rows. The late/on-time label is complete, but "45 minutes or 3 hours" is unanswerable on most of them.
These are measured limits, not guesses. Knowing where a model fails is as much a result as knowing where it works.
The working half
The departure delay board.
The part running live today: every scheduled departure for tomorrow, scored against its own history, then compared with what actually happened by the end of the day. Because of the limitation above, the board uses the lookup table for now — and shows its own scoreboard openly.