ochsncon commited on
Commit
e4e908a
·
verified ·
1 Parent(s): 2956bec

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +46 -6
  2. app.py +135 -0
  3. requirements.txt +7 -0
README.md CHANGED
@@ -1,12 +1,52 @@
1
  ---
2
- title: ApartmentPricePredictor
3
- emoji: 🏆
4
- colorFrom: red
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 6.9.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Zürich Apartment Price Predictor
3
+ emoji: 🏠
4
+ colorFrom: blue
5
+ colorTo: red
6
  sdk: gradio
7
+ sdk_version: 6.8.0
8
  app_file: app.py
9
  pinned: false
10
+ short_description: Machine Learning Apartment Rent Price Predictor for Zurich
11
  ---
12
 
13
+ # Model Iterations Documentation
14
+ ## Task: Apartment Price Prediction (Regression)
15
+
16
+ ---
17
+
18
+ ---
19
+
20
+ ## Summary of Iterative Process
21
+
22
+ | Iteration | Objective | Key Changes | Models Used | CV Mean R² | CV Std Dev | Test MAE (CHF) | Fit Diagnosis |
23
+ |-----------|-----------|-------------|-------------|------------|------------|----------------|---------------|
24
+ | **1** | Build baseline model | - Basic cleaning<br>- 7 numerical features<br>- Train-test split (80/20)<br>- 5-fold CV | Linear Regression<br>Random Forest (n_estimators=100) | 0.429 (LR)<br>0.441 (RF) | 0.073 (LR)<br>0.075 (RF) | 549.90 (LR)<br>553.98 (RF) | ☐ Overfitting ☑ Underfitting ☐ Good Fit |
25
+ | **2** | Improve through feature engineering | - 7 new features created<br>- Feature scaling (Ridge)<br>- Hyperparameter tuning<br>- 5-fold CV | Ridge (alpha=10.0)<br>Random Forest (n_estimators=200, max_depth=15)<br>Gradient Boosting (n_estimators=150, max_depth=5) | 0.461 (Ridge)<br>0.481 (RF)<br>0.521 (GB) | 0.073 (Ridge)<br>0.070 (RF)<br>0.068 (GB) | 541.11 (Ridge)<br>544.92 (RF)<br>537.78 (GB) | ☐ Overfitting ☐ Underfitting ☑ Good Fit |
26
+
27
+ ---
28
+
29
+ ## Notes
30
+
31
+ **Metric:** MAE (Mean Absolute Error), R² (5-Fold Cross-Validation)
32
+
33
+ **Created Features:**
34
+ - rooms_per_sqm: Raumdichte (Zimmer pro m²)
35
+ - wealth_index: Wohlstandsindikator (tax_income × emp normalisiert)
36
+ - is_zurich_city: Binär-Feature für Zürich Stadt (PLZ 8000-8099)
37
+ - pop_emp_ratio: Bevölkerungs-Arbeitsplatz-Verhältnis
38
+ - log_area: Log-Transformation der Wohnfläche
39
+ - log_pop: Log-Transformation der Bevölkerung
40
+ - log_tax_income: Log-Transformation des Steuereinkommens
41
+
42
+ **Final Selected Features:**
43
+ - area (28.7% importance)
44
+ - log_area (25.0% importance)
45
+ - is_zurich_city (13.0% importance)
46
+ - rooms_per_sqm (12.2% importance)
47
+ - rooms, log_pop, pop_dens, pop, pop_emp_ratio, log_tax_income, emp, wealth_index, tax_income, frg_pct
48
+
49
+ **Final Model:** Gradient Boosting Regressor
50
+
51
+ **Reason for Selection:**
52
+ Best R² score (0.521) and lowest MAE (537.78 CHF). Consistent performance between CV and test set with lowest standard deviation (±67.62 CHF), indicating stable predictions.
app.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import numpy as np
4
+ import pickle
5
+ import os
6
+
7
+ # Load model (or create dummy if not exists)
8
+ MODEL_PATH = "models/model.pkl"
9
+
10
+ if os.path.exists(MODEL_PATH):
11
+ with open(MODEL_PATH, 'rb') as f:
12
+ model_package = pickle.load(f)
13
+
14
+ # Check if it's the new model format (dict) or old format (single model)
15
+ if isinstance(model_package, dict):
16
+ model = model_package['model']
17
+ scaler = model_package.get('scaler', None)
18
+ features = model_package.get('features', None)
19
+ model_type = model_package.get('model_type', 'unknown')
20
+ using_dummy = False
21
+ print(f"✅ Loaded model: {model_type}")
22
+ else:
23
+ # Old format - single model
24
+ model = model_package
25
+ scaler = None
26
+ features = None
27
+ model_type = 'legacy'
28
+ using_dummy = False
29
+ print("✅ Loaded legacy model")
30
+ else:
31
+ # Dummy model for initial deployment
32
+ model = None
33
+ scaler = None
34
+ features = None
35
+ model_type = None
36
+ using_dummy = True
37
+ print("⚠️ No model found - using dummy")
38
+
39
+ def create_features(rooms, area, postalcode, pop, pop_dens, frg_pct, emp, tax_income):
40
+ """Create engineered features from input"""
41
+ data = {
42
+ 'rooms': rooms,
43
+ 'area': area,
44
+ 'pop': pop,
45
+ 'pop_dens': pop_dens,
46
+ 'frg_pct': frg_pct,
47
+ 'emp': emp,
48
+ 'tax_income': tax_income,
49
+ # Engineered features
50
+ 'rooms_per_sqm': rooms / area,
51
+ 'wealth_index': (tax_income / 100000) * (emp / 100000),
52
+ 'is_zurich_city': 1 if (postalcode >= 8000 and postalcode < 8100) else 0,
53
+ 'pop_emp_ratio': pop / (emp + 1),
54
+ 'log_area': np.log1p(area),
55
+ 'log_pop': np.log1p(pop),
56
+ 'log_tax_income': np.log1p(tax_income)
57
+ }
58
+ return pd.DataFrame([data])
59
+
60
+ def predict_price(rooms, area, postalcode, pop, pop_dens, frg_pct, emp, tax_income):
61
+ """
62
+ Predict apartment rental price based on input features
63
+ """
64
+ if using_dummy:
65
+ # Simple dummy calculation for testing
66
+ base_price = area * 25 # rough estimate: 25 CHF per m²
67
+ room_factor = rooms * 200
68
+ location_factor = (tax_income / 1000) * 0.5
69
+
70
+ estimated_price = base_price + room_factor + location_factor
71
+ return f"💰 Geschätzter Mietpreis: CHF {estimated_price:.2f}/Monat\n\n⚠️ Dummy-Modell - Training folgt!"
72
+
73
+ else:
74
+ # Create feature dataframe
75
+ input_data = create_features(rooms, area, postalcode, pop, pop_dens, frg_pct, emp, tax_income)
76
+
77
+ # Select only the features the model was trained on
78
+ if features is not None:
79
+ input_data = input_data[features]
80
+
81
+ # Apply scaling if needed (for Ridge)
82
+ if scaler is not None:
83
+ input_data_scaled = scaler.transform(input_data)
84
+ prediction = model.predict(input_data_scaled)[0]
85
+ else:
86
+ prediction = model.predict(input_data)[0]
87
+
88
+ return f"💰 Geschätzter Mietpreis: CHF {prediction:.2f}/Monat\n\n🤖 Modell: {model_type.replace('_', ' ').title()}"
89
+
90
+ # Gradio Interface
91
+ with gr.Blocks(title="Zürich Apartment Price Predictor") as demo:
92
+ gr.Markdown("# 🏠 Zürich Apartment Rent Predictor")
93
+ gr.Markdown("Vorhersage von Mietpreisen für Wohnungen im Kanton Zürich")
94
+
95
+ with gr.Row():
96
+ with gr.Column():
97
+ rooms = gr.Number(label="Anzahl Zimmer", value=3.5, minimum=1, maximum=10)
98
+ area = gr.Number(label="Wohnfläche (m²)", value=75, minimum=10, maximum=500)
99
+ postalcode = gr.Number(label="Postleitzahl", value=8001, minimum=8000, maximum=8999)
100
+
101
+ with gr.Column():
102
+ pop = gr.Number(label="Bevölkerung (Gemeinde)", value=420217)
103
+ pop_dens = gr.Number(label="Bevölkerungsdichte", value=4778.99)
104
+ frg_pct = gr.Number(label="Ausländeranteil (%)", value=32.46)
105
+ emp = gr.Number(label="Anzahl Arbeitsplätze", value=491193)
106
+ tax_income = gr.Number(label="Durchschn. Steuereinkommen", value=85446)
107
+
108
+ predict_btn = gr.Button("🔍 Preis berechnen", variant="primary")
109
+ output = gr.Textbox(label="Ergebnis", lines=3)
110
+
111
+ predict_btn.click(
112
+ fn=predict_price,
113
+ inputs=[rooms, area, postalcode, pop, pop_dens, frg_pct, emp, tax_income],
114
+ outputs=output
115
+ )
116
+
117
+ gr.Markdown("---")
118
+ gr.Markdown("### 📊 Beispiel-Werte")
119
+ gr.Markdown("""
120
+ - **Zürich City (8001)**: PLZ 8001, Pop: 420217, Pop_dens: 4778.99, Frg_pct: 32.46, Emp: 491193, Tax: 85446
121
+ - **Winterthur (8400)**: PLZ 8400, Pop: 114220, Pop_dens: 2009.6, Frg_pct: 29.9, Emp: 57583, Tax: 72190
122
+ - **Rüti ZH (8630)**: PLZ 8630, Pop: 12286, Pop_dens: 1221.3, Frg_pct: 24.8, Emp: 5053, Tax: 66676
123
+ """)
124
+
125
+ gr.Markdown("---")
126
+ gr.Markdown("### 🎯 Modell-Performance")
127
+ gr.Markdown("""
128
+ - **Model**: Gradient Boosting Regressor
129
+ - **R² Score**: 0.521
130
+ - **MAE**: 537.78 CHF
131
+ - **Features**: 14 (inkl. engineered features)
132
+ """)
133
+
134
+ if __name__ == "__main__":
135
+ demo.launch(theme=gr.themes.Soft())
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ pandas>=2.0.0
3
+ scikit-learn>=1.3.0
4
+ numpy>=1.24.0
5
+ matplotlib>=3.7.0
6
+ seaborn>=0.12.0
7
+ xgboost>=2.0.0