Jak nám jezdí vlaky?

O mně

David Koňařík

Student MFF UK

Autor JrUtil, softwarového projektu na zpracovávání dat veřejné dopravy, a spousty žádostí o informace.

https://dvdkon.gitlab.io
dvd​kon​@konarici.cz

Téma

Zpracování dat o reálném provozu vlaků.

Příklad dat

Vlak Os 10455 dne 2022-08-20

Zastávka prav.příj. příjezd prav.odj. odjezd
P.-Velká Chuchle z 13:44 13:43 13:44 13:44
Odb Závodiště 13:45 13:45 13:45 13:45
Odb Barrandov 13:47 13:48 13:47 13:48
Pr.-Smíchov již.zhl. 13:50 13:49 13:50 13:49
Praha-Smíchov 13:53 13:51 13:54 13:54
Pr.-Smíchov sev.zhl. 13:55 13:55 13:55 13:55
Výh Praha-Vyšehrad 13:57 13:57 13:57 13:57
Praha hl.n. 14:01 14:01

Rozsah dat

Skoro kompletní data za zhruba poslední rok (1. 8. 2021–2022).

16 897 čísel vlaků, 2 718 257 jízd ~7 000 000 celkem minut zpoždění v koncové zastávce, ~900 000 minut náskoku, ~140 sekund průměrné zpoždění (včetně náskoků), 0 minut medián zpoždění.

-- Počet čísel vlaků
SELECT COUNT(*) FROM (SELECT DISTINCT tripid FROM tripdetails) AS i;

-- Počet jízd
SELECT COUNT(*) FROM tripdetails;

CREATE TEMPORARY TABLE final_delays AS (
    SELECT DISTINCT ON (tripid, tripstartdate)
        tripid, tripstartdate, arrivedat - shouldarriveat AS delay
    FROM stophistory AS sh
    WHERE arrivedat IS NOT NULL
    ORDER BY tripid, tripstartdate, tripstopindex DESC);

-- Součet zpoždění (pro náskok jen otočit nerovnost)
SELECT EXTRACT(epoch FROM SUM(delay))/60
FROM final_delays WHERE delay > '0';

-- Průměrné zpoždění
SELECT EXTRACT(epoch FROM AVG(delay))
FROM final_delays;

-- Medián zpoždění
SELECT EXTRACT(epoch FROM percentile_cont(0.5) within group (order by delay))
FROM final_delays;

Dálkové vlaky

1 135 čísel vlaků, 87 107 jízd ~1 1000 000 celkem minut zpoždění v koncové zastávce, ~37 000 minut náskoku, ~740 sekund průměrné zpoždění (včetně náskoků), 5 minut medián zpoždení.

CREATE TEMPORARY TABLE final_delays_longdistance AS (
SELECT * FROM final_delays WHERE tripid ~ '-CZTRAINT-(EC|IC|Ex|SC|rj|LE|RJ)-.*');

SELECT COUNT(DISTINCT tripid) FROM final_delays_longdistance;
SELECT COUNT(*) FROM final_delays_longdistance;

SELECT EXTRACT(epoch FROM SUM(delay))/60
FROM final_delays_longdistance WHERE delay > '0';

SELECT EXTRACT(epoch FROM AVG(delay))
FROM final_delays_longdistance;

SELECT EXTRACT(epoch FROM percentile_cont(0.5) within group (order by delay))
FROM final_delays_longdistance;

Nejzpožděnější vlaky

Vlak Den Zastávka Zpoždění Poznámka
Ex 11986 2022-02-28 Mosty u Jablunkova 19:11:00 Špatný JŘ?
Ex 43774 2022-02-28 Mosty u Jablunkova 19:11:00 Nedojel, špatný JŘ?
R 1122 RegioJet 2022-04-29 Bohumín os.n. 19:00:00 Jediný záznam
Os 18301 2022-06-10 Kralupy nad Vltavou 14:20:00 Jediný záznam
Os 7906 2022-02-17 Březnice 14:07:00 Nedojel
Ex 566 Západní expres 2022-02-17 Plzeň hl.n.os.n. 13:16:00 Nedojel
Os 5055 2022-03-01 Choceň 13:14:00 Jediný záznam
Os 21247 2022-01-08 Včelnička 12:56:00 Špatný JŘ?
-- Nejzpožděnější vlaky
SELECT CONCAT('[', shortname, '](https://rt.jr.ggu.cz/Trip/', tripid, '/', tripstartdate ,')'), tripstartdate, name, delay
FROM (
    SELECT DISTINCT ON (i.tripid, i.tripstartdate) td.shortname, s.name, i.tripid, i.tripstartdate, delay
    FROM (
        SELECT GREATEST(COALESCE(departedat - shoulddepartat, '0'), COALESCE(arrivedat - shoulddepartat, '0')) AS delay, *
        FROM stophistory
        ORDER BY delay DESC
        LIMIT 100) AS i
    LEFT JOIN stops AS s ON s.id = i.stopid AND s.validdaterange @> i.tripstartdate
    LEFT JOIN tripdetails AS td ON td.tripid = i.tripid AND td.tripstartdate = i.tripstartdate
    ORDER BY i.tripid, i.tripstartdate, delay DESC) AS i2
ORDER BY delay DESC;

Nejzpožděnější (rozumnější) vlaky

Vlaky, které dojely a měly na odjezdu z výchozí stanice max. 5h zpoždění.

Vlak Den Zastávka Zpoždění
Os 13857 2021-09-13 Litovel 09:23:00
R 607 Krušnohor 2022-02-17 Odb Dubina 07:45:00
Os 96005 2021-10-20 Šakvice 07:39:00
SC 506 Pendolino 2022-02-17 Praha hl.n. 07:38:00
R 705 Vltava 2022-02-17 Č.Buděj. býv. st.7 07:10:00
Os 6058 2022-01-16 M.Boleslav m. předn. 07:00:00
Os 21241 2021-12-26 hi JH-Skrýchov JHMD 06:56:00
SELECT CONCAT('[', shortname, '](https://rt.jr.ggu.cz/Trip/', tripid, '/', tripstartdate ,')'), tripstartdate, name, delay
FROM (
    SELECT DISTINCT ON (i.tripid, i.tripstartdate) td.shortname,  i.tripstartdate, s.name, i.delay, i.tripid
    FROM (
        SELECT GREATEST(COALESCE(departedat - shoulddepartat, '0'), COALESCE(arrivedat - shoulddepartat, '0')) AS delay, *
        FROM stophistory AS sh
        ORDER BY delay DESC
        LIMIT 1000) AS i
    LEFT JOIN stops AS s ON s.id = i.stopid AND s.validdaterange @> i.tripstartdate
    LEFT JOIN tripdetails AS td ON td.tripid = i.tripid AND td.tripstartdate = i.tripstartdate
    ORDER BY i.tripid, i.tripstartdate, delay DESC) AS i2
WHERE (SELECT arrivedat
       FROM stophistory AS sh2
       WHERE sh2.tripid = i2.tripid AND sh2.tripstartdate = i2.tripstartdate
       ORDER BY tripstopindex DESC LIMIT 1) IS NOT NULL
  AND (SELECT departedat - shoulddepartat
       FROM stophistory AS sh2
       WHERE sh2.tripid = i2.tripid AND sh2.tripstartdate = i2.tripstartdate
       AND tripstopindex = 1) < '5 hours'
ORDER BY delay DESC;

Histogram zpoždění

Specificky zpoždění v sekundách v poslední zaznamenané stanici.

Skript delay_histogram.py

Histogram zpoždění

Osobní vlaky
Vlaky R
Dálkové vlaky

Histogram změny zpoždění

Brno hl.n. (28 000 : 26 000)

Skript stop_histogram.py.

Počítá “změnu zpoždění”, tedy zpoždění na odjezdu - na příjezdu.

Histogram změny zpoždění

České Budějovice (3 000 : 4 600)

Histogram změny zpoždění

Hostivice (22 000 : 14 800)

Histogram změny zpoždění

Úvaly ➞ Kolín

Skript route_histogram.py.

Histogram změny zpoždění

Praha hl.n. ➞ Brno hl.n.

Změna zpoždění v zastávce

Z QGISu

Změna zpoždění (dálkové vlaky)

Změna zpoždění na trase

Čištění dat

Čas od času opravdu divné vlaky, například:

A taky chyby sbírání dat, nejčastěji špatně určené datum.

Vesměs hrubě promazáno.

CREATE TEMPORARY TABLE stophistory_large_change AS
SELECT * FROM
    (SELECT
        tripid, tripstartdate, tripstopindex,
        shouldarriveat - LAG(shouldarriveat, 1) OVER w AS sarr_change,
        arrivedat - LAG(arrivedat, 1) OVER w AS arr_change,
        shoulddepartat - LAG(shoulddepartat, 1) OVER w AS sdep_change,
        departedat - LAG(departedat, 1) OVER w AS dep_change
    FROM stophistory
    WINDOW w AS (PARTITION BY tripid, tripstartdate ORDER BY tripstopindex ASC)) AS i
WHERE COALESCE(sarr_change > '12 hours' OR sarr_change < '-12 hours', FALSE)
   OR COALESCE(arr_change > '12 hours' OR arr_change < '-12 hours', FALSE)
   OR COALESCE(sdep_change > '12 hours' OR sdep_change < '-12 hours', FALSE)
   OR COALESCE(dep_change > '12 hours' OR dep_change < '-12 hours', FALSE);

CREATE TEMPORARY TABLE stophistory_large_delay AS
SELECT * FROM
    (SELECT
        tripid, tripstartdate, tripstopindex,
        arrivedat - shouldarriveat AS arrdelay,
        departedat - shoulddepartat AS depdelay
     FROM stophistory) AS i
WHERE COALESCE(arrdelay > '20 hours' OR arrdelay < '-1 hour', FALSE)
   OR COALESCE(depdelay > '20 hours' OR depdelay < '-1 hour', FALSE);

DELETE FROM stophistory AS sh
WHERE EXISTS (SELECT FROM stophistory_large_change AS shc
              WHERE shc.tripid = sh.tripid
                  AND shc.tripstartdate = sh.tripstartdate)
   OR EXISTS (SELECT FROM stophistory_large_delay AS shd
                 WHERE shd.tripid = sh.tripid
                   AND shd.tripstartdate = sh.tripstartdate);

Zdroj(e) dat

RtCollect, součást JrUtil, periodicky scrapuje všechny vlaky v GRAPPu.

CSV API RtCollect: https://rt.jr.ggu.cz/api.html
prosím opatrně, streamování nemusí fungovat, dotazy raději po měsících.

Většina vlaků v seznamu zůstane i desítky minut po příjezdu, přeshraniční ne.

Existuje i SOAP rozhraní, ale…

Žádací dobrodružství

Podle zákona 106/1999 Sb. jsem SŽ žádal o několik věcí:

Žádost: “Aktuální polohy … ve strojově čitelném formátu formou on-line přístupu”

Odpovědi:

Žádací dobrodružství, kap. 2

Žádost: historické polohy vlaků

Odpověď:

Žádací dobrodružství, spinoff

Vlakové JŘ jsou už zveřejňovány pořádně a úplně.
Včetně kolejí a nástupišť!

Linkové JŘ už ne: Nejprve nic, pak XLS, nyní osekané “skoro-JDF”.

CHAPS odmítá zákonnou povinnost, Ministerstvo buď odkazuje na ně, nebo popírá existenci úplných dat.

Není všem dnům konec: čekám na vyřízení stížnosti.

Kam dál

Data jsou k dispozici, těším se na vaše využití!

Po OpenAltu zveřejním kód na https://dvdkon.gitlab.io/openalt-2022-vlaky

Fórum o datech veřejné dopravy: https://dadof.konarici.cz
Všechny svoje další projekty/data budu oznamovat zde.

Poznámky na konec

V této prezentaci jsou čas od času poznámky, které zobrazíte stisknutím n nebo kliknutím sem.

Toto je poznámka!