Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import numpy as np | |
| import joblib | |
| from typing import Dict, List, Any | |
| import logging | |
| # Configuration de la page Streamlit | |
| st.set_page_config( | |
| page_title='GetAround project', | |
| page_icon='🚗', | |
| layout="wide", | |
| initial_sidebar_state="auto", | |
| menu_items=None | |
| ) | |
| # Configuration du logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Constantes | |
| DATA_FILES = { | |
| 'pricing': 'data/pricing_project.csv', | |
| 'delay': 'data/delay_analysis.csv' | |
| } | |
| # Fonctions utilitaires | |
| def load_data(file_path: str, sep: str = ',') -> pd.DataFrame: | |
| """ | |
| Charge les données depuis un fichier CSV. | |
| Args: | |
| file_path (str): Chemin du fichier CSV | |
| sep (str): Séparateur utilisé dans le fichier CSV | |
| Returns: | |
| pd.DataFrame: DataFrame contenant les données | |
| """ | |
| try: | |
| return pd.read_csv(file_path, sep=sep) | |
| except Exception as e: | |
| logger.error(f"Erreur lors du chargement des données: {e}") | |
| st.error(f"Erreur lors du chargement des données: {e}") | |
| return pd.DataFrame() | |
| def display_metrics(df_delay: pd.DataFrame, df_pricing: pd.DataFrame, col: st.columns) -> None: | |
| """ | |
| Affiche les métriques principales dans les colonnes spécifiées. | |
| Args: | |
| df_delay (pd.DataFrame): DataFrame contenant les données de retard | |
| df_pricing (pd.DataFrame): DataFrame contenant les données de prix | |
| col (st.columns): Colonnes Streamlit pour l'affichage | |
| """ | |
| nb_rentals = len(df_delay) | |
| with col[0]: | |
| st.metric( | |
| label="Nombres de voitures dans le parc :", | |
| value=df_delay['car_id'].nunique() | |
| ) | |
| connect_percentage = round( | |
| len(df_pricing[df_pricing['has_getaround_connect'] == True]) / len(df_pricing) * 100 | |
| ) | |
| st.metric( | |
| label="Pourcentage de voitures équipées 'Connect' :", | |
| value=f"{connect_percentage} %" | |
| ) | |
| with col[2]: | |
| st.metric( | |
| label="Nombres de locations :", | |
| value=nb_rentals | |
| ) | |
| connect_rentals_percentage = round( | |
| len(df_delay[df_delay['checkin_type'] == 'connect']) / nb_rentals * 100 | |
| ) | |
| st.metric( | |
| label="Pourcentage de location via 'Connect' :", | |
| value=f"{connect_rentals_percentage} %" | |
| ) | |
| with col[1]: | |
| delay_percentage = round( | |
| len(df_delay[df_delay['delay_at_checkout_in_minutes'] > 0]) / nb_rentals * 100 | |
| ) | |
| st.metric( | |
| label="Pourcentage de locations rendues avec retard :", | |
| value=f"{delay_percentage} %" | |
| ) | |
| cancel_percentage = round( | |
| len(df_delay[df_delay['state'] == 'canceled']) / nb_rentals * 100 | |
| ) | |
| st.metric( | |
| label="Pourcentage de locations annulées :", | |
| value=f"{cancel_percentage} %" | |
| ) | |
| def main_page() -> None: | |
| """Page d'accueil de l'application.""" | |
| st.markdown("# Accueil") | |
| st.sidebar.markdown("# Accueil") | |
| st.header('Statistiques') | |
| # Chargement des données | |
| dataset_pricing = load_data(DATA_FILES['pricing']) | |
| dataset_delay = load_data(DATA_FILES['delay'], sep=';') | |
| if dataset_pricing.empty or dataset_delay.empty: | |
| st.error("Impossible de charger les données. Veuillez vérifier les fichiers.") | |
| return | |
| # Affichage des métriques | |
| main_metrics_cols = st.columns([33, 33, 34]) | |
| display_metrics(dataset_delay, dataset_pricing, main_metrics_cols) | |
| # Footer | |
| st.markdown("---") | |
| footer = """ | |
| <style> | |
| .footer { | |
| position: fixed; | |
| left: 0; | |
| bottom: 0; | |
| width: 100%; | |
| background-color: transparent; | |
| color: white; | |
| text-align: center; | |
| } | |
| </style> | |
| """ | |
| st.markdown(footer, unsafe_allow_html=True) | |
| def page2() -> None: | |
| """Page d'analyse des retards.""" | |
| st.title("Dashboard : Analyse d'un jeu de données de GetAround 🚗💲") | |
| st.markdown(""" | |
| Voici quelques informations clefs pour comprendre la dynamique des retards lors des réservations | |
| sur GetAround 🚗, ainsi que leur impact sur les locations, et donc sur le chiffre d'affaire | |
| potentiel de GetAround 🚗. | |
| """) | |
| st.markdown("---") | |
| # Chargement des données | |
| dataset_delay = load_data(DATA_FILES['delay'], sep=';') | |
| if dataset_delay.empty: | |
| st.error("Impossible de charger les données. Veuillez vérifier les fichiers.") | |
| return | |
| # Partie 1: Overview des retards | |
| st.subheader("Partie 1 : Overview des retards") | |
| main_metrics_cols_1 = st.columns([34, 33, 33]) | |
| with main_metrics_cols_1[0]: | |
| # Graphique des retards | |
| ended_rentals = dataset_delay[dataset_delay["state"] == "ended"] | |
| labels = ["A l'heure ou en avance", 'En retard'] | |
| values = [ | |
| len(ended_rentals[ended_rentals["delay_at_checkout_in_minutes"] <= 0]), | |
| len(ended_rentals[ended_rentals["delay_at_checkout_in_minutes"] > 0]) | |
| ] | |
| fig = px.pie( | |
| names=labels, | |
| values=values, | |
| title="Part des retards dans les réservations abouties" | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| with main_metrics_cols_1[1]: | |
| # Distribution des retards | |
| delayed_rentals = ended_rentals[ended_rentals["delay_at_checkout_in_minutes"] > 0] | |
| fig2 = px.histogram( | |
| delayed_rentals, | |
| x="delay_at_checkout_in_minutes", | |
| range_x=[0, 12*60], | |
| title="Distribution des retards en minutes", | |
| labels={"delay_at_checkout_in_minutes": "Retard au checkout (mn)"} | |
| ) | |
| st.plotly_chart(fig2, use_container_width=True) | |
| with main_metrics_cols_1[2]: | |
| # Métriques des retards | |
| moyenne_retard = delayed_rentals["delay_at_checkout_in_minutes"].median() | |
| st.metric( | |
| label="Retard médian : ", | |
| value=f"{round(moyenne_retard, 2)} minutes" | |
| ) | |
| retard_une_h = 100 * ( | |
| len(delayed_rentals[delayed_rentals["delay_at_checkout_in_minutes"] > 60]) / | |
| len(ended_rentals) | |
| ) | |
| st.metric( | |
| label="Retard supérieur à 1h :", | |
| value=f"{round(retard_une_h, 2)} %" | |
| ) | |
| # Partie 2: Analyse des délais | |
| st.markdown("---") | |
| st.subheader("Partie 2 : Impact des délais entre locations") | |
| main_metrics_cols_2 = st.columns([70, 30]) | |
| with main_metrics_cols_2[0]: | |
| with st.spinner('Chargement...'): | |
| # Préparation des données | |
| dataset_delay.dropna(subset=['delay_at_checkout_in_minutes'], inplace=True) | |
| dataset_delay = dataset_delay.reset_index(drop=True) | |
| dataset_delay['delay_problem'] = ( | |
| dataset_delay['delay_at_checkout_in_minutes'] - | |
| dataset_delay['time_delta_with_previous_rental_in_minutes'] | |
| ) | |
| # Calcul des statistiques par seuil | |
| def compute_stats_threshold(delay_tresh: int, check_type: str) -> int: | |
| mask = ( | |
| (dataset_delay['delay_problem'] > delay_tresh) & | |
| (dataset_delay['checkin_type'] == check_type) | |
| ) | |
| return dataset_delay[mask].count()[0] | |
| # Calcul des ratios de locations perdues | |
| nb_rent_connect = dataset_delay[dataset_delay['checkin_type'] == 'connect'].count()[0] | |
| nb_rent_mobile = dataset_delay[dataset_delay['checkin_type'] == 'mobile'].count()[0] | |
| results = { | |
| 'Threshold (min)': range(400), | |
| 'Rent_lost_mobile(%)': [], | |
| 'Rent_lost_connect(%)': [] | |
| } | |
| for i in range(400): | |
| results['Rent_lost_mobile(%)'].append( | |
| compute_stats_threshold(i, 'mobile') / nb_rent_mobile * 100 | |
| ) | |
| results['Rent_lost_connect(%)'].append( | |
| compute_stats_threshold(i, 'connect') / nb_rent_connect * 100 | |
| ) | |
| df_delay_stat_treshold = pd.DataFrame(results) | |
| # Affichage du graphique | |
| st.line_chart( | |
| data=df_delay_stat_treshold, | |
| x='Threshold (min)', | |
| y=["Rent_lost_mobile(%)", 'Rent_lost_connect(%)'], | |
| use_container_width=True | |
| ) | |
| # Sélecteur de délai | |
| delay = st.slider( | |
| 'Quel délai en deux locations (en minutes) :', | |
| 0, 400, 60 | |
| ) | |
| with main_metrics_cols_2[1]: | |
| # Affichage des métriques de délai | |
| st.metric( | |
| label=f"Pourcentage de location perdue sur mobile pour un délai de {delay} minutes :", | |
| value=f"{round(df_delay_stat_treshold.iloc[delay][1], 2)} %" | |
| ) | |
| st.metric( | |
| label=f"Pourcentage de location perdue sur l'app pour un délai de {delay} minutes :", | |
| value=f"{round(df_delay_stat_treshold.iloc[delay][2], 2)} %" | |
| ) | |
| def predict_price(values: List[Any]) -> float: | |
| """ | |
| Prédit le prix de location d'un véhicule. | |
| Args: | |
| values (List[Any]): Liste des caractéristiques du véhicule | |
| Returns: | |
| float: Prix prédit | |
| """ | |
| try: | |
| predict_array = np.zeros((1, 13)) | |
| im_df = pd.DataFrame( | |
| predict_array, | |
| columns=[ | |
| 'model_key', 'mileage', 'engine_power', 'fuel', 'paint_color', | |
| 'car_type', 'private_parking_available', 'has_gps', | |
| 'has_air_conditioning', 'automatic_car', 'has_getaround_connect', | |
| 'has_speed_regulator', 'winter_tires' | |
| ] | |
| ) | |
| im_df[0:1] = values | |
| loaded_model = joblib.load('model/finalized_model.sav') | |
| pipeline = joblib.load('model/finalized_prepoc.sav') | |
| result = loaded_model.predict(pipeline.transform(im_df)) | |
| return result[0] | |
| except Exception as e: | |
| logger.error(f"Erreur lors de la prédiction: {e}") | |
| st.error(f"Erreur lors de la prédiction: {e}") | |
| return 0.0 | |
| def page3() -> None: | |
| """Page de prédiction des prix.""" | |
| st.markdown("# Prédiction") | |
| st.sidebar.markdown("# Prédiction 🎉") | |
| st.markdown("**Veuillez entrer les informations concernant votre véhicule :**") | |
| # Chargement des données | |
| dataset_pricing = load_data(DATA_FILES['pricing']) | |
| if dataset_pricing.empty: | |
| st.error("Impossible de charger les données. Veuillez vérifier les fichiers.") | |
| return | |
| # Formulaire de saisie | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| marque = st.selectbox( | |
| 'Marque :', | |
| tuple(dataset_pricing['model_key'].unique()) | |
| ) | |
| kil = st.number_input( | |
| "Entrer le kilométrage :", | |
| 1, 1000000, 150000, 10 | |
| ) | |
| puissance = st.number_input( | |
| "Entrer la puissance du véhicule (en CV) :", | |
| 40, 400, 100, 1 | |
| ) | |
| energie = st.selectbox( | |
| 'Carburant :', | |
| tuple(dataset_pricing['fuel'].unique()) | |
| ) | |
| couleur = st.selectbox( | |
| 'Couleur du véhicule :', | |
| tuple(dataset_pricing['paint_color'].unique()) | |
| ) | |
| car_type = st.selectbox( | |
| 'Type de véhicule :', | |
| tuple(dataset_pricing['car_type'].unique()) | |
| ) | |
| with col2: | |
| # Options booléennes | |
| options = { | |
| 'parking': 'Place de parking privée', | |
| 'gps': 'GPS intégré', | |
| 'ac': 'Climatisation', | |
| 'auto': 'Boîte automatique', | |
| 'gac': 'GetAround Connect', | |
| 'speed': 'Régulateur de vitesse', | |
| 'hiver': 'Pneus hiver' | |
| } | |
| values = {} | |
| for key, label in options.items(): | |
| values[key] = st.selectbox(label + ' :', ('Yes', 'No')) == 'Yes' | |
| # Bouton de prédiction | |
| if st.button("Predict"): | |
| list_values = [ | |
| marque, int(kil), int(puissance), energie, couleur, car_type, | |
| values['parking'], values['gps'], values['ac'], values['auto'], | |
| values['gac'], values['speed'], values['hiver'] | |
| ] | |
| result = predict_price(list_values) | |
| if result > 0: | |
| st.success( | |
| f"Le montant de location à la journée de votre véhicule " | |
| f"s'élève à {result:.2f} €" | |
| ) | |
| # Configuration des pages | |
| page_names_to_funcs = { | |
| "Accueil": main_page, | |
| "Dashboard": page2, | |
| "Prédiction": page3, | |
| } | |
| # Sélection et affichage de la page | |
| selected_page = st.sidebar.selectbox( | |
| "Selectionner une page :", | |
| page_names_to_funcs.keys() | |
| ) | |
| page_names_to_funcs[selected_page]() |