Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import joblib
|
5 |
+
from xgboost import XGBRegressor
|
6 |
+
from sklearn.preprocessing import StandardScaler, OneHotEncoder
|
7 |
+
import shap
|
8 |
+
from streamlit_shap import st_shap
|
9 |
+
|
10 |
+
# Page configuration
|
11 |
+
st.set_page_config(
|
12 |
+
page_title="Airbnb Price Prediction in Copenhagen",
|
13 |
+
page_icon="π ")
|
14 |
+
|
15 |
+
st.title('Predict Airbnb Prices in Copenhagen')
|
16 |
+
|
17 |
+
# Display an image
|
18 |
+
st.image('https://gdkfiles.visitdenmark.com/files/382/164757_Nyhavn_Jacob-Schjrring-og-Simon-Lau.jpg?width=987', caption='Copenhagen', use_column_width=True)
|
19 |
+
|
20 |
+
# Load model and preprocessing objects
|
21 |
+
@st.cache_resource
|
22 |
+
def load_model_objects():
|
23 |
+
model_xgb = joblib.load('model_xgb.joblib')
|
24 |
+
scaler = joblib.load('scaler.joblib')
|
25 |
+
ohe = joblib.load('ohe.joblib')
|
26 |
+
return model_xgb, scaler, ohe
|
27 |
+
|
28 |
+
model_xgb, scaler, ohe = load_model_objects()
|
29 |
+
|
30 |
+
# Create SHAP explainer
|
31 |
+
explainer = shap.TreeExplainer(model_xgb)
|
32 |
+
|
33 |
+
# App description
|
34 |
+
with st.expander("What's this app?"):
|
35 |
+
st.markdown("""
|
36 |
+
This app helps you determine an appropriate nightly rate for your Airbnb listing in Copenhagen.
|
37 |
+
We've trained an AI model on successful listings in Copenhagen to provide pricing suggestions based on a few key inputs.
|
38 |
+
Our recommendation is to adjust the suggested price by about Β±350 DKK depending on your specific amenities and the quality of your place.
|
39 |
+
As a bonus feature π, we've included an AI explainer π€ to help you understand the factors influencing the predicted price.
|
40 |
+
""")
|
41 |
+
|
42 |
+
st.subheader('Describe your place')
|
43 |
+
|
44 |
+
# User inputs
|
45 |
+
col1, col2 = st.columns(2)
|
46 |
+
|
47 |
+
with col1:
|
48 |
+
n_hood = st.selectbox('Neighborhood', options=ohe.categories_[0])
|
49 |
+
room_type = st.radio('Room Type', options=ohe.categories_[1])
|
50 |
+
instant_bookable = st.checkbox('Instant Booking Available')
|
51 |
+
accommodates = st.number_input('Maximum Guests', min_value=1, max_value=16, value=2)
|
52 |
+
|
53 |
+
with col2:
|
54 |
+
bedrooms = st.number_input('Number of Bedrooms', min_value=0, max_value=10, value=1)
|
55 |
+
beds = st.number_input('Number of Beds', min_value=1, max_value=16, value=1)
|
56 |
+
min_nights = st.number_input('Minimum Nights Stay', min_value=1, max_value=30, value=1)
|
57 |
+
|
58 |
+
# Prediction button
|
59 |
+
if st.button('Predict Price π'):
|
60 |
+
# Prepare categorical features
|
61 |
+
cat_features = pd.DataFrame({'neighbourhood_cleansed': [n_hood], 'room_type': [room_type]})
|
62 |
+
cat_encoded = pd.DataFrame(ohe.transform(cat_features).todense(),
|
63 |
+
columns=ohe.get_feature_names_out(['neighbourhood_cleansed', 'room_type']))
|
64 |
+
|
65 |
+
# Prepare numerical features
|
66 |
+
num_features = pd.DataFrame({
|
67 |
+
'instant_bookable': [instant_bookable],
|
68 |
+
'accommodates': [accommodates],
|
69 |
+
'bedrooms': [bedrooms],
|
70 |
+
'beds': [beds],
|
71 |
+
'minimum_nights_avg_ntm': [min_nights]
|
72 |
+
})
|
73 |
+
num_scaled = pd.DataFrame(scaler.transform(num_features), columns=num_features.columns)
|
74 |
+
|
75 |
+
# Combine features
|
76 |
+
features = pd.concat([num_scaled, cat_encoded], axis=1)
|
77 |
+
|
78 |
+
# Make prediction
|
79 |
+
predicted_price = model_xgb.predict(features)[0]
|
80 |
+
|
81 |
+
# Display prediction
|
82 |
+
st.metric(label="Predicted price per night", value=f'{round(predicted_price)} DKK')
|
83 |
+
|
84 |
+
# Calculate and display price range
|
85 |
+
lower_range = max(0, round(predicted_price - 350))
|
86 |
+
upper_range = round(predicted_price + 350)
|
87 |
+
st.write(f"Suggested price range: {lower_range} - {upper_range} DKK")
|
88 |
+
|
89 |
+
# SHAP explanation
|
90 |
+
st.subheader('Price Factors Explained π€')
|
91 |
+
shap_values = explainer.shap_values(features)
|
92 |
+
st_shap(shap.force_plot(explainer.expected_value, shap_values, features), height=400, width=600)
|
93 |
+
|
94 |
+
st.markdown("""
|
95 |
+
This plot shows how each feature contributes to the predicted price:
|
96 |
+
- Blue bars push the price lower
|
97 |
+
- Red bars push the price higher
|
98 |
+
- The length of each bar indicates the strength of the feature's impact
|
99 |
+
""")
|
100 |
+
|
101 |
+
# Footer
|
102 |
+
st.markdown("---")
|
103 |
+
st.markdown("Developed with β€οΈ using Streamlit and AI")
|