Esvanth commited on
Commit
a7a00a1
·
1 Parent(s): efe8b7d

Improve UI text clarity and fix README accuracy

Browse files
Files changed (2) hide show
  1. README.md +2 -3
  2. app.py +58 -41
README.md CHANGED
@@ -43,14 +43,13 @@ Trains two ML models on 730 days of synthetic sales data:
43
  - **Linear Regression** — fast and interpretable
44
  - **Random Forest** — captures non-linear seasonal patterns
45
 
46
- Features a **what-if predictor** enter any day, month, and promotion flag to get an instant sales prediction.
47
 
48
  ### Task 6 — Business Case *(Voluntary — AI Student)*
49
  Quantifies the financial and environmental impact of the AI system with fully interactive sliders:
50
  - **ROI calculator** — adjusts fleet size, fuel cost, wage rates and shows live annual savings
51
  - **3-year ROI projection** — cumulative benefit vs cost with breakeven line
52
- - **CO₂ impact** — tonnes saved per year, tree and car equivalents
53
- - **Implementation roadmap** — 5-phase Gantt chart across 8 months
54
 
55
  ---
56
 
 
43
  - **Linear Regression** — fast and interpretable
44
  - **Random Forest** — captures non-linear seasonal patterns
45
 
46
+ Trains on 730 days of data and evaluates both models on 140 unseen test days using MAE, RMSE, R², and MAPE.
47
 
48
  ### Task 6 — Business Case *(Voluntary — AI Student)*
49
  Quantifies the financial and environmental impact of the AI system with fully interactive sliders:
50
  - **ROI calculator** — adjusts fleet size, fuel cost, wage rates and shows live annual savings
51
  - **3-year ROI projection** — cumulative benefit vs cost with breakeven line
52
+ - **CO₂ impact** — estimated tonnes of CO₂ saved per year
 
53
 
54
  ---
55
 
app.py CHANGED
@@ -212,9 +212,9 @@ with st.sidebar:
212
  </div>""", unsafe_allow_html=True)
213
  st.divider()
214
 
215
- st.markdown("**How to use**")
216
- for n, t in [("1","Pick a tab above"),("2","Read the short description"),
217
- ("3","Press Run (Tasks 2, 3, 5)"),("4","Use Play to animate (Tasks 1, 3)")]:
218
  st.markdown(f"""<div style='display:flex;gap:8px;align-items:center;margin:5px 0;'>
219
  <div style='background:#0f172a;color:#fff;width:18px;height:18px;border-radius:50%;
220
  font-size:.68rem;font-weight:800;display:flex;align-items:center;
@@ -236,7 +236,7 @@ with st.sidebar:
236
  unsafe_allow_html=True)
237
 
238
  st.divider()
239
- st.caption("All outputs are from the real task Python scripts.")
240
 
241
  # ── header ────────────────────────────────────────────────────────────────────
242
  st.markdown("""
@@ -244,7 +244,7 @@ st.markdown("""
244
  padding:20px 26px;margin-bottom:18px;'>
245
  <div style='color:#f8fafc;font-size:1.5rem;font-weight:800;margin-bottom:2px;'>EcoCart AI System</div>
246
  <div style='color:#94a3b8;font-size:.85rem;'>
247
- Route optimisation &nbsp;·&nbsp; Customer segmentation &nbsp;·&nbsp; Demand forecasting
248
  </div>
249
  </div>""", unsafe_allow_html=True)
250
 
@@ -263,9 +263,10 @@ T1, T2, T3, T4, T5, T6 = st.tabs([
263
  with T1:
264
  st.markdown("""
265
  <div class='card card-navy'>
266
- <strong>What is this?</strong>&nbsp; Three AI agent types navigate the same 9-stop delivery map.
267
- Each makes decisions differently. Use <b>Play</b> to watch the route animate stop by stop,
268
- or drag the slider to step through manually.
 
269
  </div>""", unsafe_allow_html=True)
270
 
271
  # ── route data ────────────────────────────────────────────────────────────
@@ -307,9 +308,9 @@ with T1:
307
  ROUTES = _get_routes()
308
  RCOLS = {"Reactive Agent":BLUE, "Goal-Based Agent":GREEN, "Utility-Based Agent":AMBER}
309
  RDESC = {
310
- "Reactive Agent": "Always picks the nearest unvisited stop. Simple, but not the shortest route.",
311
- "Goal-Based Agent": "Plans the full route before starting using 2-opt optimisation. Shortest total km.",
312
- "Utility-Based Agent":"Scores stops by urgency ÷ distance. Reaches high-priority ★ stops first.",
313
  }
314
 
315
  # ── agent selection row ───────────────────────────────────────────────────
@@ -499,9 +500,10 @@ with T1:
499
  with T2:
500
  st.markdown("""
501
  <div class='card card-amber'>
502
- <strong>What is this?</strong>&nbsp; EcoCart uses K-Means clustering to group customers into
503
- High / Medium / Low Value segments. Rural customers were being unfairly placed in Low Value
504
- this task finds why and fixes it. Fairness threshold: Disparate Impact 0.80.
 
505
  </div>""", unsafe_allow_html=True)
506
 
507
  run_t2 = st.button("▶ Run Task 2 — Segmentation & Bias Fix",
@@ -543,9 +545,10 @@ with T2:
543
 
544
  st.markdown("""
545
  <div class='insight'>
546
- <b>Before:</b> 0% of rural customers in High Value — Disparate Impact = 0.0 (biased).<br>
547
- <b>After fix:</b> 57.3% of rural customers in High Value Disparate Impact = 0.847 (fair, above 0.80).<br>
548
- Fix: oversample rural customers + adjust spend features + re-cluster.
 
549
  </div>""", unsafe_allow_html=True)
550
 
551
  # ══════════════════════════════════════════════════════════════════════════════
@@ -554,9 +557,10 @@ with T2:
554
  with T3:
555
  st.markdown("""
556
  <div class='card card-blue'>
557
- <strong>What is this?</strong>&nbsp; Four search algorithms (BFS, DFS, A*, IDA*) find routes across
558
- a 20-node delivery network (10 urban, 10 rural). Run the script to see all results,
559
- then use the <b>interactive replay</b> below to watch BFS or A* explore node by node.
 
560
  </div>""", unsafe_allow_html=True)
561
 
562
  run_t3 = st.button("▶ Run Task 3 — Route Optimisation",
@@ -599,16 +603,18 @@ with T3:
599
 
600
  st.markdown("""
601
  <div class='insight'>
602
- A* found the optimal path (5.69 km) expanding only 7 nodes.
603
- BFS found same path (11 nodes). DFS was not optimal (6.84 km, 18 nodes).
604
- IDA* matched A* cost but expanded 43 nodes better for very large networks (no RAM needed).
 
 
605
  </div>""", unsafe_allow_html=True)
606
 
607
  # ── interactive route replay ──────────────────────────────────────────────
608
  st.markdown("<br>", unsafe_allow_html=True)
609
  st.markdown("""
610
  <div style='font-weight:700;font-size:1rem;color:#0f172a;margin-bottom:10px;'>
611
- Interactive search replaywatch the algorithm explore step by step
612
  </div>""", unsafe_allow_html=True)
613
 
614
  NODES_R = {
@@ -849,9 +855,11 @@ with T3:
849
  with T4:
850
  st.markdown("""
851
  <div class='card card-navy'>
852
- <strong>What is this?</strong>&nbsp; A* and IDA* both find the optimal route but work differently.
853
- A* stores all explored nodes in memory (fast). IDA* uses almost no memory by re-searching with a
854
- stricter cost limit each pass. This tab shows benchmark results: 10 routes, 20 runs each.
 
 
855
  </div>""", unsafe_allow_html=True)
856
 
857
  urban_data=[
@@ -898,10 +906,11 @@ with T4:
898
 
899
  st.markdown("""
900
  <div class='insight'>
901
- Both algorithms found <b>identical optimal paths</b> on every route.<br>
902
- A* expanded fewer nodes every time (e.g. R4→R9: A*=7, IDA*=50). A* is faster on small graphs.<br>
903
- IDA* uses O(depth) memory vs A*'s O(b<sup>d</sup>). On a national network with millions of nodes,
904
- IDA* would be the only practical choice.
 
905
  </div>""", unsafe_allow_html=True)
906
 
907
  # ══════════════════════════════════════════════════════════════════════════════
@@ -910,8 +919,10 @@ with T4:
910
  with T5:
911
  st.markdown("""
912
  <div class='card card-green'>
913
- <strong>What is this?</strong>&nbsp; Two ML models Linear Regression and Random Forest
914
- are trained on 730 days of sales data and tested on 140 unseen days to predict daily demand.
 
 
915
  </div>""", unsafe_allow_html=True)
916
 
917
  run_t5 = st.button("▶ Run Task 5 — Demand Forecasting",
@@ -958,9 +969,11 @@ with T5:
958
 
959
  st.markdown("""
960
  <div class='insight'>
961
- Linear Regression (R²=0.762) beat Random Forest (R²=0.716) on this dataset.<br>
962
- Top 3 features: <b>lag_7</b> (same day last week), <b>lag_14</b>, and <b>is_promo</b>.
963
- Weekly patterns and promotions drive demand most.
 
 
964
  </div>""", unsafe_allow_html=True)
965
 
966
  # ══════════════════════════════════════════════════════════════════════════════
@@ -969,8 +982,11 @@ with T5:
969
  with T6:
970
  st.markdown("""
971
  <div class='card card-amber'>
972
- <strong>What is this?</strong>&nbsp; An estimated business case for deploying the AI system.
973
- All numbers are assumptions adjust the sliders to model different scenarios.
 
 
 
974
  </div>""", unsafe_allow_html=True)
975
 
976
  ctrl, main = st.columns([1, 3])
@@ -1029,7 +1045,8 @@ with T6:
1029
 
1030
  st.markdown(f"""
1031
  <div class='warn-box'>
1032
- All numbers are <b>assumptions for illustration</b>, not measured values.
1033
- Based on: {fleet} vehicles · {daily} deliveries/day · {avg_km} km avg ·
1034
- {rt_save}% A* saving · €{seg_rev}k rural uplift.
 
1035
  </div>""", unsafe_allow_html=True)
 
212
  </div>""", unsafe_allow_html=True)
213
  st.divider()
214
 
215
+ st.markdown("**How to navigate**")
216
+ for n, t in [("1","Pick a task tab above"),("2","Tasks 2, 3 and 5 — press Run first"),
217
+ ("3","Tasks 1 and 3 — press Play to animate"),("4","Task 6 drag sliders to explore")]:
218
  st.markdown(f"""<div style='display:flex;gap:8px;align-items:center;margin:5px 0;'>
219
  <div style='background:#0f172a;color:#fff;width:18px;height:18px;border-radius:50%;
220
  font-size:.68rem;font-weight:800;display:flex;align-items:center;
 
236
  unsafe_allow_html=True)
237
 
238
  st.divider()
239
+ st.caption("Every number and chart comes from running the actual Python scripts — nothing is hardcoded except Task 4 benchmark tables and Task 6 estimates.")
240
 
241
  # ── header ────────────────────────────────────────────────────────────────────
242
  st.markdown("""
 
244
  padding:20px 26px;margin-bottom:18px;'>
245
  <div style='color:#f8fafc;font-size:1.5rem;font-weight:800;margin-bottom:2px;'>EcoCart AI System</div>
246
  <div style='color:#94a3b8;font-size:.85rem;'>
247
+ Six AI tasks, one logistics problem &nbsp;·&nbsp; Every result runs from real Python scripts
248
  </div>
249
  </div>""", unsafe_allow_html=True)
250
 
 
263
  with T1:
264
  st.markdown("""
265
  <div class='card card-navy'>
266
+ Three AI agents tackle the same 9-stop delivery problem — but each thinks completely differently.
267
+ The <b>Reactive</b> agent rushes to the nearest stop. The <b>Goal-Based</b> agent plans the
268
+ whole route before leaving. The <b>Utility-Based</b> agent chases high-priority stops first.
269
+ Same map, same stops — very different outcomes. Press <b>Play</b> to watch or drag the slider to step through.
270
  </div>""", unsafe_allow_html=True)
271
 
272
  # ── route data ────────────────────────────────────────────────────────────
 
308
  ROUTES = _get_routes()
309
  RCOLS = {"Reactive Agent":BLUE, "Goal-Based Agent":GREEN, "Utility-Based Agent":AMBER}
310
  RDESC = {
311
+ "Reactive Agent": "No planning — just go to the nearest stop. Fast to decide, but the total route is often longer.",
312
+ "Goal-Based Agent": "Plans the full route before moving using 2-opt optimisation. Always finds the shortest total distance.",
313
+ "Utility-Based Agent":"Scores every stop by priority ÷ distance. Gets to the most urgent ★ stops first, not just the closest.",
314
  }
315
 
316
  # ── agent selection row ───────────────────────────────────────────────────
 
500
  with T2:
501
  st.markdown("""
502
  <div class='card card-amber'>
503
+ The K-Means model was quietly being unfair not one rural customer made it to High Value.
504
+ Zero. This task figures out why that happened (the data was biased from the start) and applies
505
+ a three-step fix. The fairness test used is <b>Disparate Impact</b>: if rural customers are
506
+ less than 80% as likely as urban ones to reach High Value, the model fails. Press <b>Run</b> to see the before and after.
507
  </div>""", unsafe_allow_html=True)
508
 
509
  run_t2 = st.button("▶ Run Task 2 — Segmentation & Bias Fix",
 
545
 
546
  st.markdown("""
547
  <div class='insight'>
548
+ Before the fix, 0% of rural customers reached High Value — Disparate Impact was 0.0, a complete fairness failure.
549
+ After oversampling rural customers to match urban count, adjusting spend for the delivery cost premium (+€12),
550
+ and correcting frequency for order batching (×1.5), the Disparate Impact rose to <b>0.847</b> — above the 0.80 threshold.
551
+ The model now treats both groups fairly.
552
  </div>""", unsafe_allow_html=True)
553
 
554
  # ══════════════════════════════════════════════════════════════════════════════
 
557
  with T3:
558
  st.markdown("""
559
  <div class='card card-blue'>
560
+ Four algorithms, one problem: find the best delivery route on a custom 20-node map (10 urban, 10 rural stops).
561
+ Each algorithm searches differently some are optimal, one is not, and the best one does it
562
+ with the fewest steps. Press <b>Run</b> to see the benchmark results, then use the
563
+ <b>interactive replay</b> below to watch any algorithm explore the network node by node.
564
  </div>""", unsafe_allow_html=True)
565
 
566
  run_t3 = st.button("▶ Run Task 3 — Route Optimisation",
 
603
 
604
  st.markdown("""
605
  <div class='insight'>
606
+ A* found the shortest path (5.69 km) using only 7 node expansions — the most efficient result.
607
+ BFS found the same optimal path but needed 11 expansions. DFS was the only algorithm that got
608
+ it wrong, returning a 6.84 km suboptimal route because it dives deep without comparing alternatives.
609
+ IDA* also found 5.69 km but needed 43 expansions — its advantage is near-zero memory use,
610
+ which matters at national scale but not here.
611
  </div>""", unsafe_allow_html=True)
612
 
613
  # ── interactive route replay ──────────────────────────────────────────────
614
  st.markdown("<br>", unsafe_allow_html=True)
615
  st.markdown("""
616
  <div style='font-weight:700;font-size:1rem;color:#0f172a;margin-bottom:10px;'>
617
+ Try it yourselfpick any start, end, and algorithm, then replay the search step by step
618
  </div>""", unsafe_allow_html=True)
619
 
620
  NODES_R = {
 
855
  with T4:
856
  st.markdown("""
857
  <div class='card card-navy'>
858
+ A* and IDA* always find the same shortest path the question is <i>how</i> they get there.
859
+ A* keeps a record of every node it has visited (fast, but memory grows). IDA* forgets everything
860
+ and re-searches from scratch each pass, tightening its cost limit each time (slower, but uses
861
+ almost no memory). These benchmarks run <b>10 routes × 20 timing runs</b> to see which wins on
862
+ EcoCart's network — and when IDA* would be the better choice.
863
  </div>""", unsafe_allow_html=True)
864
 
865
  urban_data=[
 
906
 
907
  st.markdown("""
908
  <div class='insight'>
909
+ Both algorithms found <b>identical optimal paths</b> on every single route — path costs match exactly.
910
+ But A* was faster and expanded fewer nodes every time. The starkest example: R4→R9, where
911
+ A* needed 7 node expansions in 0.130 ms while IDA* needed 50 in 0.642 ms.
912
+ For EcoCart's current network, A* is the clear winner. IDA*'s value shows up at national scale —
913
+ when the network has millions of nodes and storing A*'s visited set would exhaust memory.
914
  </div>""", unsafe_allow_html=True)
915
 
916
  # ══════════════════════════════════════════════════════════════════════════════
 
919
  with T5:
920
  st.markdown("""
921
  <div class='card card-green'>
922
+ Can a simple model beat a complex one? Two models are trained on <b>730 days</b> of EcoCart
923
+ sales history Linear Regression (transparent, fast) and Random Forest (200 decision trees,
924
+ captures non-linear patterns). Both are then tested on <b>140 days they have never seen</b>.
925
+ Press <b>Run</b> to find out which wins, and why the result might surprise you.
926
  </div>""", unsafe_allow_html=True)
927
 
928
  run_t5 = st.button("▶ Run Task 5 — Demand Forecasting",
 
969
 
970
  st.markdown("""
971
  <div class='insight'>
972
+ Linear Regression (R²=0.762) outperformed Random Forest (R²=0.716) the simpler model won.
973
+ The reason: once you give the model last week's same-day sales (<b>lag_7</b>), the pattern
974
+ becomes mostly linear. Random Forest's extra complexity adds nothing. The top three predictors
975
+ were lag_7, lag_14, and is_promo — confirming that weekly rhythm and promotions drive
976
+ demand far more than anything else.
977
  </div>""", unsafe_allow_html=True)
978
 
979
  # ══════════════════════════════════════════════════════════════════════════════
 
982
  with T6:
983
  st.markdown("""
984
  <div class='card card-amber'>
985
+ What does all of this actually save EcoCart? This tab turns the technical results into
986
+ a financial modelthe savings from A* routing, the rural revenue unlocked by fixing the
987
+ segmentation bias, and the CO₂ avoided. <b>None of these numbers are real</b> — they are
988
+ estimates based on typical fleet operations. Use the sliders on the left to plug in
989
+ EcoCart's actual numbers and see how the ROI changes.
990
  </div>""", unsafe_allow_html=True)
991
 
992
  ctrl, main = st.columns([1, 3])
 
1045
 
1046
  st.markdown(f"""
1047
  <div class='warn-box'>
1048
+ <b>Reminder:</b> these are estimates for illustration only — not measured values.
1049
+ Current inputs: {fleet} vehicles, {daily} deliveries/day, {avg_km} km avg route,
1050
+ {rt_save}% saving from A* routing, €{seg_rev}k rural revenue uplift assumed.
1051
+ Change the sliders to model your own scenario.
1052
  </div>""", unsafe_allow_html=True)