Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -185,7 +185,10 @@ def calculate_threshold_metrics(df, threshold_minutes, scope="all"):
|
|
| 185 |
# Cancellation metrics - now much more accurate
|
| 186 |
current_cancellations = int(df_with_prev["cancelled_due_to_previous_delay"].sum())
|
| 187 |
cancellations_prevented = int(df_with_prev["cancellation_prevented"].sum())
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
| 189 |
|
| 190 |
return {
|
| 191 |
"total_rentals": total_rentals,
|
|
@@ -317,22 +320,21 @@ if selected == "π Data Overview":
|
|
| 317 |
col1, col2 = st.columns(2)
|
| 318 |
|
| 319 |
with col1:
|
| 320 |
-
# Delay status distribution
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
"Late Return" if pd.notnull(x) and x > 0 else "Missing Data"
|
| 325 |
)
|
| 326 |
|
| 327 |
-
delay_counts =
|
| 328 |
fig_delay_status = px.pie(
|
| 329 |
values=delay_counts.values,
|
| 330 |
names=delay_counts.index,
|
| 331 |
-
title="Return Status Distribution",
|
| 332 |
color_discrete_sequence=px.colors.qualitative.Bold
|
| 333 |
)
|
| 334 |
fig_delay_status.update_layout(
|
| 335 |
-
annotations=[dict(text="
|
| 336 |
x=0.5, y=-0.1, xref="paper", yref="paper",
|
| 337 |
showarrow=False, font=dict(size=10))]
|
| 338 |
)
|
|
@@ -440,18 +442,24 @@ if selected == "π Data Overview":
|
|
| 440 |
)
|
| 441 |
|
| 442 |
delay_related_cancels = df_analysis["cancelled_due_to_previous_delay"].sum()
|
| 443 |
-
|
|
|
|
| 444 |
|
| 445 |
col2_1, col2_2 = st.columns(2)
|
| 446 |
with col2_1:
|
| 447 |
-
st.metric("Total Cancellations", f"{
|
|
|
|
| 448 |
with col2_2:
|
| 449 |
st.metric("Due to Previous Delay", f"{delay_related_cancels:,}",
|
| 450 |
help="Cancelled rentals where the previous rental on same car was late")
|
| 451 |
|
| 452 |
-
if
|
| 453 |
-
delay_cancel_rate = (delay_related_cancels /
|
| 454 |
-
st.metric("% of Cancellations Due to Previous Delays", f"{delay_cancel_rate:.1f}%")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
|
| 456 |
# Problem cases analysis (including cancellations)
|
| 457 |
st.markdown('<div class="section-header">π¨ Current Problem Cases Analysis</div>', unsafe_allow_html=True)
|
|
@@ -702,7 +710,7 @@ elif selected == "π Threshold Analysis":
|
|
| 702 |
"Cancellations Prevented vs Threshold",
|
| 703 |
"Efficiency (Solve Rate) vs Threshold",
|
| 704 |
"Revenue Impact vs Threshold",
|
| 705 |
-
"Cancellation Rate vs Threshold"
|
| 706 |
)
|
| 707 |
)
|
| 708 |
|
|
@@ -749,7 +757,7 @@ elif selected == "π Threshold Analysis":
|
|
| 749 |
# Cancellation rate
|
| 750 |
fig.add_trace(
|
| 751 |
go.Scatter(x=sweep_df["threshold"], y=sweep_df["cancellation_rate"],
|
| 752 |
-
mode="lines+markers", name="Cancellation Rate (%)",
|
| 753 |
line=dict(color="#e67e22"), showlegend=False),
|
| 754 |
row=2, col=3
|
| 755 |
)
|
|
@@ -877,7 +885,7 @@ elif selected == "π― Scope Analysis":
|
|
| 877 |
"Cancellations Prevented by Scope",
|
| 878 |
"Efficiency by Scope",
|
| 879 |
"Revenue Impact by Scope",
|
| 880 |
-
"Cancellation Rate by Scope"
|
| 881 |
)
|
| 882 |
)
|
| 883 |
|
|
@@ -926,7 +934,7 @@ elif selected == "π― Scope Analysis":
|
|
| 926 |
row=2, col=2
|
| 927 |
)
|
| 928 |
|
| 929 |
-
#
|
| 930 |
fig.add_trace(
|
| 931 |
go.Scatter(x=scope_data["threshold"], y=scope_data["cancellation_rate"],
|
| 932 |
mode="lines+markers", name=f"{scope_name}",
|
|
|
|
| 185 |
# Cancellation metrics - now much more accurate
|
| 186 |
current_cancellations = int(df_with_prev["cancelled_due_to_previous_delay"].sum())
|
| 187 |
cancellations_prevented = int(df_with_prev["cancellation_prevented"].sum())
|
| 188 |
+
|
| 189 |
+
# Remaining cancellation rate after implementing threshold
|
| 190 |
+
remaining_cancellations = current_cancellations - cancellations_prevented
|
| 191 |
+
cancellation_rate = (remaining_cancellations / rentals_with_previous) * 100 if rentals_with_previous > 0 else 0
|
| 192 |
|
| 193 |
return {
|
| 194 |
"total_rentals": total_rentals,
|
|
|
|
| 320 |
col1, col2 = st.columns(2)
|
| 321 |
|
| 322 |
with col1:
|
| 323 |
+
# Delay status distribution (excluding missing data)
|
| 324 |
+
df_with_delay_data = df[df["delay_at_checkout_in_minutes"].notnull()].copy()
|
| 325 |
+
df_with_delay_data["delay_status"] = df_with_delay_data["delay_at_checkout_in_minutes"].apply(
|
| 326 |
+
lambda x: "Early Return" if x < 0 else "On Time" if x == 0 else "Late Return"
|
|
|
|
| 327 |
)
|
| 328 |
|
| 329 |
+
delay_counts = df_with_delay_data["delay_status"].value_counts()
|
| 330 |
fig_delay_status = px.pie(
|
| 331 |
values=delay_counts.values,
|
| 332 |
names=delay_counts.index,
|
| 333 |
+
title="Return Status Distribution (Data Available Only)",
|
| 334 |
color_discrete_sequence=px.colors.qualitative.Bold
|
| 335 |
)
|
| 336 |
fig_delay_status.update_layout(
|
| 337 |
+
annotations=[dict(text=f"Based on {len(df_with_delay_data):,} rentals with delay data",
|
| 338 |
x=0.5, y=-0.1, xref="paper", yref="paper",
|
| 339 |
showarrow=False, font=dict(size=10))]
|
| 340 |
)
|
|
|
|
| 442 |
)
|
| 443 |
|
| 444 |
delay_related_cancels = df_analysis["cancelled_due_to_previous_delay"].sum()
|
| 445 |
+
total_cancels_with_prev = (df_analysis["state"] == "canceled").sum()
|
| 446 |
+
total_cancels_all = (df["state"] == "canceled").sum()
|
| 447 |
|
| 448 |
col2_1, col2_2 = st.columns(2)
|
| 449 |
with col2_1:
|
| 450 |
+
st.metric("Total Cancellations (All)", f"{total_cancels_all:,}",
|
| 451 |
+
help="All cancelled rentals in the dataset")
|
| 452 |
with col2_2:
|
| 453 |
st.metric("Due to Previous Delay", f"{delay_related_cancels:,}",
|
| 454 |
help="Cancelled rentals where the previous rental on same car was late")
|
| 455 |
|
| 456 |
+
if total_cancels_all > 0:
|
| 457 |
+
delay_cancel_rate = (delay_related_cancels / total_cancels_all * 100)
|
| 458 |
+
st.metric("% of All Cancellations Due to Previous Delays", f"{delay_cancel_rate:.1f}%")
|
| 459 |
+
|
| 460 |
+
# Additional context
|
| 461 |
+
st.info(f"Note: {total_cancels_with_prev:,} cancellations had previous rentals (analyzed for delay impact)")
|
| 462 |
+
|
| 463 |
|
| 464 |
# Problem cases analysis (including cancellations)
|
| 465 |
st.markdown('<div class="section-header">π¨ Current Problem Cases Analysis</div>', unsafe_allow_html=True)
|
|
|
|
| 710 |
"Cancellations Prevented vs Threshold",
|
| 711 |
"Efficiency (Solve Rate) vs Threshold",
|
| 712 |
"Revenue Impact vs Threshold",
|
| 713 |
+
"Remaining Cancellation Rate vs Threshold"
|
| 714 |
)
|
| 715 |
)
|
| 716 |
|
|
|
|
| 757 |
# Cancellation rate
|
| 758 |
fig.add_trace(
|
| 759 |
go.Scatter(x=sweep_df["threshold"], y=sweep_df["cancellation_rate"],
|
| 760 |
+
mode="lines+markers", name="Remaining Cancellation Rate (%)",
|
| 761 |
line=dict(color="#e67e22"), showlegend=False),
|
| 762 |
row=2, col=3
|
| 763 |
)
|
|
|
|
| 885 |
"Cancellations Prevented by Scope",
|
| 886 |
"Efficiency by Scope",
|
| 887 |
"Revenue Impact by Scope",
|
| 888 |
+
"Remaining Cancellation Rate by Scope"
|
| 889 |
)
|
| 890 |
)
|
| 891 |
|
|
|
|
| 934 |
row=2, col=2
|
| 935 |
)
|
| 936 |
|
| 937 |
+
# Remaining cancellation rate
|
| 938 |
fig.add_trace(
|
| 939 |
go.Scatter(x=scope_data["threshold"], y=scope_data["cancellation_rate"],
|
| 940 |
mode="lines+markers", name=f"{scope_name}",
|