Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -12,8 +12,8 @@ warnings.filterwarnings('ignore')
|
|
| 12 |
|
| 13 |
# ========== PAGE CONFIGURATION ==========
|
| 14 |
st.set_page_config(
|
| 15 |
-
page_title="
|
| 16 |
-
page_icon="🚗",
|
| 17 |
layout="wide",
|
| 18 |
initial_sidebar_state="expanded"
|
| 19 |
)
|
|
@@ -292,6 +292,111 @@ if selected == "Overview & Problems":
|
|
| 292 |
</div>
|
| 293 |
""", unsafe_allow_html=True)
|
| 294 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
# Visual analysis
|
| 296 |
st.markdown('<div class="section-header">Delay Patterns</div>', unsafe_allow_html=True)
|
| 297 |
|
|
|
|
| 12 |
|
| 13 |
# ========== PAGE CONFIGURATION ==========
|
| 14 |
st.set_page_config(
|
| 15 |
+
page_title="Car Rental Delay Analysis",
|
| 16 |
+
#page_icon="🚗",
|
| 17 |
layout="wide",
|
| 18 |
initial_sidebar_state="expanded"
|
| 19 |
)
|
|
|
|
| 292 |
</div>
|
| 293 |
""", unsafe_allow_html=True)
|
| 294 |
|
| 295 |
+
# Direct answer to PM's questions
|
| 296 |
+
st.markdown('<div class="section-header">How Often Are Drivers Late & Impact on Next Driver</div>', unsafe_allow_html=True)
|
| 297 |
+
|
| 298 |
+
# Calculate late return frequency and impact
|
| 299 |
+
df_late_analysis = df[df["has_previous_rental"]].copy()
|
| 300 |
+
|
| 301 |
+
# Join with previous rental delay data
|
| 302 |
+
prev_rental_data = df[["rental_id", "delay_at_checkout_in_minutes"]].rename(
|
| 303 |
+
columns={"rental_id": "previous_ended_rental_id",
|
| 304 |
+
"delay_at_checkout_in_minutes": "previous_delay"}
|
| 305 |
+
)
|
| 306 |
+
df_late_analysis = df_late_analysis.merge(prev_rental_data, on="previous_ended_rental_id", how="left")
|
| 307 |
+
|
| 308 |
+
# Calculate late return frequency
|
| 309 |
+
df_late_analysis["previous_delay_clean"] = df_late_analysis["previous_delay"].clip(-720, 720)
|
| 310 |
+
df_late_analysis["previous_was_late"] = (
|
| 311 |
+
df_late_analysis["previous_delay"].notnull() &
|
| 312 |
+
(df_late_analysis["previous_delay_clean"] > 0)
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
# Calculate impact on next driver
|
| 316 |
+
df_late_analysis["causes_problem"] = (
|
| 317 |
+
df_late_analysis["previous_delay"].notnull() &
|
| 318 |
+
(df_late_analysis["previous_delay_clean"] > df_late_analysis["time_delta_with_previous_rental_in_minutes"])
|
| 319 |
+
)
|
| 320 |
+
df_late_analysis["wait_time"] = np.maximum(
|
| 321 |
+
0,
|
| 322 |
+
df_late_analysis["previous_delay_clean"] - df_late_analysis["time_delta_with_previous_rental_in_minutes"]
|
| 323 |
+
).fillna(0)
|
| 324 |
+
|
| 325 |
+
late_returns = df_late_analysis[df_late_analysis["previous_was_late"]]
|
| 326 |
+
impacted_next_drivers = df_late_analysis[df_late_analysis["causes_problem"]]
|
| 327 |
+
|
| 328 |
+
col1, col2 = st.columns(2)
|
| 329 |
+
|
| 330 |
+
with col1:
|
| 331 |
+
st.markdown("#### Late Return Frequency")
|
| 332 |
+
total_with_delay_data = df_late_analysis[df_late_analysis["previous_delay"].notnull()]
|
| 333 |
+
late_frequency = (len(late_returns) / len(total_with_delay_data)) * 100 if len(total_with_delay_data) > 0 else 0
|
| 334 |
+
|
| 335 |
+
st.metric("Late Returns", f"{len(late_returns):,}")
|
| 336 |
+
st.metric("Late Return Rate", f"{late_frequency:.1f}%",
|
| 337 |
+
help="Percentage of returns that are late (delay > 0 minutes)")
|
| 338 |
+
|
| 339 |
+
# Late return severity
|
| 340 |
+
if len(late_returns) > 0:
|
| 341 |
+
avg_late_delay = late_returns["previous_delay_clean"].mean()
|
| 342 |
+
st.metric("Average Late Delay", f"{avg_late_delay:.1f} min")
|
| 343 |
+
|
| 344 |
+
with col2:
|
| 345 |
+
st.markdown("#### Impact on Next Driver")
|
| 346 |
+
impact_rate = (len(impacted_next_drivers) / len(df_late_analysis)) * 100 if len(df_late_analysis) > 0 else 0
|
| 347 |
+
|
| 348 |
+
st.metric("Next Drivers Impacted", f"{len(impacted_next_drivers):,}")
|
| 349 |
+
st.metric("Impact Rate", f"{impact_rate:.1f}%",
|
| 350 |
+
help="Percentage of consecutive rentals where late return causes waiting")
|
| 351 |
+
|
| 352 |
+
if len(impacted_next_drivers) > 0:
|
| 353 |
+
avg_wait = impacted_next_drivers["wait_time"].mean()
|
| 354 |
+
st.metric("Average Wait Time", f"{avg_wait:.1f} min",
|
| 355 |
+
help="Average additional wait time when impacted")
|
| 356 |
+
|
| 357 |
+
# Visual analysis of the relationship
|
| 358 |
+
col1, col2 = st.columns(2)
|
| 359 |
+
|
| 360 |
+
with col1:
|
| 361 |
+
# Late return distribution
|
| 362 |
+
if len(late_returns) > 0:
|
| 363 |
+
late_delays = late_returns["previous_delay_clean"]
|
| 364 |
+
late_filtered = late_delays[late_delays <= 300] # Cap at 5 hours for visualization
|
| 365 |
+
|
| 366 |
+
fig_late = px.histogram(
|
| 367 |
+
late_filtered,
|
| 368 |
+
nbins=20,
|
| 369 |
+
title="Distribution of Late Return Delays",
|
| 370 |
+
labels={"value": "Delay (minutes)", "count": "Number of Late Returns"}
|
| 371 |
+
)
|
| 372 |
+
st.plotly_chart(fig_late, use_container_width=True)
|
| 373 |
+
|
| 374 |
+
with col2:
|
| 375 |
+
# Wait time impact distribution
|
| 376 |
+
if len(impacted_next_drivers) > 0:
|
| 377 |
+
wait_times = impacted_next_drivers[impacted_next_drivers["wait_time"] > 0]["wait_time"]
|
| 378 |
+
|
| 379 |
+
fig_wait = px.histogram(
|
| 380 |
+
wait_times,
|
| 381 |
+
nbins=20,
|
| 382 |
+
title="Wait Time Distribution for Impacted Next Drivers",
|
| 383 |
+
labels={"value": "Wait Time (minutes)", "count": "Number of Impacted Drivers"}
|
| 384 |
+
)
|
| 385 |
+
st.plotly_chart(fig_wait, use_container_width=True)
|
| 386 |
+
|
| 387 |
+
# Key insight summary
|
| 388 |
+
late_to_impact_ratio = (len(impacted_next_drivers) / len(late_returns)) * 100 if len(late_returns) > 0 else 0
|
| 389 |
+
|
| 390 |
+
st.markdown(f"""
|
| 391 |
+
<div class="insight-box">
|
| 392 |
+
<strong>Key Insights:</strong><br>
|
| 393 |
+
• <strong>{late_frequency:.1f}%</strong> of returns are late (drivers return after scheduled time)<br>
|
| 394 |
+
• <strong>{impact_rate:.1f}%</strong> of consecutive rentals are negatively impacted by previous late returns<br>
|
| 395 |
+
• <strong>{late_to_impact_ratio:.1f}%</strong> of late returns actually cause problems for the next driver<br>
|
| 396 |
+
• When problems occur, next drivers wait an average of <strong>{avg_wait:.1f} minutes</strong>
|
| 397 |
+
</div>
|
| 398 |
+
""", unsafe_allow_html=True)
|
| 399 |
+
|
| 400 |
# Visual analysis
|
| 401 |
st.markdown('<div class="section-header">Delay Patterns</div>', unsafe_allow_html=True)
|
| 402 |
|