Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,139 +1,92 @@
|
|
|
|
|
|
1 |
import gradio as gr
|
2 |
-
import
|
3 |
-
import
|
4 |
import pandas as pd
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
6 |
-
|
7 |
-
|
8 |
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
11 |
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
"Ottenbach": 11,
|
20 |
-
"Dübendorf": 191,
|
21 |
-
"Richterswil": 138,
|
22 |
-
"Maur": 195,
|
23 |
-
"Embrach": 56,
|
24 |
-
"Bülach": 53,
|
25 |
-
"Winterthur": 230,
|
26 |
-
"Oetwil am See": 157,
|
27 |
-
"Russikon": 178,
|
28 |
-
"Obfelden": 10,
|
29 |
-
"Wald (ZH)": 120,
|
30 |
-
"Niederweningen": 91,
|
31 |
-
"Dällikon": 84,
|
32 |
-
"Buchs (ZH)": 83,
|
33 |
-
"Rüti (ZH)": 118,
|
34 |
-
"Hittnau": 173,
|
35 |
-
"Bassersdorf": 52,
|
36 |
-
"Glattfelden": 58,
|
37 |
-
"Opfikon": 66,
|
38 |
-
"Hinwil": 117,
|
39 |
-
"Regensberg": 95,
|
40 |
-
"Langnau am Albis": 136,
|
41 |
-
"Dietikon": 243,
|
42 |
-
"Erlenbach (ZH)": 151,
|
43 |
-
"Kappel am Albis": 6,
|
44 |
-
"Stäfa": 158,
|
45 |
-
"Zell (ZH)": 231,
|
46 |
-
"Turbenthal": 228,
|
47 |
-
"Oberglatt": 92,
|
48 |
-
"Winkel": 72,
|
49 |
-
"Volketswil": 199,
|
50 |
-
"Kilchberg (ZH)": 135,
|
51 |
-
"Wetzikon (ZH)": 121,
|
52 |
-
"Zumikon": 160,
|
53 |
-
"Weisslingen": 180,
|
54 |
-
"Elsau": 219,
|
55 |
-
"Hettlingen": 221,
|
56 |
-
"Rüschlikon": 139,
|
57 |
-
"Stallikon": 13,
|
58 |
-
"Dielsdorf": 86,
|
59 |
-
"Wallisellen": 69,
|
60 |
-
"Dietlikon": 54,
|
61 |
-
"Meilen": 156,
|
62 |
-
"Wangen-Brüttisellen": 200,
|
63 |
-
"Flaach": 28,
|
64 |
-
"Regensdorf": 96,
|
65 |
-
"Niederhasli": 90,
|
66 |
-
"Bauma": 297,
|
67 |
-
"Aesch (ZH)": 241,
|
68 |
-
"Schlieren": 247,
|
69 |
-
"Dürnten": 113,
|
70 |
-
"Unterengstringen": 249,
|
71 |
-
"Gossau (ZH)": 115,
|
72 |
-
"Oberengstringen": 245,
|
73 |
-
"Schleinikon": 98,
|
74 |
-
"Aeugst am Albis": 1,
|
75 |
-
"Rheinau": 38,
|
76 |
-
"Höri": 60,
|
77 |
-
"Rickenbach (ZH)": 225,
|
78 |
-
"Rafz": 67,
|
79 |
-
"Adliswil": 131,
|
80 |
-
"Zollikon": 161,
|
81 |
-
"Urdorf": 250,
|
82 |
-
"Hombrechtikon": 153,
|
83 |
-
"Birmensdorf (ZH)": 242,
|
84 |
-
"Fehraltorf": 172,
|
85 |
-
"Weiach": 102,
|
86 |
-
"Männedorf": 155,
|
87 |
-
"Küsnacht (ZH)": 154,
|
88 |
-
"Hausen am Albis": 4,
|
89 |
-
"Hochfelden": 59,
|
90 |
-
"Fällanden": 193,
|
91 |
-
"Greifensee": 194,
|
92 |
-
"Mönchaltorf": 196,
|
93 |
-
"Dägerlen": 214,
|
94 |
-
"Thalheim an der Thur": 39,
|
95 |
-
"Uetikon am See": 159,
|
96 |
-
"Seuzach": 227,
|
97 |
-
"Uitikon": 248,
|
98 |
-
"Affoltern am Albis": 2,
|
99 |
-
"Geroldswil": 244,
|
100 |
-
"Niederglatt": 89,
|
101 |
-
"Thalwil": 141,
|
102 |
-
"Rorbas": 68,
|
103 |
-
"Pfungen": 224,
|
104 |
-
"Weiningen (ZH)": 251,
|
105 |
-
"Bubikon": 112,
|
106 |
-
"Neftenbach": 223,
|
107 |
-
"Mettmenstetten": 9,
|
108 |
-
"Otelfingen": 94,
|
109 |
-
"Flurlingen": 29,
|
110 |
-
"Stadel": 100,
|
111 |
-
"Grüningen": 116,
|
112 |
-
"Henggart": 31,
|
113 |
-
"Dachsen": 25,
|
114 |
-
"Bonstetten": 3,
|
115 |
-
"Bachenbülach": 51,
|
116 |
-
"Horgen": 295
|
117 |
-
}
|
118 |
|
119 |
-
#
|
120 |
-
def
|
121 |
-
|
122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
|
124 |
-
|
125 |
-
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
return np.round(prediction[0][0], 0)
|
131 |
|
132 |
-
#
|
133 |
iface = gr.Interface(
|
134 |
-
fn=
|
135 |
-
inputs=[
|
136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
)
|
138 |
|
139 |
-
|
|
|
|
1 |
+
# app.py
|
2 |
+
|
3 |
import gradio as gr
|
4 |
+
import joblib
|
5 |
+
import json
|
6 |
import pandas as pd
|
7 |
+
import numpy as np
|
8 |
+
|
9 |
+
# --- 1. Modell & Metadaten laden ---
|
10 |
+
# Passe den Pfad an, falls nötig
|
11 |
+
MODEL_PATH = "model.pkl" # oder "random_forest_model.joblib"
|
12 |
+
FEATURES_PATH = "features.json"
|
13 |
|
14 |
+
model = joblib.load(MODEL_PATH)
|
15 |
+
features = json.load(open(FEATURES_PATH, "r"))
|
16 |
|
17 |
+
# Extrahiere die Modell-Kürzel aus den Feature-Namen
|
18 |
+
model_dummies = [
|
19 |
+
feat.replace("Model_group_", "")
|
20 |
+
for feat in features
|
21 |
+
if feat.startswith("Model_group_")
|
22 |
+
]
|
23 |
|
24 |
+
# Median-PS zum Auffüllen, falls notwendig
|
25 |
+
# Hier setzen wir als Default das Median-PS aus Trainingsdaten;
|
26 |
+
# Du kannst hier auch einen festen Wert angeben
|
27 |
+
med_ps = np.median([ # Beispiel: ersetze durch echten Median
|
28 |
+
# Du kannst das eigentlich in deinem Notebook ausrechnen und hier fest eintragen
|
29 |
+
300, # Platzhalter
|
30 |
+
])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
+
# --- 2. Vorhersage-Funktion ---
|
33 |
+
def predict_price(model_main: str, kilometer: float, ps: float, year: int):
|
34 |
+
# 2.1 Basis-Input-Vector erstellen
|
35 |
+
x = dict.fromkeys(features, 0.0)
|
36 |
+
x['Kilometer'] = float(kilometer)
|
37 |
+
x['PS'] = float(ps)
|
38 |
+
x['month'] = 1 # Standard-Januar; nur für Alter nötig
|
39 |
+
x['year'] = int(year)
|
40 |
+
|
41 |
+
# Alter berechnen
|
42 |
+
today = pd.Timestamp('2025-05-24')
|
43 |
+
dt = pd.Timestamp(year=year, month=1, day=1)
|
44 |
+
age_days = (today - dt).days
|
45 |
+
x['age_days'] = float(age_days)
|
46 |
+
x['age_years'] = float(age_days) / 365.0
|
47 |
+
|
48 |
+
# Getriebe: per Default Automatik (gear_Schaltgetriebe = 0)
|
49 |
+
# Wenn du willst, kannst du hier noch ein Dropdown für Getriebe hinzufügen
|
50 |
+
|
51 |
+
# 2.2 Modell-Dummy setzen
|
52 |
+
key = f"Model_group_{model_main}"
|
53 |
+
if key in x:
|
54 |
+
x[key] = 1.0
|
55 |
+
else:
|
56 |
+
x['Model_group_Other'] = 1.0
|
57 |
|
58 |
+
# 2.3 DataFrame bauen
|
59 |
+
df_input = pd.DataFrame([x], columns=features)
|
60 |
|
61 |
+
# 2.4 Vorhersage
|
62 |
+
pred = model.predict(df_input)[0]
|
63 |
+
return round(float(pred), 2)
|
|
|
64 |
|
65 |
+
# --- 3. Gradio-Interface ---
|
66 |
iface = gr.Interface(
|
67 |
+
fn=predict_price,
|
68 |
+
inputs=[
|
69 |
+
gr.Dropdown(
|
70 |
+
choices=sorted(model_dummies),
|
71 |
+
label="BMW Modell-Kürzel",
|
72 |
+
value=model_dummies[0]
|
73 |
+
),
|
74 |
+
gr.Number(label="Kilometer", value=50000),
|
75 |
+
gr.Number(label="PS", value=med_ps),
|
76 |
+
gr.Number(label="Baujahr", value=2020, precision=0)
|
77 |
+
],
|
78 |
+
outputs=gr.Number(label="Geschätzter Preis (CHF)"),
|
79 |
+
title="BMW Price Predictor",
|
80 |
+
description=(
|
81 |
+
"Wähle das BMW-Kürzel (z.B. M3, X5, 120i), gib Kilometer, PS und Baujahr ein – "
|
82 |
+
"und erhalte eine geschätzte Preisprognose in CHF."
|
83 |
+
),
|
84 |
+
examples=[
|
85 |
+
["M3", 20000, 450, 2021],
|
86 |
+
["X5", 50000, 300, 2019],
|
87 |
+
["M4", 10000, 480, 2023],
|
88 |
+
]
|
89 |
)
|
90 |
|
91 |
+
if __name__ == "__main__":
|
92 |
+
iface.launch()
|