LioD19's picture
Update src/app.py
3166e72 verified
Raw
History Blame Contribute Delete
31.2 kB
import streamlit as st
import pandas as pd
import numpy as np
import pickle
import os
import sys
from data_processing import load_sample_data, preprocess_inputs
from model_utils import load_model, predict_price
from streamlit_folium import folium_static
from visualizations import (
create_price_distribution_chart,
create_feature_impact_chart,
create_price_vs_area_chart,
create_correlation_heatmap,
create_interactive_map,
create_price_by_zipcode_chart,
create_comparison_dashboard
)
from ai_chatbot import display_chat_interface
# Set page config
st.set_page_config(
page_title="House Price Predictor",
page_icon="🏠",
layout="wide"
)
# Intégration directe du CSS
st.markdown("""
<style>
/* Custom Streamlit CSS */
/* Main background and text colors */
.stApp {
background-color: #171933;
color: #D4DCFF;
}
.stMarkdown {
color: #D4DCFF;
}
.stSidebar {
background-color: #22244D;
color: #D4DCFF;
}
.stSidebar .st-emotion-cache-l1ktzw {
color: #D4DCFF;
}
/* Headers styling */
h1, h2, h3 {
font-family: 'Roboto', sans-serif;
}
h1 {
font-size: 2.5rem;
color: #D4DCFF;
text-align: center;
margin-bottom: 1rem;
font-weight: 700;
padding-bottom: 0.3em;
}
h2 {
font-weight: 600;
color: #D4DCFF;
}
h3 {
font-weight: 500;
}
.e194bff02 {
color: #D4DCFF;
}
/* Form styling */
.stForm {
background-color: #22244D;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.stForm input, .stForm .st-dr, .stForm .e5tuigk2 {
background-color: #171933;
}
.stForm .st-ds,
.stForm #number_input_1,
.stForm #number_input_2,
.stForm #number_input_3,
.stForm #number_input_4,
.stForm #number_input_5,
.stForm #number_input_6,
.stForm #number_input_7,
.stForm #number_input_8,
.stForm #number_input_9,
.stForm #number_input_10,
.stForm #number_input_11,
.stForm #number_input_12{
color: #D4DCFF;
}
.st-cw .st-ce .st-bn .st-cx .st-cy .st-cz .st-d0 {
background-color: #22244D;
}
.em9zgd08 {
background-color: #5396E7;
}
/* Button styling */
.stButton > button {
background-color: #3498db;
color: white;
font-weight: 600;
border-radius: 5px;
padding: 0.5em 1em;
transition: all 0.3s ease;
}
.stButton > button:hover {
background-color: #2980b9;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
/* Input widgets styling - Version cohérente */
.stNumberInput > div > div > input,
.stSelectbox > div > div > input,
.stTextInput > div > div > input,
.stTextArea > div > div > textarea,
.stMultiselect > div > div > div,
.stSlider > div > div > div > div,
.stCheckbox > div > div > div,
.stRadio > div > div > div {
background-color: #171933 !important;
border: 1px solid #3498db !important;
border-radius: 5px !important;
color: #D4DCFF !important;
}
/* Focus states pour les inputs */
.stNumberInput > div > div > input:focus,
.stSelectbox > div > div > input:focus,
.stTextInput > div > div > input:focus,
.stTextArea > div > div > textarea:focus {
border-color: #3498db !important;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2) !important;
}
/* Styling pour les dropdowns et selectbox */
.stSelectbox > div > div > div[data-baseweb="select"] {
background-color: #171933 !important;
border: 1px solid #3498db !important;
}
/* Styling pour les multiselect */
.stMultiselect > div > div > div[data-baseweb="select"] {
background-color: #171933 !important;
border: 1px solid #3498db !important;
}
/* Styling pour les sliders */
.stSlider > div > div > div > div[data-baseweb="slider"] {
background-color: #171933 !important;
}
/* Styling pour les checkboxes et radios */
.stCheckbox > div > div > div[data-baseweb="checkbox"],
.stRadio > div > div > div[data-baseweb="radio"] {
background-color: #171933 !important;
border: 1px solid #3498db !important;
}
/* Hover effects pour tous les inputs */
.stNumberInput:hover > div > div > input,
.stSelectbox:hover > div > div > input,
.stTextInput:hover > div > div > input,
.stTextArea:hover > div > div > textarea,
.stMultiselect:hover > div > div > div,
.stSlider:hover > div > div > div > div,
.stCheckbox:hover > div > div > div,
.stRadio:hover > div > div > div {
border-color: #2980b9 !important;
box-shadow: 0 2px 4px rgba(52, 152, 219, 0.1) !important;
}
/* Sidebar styling */
.css-1d391kg {
background-color: #ffffff;
}
.css-1d391kg .stMarkdown {
color: #D4DCFF;
}
/* Success message styling */
.element-container .stAlert.st-ae {
border-radius: 8px;
}
.element-container .stSuccess {
color: #155724;
border-color: #c3e6cb;
}
/* Error message styling */
.element-container .stError {
color: #721c24;
border-color: #f5c6cb;
}
/* Multiselect dropdown styling */
.stMultiSelect > div {
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* Add some animation to the chart */
[data-testid="stDataFrame"] {
transition: transform 0.3s ease;
}
[data-testid="stDataFrame"]:hover {
transform: scale(1.01);
}
/* Card-like sections */
.stExpander {
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background-color: #22244D;
}
.e194bff00 {
color: #D4DCFF;
}
/* Section dividers */
hr {
border: 0;
height: 1px;
background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(52, 152, 219, 0.75), rgba(0, 0, 0, 0));
margin: 2em 0;
}
/* Hover effects for selectbox */
.stSelectbox:hover {
border-color: #3498db;
}
/* Property Details Section Styling */
[data-testid="stHeader"]:has(h1, h2, h3)::before {
content: "🏠";
margin-right: 10px;
}
/* Custom styling for specific components */
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;
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;
}
.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;
}
.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);
}
/* Labels styling pour une meilleure cohérence */
.stNumberInput > label,
.stSelectbox > label,
.stTextInput > label,
.stTextArea > label,
.stMultiselect > label,
.stSlider > label,
.stCheckbox > label,
.stRadio > label {
color: #D4DCFF !important;
font-weight: 600 !important;
margin-bottom: 8px !important;
}
/* Conteneurs de widgets pour une meilleure organisation */
.stNumberInput,
.stSelectbox,
.stTextInput,
.stTextArea,
.stMultiselect,
.stSlider,
.stCheckbox,
.stRadio {
margin-bottom: 15px !important;
}
/* Styling pour les placeholders */
.stNumberInput > div > div > input::placeholder,
.stTextInput > div > div > input::placeholder,
.stTextArea > div > div > textarea::placeholder {
color: #95a5a6 !important;
opacity: 0.7 !important;
}
/* Amélioration du styling des boutons de formulaire */
.stFormSubmitButton > button {
background-color: #3498db !important;
color: white !important;
font-weight: 600 !important;
border-radius: 8px !important;
padding: 12px 24px !important;
border: none !important;
transition: all 0.3s ease !important;
box-shadow: 0 2px 4px rgba(52, 152, 219, 0.2) !important;
}
.stFormSubmitButton > button:hover {
background-color: #2980b9 !important;
box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3) !important;
transform: translateY(-1px) !important;
}
/* Styling pour les messages d'erreur et de succès */
.stAlert {
border-radius: 8px !important;
border: none !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
}
/* Amélioration du styling des onglets */
.stTabs > div > div > div > div > div {
background-color: #22244D !important;
border-radius: 8px !important;
}
.stTabs > div > div > div > div > div > button {
color: #D4DCFF !important;
background-color: transparent !important;
border: none !important;
border-radius: 6px !important;
transition: all 0.3s ease !important;
}
.stTabs > div > div > div > div > div > button[aria-selected="true"] {
background-color: #3498db !important;
color: white !important;
box-shadow: 0 2px 4px rgba(52, 152, 219, 0.2) !important;
}
</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()
# Charger le modèle directement au démarrage de l'application
if 'model' not in st.session_state:
try:
st.session_state.model = load_model()
st.success("✅ Modèle chargé avec succès !")
except Exception as e:
st.error(f"Erreur lors du chargement du modèle : {str(e)}")
st.session_state.model = None
# Sidebar for data statistics and settings
with st.sidebar:
# Settings section
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)
# 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"):
# 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 terrain (pieds carrés)</div>', unsafe_allow_html=True)
sqft_lot = st.number_input("Taille du terrain", min_value=200, max_value=100000, 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 "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=9000, value=1000, step=100, label_visibility="collapsed")
# Column 3 features
with col3:
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")
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")
if "Year Renovated" not in omitted_fields:
st.markdown('<div class="feature-label">Année de rénovation (0 = jamais rénové)</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=10000, max_value=99999, value=98000, step=1, label_visibility="collapsed")
# Geographic Location (optional)
if "Latitude" not in omitted_fields or "Longitude" not in omitted_fields:
st.markdown("<hr>", unsafe_allow_html=True)
st.markdown('<h3 style="color: #D4DCFF; text-align: center; margin-bottom: 15px;">Localisation Géographique</h3>', unsafe_allow_html=True)
geo_col1, geo_col2 = st.columns(2)
with geo_col1:
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=45.0, max_value=50.0, value=47.5, step=0.001, format="%.6f", label_visibility="collapsed")
with geo_col2:
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=-125.0, max_value=-115.0, value=-122.0, step=0.001, format="%.6f", label_visibility="collapsed")
# Close the form div
st.markdown('</div>', unsafe_allow_html=True)
# Submit button
submit_button = st.form_submit_button(
label="Prédire le Prix",
use_container_width=True,
type="primary"
)
# Create input dictionary from form values
input_dict = {
'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
}
# Handle form submission
if submit_button:
# Check if model is loaded
if st.session_state.model is None:
st.error("⚠️ Le modèle n'a pas pu être chargé. Veuillez vérifier que le fichier 'model.pkl' existe.")
else:
try:
# Preprocess inputs
processed_input = preprocess_inputs(input_dict)
# Make prediction
confidence_level_decimal = confidence_level / 100
error_margin_decimal = error_margin_percent / 100
prediction_result = predict_price(
st.session_state.model,
processed_input,
with_confidence_interval=True,
confidence_level=confidence_level_decimal,
error_margin=error_margin_decimal
)
# Suppression de l'affichage du prix estimé ici (évite le double affichage)
# On conserve uniquement la sauvegarde dans la session
property_data_for_chat = {k: v for k, v in input_dict.items() if v is not None}
st.session_state.last_prediction_data = {
'property_data': property_data_for_chat,
'prediction_result': prediction_result,
'market_data': sample_data,
'confidence_level': confidence_level,
'error_margin_percent': error_margin_percent
}
except Exception as e:
st.error(f"Erreur lors de la prédiction: {str(e)}")
st.info("Veuillez vérifier que le modèle chargé est compatible avec les caractéristiques que vous avez saisies.")
# Affichage persistant des visualisations et du chatbot
if 'last_prediction_data' in st.session_state and st.session_state.last_prediction_data:
prediction_data = st.session_state.last_prediction_data
prediction_result = prediction_data['prediction_result']
property_data = prediction_data['property_data']
sample_data = prediction_data['market_data']
confidence_level = prediction_data.get('confidence_level', 95)
error_margin_percent = prediction_data.get('error_margin_percent', 15)
if isinstance(prediction_result, tuple) and len(prediction_result) == 3:
predicted_price, lower_bound, upper_bound = prediction_result
else:
predicted_price = prediction_result
lower_bound = upper_bound = None
# Affichage du résultat de la prédiction
st.markdown(f"""
<div class="prediction-result">
💰 Prix Estimé: ${predicted_price:,.2f}
</div>
""", unsafe_allow_html=True)
if lower_bound is not None and upper_bound is not None:
st.info(f"""
Cette prédiction est basée sur les caractéristiques que vous avez fournies.
L'intervalle de confiance indique que le prix réel a {confidence_level}% de chances
de se situer entre les valeurs indiquées, en supposant une marge d'erreur de {error_margin_percent}%.
""")
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: ${lower_bound:,.2f} - ${upper_bound:,.2f}")
st.progress((predicted_price - lower_bound) / (upper_bound - lower_bound))
# Visualisations interactives
st.markdown("<hr>", unsafe_allow_html=True)
st.markdown('<h2 style="color: #D4DCFF; text-align: center; margin: 20px 0;">📊 Visualisations Interactives</h2>', unsafe_allow_html=True)
tab1, tab2, tab3, tab4, tab5 = st.tabs([
"📈 Distribution des Prix",
"🏠 Prix vs Caractéristiques",
"🗺️ Carte Interactive",
"📊 Analyse Comparative",
"🔍 Corrélations"
])
with tab1:
st.markdown('<h3 style="color: #D4DCFF;margin-bottom: 10px;padding-left: 20px;">Distribution des Prix du Marché</h3>', unsafe_allow_html=True)
price_dist_chart = create_price_distribution_chart(sample_data, predicted_price)
if price_dist_chart:
st.plotly_chart(price_dist_chart, use_container_width=True)
price_area_chart = create_price_vs_area_chart(sample_data, predicted_price, property_data.get('sqft_living'))
if price_area_chart:
st.plotly_chart(price_area_chart, use_container_width=True)
with tab2:
st.markdown('<h3 style="color: #D4DCFF;margin-bottom: 10px;padding-left: 20px;">Impact des Caractéristiques sur le Prix</h3>', unsafe_allow_html=True)
feature_options = {
'bedrooms': 'Nombre de chambres',
'bathrooms': 'Nombre de salles de bain',
'sqft_living': 'Surface habitable',
'floors': 'Nombre d\'étages',
'grade': 'Qualité de construction',
'condition': 'État de la propriété'
}
selected_feature = st.selectbox(
"Choisissez une caractéristique à analyser :",
options=list(feature_options.keys()),
format_func=lambda x: feature_options[x]
)
feature_chart = create_feature_impact_chart(sample_data, selected_feature, predicted_price)
if feature_chart:
st.plotly_chart(feature_chart, use_container_width=True)
with tab3:
st.markdown('<h3 style="color: #D4DCFF;margin-bottom: 10px;padding-left: 20px;">Carte Interactive des Propriétés</h3>', unsafe_allow_html=True)
lat = property_data.get('lat')
long = property_data.get('long')
if lat is not None and long is not None:
interactive_map = create_interactive_map(sample_data, lat, long, predicted_price)
if interactive_map:
folium_static(interactive_map, width=800, height=500)
else:
st.info("📍 Activez les coordonnées géographiques pour voir la carte interactive")
interactive_map = create_interactive_map(sample_data)
if interactive_map:
folium_static(interactive_map, width=800, height=500)
with tab4:
st.markdown('<h3 style="color: #D4DCFF;margin-bottom: 10px;padding-left: 20px;">Analyse Comparative avec le Marché</h3>', unsafe_allow_html=True)
comparison_chart, comparison_stats = create_comparison_dashboard(sample_data, predicted_price, property_data)
if comparison_chart:
st.plotly_chart(comparison_chart, use_container_width=True)
if comparison_stats:
st.markdown('<h4 style="color: #D4DCFF; margin-top: 20px;margin-bottom: 10px;padding-left: 20px;">Statistiques Comparatives</h4>', unsafe_allow_html=True)
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Prix Moyen", comparison_stats['Prix moyen du marché'])
with col2:
st.metric("Prix Médian", comparison_stats['Prix médian du marché'])
with col3:
st.metric("Votre Prédiction", comparison_stats['Votre prédiction'])
col4, col5 = st.columns(2)
with col4:
st.metric("Différence", comparison_stats['Différence avec la moyenne'])
with col5:
st.metric("Pourcentage", comparison_stats['Pourcentage vs moyenne'])
zipcode_chart = create_price_by_zipcode_chart(sample_data, predicted_price, property_data.get('zipcode'))
if zipcode_chart:
st.plotly_chart(zipcode_chart, use_container_width=True)
with tab5:
st.markdown('<h3 style="color: #D4DCFF;margin-bottom: 10px;padding-left: 20px;">Matrice de Corrélation</h3>', unsafe_allow_html=True)
correlation_chart = create_correlation_heatmap(sample_data)
if correlation_chart:
st.plotly_chart(correlation_chart, use_container_width=True)
st.markdown("""
<div style="background-color: #22244D; padding: 15px; border-radius: 8px; margin-top: 15px;">
<h4 style="color: #D4DCFF; margin-bottom: 10px;">📋 Interprétation des Corrélations</h4>
<ul style="color: #D4DCFF;">
<li><strong>Rouge foncé</strong> : Corrélation positive forte (proche de 1)</li>
<li><strong>Bleu foncé</strong> : Corrélation négative forte (proche de -1)</li>
<li><strong>Blanc</strong> : Pas de corrélation (proche de 0)</li>
</ul>
</div>
""", unsafe_allow_html=True)
# Chatbot
st.markdown("<hr>", unsafe_allow_html=True)
st.markdown('<h2 style="color: #D4DCFF; text-align: center; margin: 20px 0;">🤖 Assistant IA Immobilier</h2>', unsafe_allow_html=True)
display_chat_interface(
property_data=property_data,
prediction_result=prediction_result,
market_data=sample_data
)
# Add a footer
st.markdown("""
<div class="footer">
Développé avec BONDA DENICLO Emilio | © 2025
</div>
""", unsafe_allow_html=True)