Spaces:
Runtime error
Runtime error
| import numpy as np | |
| import pandas as pd | |
| import plotly.express as px | |
| import streamlit as st | |
| TARGET_COLUMN = "Reached.on.Time_Y.N" | |
| TARGET_LABELS = {1: "On Time", 0: "Late"} | |
| def _label_target(series: pd.Series) -> pd.Series: | |
| return series.map(TARGET_LABELS).fillna("Unknown") | |
| def _get_categorical_columns(data: pd.DataFrame) -> list[str]: | |
| return data.select_dtypes(include=["object"]).columns.tolist() | |
| def _get_numeric_columns(data: pd.DataFrame) -> list[str]: | |
| """Return only business-relevant numeric features (exclude ID & target).""" | |
| candidate_cols = [ | |
| "Customer_care_calls", | |
| "Customer_rating", | |
| "Cost_of_the_Product", | |
| "Prior_purchases", | |
| "Discount_offered", | |
| "Weight_in_gms", | |
| ] | |
| return [c for c in candidate_cols if c in data.columns] | |
| def _make_numeric_bins(series: pd.Series, n_bins: int = 5) -> pd.Categorical: | |
| """Create human-friendly ranges like '0–1000' instead of raw numbers.""" | |
| min_v = float(series.min()) | |
| max_v = float(series.max()) | |
| if min_v == max_v: | |
| return pd.cut(series, bins=[min_v - 1, max_v + 1], labels=[f"{min_v:.0f}"]) | |
| bins = np.linspace(min_v, max_v, n_bins + 1) | |
| labels = [f"{bins[i]:.0f}–{bins[i+1]:.0f}" for i in range(len(bins) - 1)] | |
| return pd.cut(series, bins=bins, labels=labels, include_lowest=True) | |
| def eda_page(data: pd.DataFrame) -> None: | |
| st.header("Exploratory Data Analysis") | |
| total_shipments = len(data) | |
| on_time_rate = data[TARGET_COLUMN].mean() * 100 | |
| median_discount = data["Discount_offered"].median() | |
| median_weight = data["Weight_in_gms"].median() | |
| loyal_share = (data["Prior_purchases"] > 3).mean() * 100 | |
| c1, c2, c3, c4 = st.columns(4) | |
| c1.metric("Total shipments", f"{total_shipments:,}") | |
| c2.metric("On-time delivery rate", f"{on_time_rate:.1f}%") | |
| c3.metric("Median discount", f"{median_discount:.1f}%") | |
| c4.metric("Loyal customers (> 3 prior purchases)", f"{loyal_share:.1f}%") | |
| st.caption( | |
| "Quick overview: how many shipments you have, how reliable deliveries are, " | |
| "and how generous you are with discounts and loyal customers." | |
| ) | |
| tab_target, tab_category, tab_numeric, tab_segments = st.tabs( | |
| ["Delivery status", "By category", "By numeric feature", "Business segments"] | |
| ) | |
| with tab_target: | |
| st.subheader("Overall delivery status") | |
| target_series = _label_target(data[TARGET_COLUMN]) | |
| target_counts = ( | |
| target_series.value_counts().rename_axis("Status").reset_index(name="Count") | |
| ) | |
| target_counts["Percentage"] = ( | |
| target_counts["Count"] / target_counts["Count"].sum() * 100 | |
| ) | |
| fig = px.pie( | |
| target_counts, | |
| values="Count", | |
| names="Status", | |
| hole=0.35, | |
| ) | |
| fig.update_traces(textposition="inside", textinfo="percent+label") | |
| st.plotly_chart(fig, use_container_width=True) | |
| late_row = target_counts.loc[target_counts["Status"] == "Late"] | |
| if not late_row.empty: | |
| late_pct = late_row["Percentage"].iloc[0] | |
| st.write( | |
| f"**Insight:** about **{late_pct:.1f}%** of all shipments arrive late. " | |
| "This is the high-level reliability of your operation." | |
| ) | |
| with tab_category: | |
| st.subheader("How delivery status changes by category") | |
| categorical_columns = _get_categorical_columns(data) | |
| if not categorical_columns: | |
| st.info("This dataset does not contain categorical features.") | |
| else: | |
| selected_cat = st.selectbox( | |
| "Choose a categorical feature", | |
| options=categorical_columns, | |
| ) | |
| # Instead of 100% bars, show on-time rate per category | |
| summary = ( | |
| data.groupby(selected_cat) | |
| .agg( | |
| on_time_percent=(TARGET_COLUMN, lambda x: x.mean() * 100), | |
| total_shipments=(TARGET_COLUMN, "size"), | |
| ) | |
| .reset_index() | |
| ) | |
| fig_cat = px.bar( | |
| summary, | |
| x=selected_cat, | |
| y="on_time_percent", | |
| text_auto=".1f", | |
| labels={ | |
| "on_time_percent": "On-time delivery rate (%)", | |
| "total_shipments": "Number of shipments", | |
| }, | |
| ) | |
| st.plotly_chart(fig_cat, use_container_width=True) | |
| st.caption( | |
| "Each bar shows **what percentage of shipments are delivered on time** " | |
| f"for each value of **{selected_cat}**. Lower bars = higher risk segments." | |
| ) | |
| st.write("Detailed numbers:") | |
| st.dataframe( | |
| summary.rename( | |
| columns={ | |
| "on_time_percent": "On-time rate (%)", | |
| "total_shipments": "Total shipments", | |
| } | |
| ), | |
| use_container_width=True, | |
| ) | |
| with tab_numeric: | |
| st.subheader("Distribution of numeric features") | |
| numeric_columns = _get_numeric_columns(data) | |
| if not numeric_columns: | |
| st.info("This dataset does not contain numeric features (other than ID/target).") | |
| else: | |
| selected_num = st.selectbox( | |
| "Choose a numeric feature", | |
| options=numeric_columns, | |
| ) | |
| # Convert numeric values into simple ranges (bins) | |
| binned = _make_numeric_bins(data[selected_num]) | |
| temp = data.copy() | |
| temp["range"] = binned | |
| summary_num = ( | |
| temp.groupby("range") | |
| .agg( | |
| on_time_percent=(TARGET_COLUMN, lambda x: x.mean() * 100), | |
| total_shipments=(TARGET_COLUMN, "size"), | |
| ) | |
| .reset_index() | |
| .dropna() | |
| ) | |
| fig_num = px.bar( | |
| summary_num, | |
| x="range", | |
| y="on_time_percent", | |
| text_auto=".1f", | |
| labels={ | |
| "range": f"{selected_num} range", | |
| "on_time_percent": "On-time delivery rate (%)", | |
| }, | |
| ) | |
| st.plotly_chart(fig_num, use_container_width=True) | |
| st.caption( | |
| f"Bars show how **on-time delivery rate** changes across different ranges of " | |
| f"**{selected_num}**. For example, you can see whether very high values are " | |
| "associated with more late shipments." | |
| ) | |
| st.write("Detailed numbers:") | |
| st.dataframe( | |
| summary_num.rename( | |
| columns={ | |
| "range": f"{selected_num} range", | |
| "on_time_percent": "On-time rate (%)", | |
| "total_shipments": "Total shipments", | |
| } | |
| ), | |
| use_container_width=True, | |
| ) | |
| with tab_segments: | |
| st.subheader("Business segments: where do we perform well or poorly?") | |
| grouping_column = st.selectbox( | |
| "Group by", | |
| ["Mode_of_Shipment", "Warehouse_block", "Product_importance", "Gender"], | |
| ) | |
| summary = ( | |
| data.groupby(grouping_column) | |
| .agg( | |
| on_time_percent=(TARGET_COLUMN, lambda x: x.mean() * 100), | |
| avg_cost=("Cost_of_the_Product", "mean"), | |
| avg_discount=("Discount_offered", "mean"), | |
| ) | |
| .reset_index() | |
| ) | |
| fig_segment = px.bar( | |
| summary, | |
| x=grouping_column, | |
| y="on_time_percent", | |
| text_auto=".1f", | |
| color="avg_discount", | |
| color_continuous_scale="Blues", | |
| labels={ | |
| "on_time_percent": "On-time delivery rate (%)", | |
| "avg_discount": "Average discount", | |
| }, | |
| ) | |
| st.plotly_chart(fig_segment, use_container_width=True) | |
| st.dataframe( | |
| summary.rename( | |
| columns={ | |
| "on_time_percent": "On-time rate (%)", | |
| "avg_cost": "Avg product cost", | |
| "avg_discount": "Avg discount", | |
| } | |
| ), | |
| use_container_width=True, | |
| ) | |
| st.caption( | |
| "Use this view to spot **high-risk segments**: groups with low on-time rate, " | |
| "especially if they also receive high discounts or involve high product cost." | |
| ) | |