import pandas as pd import numpy as np from sklearn.ensemble import GradientBoostingRegressor from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error import gradio as gr import warnings warnings.filterwarnings('ignore') df = pd.read_csv('02_simpleml_komplet_pohyb.csv') zony = pd.read_csv('00_zony.csv') df = df.dropna() df = df[df['pocet_cest'] >= 0] df = df[df['vzdalenost'] >= 0] zony_cil = zony[['nazev','velikost','izolace','skola','zamestnavatel','uzel']].copy() zony_cil.columns = ['cil','cil_velikost','cil_izolace','cil_skola','cil_zamestnavatel','cil_uzel'] df = df.merge(zony_cil, on='cil', how='left') zony_src = zony[['nazev','velikost','izolace']].copy() zony_src.columns = ['zdroj','zdroj_velikost','zdroj_izolace'] df = df.merge(zony_src, on='zdroj', how='left') df = df.fillna(df.median(numeric_only=True)) le_dict = {} for col in ['denni_typ','casove_okno','vekova_skupina','ucel','hlavni_mod']: le = LabelEncoder() df[col+'_enc'] = le.fit_transform(df[col].astype(str)) le_dict[col] = le def hodina_na_okno(h): h = int(h) if 6 <= h <= 8: return 'rano_spicka' elif 9 <= h <= 11: return 'dopoledne' elif 12 <= h <= 14: return 'odpoledne' elif 15 <= h <= 18: return 'vecer_spicka' else: return 'vecer_pozde' OKNO_CZ = { 'rano_spicka': 'Ranni spicka (6-8 h)', 'dopoledne': 'Dopoledne (9-11 h)', 'odpoledne': 'Odpoledne (12-14 h)', 'vecer_spicka':'Vecerni spicka (15-18 h)', 'vecer_pozde': 'Pozdni vecer (19 h+)', } OKNO_REVERSE = {v: k for k, v in OKNO_CZ.items()} VEK_CZ = { 'deti_6_14': 'Deti (6-14 let)', 'studenti_15_19': 'Studenti (15-19 let)', 'dospeli_prace_20_64': 'Dospeli / pracujici (20-64)', 'seniori_65plus': 'Seniori (65+)', } VEK_REVERSE = {v: k for k, v in VEK_CZ.items()} UCEL_CZ = { 'prace': 'Prace / dojizdenj', 'skola': 'Skola', 'nakup_zdravi': 'Nakupy / zdravi', 'volny_cas': 'Volny cas', 'ostatni': 'Ostatni', } UCEL_REVERSE = {v: k for k, v in UCEL_CZ.items()} POCASI_MAP = {'Hezky': 0.1, 'Promenlivě': 0.4, 'Špatně': 0.7, 'Extremně špatně': 1.0} FEAT = [ 'rok','ctvrtleti','denni_typ_enc','casove_okno_enc', 'vekova_skupina_enc','ucel_enc','hlavni_mod_enc', 'udalost','udalost_velikost','podil_spatne_pocasi', 'vzdalenost','cil_velikost','cil_izolace','cil_skola', 'cil_zamestnavatel','cil_uzel','zdroj_velikost','zdroj_izolace' ] X = df[FEAT]; y = df['pocet_cest'] Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.2, random_state=42) mdl = GradientBoostingRegressor(n_estimators=200, learning_rate=0.1, max_depth=4, random_state=42) mdl.fit(Xtr, ytr) mae_m = mean_absolute_error(yte, mdl.predict(Xte)) mae_b = mean_absolute_error(yte, [ytr.mean()]*len(yte)) zlep = round((1 - mae_m/mae_b)*100, 1) FEAT_LABELS = { 'rok':'Rok','ctvrtleti':'Ctvrtleti','denni_typ_enc':'Typ dne', 'casove_okno_enc':'Cas odjezdu','vekova_skupina_enc':'Vek cestujicich', 'ucel_enc':'Ucel cesty','hlavni_mod_enc':'Dopravni mod', 'udalost':'Udalost v cili','udalost_velikost':'Velikost udalosti', 'podil_spatne_pocasi':'Pocasi','vzdalenost':'Vzdalenost trasy', 'cil_velikost':'Velikost cilove obce','cil_izolace':'Izolovanost cile', 'cil_skola':'Skola v cili','cil_zamestnavatel':'Zamestnavatel v cili', 'cil_uzel':'Dopravni uzel v cili','zdroj_velikost':'Velikost vychozi obce', 'zdroj_izolace':'Izolovanost vychozi obce' } top5 = sorted(zip([FEAT_LABELS[f] for f in FEAT], mdl.feature_importances_), key=lambda x: -x[1])[:5] def enc(col, val): le = le_dict[col] return int(le.transform([val])[0]) if val in le.classes_ else 0 def predikuj(zdroj, cil, rezim_casu, okno_vyber, hodina_odjezdu, vekova_skupina, ucel, je_udalost, udalost_vel, pocasi, datum): import datetime if rezim_casu == "Casove okno": casove_okno = OKNO_REVERSE.get(okno_vyber, 'odpoledne') hodina_disp = {"rano_spicka":"~7:00","dopoledne":"~10:00","odpoledne":"~13:00", "vecer_spicka":"~17:00","vecer_pozde":"~20:00"}.get(casove_okno,"") else: casove_okno = hodina_na_okno(hodina_odjezdu) hodina_disp = f"{int(hodina_odjezdu):02d}:00" try: d = datetime.datetime.strptime(datum, "%Y-%m-%d") rok=d.year; ctvrtleti=(d.month-1)//3+1; dow=d.weekday() denni_typ = 'vsedni' if dow<5 else ('sobota' if dow==5 else 'nedele') except: rok=2025; ctvrtleti=2; denni_typ='vsedni' vc = VEK_REVERSE.get(vekova_skupina,'dospeli_prace_20_64') uc = UCEL_REVERSE.get(ucel,'prace') pc = POCASI_MAP.get(pocasi, 0.4) mask = (df['zdroj']==zdroj)&(df['cil']==cil) vzdal = float(df[mask]['vzdalenost'].mean()) if mask.any() else float(df['vzdalenost'].mean()) zc = zony[zony['nazev']==cil]; zs = zony[zony['nazev']==zdroj] row = { 'rok':rok,'ctvrtleti':ctvrtleti, 'denni_typ_enc':enc('denni_typ',denni_typ), 'casove_okno_enc':enc('casove_okno',casove_okno), 'vekova_skupina_enc':enc('vekova_skupina',vc), 'ucel_enc':enc('ucel',uc), 'hlavni_mod_enc':enc('hlavni_mod','autobus'), 'udalost':1 if je_udalost else 0, 'udalost_velikost':float(udalost_vel), 'podil_spatne_pocasi':pc, 'vzdalenost':vzdal, 'cil_velikost':float(zc['velikost'].values[0]) if len(zc) else 0.5, 'cil_izolace':float(zc['izolace'].values[0]) if len(zc) else 0.5, 'cil_skola':int(zc['skola'].values[0]) if len(zc) else 0, 'cil_zamestnavatel':int(zc['zamestnavatel'].values[0]) if len(zc) else 0, 'cil_uzel':int(zc['uzel'].values[0]) if len(zc) else 0, 'zdroj_velikost':float(zs['velikost'].values[0]) if len(zs) else 0.5, 'zdroj_izolace':float(zs['izolace'].values[0]) if len(zs) else 0.5, } pocet = max(0.0, round(float(mdl.predict(pd.DataFrame([row])[FEAT])[0]),1)) if pocet >= 8: stav="PEVNY SPOJ"; rezim="Spoj jede automaticky — dostatecna poptavka." akce="Standardni provoz. Neni treba zasah."; bg="#d4edda"; bc="#28a745"; ikona="🟢" elif pocet >= 4: stav="FLEXBUS — ON DEMAND" rezim="Spoj jede pouze pokud cestujici objednaji pres aplikaci (min. 1 hod. predem)." akce="Aktivovat FlexBus okno. Sledujte objednavky do 1 hod. pred odjezdem." bg="#fff3cd"; bc="#e0a800"; ikona="🟡" else: stav="SPOJ NEJEDE"; rezim="Poptavka prilis nizka — spoj se nevyplati." akce="Zvazze zachranny tarif (taxi voucher) pro izolované oblasti." bg="#f8d7da"; bc="#dc3545"; ikona="🔴" faktory=[] if je_udalost: faktory.append(f"udalost v cili (vel. {udalost_vel})") if pc>=0.7: faktory.append("spatne pocasi snizuje poptavku") if len(zc) and float(zc['izolace'].values[0])>0.4: faktory.append("izolovaná cilova obec") if denni_typ in ['sobota','nedele']: faktory.append("vikend — jiny vzorec pohybu") if casove_okno in ['rano_spicka','vecer_spicka']: faktory.append("spickova hodina — vyssi poptavka") fakt = " | ".join(faktory) if faktory else "standardni podminky" okno_label = OKNO_CZ.get(casove_okno, casove_okno) result = f"""
| Trasa | {zdroj} -> {cil} |
| Datum / cas | {datum} {hodina_disp} ({okno_label}) |
| Cestujici | {vekova_skupina} — {ucel} |
| Klic. faktory | {fakt} |