LioD19's picture
Upload 5 files
f4a61c0 verified
Raw
History Blame Contribute Delete
22.4 kB
import streamlit as st
import pandas as pd
import numpy as np
import pickle
import os
from data_processing import load_sample_data, preprocess_inputs
from model_utils import load_model, predict_price
# Set page config
st.set_page_config(
page_title="House Price Predictor",
page_icon="🏠",
layout="wide"
)
# Load custom CSS
def load_css(css_file):
try:
# Essayer avec UTF-8 explicitement
with open(css_file, 'r', encoding='utf-8') as f:
css = f.read()
return css
except UnicodeDecodeError:
# Fallback sur une autre encodage si UTF-8 échoue
try:
with open(css_file, 'r', encoding='latin-1') as f:
css = f.read()
return css
except Exception as e:
return None
try:
css = load_css('style.css')
if css:
st.markdown(f'<style>{css}</style>', unsafe_allow_html=True)
else:
# Si le fichier CSS ne peut pas être chargé, ajouter un style minimal inline
st.markdown("""
<style>
h1 {
font-size: 2rem;
color: #7f8c8d;
text-align: center;
margin-bottom: 1rem;
}
.sub-header {
font-size: 1.2rem;
color: #7f8c8d;
text-align: center;
margin-bottom: 2rem;
}
.feature-section {
background-color: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.prediction-result {
font-size: 1.5rem;
font-weight: bold;
text-align: center;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
background: linear-gradient(to right, #3498db, #2c3e50);
color: white;
}
</style>
""", unsafe_allow_html=True)
except Exception as e:
st.write(f"Erreur lors du chargement du CSS (mode dégradé activé): {e}")
# Custom styling for specific components
st.markdown("""
<style>
h1 {
font-size: 2.5rem;
color: #D4DCFF;
text-align: center;
margin-bottom: 1rem;
}
.sub-header {
font-size: 1.2rem;
color: #D4DCFF;
text-align: center;
margin-bottom: 2rem;
font-style: italic;
}
.feature-section {
color: #D4DCFF:
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
}
.prediction-result {
font-size: 1.5rem;
font-weight: bold;
text-align: center;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
background: linear-gradient(to right, #3498db, #2c3e50);
color: white;
}
.confidence-interval {
font-size: 1.1rem;
text-align: center;
color: #7f8c8d;
margin-top: -10px;
margin-bottom: 20px;
}
.footer {
text-align: center;
color: #95a5a6;
padding: 20px;
font-size: 0.8rem;
}
</style>
""", unsafe_allow_html=True)
# Title and description with custom styling
st.markdown('<h1>🏠 Prédiction de Prix Immobilier</h1>', unsafe_allow_html=True)
st.markdown('<p class="sub-header">Cette application vous permet de prédire le prix des propriétés en utilisant un modèle XGBoost pré-entraîné.</p>', unsafe_allow_html=True)
# Load sample data for statistics
sample_data = load_sample_data()
# Sidebar for model upload and data statistics
with st.sidebar:
st.markdown("""
<style>
.sidebar-header {
background-color: #171933;
color: #D4DCFF;
border-radius: 5px;
text-align: center;
font-weight: bold;
}
.sidebar-box {
border-radius: 8px;
margin-bottom: 20px;
}
.sidebar-subheader {
background-color: #171933;
color: #2c3e50;
margin-top: 20px;
font-weight: 600;
text-align: center;
border-radius: 5px;
margin-bottom: 20px;
}
.stat-item {
background-color: #171933;
padding: 8px;
border-radius: 5px;
margin-bottom: 5px;
border-left: 3px solid #02b35a;
}
</style>
""", unsafe_allow_html=True)
# Model upload section
st.markdown('<h2 class="sidebar-header">Télécharger le Modèle pour faire des prédictions</h2>', unsafe_allow_html=True)
st.markdown('<div class="sidebar-box">', unsafe_allow_html=True)
uploaded_model = st.file_uploader("", type=["pkl", "joblib", "sav"])
if uploaded_model is not None:
st.success("✅ Modèle téléchargé avec succès!")
# Confidence interval settings
st.markdown('<h2 class="sidebar-header">⚙️ Paramètres</h2>', unsafe_allow_html=True)
st.markdown('<div style="color: #D4DCFF;" class="sidebar-box">', unsafe_allow_html=True)
# Add confidence level settings
confidence_level = st.slider(
"Niveau de confiance (%)",
min_value=50,
max_value=99,
value=95,
step=5,
help="Niveau de confiance pour l'intervalle de prédiction"
)
# Add error margin slider
error_margin_percent = st.slider(
"Marge d'erreur estimée (%)",
min_value=5,
max_value=30,
value=15,
step=5,
help="Pourcentage d'erreur estimée pour le modèle"
)
st.markdown('</div>', unsafe_allow_html=True)
# Dataset Statistics
if sample_data is not None:
# Preview
st.markdown('<div class="sidebar-box">', unsafe_allow_html=True)
st.markdown('<h3 style="margin-bottom: 15px;" class="sidebar-subheader">📊 Aperçu du Dataset</h3>', unsafe_allow_html=True)
st.dataframe(sample_data.head(3), use_container_width=True)
st.markdown('</div>', unsafe_allow_html=True)
# Features stats
st.markdown('<div class="sidebar-box">', unsafe_allow_html=True)
st.markdown('<h3 class="sidebar-subheader">Caractéristiques Numériques</h3>', unsafe_allow_html=True)
# Price stats (highlighted)
if 'price' in sample_data.columns:
price_stats = sample_data['price'].describe()
st.markdown("""
<div style="background-color: #171933; padding: 10px; border-radius: 5px; margin-bottom: 15px; margin-top: 15px; border-left: 3px solid #3498db;">
<h4 style="margin: 0; color: #D4DCFF;">Prix (Target Variable)</h4>
<p style="margin: 5px 0;">Min: ${:,.2f} | Max: ${:,.2f}</p>
<p style="margin: 5px 0;">Mean: ${:,.2f} | Median: ${:,.2f}</p>
</div>
""".format(
price_stats['min'],
price_stats['max'],
price_stats['mean'],
price_stats['50%']
), unsafe_allow_html=True)
# Other features
num_cols = sample_data.select_dtypes(include=['int64', 'float64']).columns
for col in num_cols:
if col != 'id' and col != 'price': # Exclude ID and target variable
stats = sample_data[col].describe()
st.markdown(f"""
<div class="stat-item">
<strong>{col}</strong><br/>
Min: {stats['min']:.2f} | Max: {stats['max']:.2f}<br/>
Mean: {stats['mean']:.2f}
</div>
""", unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
# Main content
st.markdown('<h2 style="color: #D4DCFF; padding-bottom: 5px;">Détails de la Propriété</h2>', unsafe_allow_html=True)
# Initialize model
model = None
if uploaded_model is not None:
try:
model = load_model(uploaded_model)
# Success message is now shown in the sidebar
except Exception as e:
st.error(f"Erreur lors du chargement du modèle : {e}")
st.info("Veuillez vérifier que le modèle est un fichier XGBoost valide au format .pkl")
# Define all available features
all_features = {
'bedrooms': "Bedrooms",
'bathrooms': "Bathrooms",
'sqft_living': "Living Area (sqft)",
'sqft_lot': "Lot Size (sqft)",
'floors': "Floors",
'waterfront': "Waterfront",
'view': "View",
'condition': "Condition",
'grade': "Grade",
'yr_built': "Year Built",
'yr_renovated': "Year Renovated",
'zipcode': "Zipcode",
'lat': "Latitude",
'long': "Longitude",
'sqft_above': "Square Feet Above Ground",
'sqft_basement': "Square Feet Basement"
}
# Multi-select to choose fields to omit - styling with custom container
st.markdown('<div class="feature-section">', unsafe_allow_html=True)
st.subheader("Sélectionnez les champs à omettre pour la prédiction")
omitted_fields = st.multiselect(
"",
options=list(all_features.values())
)
st.markdown('</div>', unsafe_allow_html=True)
# Create lookup from display name to field name
display_to_field = {v: k for k, v in all_features.items()}
# Get the field names that are omitted
omitted_field_names = [display_to_field[field] for field in omitted_fields]
# Create form for user inputs
with st.form("property_form"):
# Apply CSS to the form
st.markdown("""
<style>
.property-form {
background-color: #22244D;
}
.feature-label {
font-weight: 600;
color: #D4DCFF;
margin-bottom: 5px;
}
.submit-btn {
background-color: #3498db;
color: white;
font-weight: bold;
padding: 10px 20px;
border-radius: 5px;
border: none;
cursor: pointer;
transition: all 0.3s ease;
display: block;
width: 100%;
text-align: center;
margin-top: 20px;
}
.submit-btn:hover {
background-color: #2980b9;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
</style>
""", unsafe_allow_html=True)
# Wrap the form in a styled container
st.markdown('<div class="property-form">', unsafe_allow_html=True)
# Create columns for a cleaner form layout
col1, col2, col3 = st.columns(3)
# Initialize input variables with None
bedrooms = bathrooms = sqft_living = sqft_lot = floors = None
waterfront = view = condition = grade = yr_built = None
yr_renovated = zipcode = lat = long = sqft_above = sqft_basement = None
# Column 1 features
with col1:
if "Bedrooms" not in omitted_fields:
st.markdown('<div class="feature-label">Chambres</div>', unsafe_allow_html=True)
bedrooms = st.number_input("Chambres", min_value=0, max_value=10, value=3, step=1, label_visibility="collapsed")
if "Bathrooms" not in omitted_fields:
st.markdown('<div class="feature-label">Salles de bain</div>', unsafe_allow_html=True)
bathrooms = st.number_input("Salles de bain", min_value=0.0, max_value=10.0, value=2.0, step=0.25, label_visibility="collapsed")
if "Living Area (sqft)" not in omitted_fields:
st.markdown('<div class="feature-label">Surface habitable (pieds carrés)</div>', unsafe_allow_html=True)
sqft_living = st.number_input("Surface habitable", min_value=200, max_value=10000, value=1500, step=100, label_visibility="collapsed")
if "Lot Size (sqft)" not in omitted_fields:
st.markdown('<div class="feature-label">Taille du lot (pieds carrés)</div>', unsafe_allow_html=True)
sqft_lot = st.number_input("Taille du lot", min_value=500, max_value=200000, value=5000, step=500, label_visibility="collapsed")
if "Floors" not in omitted_fields:
st.markdown('<div class="feature-label">Étages</div>', unsafe_allow_html=True)
floors = st.number_input("Étages", min_value=1.0, max_value=4.0, value=1.0, step=0.5, label_visibility="collapsed")
# Column 2 features
with col2:
if "Waterfront" not in omitted_fields:
st.markdown('<div class="feature-label">Vue sur l\'eau</div>', unsafe_allow_html=True)
waterfront = st.selectbox("Vue sur l'eau", options=[0, 1],
format_func=lambda x: "Oui" if x == 1 else "Non",
label_visibility="collapsed")
if "View" not in omitted_fields:
st.markdown('<div class="feature-label">Vue</div>', unsafe_allow_html=True)
view = st.selectbox("Vue", options=[0, 1, 2, 3, 4],
format_func=lambda x: {0: "Aucune", 1: "Passable", 2: "Moyenne", 3: "Bonne", 4: "Excellente"}[x],
label_visibility="collapsed")
if "Condition" not in omitted_fields:
st.markdown('<div class="feature-label">État</div>', unsafe_allow_html=True)
condition = st.selectbox("État", options=[1, 2, 3, 4, 5],
format_func=lambda x: {1: "Mauvais", 2: "Passable", 3: "Moyen", 4: "Bon", 5: "Excellent"}[x],
label_visibility="collapsed")
if "Grade" not in omitted_fields:
st.markdown('<div class="feature-label">Qualité de construction</div>', unsafe_allow_html=True)
grade = st.selectbox("Qualité", options=list(range(1, 14)),
format_func=lambda x: {
1: "Très mauvaise", 2: "Mauvaise", 3: "Mauvaise", 4: "Moyenne inférieure",
5: "Moyenne", 6: "Moyenne", 7: "Bonne", 8: "Bonne",
9: "Meilleure", 10: "Meilleure", 11: "Excellente",
12: "Excellente", 13: "Luxe"
}[x],
label_visibility="collapsed")
if "Year Built" not in omitted_fields:
st.markdown('<div class="feature-label">Année de construction</div>', unsafe_allow_html=True)
yr_built = st.number_input("Année de construction", min_value=1900, max_value=2023, value=1980, step=1, label_visibility="collapsed")
# Column 3 features
with col3:
if "Year Renovated" not in omitted_fields:
st.markdown('<div class="feature-label">Année de rénovation (0 si aucune)</div>', unsafe_allow_html=True)
yr_renovated = st.number_input("Année de rénovation", min_value=0, max_value=2023, value=0, step=1, label_visibility="collapsed")
if "Zipcode" not in omitted_fields:
st.markdown('<div class="feature-label">Code postal</div>', unsafe_allow_html=True)
zipcode = st.number_input("Code postal", min_value=98000, max_value=99000, value=98000, step=1, label_visibility="collapsed")
if "Latitude" not in omitted_fields:
st.markdown('<div class="feature-label">Latitude</div>', unsafe_allow_html=True)
lat = st.number_input("Latitude", min_value=47.0, max_value=48.0, value=47.5, step=0.01, format="%.4f", label_visibility="collapsed")
if "Longitude" not in omitted_fields:
st.markdown('<div class="feature-label">Longitude</div>', unsafe_allow_html=True)
long = st.number_input("Longitude", min_value=-123.0, max_value=-121.0, value=-122.0, step=0.01, format="%.4f", label_visibility="collapsed")
if "Square Feet Above Ground" not in omitted_fields:
st.markdown('<div class="feature-label">Surface au-dessus du sol (pieds carrés)</div>', unsafe_allow_html=True)
sqft_above = st.number_input("Surface au-dessus du sol", min_value=200, max_value=10000, value=1000, step=100, label_visibility="collapsed")
if "Square Feet Basement" not in omitted_fields:
st.markdown('<div class="feature-label">Surface du sous-sol (pieds carrés)</div>', unsafe_allow_html=True)
sqft_basement = st.number_input("Surface du sous-sol", min_value=0, max_value=5000, value=0, step=100, label_visibility="collapsed")
# Close the container div
st.markdown('</div>', unsafe_allow_html=True)
# Submit button with custom styling
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
submitted = st.form_submit_button("Prédire le Prix", use_container_width=True)
# Add additional styling to the button
st.markdown("""
<style>
div.stButton > button {
background-color: #3498db;
color: white;
font-weight: bold;
border-radius: 5px;
border: none;
padding: 0.5em 1em;
font-size: 1.2em;
}
div.stButton > button:hover {
background-color: #2980b9;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
</style>
""", unsafe_allow_html=True)
# Process the form submission
if submitted:
if model is None:
st.error("Veuillez télécharger un modèle d'abord.")
else:
try:
# Get the confidence level and error margin from the sidebar
conf_level = confidence_level / 100.0
error_margin = error_margin_percent / 100.0
# Prepare input data
input_data = {
'bedrooms': bedrooms,
'bathrooms': bathrooms,
'sqft_living': sqft_living,
'sqft_lot': sqft_lot,
'floors': floors,
'waterfront': waterfront,
'view': view,
'condition': condition,
'grade': grade,
'sqft_above': sqft_above,
'sqft_basement': sqft_basement,
'yr_built': yr_built,
'yr_renovated': yr_renovated,
'zipcode': zipcode,
'lat': lat,
'long': long,
'sqft_living15': sqft_living, # Using the same value as sqft_living as an approximation
'sqft_lot15': sqft_lot # Using the same value as sqft_lot as an approximation
}
# Track omitted features
omitted_features = {k: None for k, v in input_data.items() if v is None}
# Preprocess inputs
processed_input = preprocess_inputs(input_data)
# Make prediction with confidence interval
predicted_price, interval_lower, interval_upper = predict_price(
model,
processed_input,
with_confidence_interval=True,
confidence_level=conf_level,
error_margin=error_margin
)
# Display prediction with custom styling
st.markdown(f'<div class="prediction-result">Prix Prédit: ${predicted_price:,.2f}</div>', unsafe_allow_html=True)
# Display confidence interval
st.markdown(f"""
<div class="confidence-interval">
Intervalle de confiance ({confidence_level}%): ${interval_lower:,.2f} - ${interval_upper:,.2f}
</div>
""", unsafe_allow_html=True)
# Show input summary with styled container
with st.expander("📋 Résumé des détails de la propriété"):
st.markdown('<div class="feature-section">', unsafe_allow_html=True)
# Show which features were used as provided by user
st.subheader("🔹 Caractéristiques fournies par l'utilisateur")
provided_features = {k: v for k, v in input_data.items() if v is not None}
if provided_features:
st.json(provided_features)
else:
st.info("Aucune caractéristique n'a été fournie par l'utilisateur.")
# Show which features were omitted and filled with default values
if omitted_features:
st.subheader("🔸 Caractéristiques omises (remplies avec des valeurs par défaut)")
st.write("Les caractéristiques suivantes ont été omises et remplies avec des valeurs par défaut du jeu de données:")
# Create columns for better display
omitted_cols = st.columns(3)
for i, feature in enumerate(omitted_features):
col_idx = i % 3
with omitted_cols[col_idx]:
st.markdown(f"**{feature}**: {processed_input[feature].values[0]:.2f}")
# Show the final processed input used for prediction
st.subheader("📊 Données d'entrée finales utilisées")
st.dataframe(processed_input, use_container_width=True)
# Show confidence interval details
st.subheader("📈 Détails de l'intervalle de confiance")
st.write(f"Niveau de confiance: {confidence_level}%")
st.write(f"Marge d'erreur estimée: {error_margin_percent}%")
st.write(f"Intervalle: ${interval_lower:,.2f} - ${interval_upper:,.2f}")
st.markdown('</div>', unsafe_allow_html=True)
except Exception as e:
st.error(f"Une erreur s'est produite lors de la prédiction: {e}")
st.error("Détails: " + str(e)) # More detailed error message
# Add a footer
st.markdown('<div class="footer">Développé avec BONDA DENICLO Emilio | © 2025</div>', unsafe_allow_html=True)