Spaces:
Running
Running
| # app.py — Titanic Data Adventure (met uitgebreide introductie naast foto) | |
| import gradio as gr | |
| import pandas as pd | |
| import numpy as np | |
| import os | |
| import plotly.express as px | |
| import plotly.graph_objects as go # voor de gauge | |
| from sklearn.model_selection import train_test_split | |
| from sklearn.preprocessing import OneHotEncoder, StandardScaler | |
| from sklearn.compose import ColumnTransformer | |
| from sklearn.pipeline import Pipeline | |
| from sklearn.ensemble import RandomForestClassifier | |
| from sklearn.decomposition import PCA | |
| # ====================================================== | |
| # DATA LADEN | |
| # ====================================================== | |
| REQUIRED = {"survived","pclass","sex","age","sibsp","parch","fare","embarked"} | |
| def load_data(path="Titanic-Dataset.csv"): | |
| if not os.path.exists(path): | |
| raise FileNotFoundError("❌ Titanic-Dataset.csv niet gevonden in de rootmap.") | |
| df = pd.read_csv(path) | |
| df.columns = [c.lower().strip() for c in df.columns] | |
| missing = REQUIRED - set(df.columns) | |
| if missing: | |
| raise ValueError(f"Ontbrekende kolommen: {', '.join(sorted(missing))}") | |
| for c in df.columns: | |
| if df[c].isna().any(): | |
| df[c] = df[c].fillna(df[c].mode()[0] if df[c].dtype=='O' else df[c].median()) | |
| df["family_size"] = df["sibsp"] + df["parch"] + 1 | |
| df["status"] = df["survived"].map({0:"Niet overleefd", 1:"Overleefd"}) | |
| df["sex"] = df["sex"].astype(str).str.title() | |
| df["embarked"] = df["embarked"].astype(str).str.upper() | |
| return df | |
| df = load_data() | |
| MODEL = None | |
| MODEL_ACC = None | |
| # ====================================================== | |
| # HULPFUNCTIES | |
| # ====================================================== | |
| def hero_path(): | |
| for n in ["titanic_bg.png","titanic_bg.jpg","titanic_bg.jpeg"]: | |
| if os.path.exists(n): | |
| return n | |
| return None | |
| def make_plot(fig, title): | |
| fig.update_layout( | |
| title=title, | |
| paper_bgcolor="rgba(255,255,255,0)", | |
| plot_bgcolor="rgba(255,255,255,0)", | |
| font=dict(color="#0B1C3F"), | |
| title_font=dict(size=18, color="#1B4B91"), | |
| margin=dict(l=40, r=40, t=50, b=40), | |
| legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), | |
| ) | |
| return fig | |
| # ====================================================== | |
| # MODELTRAINING + 2D VISUALISATIE | |
| # ====================================================== | |
| def train_and_embed_solid(): | |
| global MODEL, MODEL_ACC | |
| features = ["pclass","sex","age","sibsp","parch","fare","embarked","family_size"] | |
| X = df[features].copy() | |
| y = df["survived"].astype(int) | |
| cat_cols = ["sex","embarked"] | |
| num_cols = [c for c in features if c not in cat_cols] | |
| pre = ColumnTransformer([ | |
| ("num", StandardScaler(), num_cols), | |
| ("cat", OneHotEncoder(handle_unknown="ignore"), cat_cols), | |
| ]) | |
| pipe = Pipeline([ | |
| ("prep", pre), | |
| ("clf", RandomForestClassifier(n_estimators=300, random_state=42)) | |
| ]) | |
| Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25, random_state=42, stratify=y) | |
| pipe.fit(Xtr, ytr) | |
| MODEL = pipe | |
| MODEL_ACC = pipe.score(Xte, yte) | |
| Z = pre.fit_transform(X) | |
| Z = Z.toarray() if hasattr(Z, "toarray") else Z | |
| emb = PCA(n_components=2, random_state=42).fit_transform(Z) | |
| dvis = pd.DataFrame({"x": emb[:,0], "y": emb[:,1]}) | |
| dvis["Overleving"] = df["status"].values | |
| dvis["Geslacht"] = df["sex"].values | |
| dvis["Klasse"] = df["pclass"].values | |
| dvis["Leeftijd"] = df["age"].values | |
| dvis["Fare (£)"] = df["fare"].values | |
| dvis["Familie"] = df["family_size"].values | |
| for c in ["name","ticket","cabin"]: | |
| if c in df.columns: | |
| dvis[c.capitalize()] = df[c].values | |
| fig = px.scatter( | |
| dvis, x="x", y="y", | |
| color="Overleving", symbol="Klasse", | |
| hover_data=[col for col in dvis.columns if col not in ["x","y"]], | |
| color_discrete_map={"Overleefd":"#1B4B91","Niet overleefd":"#A3B1C6"}, | |
| opacity=0.8 | |
| ) | |
| fig.update_traces(marker=dict(symbol="circle", size=8, line=dict(width=0.6, color="white"))) | |
| fig = make_plot(fig, "2D-projectie (PCA) — elk bolletje is een passagier") | |
| status = f"✅ Model getraind (RandomForest) — nauwkeurigheid: **{MODEL_ACC:.2%}**. 2D-projectie gereed; hover voor details." | |
| return status, fig | |
| # ====================================================== | |
| # TEKSTBLOKKEN — ALLEEN BLAUW, GEEN ZWART/GEEN VET | |
| # ====================================================== | |
| INTRO_MD = """ | |
| <span style='color:#1B4B91'> | |
| April 1912.<br> | |
| De RMS Titanic vertrekt richting New York: een drijvend paleis, gevuld met verwachtingen.<br> | |
| Aan boord: industriëlen in avondkleding, jonge gezinnen met één koffer, bemanningsleden met routine.<br> | |
| De zee is kalm; de toekomst lijkt maakbaar. Meer dan een eeuw later kijken wij mee — niet met verrekijkers of logboeken, maar met data.<br> | |
| Elk record in deze dataset is een menselijk verhaal: iemand met een plek aan tafel, een ticket, een familie, een keuze.<br> | |
| Door de gegevens te verkennen, begrijpen we beter wie overleefde — en waarom. | |
| Waar de onderzoekers van toen vooral vertrouwden op statistiek, logboeken en getuigenissen, gebruiken we nu machine learning — een moderne vorm van datadenken. | |
| Waar een mens patronen zoekt met intuïtie, zoekt een algoritme met precisie. Het kan duizenden kleine verbanden herkennen tussen factoren die wij afzonderlijk misschien niet belangrijk vinden, maar die samen het verschil kunnen maken tussen leven en dood. | |
| Met supervised learning laten we een computer leren van bekende uitkomsten — in dit geval: wie overleefde en wie niet — om te begrijpen hoe die uitkomsten samenhangen met kenmerken als leeftijd, klasse, geslacht, familieomvang en ticketprijs. | |
| Door deze verbanden te modelleren ontstaat een nieuw soort blik op het verleden: één die niet alleen bevestigt wat we al vermoedden, maar ook onverwachte patronen blootlegt die vroeger verborgen bleven. | |
| Soms blijken ogenschijnlijk kleine factoren, zoals de combinatie van gezinssamenstelling en vertrekhaven, een verrassend grote rol te spelen in de overlevingskansen. Andere keren bevestigt het model juist de intuïtie van historici, maar met cijfers en waarschijnlijkheden in plaats van aannames. | |
| Zo wordt machine learning een hulpmiddel bij het herontdekken van oude verhalen — een digitale archeologie van data. | |
| Het is alsof we met een nieuwe bril naar de geschiedenis kijken: een bril die niet oordeelt, maar leert, vergelijkt en verbanden zichtbaar maakt die het menselijk oog destijds niet kon zien. | |
| </span> | |
| """ | |
| EXPLAIN_MD_SIDE = """ | |
| <span style='color:#1B4B91'> | |
| Bij het opstarten traint de computer een RandomForest-model dat leert wie op de Titanic overleefde – en waarom.<br> | |
| Het kijkt naar klasse, geslacht, leeftijd, familieomvang, ticketprijs en haven van vertrek.<br> | |
| Bij het opstarten traint de computer een RandomForest-model dat leert wie op de Titanic overleefde – en waarom. | |
| Een RandomForest bestaat uit een heel bos van beslisbomen: kleine modellen die elk proberen een voorspelling te doen. | |
| Elke boom stelt zijn eigen vragen aan de data. | |
| Was deze passagier man of vrouw? | |
| Hoe oud was hij of zij? | |
| In welke klasse reisde men, en hoeveel familieleden waren er mee aan boord? | |
| Wat kostte het ticket, en vanuit welke haven vertrok de persoon? | |
| Elke boom komt op basis van die antwoorden tot een conclusie: overleefd, of niet overleefd. | |
| Sommige bomen zijn optimistischer, andere voorzichtiger. | |
| Wanneer al die beslisbomen samen worden genomen, stemt het hele bos: | |
| de meerderheid beslist. Dat maakt het model robuust | |
| De nauwkeurigheid, bijvoorbeeld 74%, vertelt hoeveel van die voorspellingen juist zijn. | |
| Een score van 74% betekent dus dat het model in 74 van de 100 gevallen correct inschat of iemand overleefde. | |
| Geen perfecte voorspelling, maar wel een indrukwekkend resultaat voor een algoritme dat niets “weet” over de ramp, behalve wat de data het vertelt. | |
| Het bijzondere aan deze methode is dat ze meer kan zien dan een mens. | |
| Ze ontdekt kleine samenhangen die we zelf misschien over het hoofd zouden zien: | |
| dat jonge vrouwen in de derde klasse andere kansen hadden dan oudere mannen in de tweede, | |
| of dat gezinnen die vanuit Cherbourg vertrokken een iets hogere overlevingskans hadden dan die uit Southampton. | |
| Met elke berekening wordt het model een beetje wijzer | |
| </span> | |
| """ | |
| # ====================================================== | |
| # OVERIGE GRAFIEKEN | |
| # ====================================================== | |
| def plot_age_hist(dfx): | |
| f = px.histogram(dfx, x="age", color="status", nbins=30, barmode="overlay", opacity=0.75, | |
| color_discrete_map={"Overleefd":"#1B4B91","Niet overleefd":"#A3B1C6"}) | |
| return make_plot(f, "Leeftijdsverdeling per overlevingsstatus") | |
| def plot_gender(dfx): | |
| f = px.pie(dfx, names="sex", color="sex", | |
| color_discrete_map={"Male":"#A3B1C6","Female":"#1B4B91"}, hole=0.35) | |
| return make_plot(f, "Verdeling geslacht (alle passagiers)") | |
| def plot_fare_box(dfx): | |
| f = px.box(dfx, x="pclass", y="fare", color="status", | |
| color_discrete_map={"Overleefd":"#1B4B91","Niet overleefd":"#A3B1C6"}) | |
| return make_plot(f, "Ticketprijs per klasse (met overleving)") | |
| # ====================================================== | |
| # INTERACTIEVE VOORSPELLING — UITGEBREIDE TEKST & OPMAAK | |
| # ====================================================== | |
| def predict_and_story(pclass, sex, age, sibsp, parch, fare, embarked): | |
| if MODEL is None: | |
| return "⏳ Het model initialiseert nog. Probeer het zo nog eens." | |
| X_row = pd.DataFrame([{ | |
| "pclass": int(pclass), "sex": sex, "age": float(age), | |
| "sibsp": int(sibsp), "parch": int(parch), "fare": float(fare), | |
| "embarked": embarked, "family_size": int(sibsp)+int(parch)+1 | |
| }]) | |
| prob = float(MODEL.predict_proba(X_row)[0,1]) | |
| pct = prob * 100 | |
| klasse_txt = {1:"eerste",2:"tweede",3:"derde"}[int(pclass)] | |
| haven_txt = {"C":"Cherbourg","Q":"Queenstown","S":"Southampton"}[embarked] | |
| rol_txt = "vrouw" if sex.lower().startswith("v") else "man" | |
| familie_totaal = int(sibsp) + int(parch) + 1 | |
| if pct >= 75: | |
| analyse = ( | |
| "Je kansen zijn uitzonderlijk goed.<br>" | |
| "Het model ziet een combinatie van factoren die sterk wijzen op overleving — " | |
| "je positie aan boord, je profiel, en de omstandigheden rondom jouw reis. " | |
| "In dit scenario weegt geluk én voorrang zwaar mee: de uitkomst is gunstig." | |
| ) | |
| avontuur = ( | |
| "De nacht is helder en koud. Het dek glanst van het ijs. " | |
| "In de verte klinkt geroep; een sloep wordt neergelaten. " | |
| "Je ademt wolkjes van spanning terwijl je dichterbij sluipt. " | |
| "Er is nog plek — handen trekken je aan boord. " | |
| "Het schip helt achter je, maar jij leeft." | |
| ) | |
| elif pct >= 50: | |
| analyse = ( | |
| "Je kansen zijn redelijk goed.<br>" | |
| "Sommige kenmerken spelen in jouw voordeel, andere niet. " | |
| "Het model schat dat de balans neigt naar overleven — " | |
| "een situatie waarin oplettendheid, toeval en tijd samen het verschil kunnen maken." | |
| ) | |
| avontuur = ( | |
| "Het dek is onrustig; stemmen, touwen, water dat tegen de reling slaat. " | |
| "Je aarzelt even, zoekt naar je familie. " | |
| "In de chaos vind je een plek in een halfgevulde sloep. " | |
| "Je hoort geroep achter je, maar de boot drijft al weg. " | |
| "Je leeft — maar de stilte die volgt is zwaarder dan het lawaai." | |
| ) | |
| elif pct >= 25: | |
| analyse = ( | |
| "De kansen zijn fifty-fifty.<br>" | |
| "Het model weegt veel factoren die elkaar in evenwicht houden — " | |
| "je klasse, geslacht, leeftijd, familieomvang en vertrekhaven. " | |
| "Niets is beslissend, alles telt mee. " | |
| "De uitkomst lijkt even onzeker als die nacht zelf." | |
| ) | |
| avontuur = ( | |
| "De nacht is stil; fluiten, geroep, voetstappen echoën over het dek. " | |
| "Je klampt je vast aan je familie terwijl het schip verder helt. " | |
| "Het water glinstert als glas in het maanlicht. " | |
| "Op het laatste moment spring je — niet wetend of het water of de lucht je zal dragen. " | |
| "De nacht is lang, maar aan de horizon gloeit het eerste licht." | |
| ) | |
| else: | |
| analyse = ( | |
| "Het ziet er somber uit.<br>" | |
| "De omstandigheden zijn tegen je — klasse, positie, drukte bij de sloepen. " | |
| "Het model herkent een profiel dat destijds zelden overleefde. " | |
| "Toch blijft elke voorspelling slechts een kans; hoop is geen getal, maar een verhaal." | |
| ) | |
| avontuur = ( | |
| "Het geluid van brekend staal vult de lucht. " | |
| "Het dek helt scherp, water stroomt langs je voeten. " | |
| "Je klampt je vast aan een reling, voelt de kou door je heen snijden. " | |
| "In de verte hoor je stemmen, dan alleen nog de zee. " | |
| "De oceaan is meedogenloos — maar even, voor het verdwijnen, is alles stil." | |
| ) | |
| return f""" | |
| ### 🔮 Jouw overlevingskans: **{pct:.1f}%** | |
| **Situatie:**<br> | |
| {rol_txt}, {klasse_txt} klasse, inscheping {haven_txt}.<br> | |
| Leeftijd: {int(age)} jaar.<br> | |
| Familie aan boord: {int(sibsp)} broers/zussen en {int(parch)} ouders/kinderen (totaal {familie_totaal}).<br> | |
| Ticketprijs: £{float(fare):.2f}.<br><br> | |
| **Analyse:**<br> | |
| {analyse}<br><br> | |
| **Avontuur:**<br> | |
| {avontuur} | |
| """ | |
| # ====================================================== | |
| # LIVE GAUGE VOOR JOUW SCENARIO (met kleurbanden + threshold) | |
| # ====================================================== | |
| def live_viz(pclass, sex, age, sibsp, parch, fare, embarked): | |
| # Retourneer een gauge die live de kans toont (0–100%) met kleurbanden | |
| if MODEL is None: | |
| return make_plot(go.Figure(), "Jouw overlevingskans (live)") | |
| X_row = pd.DataFrame([{ | |
| "pclass": int(pclass), "sex": sex, "age": float(age), | |
| "sibsp": int(sibsp), "parch": int(parch), "fare": float(fare), | |
| "embarked": embarked, "family_size": int(sibsp)+int(parch)+1 | |
| }]) | |
| prob = float(MODEL.predict_proba(X_row)[0,1]) * 100.0 | |
| fig = go.Figure(go.Indicator( | |
| mode="gauge+number", | |
| value=prob, | |
| number={"suffix": "%", "valueformat": ".1f"}, | |
| gauge={ | |
| "axis": {"range": [0, 100]}, | |
| "bar": {"thickness": 0.25}, | |
| # Kleurbanden (0–25 rood, 25–50 oranje, 50–75 geel, 75–100 groen) | |
| "steps": [ | |
| {"range": [0, 25], "color": "#FDECEC"}, | |
| {"range": [25, 50], "color": "#FFF2E0"}, | |
| {"range": [50, 75], "color": "#FFF9D6"}, | |
| {"range": [75, 100], "color": "#E8F6EA"}, | |
| ], | |
| # Threshold-lijn op actuele waarde | |
| "threshold": { | |
| "line": {"color": "#1B4B91", "width": 4}, | |
| "thickness": 0.9, | |
| "value": prob | |
| }, | |
| }, | |
| title={"text": "Jouw overlevingskans (live)"} | |
| )) | |
| return make_plot(fig, "Jouw overlevingskans (live)") | |
| # ====================================================== | |
| # UI + LAYOUT | |
| # ====================================================== | |
| CUSTOM_CSS = """ | |
| body { background:#FFFFFF; color:#0B1C3F; } | |
| .gradio-container { background:#FFFFFF; } | |
| h1, h2, h3, h4 { color:#1B4B91; } | |
| .panel, .intro-card { background:#F9FBFF; border:1px solid #E0E6F3; border-radius:12px; padding:16px; } | |
| .hero-img img { border-radius:12px; border:1px solid #E0E6F3; } | |
| .kpi { display:flex; flex-direction:column; align-items:center; justify-content:center; | |
| background:#FFFFFF; border:1px solid #E0E6F3; border-radius:12px; padding:14px; } | |
| .kpi .value { font-size:1.6rem; font-weight:800; color:#1B4B91; } | |
| .kpi .label { font-size:.9rem; color:#3F557A; } | |
| .explain-card { background:#EAF0FF; border-radius:12px; padding:18px; border:1px solid #D5E0FA; } | |
| """ | |
| with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Default(primary_hue="blue")) as demo: | |
| # Header-intro + foto | |
| with gr.Row(): | |
| with gr.Column(scale=2, min_width=420): | |
| gr.Markdown(INTRO_MD, elem_classes=["intro-card"]) | |
| with gr.Column(scale=1, min_width=320): | |
| hp = hero_path() | |
| if hp: | |
| gr.Image(value=hp, interactive=False, show_label=False, elem_classes=["hero-img"]) | |
| else: | |
| gr.Markdown("⚠️ **Geen afbeelding gevonden.** Plaats `titanic_bg.png` of `titanic_bg.jpg` in de root.") | |
| # Passagierslijst — volledige dataset, scrollbaar | |
| with gr.Column(elem_classes=["panel"]): | |
| gr.Markdown("## 👥 Passagierslijst — volledige dataset (scrollbaar)") | |
| gr.DataFrame( | |
| value=df, # volledige dataset | |
| wrap=True, | |
| interactive=False, # alleen-lezen | |
| label="Titanic-passagiers", | |
| max_height=320 # vaste hoogte -> scroll binnen de tabel | |
| ) | |
| # Spacer om overlap te voorkomen | |
| gr.Markdown("") | |
| # Panel: status + 2D-plot links en uitleg rechts | |
| with gr.Column(elem_classes=["panel"]): | |
| gr.Markdown("## 🔧 Initialisatie & Modeltraining") | |
| status_md = gr.Markdown("⏳ Initialiseren…") | |
| with gr.Row(): | |
| with gr.Column(scale=2, min_width=420): | |
| train_plot = gr.Plot(label="2D-projectie — elk bolletje is een passagier") | |
| with gr.Column(scale=1, min_width=320): | |
| gr.Markdown(EXPLAIN_MD_SIDE, elem_classes=["explain-card"]) | |
| # KPIs | |
| with gr.Row(): | |
| gr.HTML(f"<div class='kpi'><div class='value'>{len(df):,}</div><div class='label'>Totaal passagiers</div></div>") | |
| gr.HTML(f"<div class='kpi'><div class='value'>{int(df['survived'].sum()):,}</div><div class='label'>Overlevenden</div></div>") | |
| gr.HTML(f"<div class='kpi'><div class='value'>{df['survived'].mean()*100:.1f}%</div><div class='label'>% Overleefd</div></div>") | |
| gr.HTML(f"<div class='kpi'><div class='value'>{', '.join(map(str, sorted(df['pclass'].unique())))}</div><div class='label'>Klassen</div></div>") | |
| # Overige visualisaties | |
| gr.Markdown("## 📊 Verken de data", elem_classes=["panel"]) | |
| with gr.Row(): | |
| g2 = gr.Plot(label="Leeftijdsverdeling per status") | |
| g3 = gr.Plot(label="Geslachtsverdeling") | |
| with gr.Row(): | |
| g4 = gr.Plot(label="Ticketprijs per klasse") | |
| # Interactieve voorspelling | |
| with gr.Column(elem_classes=["panel"]): | |
| # Tekstblok + gauge naast elkaar | |
| with gr.Row(): | |
| with gr.Column(scale=2, min_width=420): | |
| gr.Markdown("""## 🔮 Jouw scenario — bereken je overlevingskans en lees je scène | |
| Hier kun je ontdekken **hoe groot jouw kans op overleving** zou zijn geweest aan boord van de *Titanic* — en meteen het **verhaal van jouw nacht** lezen. | |
| 1. **Kies je profiel** | |
| - **Klasse:** 1e, 2e of 3e klasse (je reiscomfort en dekpositie). | |
| - **Geslacht:** man of vrouw — dit had invloed op reddingsvoorrang. | |
| - **Leeftijd:** jouw leeftijd in jaren. | |
| - **Broers/zussen** en **ouders/kinderen**: hoeveel familieleden reisden met je mee. | |
| - **Ticketprijs (£):** hoe duur je passage was. | |
| - **Vertrekhaven:** Cherbourg (C), Queenstown (Q) of Southampton (S). | |
| 2. **Klik op de knop “🎲 Bereken én vertel mijn verhaal”** | |
| Het model schat jouw **overlevingskans** op basis van historische patronen. | |
| 3. **Lees je persoonlijke scène** | |
| Onder de knop verschijnt een korte beschrijving die je meeneemt naar die nacht — | |
| gebaseerd op jouw ingevulde profiel en de berekende kans. | |
| > 💡 *De voorspelling is een statistische schatting, geen oordeel. | |
| > Ze helpt je zien hoe factoren zoals klasse, geslacht en leeftijd destijds iemands lot konden bepalen.*""") | |
| with gr.Column(scale=1, min_width=320): | |
| viz_plot = gr.Plot(label="Jouw overlevingskans (live)") | |
| with gr.Row(): | |
| ui_pclass = gr.Slider(1, 3, value=2, step=1, label="Klasse (1=1e, 3=3e)") | |
| ui_sex = gr.Radio(["Man","Vrouw"], value="Man", label="Geslacht") | |
| ui_age = gr.Slider(0, 80, value=30, label="Leeftijd") | |
| with gr.Row(): | |
| ui_sibsp = gr.Slider(0, 8, value=1, step=1, label="Broers/Zussen aan boord") | |
| ui_parch = gr.Slider(0, 6, value=0, step=1, label="Ouders/Kinder(en) aan boord") | |
| ui_fare = gr.Slider(0, 600, value=50, label="Ticketprijs (£)") | |
| ui_emb = gr.Radio(["C","Q","S"], value="S", label="Vertrekhaven") | |
| btn = gr.Button("🎲 Bereken én vertel mijn verhaal", variant="primary") | |
| story_out = gr.Markdown() | |
| # Loads & acties | |
| demo.load(fn=train_and_embed_solid, inputs=[], outputs=[status_md, train_plot]) | |
| demo.load(lambda: (plot_age_hist(df), plot_gender(df), plot_fare_box(df)), inputs=[], outputs=[g2, g3, g4]) | |
| # Initiele gauge (na modeltraining): gebruik de default UI-waarden | |
| demo.load(lambda: live_viz(2, "Man", 30, 1, 0, 50, "S"), inputs=[], outputs=[viz_plot]) | |
| # Live updates bij elke wijziging | |
| for comp in [ui_pclass, ui_sex, ui_age, ui_sibsp, ui_parch, ui_fare, ui_emb]: | |
| comp.change(live_viz, | |
| inputs=[ui_pclass, ui_sex, ui_age, ui_sibsp, ui_parch, ui_fare, ui_emb], | |
| outputs=viz_plot) | |
| # Ook updaten bij de knop | |
| btn.click(predict_and_story, | |
| inputs=[ui_pclass, ui_sex, ui_age, ui_sibsp, ui_parch, ui_fare, ui_emb], | |
| outputs=story_out) | |
| btn.click(live_viz, | |
| inputs=[ui_pclass, ui_sex, ui_age, ui_sibsp, ui_parch, ui_fare, ui_emb], | |
| outputs=viz_plot) | |
| demo.launch() | |