Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import os | |
| import plotly.express as px | |
| import requests | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| st.set_page_config(page_title="Getaround – Tableau de Bord", layout="wide") | |
| st.markdown(""" | |
| <style> | |
| .stTabs [role="tablist"] { | |
| justify-content: center; | |
| gap: 25px; | |
| } | |
| .stTabs [role="tab"] { | |
| font-size: 17px; | |
| font-weight: 600; | |
| padding: 10px 20px; | |
| border-radius: 10px; | |
| background-color: #f8f9fa; | |
| color: #444; | |
| transition: all 0.3s ease-in-out; | |
| } | |
| .stTabs [role="tab"]:hover { | |
| background-color: #e0e0e0; | |
| transform: scale(1.05); | |
| } | |
| .stTabs [role="tab"][aria-selected="true"] { | |
| background-color: #4CAF50; | |
| color: white; | |
| font-weight: 700; | |
| box-shadow: 0px 4px 8px rgba(0,0,0,0.2); | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| st.markdown( | |
| """ | |
| <h1 style='text-align: center;'>🚙🚕🚗🚙🚕🚗🚙🚕🚗🚙🚕🚗🚙🚕🚗🚙</h1> | |
| <h1 style='text-align: center;'>🚙---Tableau de Bord du projet GETAROUND---🚗</h1> | |
| <h1 style='text-align: center;'>🚙🚕🚗🚙🚕🚗🚙🚕🚗🚗🚕🚙🚙🚕🚗🚕</h1> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| st.markdown(""" | |
| Ci-dessous Vous trouverez les trois étapes du projet ainsi que l'outil de prédiction élaboré à partir des données disponibles | |
| """) | |
| tab1, tab2, tab3, tab4 = st.tabs(["📊⏱ Analyse des locations et retards", "💲Analyse tarifaire", " ✍️ Constat et conclusions", "🔮✨ Prédiction par API"]) | |
| with tab1: | |
| # Import des datasets | |
| delay = pd.read_excel("data/get_around_delay_analysis.xlsx") | |
| price=pd.read_csv('data/get_around_pricing_project.csv') | |
| annulation_by_delay=pd.read_csv('data/annulation_by_delay.csv') | |
| st.header("1. Chiffres des locations sur 21 310 locations") | |
| st.subheader("- Fréquence de location des véhicules") | |
| # ANALYSE DES LOCATIONS | |
| # Premier graph nombre de locations des véhicules | |
| car_usage = delay['car_id'].value_counts() | |
| car_usage_top10 = car_usage[car_usage <= 15] | |
| fig = px.histogram( | |
| car_usage_top10, | |
| nbins=15, | |
| labels={'value': 'Nombre de locations', 'count': 'Nombre de véhicules'} | |
| ) | |
| fig.update_layout( | |
| bargap=0.1, | |
| height=600 | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown("<h4>👉 Près de 45% des véhicules ne sont loués qu'une fois</h4>", unsafe_allow_html=True) | |
| st.markdown("---") | |
| st.header("2. Types de Location et Annulations") | |
| st.subheader("- Répartition des modes de location et Proportion d'annulation ?") | |
| # Second graph type de locations et annulations | |
| checkin_counts = delay['checkin_type'].value_counts() | |
| course_state = delay['state'].value_counts() | |
| fig = make_subplots( | |
| rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]], | |
| subplot_titles=("Répartition des types de check-in", "Répartition des statuts des locations") | |
| ) | |
| fig.add_trace( | |
| go.Pie(labels=checkin_counts.index, values=checkin_counts.values, name="Check-in type"), | |
| row=1, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Pie(labels=course_state.index, values=course_state.values, name="State"), | |
| row=1, col=2 | |
| ) | |
| fig.update_traces(hole=0, textinfo='label+percent') | |
| fig.update_layout( | |
| height=500, | |
| width=900, | |
| showlegend=False, | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown("<h4>👉 80% des locations s'effectuent part le check-in mobile contre 20% pour le connect." \ | |
| " Environ 15% des courses annulées sur un total d'à peu près 3200 courses", unsafe_allow_html=True) | |
| st.markdown("---") | |
| # Troisième graph proportion d'annulation selon le type de location | |
| st.subheader("Quelle mode de location est le plus impacté par les annulations ?") | |
| total_per_type = delay.groupby('checkin_type')['state'].count() | |
| canceled_per_type = delay[delay['state'] == 'canceled'].groupby('checkin_type')['state'].count() | |
| proportion_canceled = (canceled_per_type / total_per_type).fillna(0) * 100 | |
| prop_delay = proportion_canceled.reset_index() | |
| prop_delay.columns = ['checkin_type', 'proportion_canceled'] | |
| color_map = { | |
| 'mobile': 'pink', | |
| 'connect': 'royalblue' | |
| } | |
| fig = px.bar( | |
| prop_delay, | |
| x='checkin_type', | |
| y='proportion_canceled', | |
| text=prop_delay['proportion_canceled'].round(1), | |
| labels={'checkin_type':'Type de check-in', 'proportion_canceled':'% Annulations'}, | |
| color='checkin_type', | |
| color_discrete_map=color_map | |
| ) | |
| fig.update_layout( | |
| height=600 | |
| ) | |
| fig.update_traces(textposition='outside') | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown("<h4>👉 En fonction du type de check-in, nous relevons des proportions d'annulations différentes." \ | |
| " Les utilisateurs de la version Connect annulent plus souvent leurs locations; avec un ecart de 4% par rapport au mobile " | |
| "", unsafe_allow_html=True) | |
| st.markdown("---") | |
| st.header("3. Les retards") | |
| # Traitement outlier des retards | |
| summary = delay['delay_at_checkout_in_minutes'].describe() | |
| Q1 = delay['delay_at_checkout_in_minutes'].quantile(0.25) | |
| Q3 = delay['delay_at_checkout_in_minutes'].quantile(0.75) | |
| IQR = Q3 - Q1 | |
| lower_bound = Q1 - 1.5*IQR | |
| upper_bound = Q3 + 1.5*IQR | |
| delay_filtered = delay[(delay['delay_at_checkout_in_minutes'] >= lower_bound) & | |
| (delay['delay_at_checkout_in_minutes'] <= upper_bound)] | |
| # Quatrième Graph de distribution des retards | |
| st.markdown("""Détail la distribution des délais de retard""") | |
| fig = px.histogram( | |
| delay_filtered, | |
| x='delay_at_checkout_in_minutes', | |
| nbins=50, | |
| color='checkin_type', | |
| barmode='overlay', | |
| labels={'delay_at_checkout_in_minutes': 'Retard au checkout (minutes)', 'count': 'Nombre de locations'}, | |
| color_discrete_map={'mobile': 'royalblue', 'connect': 'crimson'} | |
| ) | |
| fig.update_layout( | |
| height=600, | |
| bargap=0.1 | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown(""" | |
| 👉 La restitution des véhicules en temps et en heures est élevée, nous notons un centrage des valeurs autour de 0, avec une distribution normale pour les deux mode de checkin. | |
| Il existe cependant des retards importants.""" | |
| ) | |
| # Centrage sur les courses annulées : | |
| # Création du dataframe relatant les cancel | |
| delay_canceled = delay[delay["state"] == "canceled"][["rental_id", "car_id", "previous_ended_rental_id", | |
| "checkin_type","time_delta_with_previous_rental_in_minutes"]] | |
| # Rattachement de la location annulée à l'id de la location précédente | |
| delay_merged = delay_canceled.merge( | |
| delay[["rental_id", "car_id", "delay_at_checkout_in_minutes", "checkin_type"]], | |
| left_on="previous_ended_rental_id", | |
| right_on="rental_id", | |
| suffixes=("_canceled", "_previous") | |
| ) | |
| # Inspection de l'écart entre le delta et le retard | |
| delay_problems = delay_merged[ | |
| delay_merged["delay_at_checkout_in_minutes"] > delay_merged["time_delta_with_previous_rental_in_minutes"] | |
| ] | |
| # Découpage des retards en tranches horaires | |
| bins = [0, 15, 30, 60, 120, 9999] | |
| labels = ["0-15 min", "15-30 min", "30-60 min", "1-2 h", ">2 h"] | |
| delay_problems["delay_category"] = pd.cut( | |
| delay_problems["delay_at_checkout_in_minutes"], | |
| bins=bins, | |
| labels=labels, | |
| include_lowest=True | |
| ) | |
| st.markdown("---") | |
| st.subheader("""Proportion de la durée des retards dans les annulations ?""") | |
| # Cinquième graph de la part des durées de retard dans les annulations selon le checkin : | |
| fig = px.pie( | |
| delay_problems, | |
| names="delay_category", | |
| facet_col="checkin_type_canceled", | |
| ) | |
| fig.for_each_annotation(lambda a: a.update( | |
| text=a.text.split("=")[-1] | |
| )) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown("""Les retards de plus de 2 heures représentent la majeure partie des annulations, | |
| ce qui paraît logique pour le futur client. Le check-in mobile présente une proportion | |
| de cette durée supérieure de 8,9%par rapport au connect.""") | |
| st.markdown("---") | |
| st.header("""Quel est l'impact réel des retards sur les annulations, en fonction du type de check-in ?""") | |
| # Création d'intervalles pour déterminer les différents créneaux de retard | |
| bins = [-9999, 0, 15, 30, 60, 120, 240, 9999] | |
| labels = ["≤0", "1-15 min", "15-30 min", "30-60 min", "1-2 h", "2-4 h", ">4 h"] | |
| delay_merged["delay_group"] = pd.cut( | |
| delay_merged["delay_at_checkout_in_minutes"], | |
| bins=bins, | |
| labels=labels, | |
| include_lowest=True | |
| ) | |
| # Groupage des annulations par intervalles de retard | |
| annulation_by_delay = ( | |
| delay_merged.groupby(["delay_group", "checkin_type_canceled"])["rental_id_canceled"] | |
| .count() | |
| .reset_index(name="nb_annulations") | |
| ) | |
| # Sixième graph pour visualisation de l'importance de la durée des retards | |
| fig = px.bar( | |
| annulation_by_delay, | |
| x="delay_group", | |
| y="nb_annulations", | |
| color="checkin_type_canceled", | |
| barmode="group", | |
| labels={ | |
| "delay_group": "Temps de retard", | |
| "nb_annulations": "Nombre d'annulations", | |
| "checkin_type_canceled": "Type de check-in" | |
| } | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Analyse des locations maintenues malgré un retard précédent | |
| st.header("""Chiffres des locations maintenues malgré un retard précédent""") | |
| # Création du dataframe relatant les ended | |
| delay_ended = delay[delay["state"] == "ended"][["rental_id", "car_id", "previous_ended_rental_id", | |
| "checkin_type","time_delta_with_previous_rental_in_minutes"]] | |
| delay_ended_merged = delay_ended.merge( | |
| delay[["rental_id", "car_id", "delay_at_checkout_in_minutes", "checkin_type"]], | |
| left_on="previous_ended_rental_id", | |
| right_on="rental_id", | |
| suffixes=("_ended", "_previous") ) | |
| delay_ended_merged["delay_group"] = pd.cut( | |
| delay_ended_merged["delay_at_checkout_in_minutes"], | |
| bins=bins, | |
| labels=labels, | |
| include_lowest=True | |
| ) | |
| # Agrégation : nombre de courses terminées malgré un retard | |
| ended_by_delay = ( | |
| delay_ended_merged.groupby(["delay_group", "checkin_type_ended"])["rental_id_ended"].count().reset_index(name="nb_ended") | |
| ) | |
| # Graphique Plotly | |
| fig = px.bar( | |
| ended_by_delay, | |
| x="delay_group", | |
| y="nb_ended", | |
| color="checkin_type_ended", | |
| barmode="group", | |
| labels={ | |
| "delay_group": "Temps de retard", | |
| "nb_ended": "Nombre de locations maintenues", | |
| "checkin_type_ended": "Type de check-in" | |
| } | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.subheader(""" | |
| Les locations maintenues sont beaucoup plus importantes que les annulations malgré un retard précédent. | |
| Pour 1 à 15 minutes de retard : | |
| - 203 maintenues | |
| - 17 annulées | |
| Pour 15 à 30 minutes de retard : | |
| - 127 maintenues | |
| - 17 annulées | |
| """) | |
| st.markdown('---') | |
| # ANALYSE TARIFAIRE | |
| with tab2: | |
| st.header("1. Le parc automobile des propriétaires") | |
| st.subheader("Quelles sont les 10 marques les plus présentes ?") | |
| # Tri des véhicules pour déterminer les véhicules uniques | |
| cols_check = [ | |
| "model_key", "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" | |
| ] | |
| price_dedup = price.drop_duplicates(subset=cols_check, keep="first") | |
| # Premier graph de la représentation des 10 premières marques | |
| model_counts = price_dedup["model_key"].value_counts().reset_index() | |
| model_counts.columns = ["model_key", "total"] | |
| top10_models = model_counts.head(10) | |
| fig = px.bar( | |
| top10_models, | |
| x="model_key", | |
| y="total", | |
| labels={"model_key": "Marque", "total": "Véhicules"} | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Second graph de la représentation des marques les plus louées | |
| st.subheader('Quelles sont les marques ayant le plus de succès ?') | |
| model_counts = price["model_key"].value_counts().reset_index() | |
| model_counts.columns = ["model_key", "total"] | |
| top10_models = model_counts.head(10) | |
| fig = px.bar( | |
| top10_models, | |
| x="model_key", | |
| y="total", | |
| labels={"model_key": "Marque", "total": "Véhicules"} | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown('Les 3 marques les plus présentes sont aussi les plus louées : Citroën, BMW et Renault') | |
| st.markdown('---') | |
| # Troisième graph pour estimer le prix d'une location | |
| st.subheader("2. Quelle base tarifaire pour estimer le prix d'une location ?") | |
| fig = px.histogram( | |
| price, | |
| x="rental_price_per_day", | |
| nbins=30, | |
| labels={"rental_price_per_day": "Prix journalier de la location"} | |
| ) | |
| # Calcul médiane, moyenne et prix de référence | |
| median_value = price["rental_price_per_day"].median() | |
| mean_value = price["rental_price_per_day"].mean() | |
| ref_price = ((mean_value + median_value)/2).round(2) | |
| # Ligne verticale médiane | |
| fig.add_vline( | |
| x=median_value, | |
| line_dash="dash", | |
| line_color="red", | |
| annotation_text=f"Médiane = {median_value:.2f}", | |
| annotation_position="top right" | |
| ) | |
| # Ligne verticale moyenne | |
| fig.add_vline( | |
| x=mean_value, | |
| line_dash="dot", | |
| line_color="blue", | |
| annotation_text=f"Moyenne = {mean_value:.2f}", | |
| annotation_position="top left" | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown(" 👉 Le constat est simple, la moyenne tarifaire d'une location est de 121 dollars quand la médiane est à 119 dollars. " \ | |
| "La prix d'une location est donc fixé à la valeur intermédiaire soit 120,11 dollars.") | |
| # Quatrième graph pour visualiser le montant des pertes selon les retards | |
| st.subheader(("3. Quelle est le réel impact de perte de CA des retards ?")) | |
| # Mise en place des intervalles | |
| ordre = ["≤0", "1-15 min", "15-30 min", "30-60 min", "1-2 h", "2-4 h", ">4 h"] | |
| # Transformer delay_group en catégorie ordonnée | |
| annulation_by_delay["delay_group"] = pd.Categorical( | |
| annulation_by_delay["delay_group"], | |
| categories=ordre, | |
| ordered=True | |
| ) | |
| # On calcule la perte potentielle pour chaque ligne | |
| annulation_by_delay["perte_potentielle"] = annulation_by_delay["nb_annulations"] * ref_price | |
| # Tableau loss pour visualiser la perte par délai et type de checkin | |
| loss = annulation_by_delay.pivot_table( | |
| index="delay_group", | |
| columns="checkin_type_canceled", | |
| values="perte_potentielle", | |
| aggfunc="sum", | |
| fill_value=0 | |
| ) | |
| # Ajouter une colonne de perte totale par intervalle | |
| loss["total_perte"] = loss.sum(axis=1) | |
| # Affichage du graph | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=loss.index, | |
| y=loss["connect"], | |
| name='Connect', | |
| marker_color='blue', | |
| hovertemplate='Connect: %{y:.2f} €<extra></extra>' | |
| )) | |
| fig.add_trace(go.Bar( | |
| x=loss.index, | |
| y=loss["mobile"], | |
| name='Mobile', | |
| marker_color='orange', | |
| hovertemplate='Mobile: %{y:.2f} €<extra></extra>' | |
| )) | |
| fig.update_layout( | |
| xaxis_title="Intervalle de retard", | |
| yaxis_title="Perte potentielle (€)", | |
| template='plotly_white', | |
| hovermode="x unified", | |
| barmode="group" | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown(""" | |
| ### 📊 Analyse des pertes de CA liées aux retards | |
| - **La plus grande perte de CA n'est clairement pas constituée par les retards**, | |
| puisque nous avons **12 011 $** de perdus **sans aucun retard**, soit près de **la moitié des pertes (48,54%)**. | |
| - **Mettre en place un délai supplémentaire ne paraît pas justifié**, puisque le nombre d'annulations liées à des | |
| petits retards (1 à 15 min) représente **17 courses**, pour un total de **2 041 $**, soit **à peine 10% des pertes**. | |
| - Pour atteindre l’équivalent des pertes non dues à un retard, il faudrait mettre en place un **délai de plus de 2 heures**, | |
| sans garantie de trouver une course, ce qui va forcément **nuire à la visibilité des propriétaires**. | |
| - Ici la conclusion est simple : si nous ajoutons un délai de 30 minutes, cela | |
| diminuera l'impact de **34 cas seulement sur un total de 17 000 locations mobiles**. Il vaut mieux donc ne pas rajouter de délai au risque | |
| d'insatisfaire un très grand nombre de propriétaires. | |
| """) | |
| st.subheader("En conclusion, il vaut mieux donc se concentrer sur les causes d'annulation tierces " \ | |
| "qui représentent près de la moitié du CA perdu") | |
| st.markdown("""---""") | |
| with tab3: | |
| st.subheader("Premier constat") | |
| st.markdown(""" | |
| 43% des annulations ne sont pas dues à un retard de restitution des véhicules, **(100 cas sur 229)**. | |
| - Sur ce point, il y a donc d'autres causes à aller chercher potentiellement pour *Get Around*, | |
| mais qui ne dépendent pas du retard de la précédente location. | |
| - Cela restera néanmoins un point important à creuser pour la société afin d'optimiser le CA. | |
| - Il est d'ailleurs presque **2 fois plus prononcé sur le connect que sur le mobile**. | |
| - Le fait de ne pas avoir à rencontrer le propriétaire favorise peut-être le désengagement du locataire. | |
| """) | |
| st.subheader("Second constat") | |
| st.markdown(""" | |
| Les annulations dans les cas de retard de **1 à 60 minutes** : | |
| - Sont **croissantes pour le connect** | |
| - Sont **décroissantes pour le mobile**, mais 3 fois plus nombreuses sur le plus petit retard | |
| - Représentent **55 cas sur les 129 restants (42% des annulations)** | |
| """) | |
| st.subheader("Dernier constat") | |
| st.markdown(""" | |
| Les retards les plus modérés de **1 à 30 minutes** : | |
| - Entraînent plus d'annulations sur le **mobile** que sur le **connect** | |
| - Représentent **34 cas sur les 129 restants (14% des annulations seulement)** | |
| """) | |
| st.markdown("---") | |
| st.subheader("Première conclusion sur les locations") | |
| st.markdown("### 📊 Analyse des annulations et facteurs en jeu") | |
| st.markdown(""" | |
| ❗ Le **facteur temps** (les retards) n’apparaît **pas comme la cause principale** des annulations. | |
| ➝ Même avec **60 minutes de retard**, on n’explique que **42% des annulations**.""") | |
| st.markdown("🔍 Cela suggère que **d’autres causes majeures** restent à identifier :") | |
| st.markdown(""" | |
| - politique tarifaire | |
| - offre et concurrence | |
| - notifications de disponibilité des véhicules | |
| - comportements utilisateurs""") | |
| st.markdown("📱 La **méthode de check-in mobile** est un levier à privilégier : elle concerne **80% des locations**.") | |
| st.markdown("""⚖️ La mise en place d’un délai supplémentaire de disponibilité du véhicule aurait un impact : | |
| - sur les **propriétaires** (perte potentielle de visibilité, donc moins de trafic et de revenus) | |
| - sur les **locataires** (réduction de l’offre disponible, frustration potentielle)""") | |
| st.markdown("""💡 Des pistes de solutions à explorer : | |
| - sensibilisation des utilisateurs à la ponctualité | |
| - notifications de rappel 30 minutes avant la fin de la location | |
| - système de bonus/malus | |
| - acompte à la réservation pour responsabiliser le locataire | |
| - programme de fidélité """) | |
| st.markdown(""" 📉 Enfin, il est nécessaire d’évaluer les **pertes potentielles pour les propriétaires** | |
| si un délai additionnel est mis en place, surtout pour le mode de location majoritaire. | |
| """) | |
| with tab4: | |
| st.subheader("Découvrez notre estimation du prix de location de votre véhicule !") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| car_brand = st.selectbox("Marque du véhicule", [ | |
| "Citroën", "Peugeot", "PGO", "Renault", "Audi", "BMW", "Ford", "Mercedes", | |
| "Opel", "Porsche", "Volkswagen", "KIA Motors", "Alfa Romeo", "Ferrari", | |
| "Fiat", "Lamborghini", "Maserati", "Lexus", "Honda", "Mazda", "Mini", | |
| "Mitsubishi", "Nissan", "SEAT", "Subaru", "Suzuki", "Toyota", "Yamaha" | |
| ]) | |
| mileage = st.number_input("Kilométrage", min_value=0, max_value=400_000, value=100_000) | |
| engine_power = st.number_input("Puissance du moteur", min_value=10, max_value=300, value=120) | |
| fuel = st.selectbox("Carburant", ["diesel", "petrol", "hybrid_petrol", "electro"]) | |
| with col2: | |
| paint_color = st.selectbox("Couleur", ["black", "grey", "white", "red", "silver", "blue", | |
| "orange", "beige", "brown", "green"]) | |
| car_type = st.selectbox("Type de véhicule", ["convertible", "coupe", "estate", "hatchback", | |
| "sedan", "subcompact", "suv", "van"]) | |
| private_parking_available = st.checkbox("Parking privé disponible") | |
| has_gps = st.checkbox("GPS") | |
| has_air_conditioning = st.checkbox("Climatisation") | |
| automatic_car = st.checkbox("Boîte automatique") | |
| has_getaround_connect = st.checkbox("Getaround Connect") | |
| has_speed_regulator = st.checkbox("Régulateur de vitesse") | |
| winter_tires = st.checkbox("Pneus hiver") | |
| # Bouton pour appeler l'API | |
| if st.button("Estimation du prix"): | |
| data = { | |
| "car_brand": car_brand, | |
| "mileage": mileage, | |
| "engine_power": engine_power, | |
| "fuel": fuel, | |
| "paint_color": paint_color, | |
| "car_type": car_type, | |
| "private_parking_available": private_parking_available, | |
| "has_gps": has_gps, | |
| "has_air_conditioning": has_air_conditioning, | |
| "automatic_car": automatic_car, | |
| "has_getaround_connect": has_getaround_connect, | |
| "has_speed_regulator": has_speed_regulator, | |
| "winter_tires": winter_tires | |
| } | |
| API_URL = "https://davidjyes-getaround-api.hf.space/predict" | |
| try: | |
| response = requests.post(API_URL, json=data) | |
| response.raise_for_status() | |
| prediction = response.json().get("prediction") | |
| st.success(f"Prix journalier estimé : {prediction} $") | |
| except requests.exceptions.RequestException as e: | |
| st.error(f"Erreur lors de l'appel à l'API : {e}") |