| from pathlib import Path |
| import pickle |
|
|
| import gradio as gr |
| import pandas as pd |
|
|
|
|
| BASE_DIR = Path(__file__).resolve().parent |
| MODEL_PATH = BASE_DIR / "apartment_price_model.pkl" |
| DATA_PATH = BASE_DIR / "original_apartment_data_analytics_hs24.csv" |
|
|
|
|
| with MODEL_PATH.open("rb") as f: |
| model = pickle.load(f) |
|
|
|
|
| df = pd.read_csv(DATA_PATH) |
| municipality_columns = ["town", "postalcode", "pop", "pop_dens", "frg_pct", "emp", "tax_income"] |
| municipality_df = df[municipality_columns].dropna().copy() |
| municipality_df["town"] = municipality_df["town"].astype(str).str.strip() |
| municipality_df = municipality_df.drop_duplicates(subset=["town"]).sort_values("town") |
| MUNICIPALITY_DATA = municipality_df.set_index("town").to_dict("index") |
|
|
|
|
| def _build_features(rooms: float, area: float, town: str) -> tuple[pd.DataFrame, dict]: |
| municipality = MUNICIPALITY_DATA[town] |
|
|
| pop = float(municipality["pop"]) |
| pop_dens = float(municipality["pop_dens"]) |
| frg_pct = float(municipality["frg_pct"]) |
| emp = float(municipality["emp"]) |
| tax_income = float(municipality["tax_income"]) |
|
|
| municipality_area_proxy = pop / pop_dens |
| emp_per_resident = emp / pop |
| foreigner_count_est = pop * (frg_pct / 100) |
|
|
| features = pd.DataFrame( |
| [ |
| { |
| "rooms": rooms, |
| "area": area, |
| "pop": pop, |
| "pop_dens": pop_dens, |
| "frg_pct": frg_pct, |
| "emp": emp, |
| "tax_income": tax_income, |
| "municipality_area_proxy": municipality_area_proxy, |
| "emp_per_resident": emp_per_resident, |
| "foreigner_count_est": foreigner_count_est, |
| } |
| ] |
| ) |
|
|
| return features, municipality |
|
|
|
|
| def predict_price(rooms: float, area: float, town: str) -> tuple[str, str]: |
| features, municipality = _build_features(rooms=rooms, area=area, town=town) |
| prediction = float(model.predict(features)[0]) |
|
|
| pop = float(municipality["pop"]) |
| pop_dens = float(municipality["pop_dens"]) |
| frg_pct = float(municipality["frg_pct"]) |
| emp = float(municipality["emp"]) |
| |
| municipality_area_proxy = pop / pop_dens |
| emp_per_resident = emp / pop |
| foreigner_count_est = pop * (frg_pct / 100) |
|
|
| details = ( |
| f"Ort: {town} (PLZ {int(municipality['postalcode'])})\n\n" |
| f"Gemeindedaten:\n" |
| f" Bevölkerung: {int(pop):,}\n" |
| f" Bevölkerungsdichte: {pop_dens:.1f}/km²\n" |
| f" Ausländeranteil: {frg_pct:.1f}%\n" |
| f" Beschäftigte: {int(emp):,}\n" |
| f" Steuerbares Einkommen: CHF {municipality['tax_income']:,.0f}\n\n" |
| f"Zusätzliche berechnete Features:\n" |
| f" Gemeindegröße: {municipality_area_proxy:.2f} km²\n" |
| f" Arbeitsplatzquote: {emp_per_resident:.3f}\n" |
| f" Ausländerpopulation: {int(foreigner_count_est):,}" |
| ) |
|
|
| return f"CHF {prediction:,.2f} pro Monat", details |
|
|
|
|
| demo = gr.Interface( |
| fn=predict_price, |
| inputs=[ |
| gr.Slider(1, 8, value=3, step=0.5, label="Zimmer"), |
| gr.Slider(20, 250, value=80, step=1, label="Wohnfläche (m²)"), |
| gr.Dropdown(choices=list(MUNICIPALITY_DATA.keys()), value="Zürich", label="Gemeinde"), |
| ], |
| outputs=[ |
| gr.Textbox(label="Geschätzte Miete"), |
| gr.Textbox(label="Verwendete Gemeindedaten"), |
| ], |
| examples=[ |
| [2.5, 60, "Zürich"], |
| [3.5, 90, "Winterthur"], |
| [4.5, 120, "Uster"], |
| ], |
| title="Apartment Price Prediction – Kanton Zürich", |
| description=( |
| "Vorhersage der monatlichen Wohnungsmiete mit einem Random-Forest-Regressionsmodell. " |
| "Die App nutzt Basismerkmale und zusätzliche Feature-Engineering-Variablen " |
| "(municipality_area_proxy, emp_per_resident, foreigner_count_est)." |
| ), |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|