import streamlit as st import pandas as pd import numpy as np import plotly.express as px import plotly.graph_objects as go import requests import json import pickle #################################################################### PAGE CONFIGURATION #################################################################### st.set_page_config(page_title="Getaround Project Dashboard", page_icon="π¦", layout="wide") #################################################################### SIDEBAR MENU #################################################################### st.sidebar.title("Navigation") page = st.sidebar.radio("Go to", ["π Home/Introduction", "π Delays Analysis", "πΈ Price Prediction", "π The End & Thank You"]) e = st.sidebar.empty() e.write("") st.sidebar.write("Made with ππβ€οΈβπ₯ by Youenn PATAT") e = st.sidebar.empty() e.write("") st.sidebar.image("Aventurine_3.png", use_container_width=True) st.sidebar.markdown("Β« π₯ Cheers, dear reader! π·Β»") #################################################################### Loading data #################################################################### #################################################################### & #################################################################### #################################################################### Cleaning data #################################################################### @st.cache_data def load_data(): data = pd.read_excel("https://full-stack-assets.s3.eu-west-3.amazonaws.com/Deployment/get_around_delay_analysis.xlsx") return data @st.cache_data def load_data_price(): data_price = pd.read_csv("https://full-stack-assets.s3.eu-west-3.amazonaws.com/Deployment/get_around_pricing_project.csv", index_col=0) return data_price data_load_state = st.text('Loading data...') data = load_data() data_price = load_data_price() data_load_state.text("") mean_rental_per_day = data_price["rental_price_per_day"].mean() # Count the number of entries with delay_at_checkout_in_minutes > mean + 3*std and < mean - 3*std mean_delay_checkout = data["delay_at_checkout_in_minutes"].mean() std_delay_checkout = data["delay_at_checkout_in_minutes"].std() outliers = data[(data['delay_at_checkout_in_minutes'] > (mean_delay_checkout + 3* std_delay_checkout)) | (data['delay_at_checkout_in_minutes'] < (mean_delay_checkout - 3* std_delay_checkout))] # Get the count of such entries num_outliers = len(outliers) # Filter out and remove the outliers data = data[(data['delay_at_checkout_in_minutes'] <= (mean_delay_checkout + 3* std_delay_checkout)) & (data['delay_at_checkout_in_minutes'] >= (mean_delay_checkout - 3* std_delay_checkout)) | (data['delay_at_checkout_in_minutes'].isna())] # We keep the Nan values to keep information of the cancel state of the rental, if not all the cancel state would be removed # Define a function to categorize delays def categorize_delay(delay): if pd.isna(delay): return "Unknown" elif delay <= 0: return "Early or in time" elif delay < 60: return "< 1 hour" elif delay < 120: return "1 to 2 hours" elif delay < 180: return "2 to 3 hours" elif delay < 360: return "3 to 6 hours" elif delay < 720: return "6 to 12 hours" elif delay < 1440: return "12 to 24 hours" else: return "1 day or more" # Apply function to create the new column data["checkout_delay_category"] = data["delay_at_checkout_in_minutes"].apply(categorize_delay) #################################################################### HOME PAGE #################################################################### if page == "π Home/Introduction": st.title("Welcome to the Getaround Project Dashboard βπβ") st.image("https://lever-client-logos.s3.amazonaws.com/2bd4cdf9-37f2-497f-9096-c2793296a75f-1568844229943.png", use_container_width=True) st.image("https://imgcdn.stablediffusionweb.com/2024/4/2/85a87b99-264f-4692-b507-7d84b2e4c351.jpg", use_container_width=True) st.markdown(""" ## Introduction This project aims to analyze the impact of a new feature of threshold to deal with problematic cases when there are delays at the check-out for a rental. π **What you'll find in this app**: * π Data insights on rental delays & affected revenue. * π Strategies to mitigate issues. * π― Conclusion & recommendations. **Use the sidebar** to navigate between pages. π In this first page, you will find out the presentation of data and first views of it. In the **Delays Analysis** page, you will find the analysis of the problem and answers. And in the last page, some thanking and link for my other works. """) st.subheader("π - Basic analysis and view of data", divider="orange") # diplay raw data for delays st.write("Raw Data") if st.checkbox('Show raw data'): st.subheader('Raw data') st.write(data) # Calculate the value counts of each delay category delay_counts = data['checkout_delay_category'].value_counts() # Calculate the percentage of each category delay_percentages = (delay_counts / delay_counts.sum()) * 100 st.markdown(""" Firstly, we want to check the proportion of check-in type (`mobile` or `connect`) and the proportion of the rentals' states (`ended` or `canceled`). """) col1, col2 = st.columns([1, 2]) with col1: #visualisation of the percentage of the mobile vs connect check rental checkin_counts = data["checkin_type"].value_counts().reset_index() checkin_counts.columns = ["checkin_type", "count"] fig1 = px.pie(checkin_counts, names="checkin_type", values="count", title="Check-in Type Distribution", color_discrete_sequence=["#3CB371", "#FFA500"]) fig1.update_traces(textfont_color="black") st.plotly_chart(fig1, use_container_width=True, key="1") # Add text in the second column with col2: #visualisation of the percentage of the mobile vs connect check rental cancel_counts = data["state"].value_counts().reset_index() cancel_counts.columns = ["state", "count"] fig2 = px.pie(cancel_counts, names="state", values="count", title="Proportion of rentals' states", color_discrete_sequence=["#3CB371", "#FFA500"]) fig2.update_traces(textfont_color="black") st.plotly_chart(fig2, use_container_width=True, key="2") st.markdown(""" So, we see that the majority of check-in are made by mobile, only 20% are made by the connected car. Moreover, in our case, with that dataset, we see that rentals are cancels for 15% of rentals. """) st.markdown(""" Now let's check the distribution of checkout delays in function of category of time. """) # Count occurrences of each category delay_counts = data["checkout_delay_category"].value_counts().reset_index() delay_counts.columns = ["Category", "Count"] delay_counts["Percentage"] = (delay_counts["Count"] / delay_counts["Count"].sum()) * 100 # Define custom colors custom_colors = { "Early or in time": "#FFA500", # Orange } # Assign green as the default color for category in delay_counts["Category"]: if category not in custom_colors: custom_colors[category] = "#3CB371" # Green # Create a bar chart fig3 = px.bar( delay_counts, x="Category", y="Count", title="Distribution of Checkout Delays", labels={"Category": "Checkout Delay Category", "Count": "Number of Rentals"}, color="Category", text=delay_counts["Percentage"].apply(lambda x: f"{x:.1f}%"), color_discrete_map=custom_colors, ) fig3.update_traces(textfont_color="black") fig3.update_xaxes(showgrid=False, tickfont=dict(color='black')) fig3.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig3.update_layout(xaxis_title="", yaxis_title="", title_font=dict(weight="bold"), showlegend=False, xaxis=dict(zeroline=True,zerolinecolor="black",zerolinewidth=2), plot_bgcolor="#BDDFD6") st.plotly_chart(fig3, use_container_width=True, theme=None) st.markdown(""" There is only 32.6% of rental checkout that are early or in time, without delay. For 23.4% we don't have informations. And the majoruty of delays are less than 2 hours. """) # Count occurrences of each category grouped by checkin_type delay_counts = data.groupby(["checkout_delay_category", "checkin_type"]).size().reset_index(name="Count") delay_counts["Percentage"] = (delay_counts["Count"] / delay_counts["Count"].sum()) * 100 # Create a grouped bar chart fig4 = px.bar( delay_counts, x="checkout_delay_category", y="Count", color="checkin_type", title="Distribution of Checkout Delays by Check-in Type", labels={"checkout_delay_category": "Checkout Delay Category", "Count": "Number of Rentals", "checkin_type": "Check-in Type"}, barmode="group", # Groups bars side by side #text="Count", text=delay_counts["Percentage"].apply(lambda x: f"{x:.1f}%"), color_discrete_sequence=["#FFA500", "#3CB371"] ) # Improve layout by setting custom order for x-axis fig4.update_traces(textfont_color="black") fig4.update_xaxes(showgrid=False, tickfont=dict(color='black')) fig4.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig4.update_layout(xaxis_title="", yaxis_title="", title_font=dict(weight="bold"), xaxis=dict(zeroline=True,zerolinecolor="black",zerolinewidth=2), plot_bgcolor="#BDDFD6") fig4.update_layout(xaxis={'categoryorder':'array', 'categoryarray': [ "Early or in time", "< 1 hour", "1 to 2 hours", "2 to 3 hours", "3 to 6 hours", "6 to 12 hours", "12 to 24 hours", "1 day or more", "Unknown" ]}) st.plotly_chart(fig4, use_container_width=True, theme=None) st.markdown(""" There is much more delay problem with mobile checkin type than connect. """) st.markdown(""" Great ! Now for the following analysis, go to the next page "**π Delays Analysis**" ! """) #################################################################### DELAYS ANALYSIS #################################################################### elif page == "π Delays Analysis": st.title("Analysis & Insights π") st.markdown(""" Here, we analyze the delay problematic and how to solve it with threshold and a certain scope. **Key Findings**: - π A minimum delay of **X minutes** reduces scheduling conflicts. - π° Potential revenue impact: **Y% of total revenue**. - β Solving **Z% of problematic cases** with the policy. *Visuals and explanations go here.* In the following, we will focus on the next steps and questions: * How often are drivers late for the next check-in? How does it impact the next driver? * Which share of our ownerβs revenue would potentially be affected by the feature? * How many rentals would be affected by the feature depending on the threshold and scope we choose? * How many problematic cases will it solve depending on the chosen threshold and scope? """) st.subheader("π - How often are drivers late for the next check-in? How does it impact the next driver?", divider="orange") st.markdown(""" So, for the first question, here's the visualization of the check-out that are `late`, `early or in time` and the `unknown` data. """) # Count occurrences of category & group category as simple "late", "in time" or "unknown" delay_drivers = data["checkout_delay_category"].apply(lambda x: "Early or in time" if x == "Early or in time" else "Unkonwn" if x == "Unknown" else "Late").value_counts().reset_index() delay_drivers.columns = ["Category", "Count"] delay_drivers["Percentage"] = (delay_drivers["Count"] / delay_drivers["Count"].sum()) * 100 # Create a bar chart fig5 = px.bar( delay_drivers, x="Category", y="Count", labels={"Category": "Checkout Delay Category", "Count": "Number of Rentals"}, title="Distribution of Checkout Delays", text=delay_drivers["Percentage"].apply(lambda x: f"{x:.1f}%"), color_discrete_sequence=["#FFA500"], ) fig5.update_traces(textfont_color="black") fig5.update_xaxes(showgrid=False, tickfont=dict(color='black')) fig5.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig5.update_layout(xaxis_title="", yaxis_title="", title_font=dict(weight="bold"), showlegend=False, xaxis=dict(zeroline=True,zerolinecolor="black",zerolinewidth=2), plot_bgcolor="#BDDFD6") st.plotly_chart(fig5, use_container_width=True, theme=None) # Count occurrences of each category delay_counts = data["checkout_delay_category"].value_counts().reset_index() delay_counts.columns = ["Category", "Count"] delay_counts["Percentage"] = (delay_counts["Count"] / delay_counts["Count"].sum()) * 100 # Define custom colors custom_colors = { "Early or in time": "#FFA500", # Orange } # Assign green as the default color for category in delay_counts["Category"]: if category not in custom_colors: custom_colors[category] = "#3CB371" # Green # Create a bar chart fig6 = px.bar( delay_counts, x="Category", y="Count", title="Distribution of Checkout Delays", labels={"Category": "Checkout Delay Category", "Count": "Number of Rentals"}, color="Category", text=delay_counts["Percentage"].apply(lambda x: f"{x:.1f}%"), color_discrete_map=custom_colors, ) fig6.update_traces(textfont_color="black") fig6.update_xaxes(showgrid=False, tickfont=dict(color='black')) fig6.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig6.update_layout(xaxis_title="", yaxis_title="", title_font=dict(weight="bold"), showlegend=False, xaxis=dict(zeroline=True,zerolinecolor="black",zerolinewidth=2), plot_bgcolor="#BDDFD6") st.plotly_chart(fig6, use_container_width=True, theme=None) st.markdown(""" Only 32.6% of the check-out are early or in time, whereas almost half of the check-out (44%) are late. """) st.markdown(""" Now, for the 2nd question, let's see how delays impact the next driver. """) mean_delay_impact = data["time_delta_with_previous_rental_in_minutes"].mean() min_delay_impact = data["time_delta_with_previous_rental_in_minutes"].min() max_delay_impact = data["time_delta_with_previous_rental_in_minutes"].max() st.markdown("#### Delay impacting informations on the next driver π:") st.write(f"βͺοΈ*Average delay impacting next driver:* {mean_delay_impact:.2f} minutes") st.write(f"βͺοΈ*Minimum delay impacting next driver:* {min_delay_impact:.2f} minutes") st.write(f"βͺοΈ*Maximum delay impacting next driver:* {max_delay_impact:.2f} minutes") delay_impact = data delay_impact["delta-late_checkout"] = delay_impact["time_delta_with_previous_rental_in_minutes"] - delay_impact["delay_at_checkout_in_minutes"] #if negative delta - late checkout, it means that the new rental cannot do its check-in negative_delay_impact = delay_impact[delay_impact["delta-late_checkout"] < 0] late_checkout = delay_drivers[delay_drivers["Category"] == "Late"]["Count"][0] nb_problematic_checkin_late = len(negative_delay_impact) # percentage calculation problematic_delays_rate = nb_problematic_checkin_late*100/late_checkout st.write(f"βͺοΈAmong all the delays ({late_checkout}), {problematic_delays_rate:.3f}% \n of delays caused problems to the next rental because the checkout\n was made later than the new rental checkin.") # Calculate the average duration of problematic delays average_problematic_delay = negative_delay_impact['delay_at_checkout_in_minutes'].mean() # Calculate the average duration of non-problematic delays average_non_problematic_delay = data[data['delay_at_checkout_in_minutes'] > 0]['delay_at_checkout_in_minutes'].mean() # Compare the averages st.write(f"βͺοΈAverage Duration of Problematic Delays: {average_problematic_delay:.0f} minutes") st.write(f"βͺοΈAverage Duration of Non-Problematic Delays: {average_non_problematic_delay:.0f} minutes") delay_impact["problematic_delay"] = delay_impact["delta-late_checkout"] < 0 delay_impact["problematic_delay"].value_counts() fig7 = px.histogram(delay_impact, x="problematic_delay", color_discrete_sequence=["#FFA500"], title="Proportion of problematic delays" ) fig7.update_xaxes( categoryorder='array', categoryarray=["Problematic", "Non-Problematic"], showgrid=False, tickfont=dict(color='black') ) fig7.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig7.add_annotation(x=3, y=10000,text=f"Avg Delay: {average_problematic_delay:.2f} min",showarrow=False) fig7.add_annotation(x=2, y=10000,text=f"Avg Delay: {average_non_problematic_delay:.2f} min",showarrow=False) fig7.update_layout( xaxis=dict( tickmode='array', tickvals=[True, False], ticktext=["Problematic Delay", "Non Problematic Delay"], zeroline=True,zerolinecolor="black",zerolinewidth=2 ), xaxis_title="", yaxis_title="", title_font=dict(weight="bold"), showlegend=False, plot_bgcolor="#BDDFD6" ) fig7.update_traces(textfont_color="black") st.plotly_chart(fig7, use_container_width=True, theme=None) st.markdown(""" For the majority of cases, it poses no problem to have delay, but for 2.857% of the case it is problematic for the following rental. """) st.subheader("π - Which share of our ownerβs revenue would potentially be affected by the feature?", divider="orange") # Define the treshold of minimum time between 2 locations (minutes) thresholds = [30, 60, 90, 120, 180, 360, 720, 1440] # Example : 1 hour data["mean_price_per_rental"] = mean_rental_per_day treshold_data = data percentage_revenue_impacted = [] percentage_revenue_impacted_displaying = {} for threshold in thresholds: treshold_data[f"affected_rentals_{threshold}"] = data["time_delta_with_previous_rental_in_minutes"] <= threshold affected_rentals = data[data["time_delta_with_previous_rental_in_minutes"] <= threshold] affected_revenue = affected_rentals["mean_price_per_rental"].sum() total_revenue = data["mean_price_per_rental"].sum() revenue_impact = (affected_revenue / total_revenue) * 100 percentage_revenue_impacted.append(revenue_impact) percentage_revenue_impacted_displaying[threshold] = round(revenue_impact, 3) col1, col2 = st.columns([1, 2]) with col1: # Select a threshold selected_threshold = st.selectbox("Select a threshold β³ (in minutes):", thresholds, key="selectbox_1") # Display impacted revenue percentage st.metric(label="π° Impacted Revenue", value=f"{percentage_revenue_impacted_displaying[selected_threshold]}%") with col2: affected_counts = [treshold_data[f"affected_rentals_{threshold}"].value_counts().get(True, 0) for threshold in thresholds] affected_rentals_plot = pd.DataFrame({"Threshold (min)": thresholds, "Affected rentals": affected_counts}) fig8 = px.line(affected_rentals_plot, x="Threshold (min)", y="Affected rentals", text="Affected rentals", title="Number of rentals affected by the treshold", color_discrete_sequence=["#3CB371"],) fig8.update_traces(textposition='top center', textfont_color="black") fig8.update_xaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black'), showline=True, linewidth=2, linecolor='black') fig8.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig8.update_layout(xaxis_title="", yaxis_title="", title_font=dict(weight="bold"), showlegend=False, xaxis=dict(zeroline=True,zerolinecolor="black",zerolinewidth=2), plot_bgcolor="#BDDFD6") st.plotly_chart(fig8, use_container_width=True, theme=None) st.subheader("π - How many rentals would be affected by the feature depending on the threshold and scope we choose?", divider="orange") all_affected_list = [] all_affected_display = {} connect_affected_list = [] connect_affected_display = {} all_affected_percentage = {} connect_affected_percentage = {} for threshold in thresholds: all_rentals = len(data) all_affected = data[data["time_delta_with_previous_rental_in_minutes"] <= threshold].shape[0] all_affected_list.append(all_affected) connect_affected = data[(data["time_delta_with_previous_rental_in_minutes"] <= threshold) & (data["checkin_type"] == "connect")].shape[0] connect_affected_list.append(connect_affected) all_affected_display[threshold] = all_affected connect_affected_display[threshold] = connect_affected all_affected_percentage[threshold] = (all_affected / all_rentals) * 100 connect_affected_percentage[threshold] = (connect_affected / all_rentals) * 100 # Select a threshold selected_threshold = st.selectbox("Select a threshold β³ (in minutes):", thresholds, key="selectbox_2") # Add a title before metrics st.markdown(f"#### π Rentals Affected by the {selected_threshold}-Minutes Threshold") col1, col2 = st.columns(2) # Display metrics side by side with col1: st.metric(label="π² All check-ins affected in number β©", value=f"{all_affected_display[selected_threshold]}") st.metric(label="π² All check-ins affected in % β©", value=f"{all_affected_percentage[selected_threshold]:.3f}") with col2: st.metric(label="π Connect check-ins affected in number β©", value=f"{connect_affected_display[selected_threshold]}") st.metric(label="π Connect check-ins affected in % β©", value=f"{connect_affected_percentage[selected_threshold]:.3f}") data_affected = pd.DataFrame({ "thresholds" : thresholds, "all_affected" : all_affected_list, "connect_affected" : connect_affected_list}) fig9 = px.scatter(data_affected, x='thresholds', y='all_affected', color_discrete_sequence=["#FFA500"], labels={'all_affected': 'All Affected'}, title="Rentals affected by Thresholds in function of the type of check-in") # Add a line for 'all_affected' fig9.add_trace(go.Scatter(x=data_affected['thresholds'], y=data_affected['all_affected'], mode='lines+markers+text', line=dict(color='#FFA500'), name='All Affected', text=data_affected['all_affected'])) fig9.add_trace(go.Scatter(x=data_affected['thresholds'], y=data_affected['connect_affected'], mode='lines+markers+text', marker_color='#3CB371', name='Connect Affected', text=data_affected['connect_affected'],)) # Texte Γ afficher sur les marqueurs fig9.update_traces(textposition='top center', textfont_color="black") fig9.update_xaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black'), showline=True, linewidth=2, linecolor='black') fig9.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig9.update_layout(xaxis_title="", yaxis_title="", title_font=dict(weight="bold"), showlegend=True, xaxis=dict(zeroline=True,zerolinecolor="black",zerolinewidth=2), plot_bgcolor="#BDDFD6") st.plotly_chart(fig9, use_container_width=True, theme=None) st.markdown(""" There are less rentals affected with the scope only on connected check-in than all (mobile + connect) check-in. Moreover, as it could be expected, more rentals are impacted with an increasing of the threshold choice.""") st.subheader("π - How many problematic cases will it solve depending on the chosen threshold and scope?", divider="orange") solved_cases_all_list = [] solved_cases_connect_list = [] for threshold, i in zip(thresholds, range(len(thresholds))): problematic_cases = negative_delay_impact[(negative_delay_impact["delay_at_checkout_in_minutes"] <= threshold)] problematic_connectec_case = negative_delay_impact[(negative_delay_impact["delay_at_checkout_in_minutes"] <= threshold) & (negative_delay_impact["checkin_type"] == "connect")] total_problems_cases = len(negative_delay_impact) total_connect_pb_cases = len(negative_delay_impact[negative_delay_impact["checkin_type"] == "connect"]) solved_cases = problematic_cases.shape[0] solved_cases_all_list.append(solved_cases) solved_cases_connect = problematic_connectec_case.shape[0] solved_cases_connect_list.append(solved_cases_connect) percentage_solved_all = (solved_cases / total_problems_cases) * 100 percentage_connect_solved = (solved_cases_connect / total_connect_pb_cases) * 100 # Convert to DataFrame df_solved_cases = pd.DataFrame({ "Threshold (minutes)": thresholds, "Solved Cases (All Check-ins)": solved_cases_all_list, "Solved Cases (Connect Check-ins)": solved_cases_connect_list, "Revenue Impacted (%)": percentage_revenue_impacted }) # Select a threshold with a slider selected_threshold = st.selectbox("Select a threshold β³ (in minutes):", thresholds, key="selectbox_3") # Get values for selected threshold selected_data = df_solved_cases[df_solved_cases["Threshold (minutes)"] == selected_threshold].iloc[0] # Display Metrics in Two Columns col1, col2, col3 = st.columns(3) with col1: st.metric(label="π² All Check-ins Solved", value=f"{selected_data['Solved Cases (All Check-ins)']}") with col2: st.metric(label="π Connect Check-ins Solved", value=f"{selected_data['Solved Cases (Connect Check-ins)']}") with col3: st.metric(label="π° Revenue Impacted", value=f"{selected_data['Revenue Impacted (%)']:.2f} %") # Create the figure fig10 = go.Figure() # Add line for "All Check-ins" fig10.add_trace(go.Scatter( x=thresholds, y=solved_cases_all_list, mode="lines+markers", name="Solved Cases (All Check-ins)", marker=dict(color="#FFA500") )) # Add line for "Connect Check-ins" fig10.add_trace(go.Scatter( x=thresholds, y=solved_cases_connect_list, mode="lines+markers", name="Solved Cases (Connect Check-ins)", marker=dict(color="#3CB371") )) # Add vertical dashed lines with text annotations for i, threshold in enumerate(thresholds): max_y_value = solved_cases_all_list[i] # Ensure line stops at "Solved Cases (All Check-ins)" # Add dashed line from y=0 to y=max_y_value fig10.add_trace(go.Scatter( x=[threshold, threshold], # Vertical line at threshold y=[0, max_y_value], # Stop at max_y_value mode="lines", line=dict(color="red", width=1.5, dash="dash"), name="Revenue Impact Annotation" if i == 0 else None, # Show legend only once showlegend=(i == 0) )) # Add text annotation slightly above the dashed line fig10.add_annotation( x=threshold, y=max_y_value + 20, # Position slightly above the dashed line text=f"{percentage_revenue_impacted[i]:.2f}%", # Format percentage showarrow=False, font=dict(size=10, color="red"), align="center", ) fig10.update_traces(textposition='top center', textfont_color="black") fig10.update_xaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black'), showline=True, linewidth=2, linecolor='black') fig10.update_yaxes(showgrid=True, gridcolor='#A9A9A9', tickfont=dict(color='black')) fig10.update_layout(title="Number of Problematic Cases Solved by Threshold",xaxis_title="",yaxis_title="", title_font=dict(weight="bold"),showlegend=True, xaxis=dict(zeroline=True,zerolinecolor="black",zerolinewidth=2), plot_bgcolor="#BDDFD6") st.plotly_chart(fig10, use_container_width=True, theme=None) st.markdown(""" #### π Data Table""") st.dataframe(df_solved_cases) st.markdown(""" Now, we can see the problematic cases solved in function of the check-in type (connect or all {mobileπ² + connectπ}) with the impacted revenue percentage of each threshold. For me the best choice to solve problem without too much economical impact is to choose the threshold of **180** or **360** minutes, for the scope of all check-in type.""") st.markdown(""" β¨ Thanks for reading all the way through! I hope you enjoyed it and found it interesting. Go to the last page, `The End & Thank You`, for a little surprise and links to my other worksβΌοΈ """) #################################################################### Price prediction #################################################################### elif page == "πΈ Price Prediction": st.title("Price Prediction for a Rental πΈπΆ") st.markdown(""" Here, you can choose the parameters of a car and with a connection to my API, you can have a day price prediction of the car. π **What you'll find in this page**: * ποΈ Object to select your car's characteristics? * πΈ A price prediction for one rental day. """) st.write("Select the car parameters below and get an estimated rental price!") # Define API URL api_url = "https://hyraxuna-api-getaround.hf.space/predict" # Define input fields for car parameters car_model = st.selectbox("Car Brand:", ['CitroΓ«n','Peugeot','PGO','Renault','Audi','BMW','Mercedes','Opel','Volkswagen','Ferrari','Mitsubishi','Nissan','SEAT','Subaru','Toyota','other']) mileage = st.slider("Mileage (km):", 0, 600000, 50000, step=1000) engine_power = st.slider("Engine Power (HP):", 0, 1000, 100, step=5) fuel = st.selectbox("Fuel Type:", ['diesel','petrol','other']) paint_color = st.selectbox("Paint Color:", ['black','grey','white','red','silver','blue','beige','brown','other']) car_type = st.selectbox("Car Type:", ['convertible','coupe','estate','hatchback','sedan','subcompact','suv','van']) # Boolean Features private_parking_available = st.checkbox("Private Parking Available") has_gps = st.checkbox("GPS Included") has_air_conditioning = st.checkbox("Air Conditioning") automatic_car = st.checkbox("Automatic Transmission") has_getaround_connect = st.checkbox("Getaround Connect Available") has_speed_regulator = st.checkbox("Speed Regulator Installed") winter_tires = st.checkbox("Winter Tires Installed") # Button to Predict if st.button("π Predict Rental Price"): st.subheader("πΆ Prediction Results") # Prepare input data as JSON input_data = { "model_key": car_model, "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 } headers = {"Content-Type": "application/json"} try: # API Request response = requests.post(api_url, data=json.dumps(input_data), headers=headers) result = response.json() if response.status_code == 200: predicted_price = result.get("prediction") st.success(f"π° Estimated Rental Price: **{predicted_price} β¬ per day**") else: st.error("β οΈ Error fetching prediction. Please check API or try again.") except Exception as e: st.error(f"β οΈ API Request Failed: {e}") #################################################################### END & THANK YOU PAGE #################################################################### elif page == "π The End & Thank You": st.title("Thank You for Exploring! π") # Create two columns col1, col2 = st.columns([1, 2]) # Adjust column ratio (1:2 for image & text) # Add an image in the first column with col1: st.image("ChibiElf1.png", use_container_width=True) # Add text in the second column with col2: st.markdown(""" **Final Thoughts** - π This analysis helps optimize the rental platform. - π Finding the right balance between user experience and revenue impact is key. **π Thank you for your time!** π© Feel free to reach out for more insights. Here are the links for my other works on **Github** & **Linkedin**: """) # Define the GitHub and LinkedIn URLs github_url = "https://github.com/HyraXuna?tab=repositories" linkedin_url = "https://www.linkedin.com/in/youenn-patat-46b59b246/" # Display clickable images for GitHub and LinkedIn st.markdown( f"""
""", unsafe_allow_html=True ) st.balloons() # π Fun effect for celebration! ### Footer st.markdown("---") st.markdown( """ """, unsafe_allow_html=True ) st.markdown("---")