Zugverspätung, 30 Minuten im Voraus.
Eigener Collector, eigenes DWH, eigenes Modell — end-to-end, zeitehrlich, Bugs offengelegt.
Modell v1.1 · Live-Daten Stand 06.08.2026, 14:04 UTCModel Card
Testset, ehrlich · ml/eval.jsonEin Gradient-Boosting-Modell sagt die Abfahrtsverspätung 30 Minuten im Voraus auf 4,80 min mittleren Fehler genau — knapp die Hälfte der naiven Baseline (8,80 min). „Wird dieser Zug > 6 min verspätet?" trifft es mit AUC 0,84. Regionalverkehr sehr gut, Nacht- und Fernverkehr schwächer — und es sagt ehrlich, warum.
| Aufgabe | Regression delay_dep · Klassifikation „verspätet > 6 min" & „Ausfall" |
|---|---|
| Horizont | T = geplante Abfahrt − 30 min, zeitehrlich (nur Features, die zu T real bekannt waren) |
| Modell | HistGradientBoosting, getrennte Köpfe, isotonische Kalibrierung |
| Daten | 766k Trainingszeilen, 72 Bahnhöfe · Test = letzte 5 Tage (85.546 Abfahrten) |
| Validierung | Rolling-Origin-Backtest, 8 Folds à 5 Tage: MAE 4,54 ± 0,98 |
| Metrik | Modell | Baseline | Lesart |
|---|---|---|---|
| MAE Verspätung | 4,80 min | 8,80 min | ~45 % besser als „rate den Schnitt" |
| Median-Fehler | 1,64 min | 7,36 min | Die Hälfte aller Züge auf < 1,7 min genau |
| AUC „verspätet > 6 min" | 0,843 | — | Solider Frühwarn-Klassifikator |
| Recall @ Precision 0,5 | 79,3 % | — | Jede 2. Warnung stimmt → 4 von 5 Verspätungen gefangen |
Was treibt die Vorhersage?
Permutation-Importance, Top 8 · der eigene Zuglauf dominiert
Ablation — ΔMAE je Feature-Gruppe
MAE-Verschlechterung ohne die Gruppe · Kaskade = der Hebel
In einem Satz: der beste Prädiktor für eine verspätete Abfahrt ist eine verspätete Ankunft. Das wichtigste Feature — die zuletzt bekannte Verspätung desselben Zuges an einem früheren Halt — ist rund zehnmal so wichtig wie das nächstbeste.
Kalibrierung — die Wahrscheinlichkeiten stimmen
| Quantil | Soll | Ist |
|---|---|---|
| q50 (Median) | 50 % | 53,0 % |
| q80 | 80 % | 80,8 % |
| q90 | 90 % | 90,1 % |
Sagt das q90-Modell „höchstens 12 min", stimmt das in 90,1 % der Fälle. Damit wird aus der Prognose eine Antwort auf die eigentliche Frage am Bahnsteig — „Krieg ich meinen Anschluss?": RE1 nach Köln, 8 min Umsteigezeit → Median +2 min, q90 +7 min → „~88 % Chance auf den Anschluss." Eine Zahl, deren Kalibrierung nachgeprüft ist.
Wo das Modell gut ist — und wo nicht Per-Zugtyp-Split
| Segment | Beispiele | MAE | Urteil |
|---|---|---|---|
| Regionalverkehr | RB 3,19 · S 3,26 · RE 3,54 | ~3 min | Sehr gut — dichtes Netz, viel Kaskaden-Signal |
| Fernverkehr Tag | ICE 8,73 · IC 8,66 | ~9 min | Mittel — lange Läufe, Fehler akkumuliert |
| Nacht / International | NJ 27,2 · EC 24,5 | 20–27 min | Schwach — dünne Daten, extreme Ausreißer |
Das ist kein Bug, das ist die Datenlage. Große Verspätungen entstehen aus Kaskaden — sichtbar nur, wenn der auslösende Zug durch beobachtete Bahnhöfe fährt. Nachtzüge verlassen das beobachtete Netz, Regionalzüge bleiben drin. Der Hebel ist deshalb nicht mehr Tuning, sondern Daten-Coverage: Upstream-Halte dichter beobachten.
Limitationen ehrlich, ungeschönt
· 30 Minuten ist die ehrliche Grenze — kürzer bringt kaum Signal, weil IRIS Störungsmeldungen zu spät rausgibt (belegt, siehe Bugs).
· Ausfall-Vorhersage bei hoher Präzision schwach (Recall@P0,5 nur 13 %). Noch nicht produktreif.
· Sondereffekte wie die Generalsanierung Wuppertal–Köln (wiedereröffnet 11.07.) verschieben Muster; Retrain mit Regime-Feature folgt im August.
· Kein Kausalmodell. Korrelationen aus Vergangenheitsdaten, unbekannte Muster extrapoliert es blind.
Reproduzierbar: ml/eval.json, ml/backtest.json, ml/ablation.json im Repo.
Das Netz, live.
aus der eigenen Pipeline, laufend aktualisiertPünktlichkeit, letzte 30 Tage
Stand 06.08.2026, 14:04 UTCLinie: Anteil Halte unter 6 min Abfahrts-Verspätung. Balken: gemessene Halte. Quelle: DB IRIS-TTS, minütlich abgefragt.
Verspätung nach Tageszeit
Anteil Halte ≥ 6 min, letzte 30 TageDie Rushhour ab 16 Uhr ist das unzuverlässigste Fenster; am pünktlichsten ist der frühe Morgen.
Verspätungsradar — der Vortag im Zeitraffer
täglich neu gerendert
Wie ein Regenradar, nur für Verspätungen: Farbe = mittlere Verspätung, Punktgröße = Zugdichte. MP4.
Butterfly-Effekt des Tages
größte Folgeverspätung im eigenen Zuglauf
Welcher Zug hat gestern die größte Folgeverspätung ausgelöst? Konservativ attribuiert: nur innerhalb desselben Zuglaufs.
Befunde
aus 5 Analyse-Spikes, Reports im RepoDrei Bugs, die uns klüger gemacht haben.
die zu guten Zahlen sind die interessantenDas Modell, das zu gut war Temporal Leakage
- Symptom
- AUC 0,86 für Verspätung, 0,85 für Ausfall — brillant. Zu brillant.
- Diagnose
- Die Features aggregierten über den gesamten Lebenszyklus einer Fahrt — inklusive Information, die zum Vorhersage-Zeitpunkt noch nicht existierte. 92 % aller Störungsmeldungen tauchen erst nach der geplanten Abfahrt auf. Zeitehrlich gerechnet: AUC 0,61, Ausfall 0,49 — schlechter als Münzwurf.
- Fix
- Umbau der Feature-Pipeline auf Append-only-Tabellen mit echten Timestamps.
- Lesson
- Ein AUC-Sprung > 0,2 ist ein Rauchmelder, keine Erfolgsmeldung. Die 0,61 hinzuschreiben statt die 0,86 zu behalten, war die eigentliche Leistung.
Der doppelte <m> die verdoppelte Realität
- Symptom
- Der
times_seen-Zähler war rund doppelt so hoch wie plausibel;reappearedhäufiger alsappeared— unmöglich. - Diagnose
- IRIS liefert jede Meldung in beiden XML-Knoten
<ar>und<dp>. Der Collector zählte doppelt. - Fix
- 4-Zeilen-Dedup über die Message-ID; Forward-Cutoff ab Fix-Zeitpunkt.
- Lesson
- Sanity-Checks auf Aggregaten fangen, was Code-Review übersieht.
Das Unwetter, das keins war Baseline-Disziplin
- Symptom
- Bei Störungscode d/45 („Unwetter") zeigte das Wetter in 95 % der Fälle klaren Himmel. Erst-These: „Die Bahn labelt Unwetter falsch."
- Diagnose
- Baseline-Check: auch ohne d/45 sind ~93 % klarer Himmel. Lift über Baseline: fast null — d/45 läuft meist als Co-Code.
- Lesson
- Ein Befund ohne Baseline ist kein Befund. Der klickbare Take wäre falsch gewesen; die Kontrollgruppe kostet 20 Minuten.
Eigene Daten, eigener Unterbau.
Die Bahn veröffentlicht Monats-Durchschnitte; eine öffentliche, minutengenaue Historie gibt es nicht.
Also bauen wir sie selbst: Ein stdlib-only-Python-Daemon pollt das IRIS-Backend im
Minutentakt, reichert um Wetter und DELFI-Fahrplan an und schreibt in eine SQLite-Datei — seit
22. März 2026 ohne Datenlücke. Der Clou: Append-only-History-Tabellen neben den
Snapshots. Erst dadurch lässt sich zeitehrlich trainieren — man rekonstruiert, was zu
einem Zeitpunkt bekannt war, statt aus der Zukunft zu lesen.
Collector auf AWS, Heimserver für Archiv + Training. Watermark-Replikation, 3-2-1-Backup, 66 Tests.
Mehr
RailCast entsteht in Zusammenarbeit von Pet (Richtung, Domain-Wissen, Entscheidungen) und zwei Claude-Agents auf getrennten Hosts — offengelegt in PROCESS.md. Daten aus öffentlicher API, Analysen in SQL/Python, unabhängig reproduzierbar.