import gradio as gr import pandas as pd import numpy as np import pickle import os # Load model model_filename = "apartment_rf_model.pkl" with open(model_filename, mode="rb") as f: model_data = pickle.load(f) model = model_data["model"] features = model_data["features"] log_target = model_data["log_target"] DEFAULT_POP_DENS_CITY = 4729.0 DEFAULT_POP_DENS_OUTSIDE = 1328.0 DEFAULT_FRG_PCT = 25.0 DEFAULT_LAT_CITY = 47.380402 DEFAULT_LON_CITY = 8.530496 DEFAULT_LAT_OUTSIDE = 47.424900 DEFAULT_LON_OUTSIDE = 8.638663 def predict_price( rooms, area, tax_income, luxurious, furnished, temporary, zurich_city, attika, loft, seesicht, kreis ): # Use data-derived defaults for hidden features pop_dens = DEFAULT_POP_DENS_CITY if zurich_city else DEFAULT_POP_DENS_OUTSIDE lat = DEFAULT_LAT_CITY if zurich_city else DEFAULT_LAT_OUTSIDE lon = DEFAULT_LON_CITY if zurich_city else DEFAULT_LON_OUTSIDE frg_pct = DEFAULT_FRG_PCT # Derived features log_area = np.log1p(area) log_rooms = np.log1p(rooms) rooms_area_ratio = rooms / (area + 1) room_per_m2 = area / rooms if rooms > 0 else area income_density_score = tax_income * pop_dens / 1e6 log_income_dens = np.log1p(income_density_score) log_pop_dens = np.log1p(pop_dens) area_rooms_interact = area * rooms # Kreis dummies kreis_cols = {f"Kreis {i}": 0 for i in range(1, 13)} if kreis in kreis_cols: kreis_cols[kreis] = 1 input_dict = { "rooms": rooms, "area": area, "log_area": log_area, "log_rooms": log_rooms, "pop_dens": pop_dens, "log_pop_dens": log_pop_dens, "frg_pct": frg_pct, "tax_income": tax_income, "income_density_score": income_density_score, "log_income_dens": log_income_dens, "rooms_area_ratio": rooms_area_ratio, "area_rooms_interact": area_rooms_interact, "lat": lat, "lon": lon, "luxurious": int(luxurious), "furnished": int(furnished), "temporary": int(temporary), "zurich_city": int(zurich_city), "room_per_m2": room_per_m2, **kreis_cols, "(ATTIKA)": int(attika), "(LOFT)": int(loft), "(SEESICHT)": int(seesicht), "(LUXURIÖS)": 0, "(POOL)": 0, "(EXKLUSIV)": 0, } input_df = pd.DataFrame([input_dict])[features] prediction_log = model.predict(input_df)[0] prediction = np.expm1(prediction_log) return f"Estimated Monthly Rent: **CHF {prediction:,.0f}**" demo = gr.Interface( fn=predict_price, inputs=[ gr.Number(label="Number of Rooms", value=3.0), gr.Number(label="Living Area (m²)", value=80), gr.Number(label="Municipal Tax Income (CHF)", value=80000), gr.Checkbox(label="Luxurious"), gr.Checkbox(label="Furnished"), gr.Checkbox(label="Temporary"), gr.Checkbox(label="Located in Zurich City"), gr.Checkbox(label="Attika (Penthouse)"), gr.Checkbox(label="Loft"), gr.Checkbox(label="Lake View (Seesicht)"), gr.Dropdown( label="Zurich District (Kreis)", choices=["None"] + [f"Kreis {i}" for i in range(1, 13)], value="None" ), ], outputs=gr.Markdown(), submit_btn="Predict Rent", examples=[ [3.0, 80, 70000, False, False, False, True, False, False, False, "Kreis 3"], [4.5, 120, 120000, True, False, False, True, True, False, True, "Kreis 1"], [2.0, 55, 55000, False, True, False, False, False, False, False, "None"], ], title="Zurich Apartment Rent Predictor", description=( "Predict the monthly rental price for an apartment in the Canton of Zurich.\n\n" "This model uses a **Gradient Boosting Regressor** trained on ~800 Zurich apartment listings, " "with features including location (district), apartment characteristics, " "and the **Income-Density Score** (a neighbourhood affluence proxy)." ), theme=gr.themes.Soft(), ) if __name__ == "__main__": demo.launch()