gumannic's picture
Upload 4 files
9f0d482 verified
import json
import gradio as gr
with open("model_data.json") as f:
DATA = json.load(f)
MUNICIPALITIES = sorted(DATA["municipalities"])
def get_base(muni, area):
lk = DATA["base_lookup"].get(muni, {})
if not lk:
return 2000
keys = sorted(int(k) for k in lk)
lo = max((k for k in keys if k <= area), default=keys[0])
hi = min((k for k in keys if k >= area), default=keys[-1])
if lo == hi:
return lk[str(lo)]
t = (area - lo) / (hi - lo)
return lk[str(lo)] + t * (lk[str(hi)] - lk[str(lo)])
def get_rooms_delta(rooms):
rooms = float(rooms)
rd = DATA["rooms_deltas"]
key = str(int(rooms)) if rooms == int(rooms) else str(rooms)
if key in rd:
return rd[key]
keys = sorted(float(k) for k in rd)
lo = max((k for k in keys if k <= rooms), default=keys[0])
hi = min((k for k in keys if k >= rooms), default=keys[-1])
lo_key = str(int(lo)) if lo == int(lo) else str(lo)
hi_key = str(int(hi)) if hi == int(hi) else str(hi)
if lo == hi:
return rd[lo_key]
t = (rooms - lo) / (hi - lo)
return rd[lo_key] + t * (rd[hi_key] - rd[lo_key])
def predict(municipality, area, rooms, luxury, lake_view, furnished, temporary):
area = float(area)
rooms = float(rooms)
p = get_base(municipality, area)
base_rooms = min(6.0, max(1.0, round(area / 25) * 0.5))
p += get_rooms_delta(rooms) - get_rooms_delta(base_rooms)
if luxury:
p += DATA["lux_delta"]
if lake_view:
p += DATA["sea_delta"]
if furnished:
p += DATA["fur_delta"]
if temporary:
p -= 200
p = max(500, min(12000, round(p / 10) * 10))
info = DATA["muni_info"].get(municipality, {})
tax = info.get("tax_income", 0)
dens = info.get("pop_dens", 0)
tags = [t for t, f in [("Luxury", luxury), ("Lake view", lake_view),
("Furnished", furnished), ("Temporary", temporary)] if f]
result = f"""## CHF {p:,.0f} / month
**Typical error: ± CHF 446** (model MAE, 5-fold CV)
---
### Input summary
| Feature | Value |
|---|---|
| Municipality | {municipality} |
| Living area | {area:.0f} m² |
| Rooms | {rooms} |
| m² per room | {area/rooms:.1f} |
| Listing tags | {", ".join(tags) if tags else "None"} |
### Municipal context (BFS data)
| | |
|---|---|
| Tax income | CHF {tax:,.0f} |
| Population density | {dens:,.0f} /km² |
---
### Feature importances
| Feature | Importance |
|---|---|
| Living area | 47.9% |
| Municipality | 17.6% |
| Population density | 10.9% |
| m² per room | 8.7% |
| Tax income | 4.3% |
| NLP listing tags ★ | ~1.5% |
*Gradient Boosting · R² 0.664 · trained on 2,400 real Zürich listings*
"""
market = sorted(DATA["market"].items(), key=lambda x: -x[1])
rows = list(market[:5])
sel = next((x for x in market if x[0] == municipality), None)
if sel and sel not in rows:
rows.append(sel)
rows.sort(key=lambda x: -x[1])
cmp = "### Market comparison (3 rooms · 75 m²)\n\n"
cmp += "| Municipality | CHF/month |\n|---|---|\n"
for name, price in rows:
marker = " ◀" if name == municipality else ""
cmp += f"| **{name}**{marker} | {price:,} |\n"
return result + "\n" + cmp
with gr.Blocks(title="Zürich Rent Predictor") as demo:
gr.Markdown("# Zürich Apartment Rent Predictor")
gr.Markdown(
"Gradient Boosting model trained on 2,400 real Canton of Zürich listings "
"enriched with BFS municipal data. "
"**New feature:** NLP listing tags extracted from German descriptions "
"(LUXURIÖS, SEESICHT, MÖBLIERT)."
)
with gr.Row():
with gr.Column():
municipality = gr.Dropdown(
choices=MUNICIPALITIES,
value="Zürich",
label="Municipality"
)
area = gr.Slider(
minimum=15, maximum=300, step=5, value=75,
label="Living Area (m²)"
)
rooms = gr.Slider(
minimum=1, maximum=6, step=0.5, value=3,
label="Number of Rooms"
)
gr.Markdown("**Listing tags** *(new feature — extracted from listing descriptions)*")
with gr.Row():
luxury = gr.Checkbox(label="💎 Luxurious")
lake_view = gr.Checkbox(label="🌊 Lake View")
with gr.Row():
furnished = gr.Checkbox(label="🛋️ Furnished")
temporary = gr.Checkbox(label="📅 Temporary")
btn = gr.Button("Predict Rent", variant="primary")
with gr.Column():
output = gr.Markdown(value="*Fill in the details and click Predict Rent.*")
inputs = [municipality, area, rooms, luxury, lake_view, furnished, temporary]
btn.click(fn=predict, inputs=inputs, outputs=output)
for inp in inputs:
if hasattr(inp, "change"):
inp.change(fn=predict, inputs=inputs, outputs=output)
gr.Markdown("---\nZHAW AI Applications · Canton of Zürich Rental Market · 2025")
if __name__ == "__main__":
demo.launch(theme=gr.themes.Default())