|
|
import gradio as gr |
|
|
import pandas as pd |
|
|
import numpy as np |
|
|
import pickle |
|
|
from math import radians, cos, sin, asin, sqrt |
|
|
|
|
|
|
|
|
with open("apartment_price_model.pkl", mode="rb") as f: |
|
|
model = pickle.load(f) |
|
|
|
|
|
|
|
|
zurich_neighborhoods = { |
|
|
"City Center (Altstadt)": {"lat": 47.3769, "lon": 8.5417, "distance": 0.0}, |
|
|
"Oerlikon": {"lat": 47.4111, "lon": 8.5458, "distance": 3.8}, |
|
|
"Altstetten": {"lat": 47.3908, "lon": 8.4889, "distance": 4.2}, |
|
|
"Wiedikon": {"lat": 47.3708, "lon": 8.5128, "distance": 2.3}, |
|
|
"Seefeld": {"lat": 47.3550, "lon": 8.5550, "distance": 2.7}, |
|
|
"Schwamendingen": {"lat": 47.4053, "lon": 8.5648, "distance": 3.5}, |
|
|
"Wollishofen": {"lat": 47.3517, "lon": 8.5304, "distance": 3.0}, |
|
|
"Enge": {"lat": 47.3656, "lon": 8.5267, "distance": 1.2}, |
|
|
"Fluntern": {"lat": 47.3797, "lon": 8.5611, "distance": 1.8}, |
|
|
"Hottingen": {"lat": 47.3683, "lon": 8.5584, "distance": 1.5}, |
|
|
"Custom Location": {"lat": 47.3769, "lon": 8.5417, "distance": 0.0} |
|
|
} |
|
|
|
|
|
|
|
|
def haversine_distance(lat1, lon1, lat2, lon2): |
|
|
"""Calculate the great circle distance between two points on earth.""" |
|
|
|
|
|
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2]) |
|
|
|
|
|
|
|
|
dlon = lon2 - lon1 |
|
|
dlat = lat2 - lat1 |
|
|
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 |
|
|
c = 2 * asin(sqrt(a)) |
|
|
r = 6371 |
|
|
return c * r |
|
|
|
|
|
def predict_price(neighborhood, rooms, area, has_balcony, is_renovated, custom_lat=None, custom_lon=None): |
|
|
|
|
|
if neighborhood == "Custom Location" and custom_lat is not None and custom_lon is not None: |
|
|
lat = custom_lat |
|
|
lon = custom_lon |
|
|
else: |
|
|
lat = zurich_neighborhoods[neighborhood]["lat"] |
|
|
lon = zurich_neighborhoods[neighborhood]["lon"] |
|
|
|
|
|
|
|
|
zurich_center_lat = zurich_neighborhoods["City Center (Altstadt)"]["lat"] |
|
|
zurich_center_lon = zurich_neighborhoods["City Center (Altstadt)"]["lon"] |
|
|
|
|
|
if neighborhood == "Custom Location" and custom_lat is not None and custom_lon is not None: |
|
|
distance_to_center = haversine_distance(custom_lat, custom_lon, zurich_center_lat, zurich_center_lon) |
|
|
else: |
|
|
distance_to_center = zurich_neighborhoods[neighborhood]["distance"] |
|
|
|
|
|
|
|
|
pop = 420217 |
|
|
pop_dens = 4778 |
|
|
frg_pct = 32.45 |
|
|
emp = 491193 |
|
|
tax_income = 85446 |
|
|
price_per_room = 0 |
|
|
|
|
|
|
|
|
input_data = pd.DataFrame([{ |
|
|
'rooms': rooms, |
|
|
'area': area, |
|
|
'pop': pop, |
|
|
'pop_dens': pop_dens, |
|
|
'frg_pct': frg_pct, |
|
|
'emp': emp, |
|
|
'tax_income': tax_income, |
|
|
'price_per_room': price_per_room, |
|
|
'distance_to_center': distance_to_center, |
|
|
'has_balcony': 1 if has_balcony else 0, |
|
|
'is_renovated': 1 if is_renovated else 0 |
|
|
}]) |
|
|
|
|
|
|
|
|
features = [ |
|
|
'rooms', 'area', 'pop', 'pop_dens', 'frg_pct', 'emp', 'tax_income', |
|
|
'price_per_room', 'distance_to_center', 'has_balcony', 'is_renovated' |
|
|
] |
|
|
|
|
|
|
|
|
predicted_price = model.predict(input_data[features])[0] |
|
|
|
|
|
|
|
|
result = f"Predicted Monthly Rent: CHF {predicted_price:.0f}" |
|
|
result += f"\n\nProperty Details:" |
|
|
result += f"\n- Location: {neighborhood}" |
|
|
result += f"\n- {rooms} rooms, {area} m²" |
|
|
result += f"\n- {distance_to_center:.2f} km from city center" |
|
|
result += f"\n- {'Has balcony' if has_balcony else 'No balcony'}" |
|
|
result += f"\n- {'Renovated' if is_renovated else 'Not renovated'}" |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
def update_custom_location(neighborhood): |
|
|
if neighborhood == "Custom Location": |
|
|
return gr.update(visible=True), gr.update(visible=True) |
|
|
else: |
|
|
return gr.update(visible=False), gr.update(visible=False) |
|
|
|
|
|
|
|
|
def reset_inputs(): |
|
|
return [ |
|
|
"City Center (Altstadt)", |
|
|
47.3769, |
|
|
8.5417, |
|
|
3.5, |
|
|
75, |
|
|
True, |
|
|
False, |
|
|
"" |
|
|
] |
|
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
|
gr.Markdown("# Zurich Apartment Rent Prediction") |
|
|
gr.Markdown(""" |
|
|
This app predicts apartment rental prices in Zurich with a special feature: Distance to City Center. |
|
|
|
|
|
**Special Feature Description:** |
|
|
The model automatically calculates how far the apartment is from Zurich city center. |
|
|
This distance is a critical factor in real estate pricing - properties closer to the city center typically |
|
|
command higher rents due to convenience and accessibility to urban amenities. |
|
|
|
|
|
Simply select a neighborhood, and the app will use its distance from the city center to help |
|
|
provide a more accurate rental price prediction. |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
neighborhood = gr.Dropdown( |
|
|
label="Neighborhood", |
|
|
choices=list(zurich_neighborhoods.keys()), |
|
|
value="City Center (Altstadt)" |
|
|
) |
|
|
custom_lat = gr.Number(label="Custom Latitude", value=47.3769, visible=False) |
|
|
custom_lon = gr.Number(label="Custom Longitude", value=8.5417, visible=False) |
|
|
rooms = gr.Number(label="Number of Rooms", value=3.5) |
|
|
area = gr.Number(label="Area (m²)", value=75) |
|
|
has_balcony = gr.Checkbox(label="Has Balcony", value=True) |
|
|
is_renovated = gr.Checkbox(label="Is Renovated", value=False) |
|
|
|
|
|
with gr.Row(): |
|
|
submit_button = gr.Button("Submit") |
|
|
clear_button = gr.Button("Clear") |
|
|
|
|
|
with gr.Column(): |
|
|
output = gr.Textbox(label="Output") |
|
|
|
|
|
|
|
|
neighborhood.change( |
|
|
fn=update_custom_location, |
|
|
inputs=neighborhood, |
|
|
outputs=[custom_lat, custom_lon] |
|
|
) |
|
|
|
|
|
|
|
|
submit_button.click( |
|
|
fn=predict_price, |
|
|
inputs=[neighborhood, rooms, area, has_balcony, is_renovated, custom_lat, custom_lon], |
|
|
outputs=output |
|
|
) |
|
|
|
|
|
|
|
|
clear_button.click( |
|
|
fn=reset_inputs, |
|
|
inputs=None, |
|
|
outputs=[neighborhood, custom_lat, custom_lon, rooms, area, has_balcony, is_renovated, output] |
|
|
) |
|
|
|
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
["Oerlikon", 3.5, 75, True, True], |
|
|
["Seefeld", 2.0, 60, False, False], |
|
|
["Wiedikon", 4.5, 120, True, False], |
|
|
], |
|
|
inputs=[neighborhood, rooms, area, has_balcony, is_renovated], |
|
|
outputs=output, |
|
|
fn=predict_price |
|
|
) |
|
|
|
|
|
demo.launch() |