nanthsar/Apartmant_Price_Zurich
Apartment Price Prediction - Canton of Zurich
This app predicts the monthly rental price of apartments in the canton of Zurich using a machine learning model.
Project Overview
Preprocessing Steps
- Removed outliers: kept apartments with price between CHF 750 and CHF 8,000
- Dropped rows with missing values
- Added existing features from the lecture: roomperm2, area categories, luxury flags, Zurich Kreis encoding, furnished and temporary flags
- Added my own new features (see below)
New Features
1. Distance to Zurich city center (dist_to_zurich_center) I calculated the distance in km from each apartment to Zurich city center using the Haversine formula. The GPS coordinates were already in the dataset from the GeoAdmin API. Apartments closer to the city center are usually more expensive.
2. Outdoor space (aussenbereich) I searched the apartment descriptions for keywords like balcony, terrace and loggia. In the data I saw that apartments that explicitly mention a balcony are on average a little cheaper. This might be because expensive apartments have one without mentioning it.
3. Price categories (UX improvement) I added a price category to make the app more user-friendly. Based on the price distribution in the dataset I defined three categories:
- 🟢 Affordable (under CHF 1,800)
- 🟡 Mid-range (CHF 1,800 - 3,000)
- 🔴 Expensive (above CHF 3,000)
Iterative Modeling Process
Iteration 1 - Baseline
Objective: Start with simple features to establish a baseline.
Preprocessing steps:
- Removed outliers (price < CHF 750 or > CHF 8,000)
- Dropped rows with missing values
- Used basic numeric features only
Features: rooms, area, pop, popdens, frgpct, emp, taxincome, roomperm2, areacatecoded, zurichcity
Finding: Both models perform similarly. R² of ~0.50 means the model explains only 50% of the price variance. I decided to add more features and try a stronger model.
Iteration 2 - Extended Features + Tuning
Objective: Improve performance by adding more features and trying a stronger model.
Preprocessing steps (changes vs. Iteration 1):
- Added luxury flags from description: ATTIKA, LOFT, SEESICHT, LUXURIÖS, POOL, EXKLUSIV
- Added furnished and temporary binary flags
- Added Zurich Kreis one-hot encoding (Kreis 1-12)
- Added new feature: disttozurich_center (Haversine distance in km)
- Added new feature: aussenbereich (balcony/terrace/loggia from description)
- Tuned Random Forest hyperparameters
- Added Gradient Boosting as second model
Finding: Gradient Boosting performed best with R²=0.63 and the lowest std dev (0.053), meaning it is the most stable model. The RMSE improved by ~100 CHF compared to the baseline. All tree-based models show overfitting (high train R² vs lower CV R²), which is typical for these model types. Cross-validation ensures the reported CV performance is still reliable on unseen data. All tree-based models show overfitting (high train R² vs lower CV R²), which is typical for Random Forest and Gradient Boosting. Cross-validation ensures the reported performance is still reliable on unseen data.
Evaluation Method
- 5-fold Cross-Validation
- Metrics: RMSE (Root Mean Squared Error) in CHF and R² (coefficient of determination)
- Lower RMSE = better | Higher R² = better (max 1.0)
- R² of 0.63 means the model explains 63% of the price variance
Final Model
Gradient Boosting Regressor - chosen because it achieved the best CV RMSE (-610 CHF) and highest R² (0.63) with the lowest standard deviation.
- n_estimators = 300
- learning_rate = 0.05
- max_depth = 4
- random_state = 42
Files
References
- Geron, A. (2019). Hands-On Machine Learning with Scikit-Learn and TensorFlow. O'Reilly.
- https://scikit-learn.org/
- https://www.gradio.app/guides/quickstart
- https://huggingface.co/docs/hub/en/spaces-sdks-gradio
