Skip to content
ML Academy

project-008 · Classical ML · aviation

Passenger no-show probabilities for overbooking and reconfirmation decisions

For each departure of the fictional carrier ZX, how many seats can be sold beyond capacity, and which bookings should receive a reconfirmation message three days before departure? Both decisions need passenger-level no-show probabilities that are calibrated at the decision time (T-3 days), not just a good ranking.

Dataset
Synthetic passenger-booking no-show data (r02 generator over the academy airline simulator)
Licence
synthetic - generated by academy code; no third-party data
Reference run
executed

At a glance

  • Data

    Synthetic passenger-booking no-show data (r02 generator over the academy airline simulator) (synthetic) · Configurable; default sized to stay under about 500,000 booking rows so the full run finishes in minutes on a laptop CPU

    Details on data
  • Baseline

    The revenue-management rate table: historical no-show rate by route x fare level from the training period, with sparse cells shrunk toward the market rate (empirical…

    Details on baseline
  • Validation

    Time-based split: first 18 months train, next 2 months calibration (for the calibrator only), last 6 months test. Group-aware: a PNR never straddles splits.

    Details on validation
  • Metric

    Log loss and Brier score (primary), expected calibration error and reliability curves, mean gap to true probabilities, ROC-AUC and PR-AUC as secondary diagnostics.

    Details on metric

Success criterion

On the final six months of the synthetic data (time-based test set):

  1. The selected calibrated model improves test log loss over the route x fare-level rate-table baseline by at least 5% (relative), and its expected calibration error (10 bins) is at most 0.01.
  2. Because the data are synthetic, predicted probabilities are also compared with the simulator's true probabilities: mean absolute gap at most 0.01 overall and at most 0.02 in every pre-registered slice, or the slice is reported as a failure.
  3. The overbooking policy derived from the model has lower expected cost per flight than the policy derived from the baseline, under hypothetical denied-boarding and empty-seat costs, across 5 generator seeds.
  4. The reconfirmation threshold is derived from a stated cost ratio, not from F1.

Context

All data are synthetic. The reference implementation expands the academy's airline simulator (flight-level schedule, markets, fare levels, holidays and the 2024-03-01 competitor-capacity break on three routes) into passenger bookings, and draws no-shows from a known logistic data-generating process. Knowing the true probabilities lets learners see whether calibration methods recover them, which real data never allow.

Overbooking trades off two costs: a denied boarding (compensation, rebooking, goodwill) and a seat that flies empty. Both cost values in this project are hypothetical. The flight-level decision needs the distribution of shows, obtained from the passenger probabilities (a Poisson-binomial distribution), so miscalibration directly becomes wrong overbooking limits.

Data

Dataset
Synthetic passenger-booking no-show data (r02 generator over the academy airline simulator) (synthetic)
Licence
synthetic - generated by academy code; no third-party data
Size
Configurable; default sized to stay under about 500,000 booking rows so the full run finishes in minutes on a laptop CPU
Verification
unverified

Access

Generated locally by python/reference/r02_noshow_classifier (specified, not yet implemented) from the flight schedule produced by python/capstone/airline_sim.py; no download, no account. Seed and size are configurable.

Grain

One row per passenger booking on one flight departure, as of the T-3 snapshot. Bookings cancelled before T-3 are excluded; the label is no-show at departure (did not board and did not cancel). Passengers in one group booking share a pnr_id and correlated outcomes.

ColumnMeaning
booking_id / pnr_idBooking key; group key for passengers booked together
flight_no / departure_date / route / marketFlight identity from the airline simulator; market is domestic, intl-short or intl-long
fare_levelL, M or H fare level at booking
booking_lead_daysDays between booking and departure
channeldirect, agency or corporate (synthetic attribute)
group_size / is_connectingPassengers on the PNR; whether the itinerary connects
prior_noshows_12mPassenger's no-shows in the 12 months before booking (point-in-time)
checked_in_onlineOnline check-in status, known only from T-1: a deliberate leakage trap column
true_p_noshowGround-truth probability from the generator; evaluation only, never a feature
no_showLabel: 1 if the passenger did not show

EDA and data-quality rules

  • Every feature has an availability time; assert it is at or before the T-3 snapshot (checked_in_online fails this by design).

  • No-show rate by month, market and fare level in the training period; sparse route x fare cells are counted before building the baseline.

  • booking_id unique; group bookings identified; groups are never split across train and test.

  • prior_noshows_12m recomputed from the history table must exclude the current flight; assert equality with the stored column.

  • Distribution of true_p_noshow checked for sanity (no probabilities of exactly 0 or 1).

Baseline

The revenue-management rate table: historical no-show rate by route x fare level from the training period, with sparse cells shrunk toward the market rate (empirical Bayes). It is what many airlines start from and is already roughly calibrated, so beating it requires real information gain.

Features

Lead time (log and bins), fare level, channel, group size, connection flag, prior no-shows, departure weekday and hour, holiday proximity, market and route. All are known at T-3. One-hot encoding for linear models; native categorical handling for gradient boosting. checked_in_online and true_p_noshow are excluded.

Candidate methods

  • Logistic regression with spline lead-time terms and a few pre-specified interactions

    Well calibrated by construction when specified sensibly, easy to explain to revenue analysts, and a strong competitor because the generator is logistic.

  • Gradient boosting (HistGradientBoosting or LightGBM) with post-hoc calibration on a later time slice

    Captures interactions without hand-crafting; boosted trees often need calibration (sigmoid or isotonic) before their probabilities can drive costs.

Validation design

  • Time-based split: first 18 months train, next 2 months calibration (for the calibrator only), last 6 months test. Group-aware: a PNR never straddles splits.
  • Hyperparameters tuned with rolling-origin folds inside the training period.
  • Seed study: repeat the whole pipeline for 5 generator seeds; report the spread of every metric.

Leakage checks

Check-in status and seat assignment become known after T-3; including them would raise discrimination sharply and make the model useless at decision time. Prior no-show counts must not include the current flight. Rate tables and encoders are fitted on training data only.

Bias and representativeness

Synthetic data carry no protected attributes, but the reconfirmation burden falls on whoever the model scores high; in real data, attributes such as nationality or name must not be used, and channel or corporate status should be reviewed as proxies. Report contact rates by channel.

Robustness checks

Slice the post-2024-03-01 period on the three break routes; holiday peaks; generator seeds; sensitivity of the overbooking policy to the hypothetical cost ratio.

Technical metrics

Log loss and Brier score (primary), expected calibration error and reliability curves, mean gap to true probabilities, ROC-AUC and PR-AUC as secondary diagnostics.

Business interpretation

Expected cost per flight = denied-boarding cost x expected denied boardings + empty-seat cost x expected empty seats, computed from the Poisson-binomial distribution of shows; overbooking limit per flight; reconfirmation contacts per flight and expected seats recovered. All costs hypothetical.

Error slices

  • Market and route, including the three break routes after 2024-03-01.

  • Fare level and channel.

  • Lead-time buckets (bookings made in the last week behave differently).

  • Group bookings versus individuals.

  • Holiday departures.

Implementation steps

  • Generate bookings from the simulator schedule with a fixed seed; write the dictionary with availability times.

  • Run point-in-time assertions and EDA; build the time- and group-aware split.

  • Fit the rate-table baseline, then both candidates; calibrate the boosted model on the calibration slice.

  • Evaluate on the test period: log loss, Brier, ECE, gap to true probabilities, slices.

  • Turn probabilities into decisions: overbooking limit by minimising expected cost per flight; reconfirmation threshold from the contact-cost / recovered-seat-value ratio.

  • Run the leakage demonstration (add checked_in_online, show the metric jump and why it is invalid) and the seed study.

  • Write the report and a short model card: intended use, decision time, limits, monitoring.

Reproduction

Reference (specified, not yet implemented): cd python && uv run python -m reference.r02_noshow_classifier.run. Planned outputs: metrics JSON, reliability plots, slice table, policy comparison and model card in the module's output folder. Seeds and split dates in config; dependencies pinned by python/uv.lock; target runtime under 5 minutes on a laptop CPU.

Limitations

  • Synthetic, logistic data-generating process: it favours logistic regression and cannot show how real passengers behave.

  • Single-leg overbooking with fixed hypothetical costs; real systems handle fare classes, upgrades, connections and regulation.

  • Cancellations after T-3 are not modelled as a separate outcome.

Production and monitoring

Daily batch scoring of open bookings at T-3 (and later snapshots if needed); calibration monitored monthly by market; drift checks on channel and lead-time mix; the rate table kept as a fallback and rollback target; analysts can override limits for events; every overbooking decision is logged with the model version for audit.

Interview questions

  • Why does calibration matter more than ROC-AUC for overbooking?

  • Your top feature is online check-in status. What do you do?

  • How do you turn passenger probabilities into an overbooking limit for one flight?

  • Would you balance the classes with SMOTE before training? Why not?

Postmortem and alternatives

A flight-level model of show-up rates (regression on aggregated counts) is simpler and may be enough when passenger-level actions such as reconfirmation are not needed. With real data, a survival or multi-snapshot model would update probabilities as departure approaches instead of using one snapshot.

Reference implementation

Status: executed — outputs below are from the recorded run

cd python && uv run python -m reference.r02_noshow_classifier.run

r02 results - calibrated passenger no-show probabilities for overbooking and reconfirmation

Recorded run of cd python && uv run python -m reference.r02_noshow_classifier.run (full mode, five generator seeds, primary seed 20261011). Every number below is copied from outputs/metrics.json, outputs/results_tables.md, outputs/slices.csv, outputs/seed_study.csv and outputs/model_card.md, written by that run. The data are synthetic (academy airline simulator schedule plus a passenger-level generator with a known logistic no-show process); all costs are hypothetical teaching values for the fictional carrier ZX.

ItemValue
Run date2026-10-11
Seedsgenerator seeds 20261011 to 20261015 (the simulator seed equals the generator seed); primary 20261011
Python / numpy / pandas / scipy / scikit-learn3.12.13 / 2.5.3 / 3.0.6 / 1.18.1 / 1.9.1
Runtime of the recorded run179.2 s wall for all five seeds (180.9 s by time; 494 s user CPU), 29 to 42 s per seed, on a 10-core laptop shared with other jobs. An earlier run of the same code, with identical results, took 204.9 s at a higher machine load.
Primary seed data480,230 bookings on 3,257 departures (22% of the simulated departures); train 2022-07-01 to 2023-12-31 = 326,762 rows (no-show 6.31%); calibration 2024-01-01 to 2024-02-29 = 35,902 (6.37%); test 2024-03-01 to 2024-08-31 = 117,566 on 770 flights (7.33%)
Baselineroute x fare-level rate table, 33 cells, none below the pseudo-count of 50, shrunk toward the market rate

Findings

  1. The learned models beat the rate table on log loss by 5.7 to 6.3% in every seed, which is above the 5% bar, and they close most of the available gap. Primary-seed test log loss: rate table 0.2584, logistic regression 0.2431, gradient boosting 0.2437 (raw) and 0.2437 (sigmoid-calibrated), isotonic-calibrated 0.2446, simulator true probabilities (oracle) 0.2416. The selected model (calibrated boosting on this seed) improves on the rate table by 5.69% and closes 87% of the rate-table-to-oracle gap; the oracle itself improves on the rate table by 6.52%. Across the five seeds the selected model's improvement is 5.69 to 6.27% (mean 5.93%, sd 0.24 pp).

  2. Ranking quality is modest, calibration is already good. Test ROC-AUC 0.604 (rate table), 0.699 (logistic), 0.696 (boosting), 0.704 (oracle); PR-AUC 0.100, 0.183, 0.182, 0.191 against a base rate of 7.3%. The rate table's ECE (10 equal-count bins) is already 0.0079, logistic regression 0.0050, raw boosting 0.0059, the selected sigmoid-calibrated boosting 0.0055 and isotonic 0.0068. Calibration of the selected model is within the 0.01 bar in all five seeds (0.0055 to 0.0088). Raw boosting was already inside the bar (0.0059 to 0.0098), so the calibration step bought little here; sigmoid was chosen in all five seeds by cross-fit log loss on the calibration slice.

  3. Logistic regression and boosting are tied; the family choice is a coin flip. On the calibration slice the cross-fit log loss of boosting+sigmoid (0.21557) is 0.0002 below logistic regression (0.21580), so boosting was selected on the primary seed; across seeds the selected family is boosting in 2 and logistic regression in 3. Test log loss differs by at most 0.0006 between the two on any seed. This is consistent with the generator being logistic (it favours the logistic model), and says nothing about real passengers.

  4. Gap to the simulator's true probabilities: the overall and every-slice bars are NOT met. Mean absolute gap 0.0116 for the selected model on the primary seed (bar 0.01), 0.0097 for logistic regression, 0.0112 for raw boosting and 0.0333 for the rate table. Across seeds the selected model's gap is 0.0096 to 0.0116 and is within the bar in one seed of five. Slices above 0.02 on the primary seed: one, IST-ADB (gap 0.0229, mean predicted 0.0703 against true 0.0927). The three break routes after 2024-03-01 have gaps of 0.0229 (IST-ADB), 0.0171 (IST-AYT) and 0.0161 (IST-FRA); together they have a gap of 0.0195 and a mean bias of -0.0188 (predicted 0.0670, true 0.0859). The other routes together have a gap of 0.0080 and a bias of +0.0036, inside the bar. The cause is the +0.30 logit shift that the generator applies to those routes at the test start and that no feature can encode; the models were trained before it. Across seeds 1 to 3 slices exceed 0.02 (their identity is recorded for the primary seed only). No slice has fewer than 100 events.

  5. Outside the route slices the largest gaps are 0.0145 to 0.0170. Lead time 60 days or more 0.0170 (bias +0.0009), the corporate channel 0.0160 (bias -0.0096), fare level H 0.0151 and departures within one day of a holiday 0.0145. All are inside the 0.02 bar.

  6. Overbooking: the model-based limits cost less than the rate-table limits in every seed. Expected cost per flight under the hypothetical EUR 600 denied boarding and EUR 150 empty seat (ratio 4), evaluated with the true probabilities over the 770 test flights:

    PolicyCost per flight (EUR)Mean limit above capacity (seats)
    no overbooking2,0660.0
    rate table92710.1
    logistic regression82510.6
    boosting, raw82810.4
    boosting, calibrated (selected)83511.0
    oracle (true probabilities)71611.7

    The selected policy saves 91.9 EUR per flight against the rate table (approximate SE 9.1 over flights; cheaper on 52.9% of flights and costlier on 25.8%) and 43.5% of the rate-table-to-oracle gap. Over five seeds the mean cost is 928 (rate table), 830 (logistic), 832 (raw boosting), 830 (selected), 716 (oracle) and 2,062 (no overbooking). Savings against the rate table at denied-to-empty ratios 1 / 2 / 4 / 8 / 16 are 86.6 / 90.6 / 91.9 / 90.0 / 89.2 EUR per flight; the advantage does not disappear over that range. Every flight receives some overbooking (the limit is above capacity on 100% of flights), because the expected no-show count is far above one seat.

  7. Calibration did not make the decision cheaper. On the primary seed the calibrated boosting policy (835) costs more than raw boosting (828) and logistic regression (825) although their test log losses differ by 0.0006 or less; policy cost responds to small shifts in the predicted no-show rate (the sigmoid, fitted on a January-February slice, moved the mean prediction from 0.0674 to 0.0701). One seed cannot say whether this is systematic: on seed 20261015 the selected policy (822) is cheaper than raw boosting (835), on seed 20261014 it is the logistic model and costs 813 against 811 for raw boosting.

  8. Reconfirmation threshold from costs, not from F1. The threshold is contact cost / (contact effect x freed-seat value) = 2 / (0.30 x 60) = 0.111. Under the true probabilities, contacting the bookings the selected model scores at or above 0.111 gives a net benefit of 27.1 EUR per flight with 23.8 contacts per flight; the rate table, whose probabilities rarely exceed 0.111, contacts 6.1 per flight and nets 2.6 EUR; the oracle nets 29.0 EUR; contacting everyone loses 103.3 EUR per flight. The F1-optimal threshold chosen on the calibration slice is 0.119 and nets 26.7 EUR per flight, only 1.5% below the cost-based rule here, so on this synthetic data the two rules nearly coincide; the cost-based one is the one that can be re-derived when costs change.

  9. The contact burden is uneven by channel. Contact rates of the selected model: agency 23.0%, corporate 20.6%, direct 9.4%, against observed no-show rates of 8.7%, 9.3% and 5.8%. The synthetic data have no protected attributes; this only shows the mechanism a real review would look for.

  10. The leakage trap is large. Adding checked_in_online (known only from T-1) to boosting moves test log loss from 0.2437 to 0.1466 (40% lower), ROC-AUC from 0.696 to 0.934 and PR-AUC from 0.182 to 0.494. The metric jump is real in the table and unobtainable at T-3, which is why the point-in-time audit rejects the column.

  11. Point-in-time and split audits pass. Every one of the 11 features has availability at or before T-3; the stored prior_noshows_12m equals its recomputation from the history table for all 480,230 bookings and every history trip dates before the booking; no PNR spans flights or splits; train, calibration and test departures are strictly ordered.

  12. Seed study.

    SeedBooking rowsTest no-show rateLL rate tableLL logisticLL boosting (raw)LL selected (family)Improvement vs rate tableECE selectedGap to true p, selectedSlices > 0.02 gapCost/flight rate tableCost selectedCost oracle
    20261011480,2307.33%0.25840.24310.24370.2437 (GBM+sigmoid)5.7%0.00550.01161927835716
    20261012483,4067.47%0.26230.24640.24650.2464 (logistic)6.0%0.00770.01063951841719
    20261013477,5787.53%0.26390.24880.24880.2488 (logistic)5.7%0.00880.01043928838713
    20261014464,6137.31%0.25830.24310.24290.2431 (logistic)5.9%0.00560.00961896813712
    20261015491,9877.46%0.26160.24540.24540.2452 (GBM+sigmoid)6.3%0.00610.01081939822721

Success criteria of the project record

CriterionResultMet
1a. Test log loss at least 5% below the rate table5.69% on the primary seed (5.69% to 6.27% across seeds)yes
1b. ECE (10 bins) at most 0.010.0055 (0.0055 to 0.0088 across seeds)yes
2a. Mean absolute gap to true probabilities at most 0.01 overall0.0116 on the primary seed (0.0096 to 0.0116; within the bar in 1 of 5 seeds)no
2b. Gap at most 0.02 in every pre-registered sliceone slice fails on the primary seed (IST-ADB, 0.0229); 1 to 3 across seeds. Reported as a failureno
3. Policy cost lower than the baseline's, across 5 seedslower in all 5 (813 to 841 against 896 to 951 EUR per flight)yes
4. Reconfirmation threshold from a stated cost ratio0.111 = 2 / (0.30 x 60)yes

Criteria 2a and 2b are reported as not met. The slice-level evidence points at the unmodelled break routes (finding 4), not at a defect in the pipeline, but the criterion is stated on the whole test period and is not met as written.

What this does not prove

  • It does not show that gradient boosting or logistic regression would be well calibrated on real passengers. The generator is logistic and its coefficients are hypothetical; the near-tie between the two families is a property of that choice.
  • The gap-to-truth criteria can be evaluated only because the simulator's probabilities are known; real data offer only realised outcomes, where reliability curves and slice-wise calibration are the available evidence.
  • The overbooking costs are expected costs under the true probabilities with hypothetical EUR figures, single leg and single class. They assume each flight sells up to the chosen limit, give extra bookings the flight's mean risk, and treat the T-3 booking-curve count as live bookings. The Poisson-binomial ignores within-PNR correlation (half of the multi-passenger PNRs share one draw in the generator), so it understates the variance of shows and the limits are likely a little aggressive. No cancellations after T-3 are modelled.
  • The test period starts on the break date by design, so the break-route slices measure drift that no model fitted on earlier data could capture, not model quality; whether a monitoring plan would catch it was not simulated.
  • Seeds regenerate the schedule, bookings and labels but share the same logistic coefficients, so the seed spread describes sampling variation of one process, not variation across processes.
  • The sigmoid-versus-isotonic and logistic-versus-boosting choices rest on a 35,902-row calibration slice and differ by 0.0002 to 0.0013 in log loss; the selection is not statistically meaningful.
  • Splits are by departure date without a 3-day purge for label delay; this cannot leak anything in the synthetic generator but would matter on real data.
  • The 5 to 6% log-loss improvement is a property of the effect sizes set in the generator (the oracle gets 6.5%); the size of the improvement says nothing about real revenue-management data.
  • The runtime depends on free cores (179 s and 205 s for the same code on the same machine).

Reproduce

cd python && uv run python -m reference.r02_noshow_classifier.run
cd python && uv run pytest tests/test_r02_noshow_classifier.py

Builder-authored reference. It is not learner evidence and is never credited to your portfolio.

Rubric4 criteria

CriterionMeetsExceeds
Point-in-time correctnessEvery feature is available at T-3 and the split is time- and group-aware.Demonstrates the leakage trap quantitatively and explains its business consequence.
Probability qualityReports log loss, Brier and reliability; calibrates the boosted model on a separate slice.Compares with true probabilities by slice and explains where and why calibration fails.
Decision translationDerives the overbooking limit and reconfirmation threshold from stated costs.Shows cost sensitivity and when the model's advantage over the rate table disappears.
Production readinessModel card with intended use and monitoring plan.Defines fallback, override and audit procedures.

References6