SHELLAPANDIANGANHUNGING commited on
Commit
f38e51e
Β·
verified Β·
1 Parent(s): 54c6593

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -40
app.py CHANGED
@@ -1455,18 +1455,39 @@ else:
1455
 
1456
 
1457
  # ================= OBJECTIVE 6 =================
 
 
 
 
 
 
 
 
1458
  st.markdown('<h3 class="objective-title">OBJECTIVE 6: Insights & Mitigation β€” How Can Red Pressure Alarms Be Reduced?</h3>', unsafe_allow_html=True)
1459
 
1460
- # --- DATA PREP (aman terhadap NaN, empty, atau division-by-zero) ---
1461
- # Front tyre stats β€” fallback ke "β€”" jika NaN
1462
- front_pressure_avg = dff[dff['Position'].isin([1, 2])]['Pressure (psi)'].mean()
1463
- front_temp_avg = dff[dff['Position'].isin([1, 2])]['Temperature (Β°C)'].mean()
 
 
 
 
 
 
 
 
1464
  front_pressure_avg_str = f"{front_pressure_avg:.1f}" if pd.notna(front_pressure_avg) else "β€”"
1465
  front_temp_avg_str = f"{front_temp_avg:.1f}" if pd.notna(front_temp_avg) else "β€”"
1466
 
1467
- # Hourly alarm stats β€” hanya Red & Amber (ignore No Alarm)
1468
- alarm_df = dff[dff['Alarm Type'].isin(['Red', 'Amber'])]
1469
- hourly_counts = alarm_df['hour'].value_counts().reindex(range(24), fill_value=0)
 
 
 
 
 
1470
  total_alarms = hourly_counts.sum()
1471
 
1472
  dominant_hour = None
@@ -1475,43 +1496,60 @@ if total_alarms > 0 and not hourly_counts.empty:
1475
  dominant_hour = int(hourly_counts.idxmax())
1476
  dominant_percentage = (hourly_counts[dominant_hour] / total_alarms) * 100
1477
 
1478
- # Zone alarm stats β€” hanya Red & Amber
1479
- zone_counts = alarm_df['Zone'].value_counts()
1480
- top_zone = "β€”"
1481
- top_zone_percentage = 0.0
1482
- if not zone_counts.empty and total_alarms > 0:
1483
- top_zone = str(zone_counts.index[0])
1484
- top_zone_percentage = (zone_counts.iloc[0] / total_alarms) * 100
1485
-
1486
- # Correlation β€” aman terhadap NaN
1487
- def safe_corr(x, y):
1488
- valid = x.notna() & y.notna()
1489
- if valid.sum() < 2:
1490
- return 0.0
1491
- c = np.corrcoef(x[valid], y[valid])[0, 1]
1492
- return c if np.isfinite(c) else 0.0
1493
-
1494
- front_df = dff[dff['Position'].isin([1, 2])]
1495
- rear_df = dff[dff['Position'].isin([3, 4])]
1496
-
1497
- corr_pressure_temp_front = safe_corr(front_df['Pressure (psi)'], front_df['Temperature (Β°C)'])
1498
- corr_speed_temp_rear = safe_corr(rear_df['Speed (km/h)'], rear_df['Temperature (Β°C)'])
1499
-
1500
- # Format korelasi: 2 desimal, hindari -0.00
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1501
  corr_pressure_temp_front_str = f"{corr_pressure_temp_front:+.2f}".replace("-0.00", "0.00")
1502
  corr_speed_temp_rear_str = f"{corr_speed_temp_rear:+.2f}".replace("-0.00", "0.00")
1503
 
1504
- # Position 1 alarm count (Red + Amber only)
1505
- pos1_alarm_count = alarm_df[alarm_df['Position'] == 1].shape[0]
1506
 
1507
- # ================= INSIGHTS (dynamic, fact-based) =================
1508
  insight_lines = []
1509
 
 
1510
  line1 = f"1. Front tyres (Pos 1 & 2): avg pressure {front_pressure_avg_str} psi, avg temperature {front_temp_avg_str}Β°C."
1511
  if pd.notna(front_pressure_avg) and front_pressure_avg > 115:
1512
  line1 += " Exceeds ideal inflation range (100–110 psi), indicating over-pressure risk."
1513
  insight_lines.append(line1)
1514
 
 
1515
  if dominant_hour is not None:
1516
  insight_lines.append(
1517
  f"2. Peak Red/Amber alarm concentration at {dominant_hour:02d}:00 ({dominant_percentage:.1f}% of total)."
@@ -1519,11 +1557,13 @@ if dominant_hour is not None:
1519
  else:
1520
  insight_lines.append("2. No statistically dominant hourly alarm peak.")
1521
 
 
1522
  insight_lines.append(
1523
  f"3. Front pressure–temperature correlation: r = {corr_pressure_temp_front_str}; "
1524
  f"Rear speed–temperature correlation: r = {corr_speed_temp_rear_str}."
1525
  )
1526
 
 
1527
  if top_zone != "β€”" and top_zone_percentage >= 10.0:
1528
  insight_lines.append(
1529
  f"4. Zone {top_zone} accounts for {top_zone_percentage:.1f}% of alarms β€” highest-risk location."
@@ -1531,6 +1571,7 @@ if top_zone != "β€”" and top_zone_percentage >= 10.0:
1531
  else:
1532
  insight_lines.append("4. Alarm distribution appears relatively uniform across zones.")
1533
 
 
1534
  if pos1_alarm_count > 0:
1535
  insight_lines.append(
1536
  f"5. Position 1 recorded {pos1_alarm_count} Red/Amber alarms β€” highest of any wheel position."
@@ -1538,23 +1579,23 @@ if pos1_alarm_count > 0:
1538
 
1539
  insight_text = "<br>".join(insight_lines)
1540
 
1541
- # ================= SUMMARY (HARD-CODED β€” EXACTLY AS PROVIDED) =================
1542
- summary_text = """
1543
  1. Front tyres are predominantly exposed to over-pressure risk, while rear tyres are prone to under-pressure events linked to ambient temperature variation, particularly during morning and evening operations.<br>
1544
- 2. Overall, over-pressure alarms significantly exceed under-pressure alarms, and pressure behaviour is not correlated with vehicle speed, suggesting that temperature rise is driven by operational and environmental factors rather than driving behaviour.
1545
  """
1546
 
1547
- # ================= RECOMMENDATION (HARD-CODED β€” EXACTLY AS PROVIDED) =================
1548
- recommendation_text = """
1549
  1. Adjust cold inflation pressures based on actual axle loads, particularly for front tyres.<br>
1550
  2. Apply shift-based pressure management to account for ambient temperature changes affecting rear tyres.<br>
1551
  3. Strengthen leak detection and valve inspections for tyres with recurring under-pressure events.<br>
1552
  4. Optimise TPMS alarm thresholds to reduce nuisance alarms and prevent alarm fatigue.<br>
1553
  5. Address non-speed temperature drivers through haul road improvement, payload control, and reduced idle under load.<br>
1554
- 6. Use TPMS trends to enable proactive tyre health monitoring and maintenance planning.
1555
  """
1556
 
1557
- # ================= RENDER =================
1558
  st.markdown('<h4 style="text-align:center; margin:10px 0 5px 0; font-weight:bold;">INSIGHTS</h4>', unsafe_allow_html=True)
1559
  st.markdown(f"""
1560
  <div class="insight-box">
 
1455
 
1456
 
1457
  # ================= OBJECTIVE 6 =================
1458
+ import streamlit as st
1459
+ import pandas as pd
1460
+ import numpy as np
1461
+
1462
+ # ⚠️ ASSUMPTION: `dff` exists in scope.
1463
+ # Jika 'Alarm Type' tidak ada di kolom, kita fallback ke mode aman (empty alarms)
1464
+ # β€” sesuai permintaan: *hard code, jgn pakai def*
1465
+
1466
  st.markdown('<h3 class="objective-title">OBJECTIVE 6: Insights & Mitigation β€” How Can Red Pressure Alarms Be Reduced?</h3>', unsafe_allow_html=True)
1467
 
1468
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” DATA PREP (HARD-CODED, NO DEF) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1469
+ # Cek apakah kolom 'Alarm Type' ada
1470
+ has_alarm_type = 'Alarm Type' in dff.columns
1471
+
1472
+ # Front tyre stats β€” fallback ke "β€”" jika NaN/empty/error
1473
+ try:
1474
+ front_pressure_avg = dff[dff['Position'].isin([1, 2])]['Pressure (psi)'].mean()
1475
+ front_temp_avg = dff[dff['Position'].isin([1, 2])]['Temperature (Β°C)'].mean()
1476
+ except Exception:
1477
+ front_pressure_avg = np.nan
1478
+ front_temp_avg = np.nan
1479
+
1480
  front_pressure_avg_str = f"{front_pressure_avg:.1f}" if pd.notna(front_pressure_avg) else "β€”"
1481
  front_temp_avg_str = f"{front_temp_avg:.1f}" if pd.notna(front_temp_avg) else "β€”"
1482
 
1483
+ # Alarm filtering β€” hanya Red & Amber, jika 'Alarm Type' tidak ada β†’ anggap 0 alarm
1484
+ if has_alarm_type:
1485
+ alarm_mask = dff['Alarm Type'].isin(['Red', 'Amber'])
1486
+ alarm_df = dff[alarm_mask].copy()
1487
+ else:
1488
+ alarm_df = dff.iloc[0:0] # empty DataFrame
1489
+
1490
+ hourly_counts = alarm_df['hour'].value_counts().reindex(range(24), fill_value=0) if 'hour' in alarm_df.columns else pd.Series([0]*24, index=range(24))
1491
  total_alarms = hourly_counts.sum()
1492
 
1493
  dominant_hour = None
 
1496
  dominant_hour = int(hourly_counts.idxmax())
1497
  dominant_percentage = (hourly_counts[dominant_hour] / total_alarms) * 100
1498
 
1499
+ # Zone alarm stats
1500
+ if 'Zone' in alarm_df.columns and not alarm_df.empty:
1501
+ zone_counts = alarm_df['Zone'].value_counts()
1502
+ if not zone_counts.empty:
1503
+ top_zone = str(zone_counts.index[0])
1504
+ top_zone_percentage = (zone_counts.iloc[0] / total_alarms) * 100 if total_alarms > 0 else 0.0
1505
+ else:
1506
+ top_zone = "β€”"
1507
+ top_zone_percentage = 0.0
1508
+ else:
1509
+ top_zone = "β€”"
1510
+ top_zone_percentage = 0.0
1511
+
1512
+ # Korelasi β€” aman tanpa fungsi
1513
+ corr_pressure_temp_front = 0.0
1514
+ corr_speed_temp_rear = 0.0
1515
+
1516
+ try:
1517
+ front_df = dff[dff['Position'].isin([1, 2])]
1518
+ rear_df = dff[dff['Position'].isin([3, 4])]
1519
+
1520
+ # Pressure–temp (front)
1521
+ x1, y1 = front_df['Pressure (psi)'], front_df['Temperature (Β°C)']
1522
+ valid1 = x1.notna() & y1.notna()
1523
+ if valid1.sum() >= 2:
1524
+ c1 = np.corrcoef(x1[valid1], y1[valid1])[0, 1]
1525
+ corr_pressure_temp_front = c1 if np.isfinite(c1) else 0.0
1526
+
1527
+ # Speed–temp (rear)
1528
+ x2, y2 = rear_df['Speed (km/h)'], rear_df['Temperature (Β°C)']
1529
+ valid2 = x2.notna() & y2.notna()
1530
+ if valid2.sum() >= 2:
1531
+ c2 = np.corrcoef(x2[valid2], y2[valid2])[0, 1]
1532
+ corr_speed_temp_rear = c2 if np.isfinite(c2) else 0.0
1533
+ except Exception:
1534
+ pass # tetap 0.0 jika error
1535
+
1536
+ # Format Β±0.xx, hindari -0.00
1537
  corr_pressure_temp_front_str = f"{corr_pressure_temp_front:+.2f}".replace("-0.00", "0.00")
1538
  corr_speed_temp_rear_str = f"{corr_speed_temp_rear:+.2f}".replace("-0.00", "0.00")
1539
 
1540
+ # Position 1 alarm count
1541
+ pos1_alarm_count = alarm_df[alarm_df['Position'] == 1].shape[0] if 'Position' in alarm_df.columns else 0
1542
 
1543
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” INSIGHTS β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1544
  insight_lines = []
1545
 
1546
+ # 1. Front tyre avg
1547
  line1 = f"1. Front tyres (Pos 1 & 2): avg pressure {front_pressure_avg_str} psi, avg temperature {front_temp_avg_str}Β°C."
1548
  if pd.notna(front_pressure_avg) and front_pressure_avg > 115:
1549
  line1 += " Exceeds ideal inflation range (100–110 psi), indicating over-pressure risk."
1550
  insight_lines.append(line1)
1551
 
1552
+ # 2. Dominant hour
1553
  if dominant_hour is not None:
1554
  insight_lines.append(
1555
  f"2. Peak Red/Amber alarm concentration at {dominant_hour:02d}:00 ({dominant_percentage:.1f}% of total)."
 
1557
  else:
1558
  insight_lines.append("2. No statistically dominant hourly alarm peak.")
1559
 
1560
+ # 3. Correlations
1561
  insight_lines.append(
1562
  f"3. Front pressure–temperature correlation: r = {corr_pressure_temp_front_str}; "
1563
  f"Rear speed–temperature correlation: r = {corr_speed_temp_rear_str}."
1564
  )
1565
 
1566
+ # 4. Top zone
1567
  if top_zone != "β€”" and top_zone_percentage >= 10.0:
1568
  insight_lines.append(
1569
  f"4. Zone {top_zone} accounts for {top_zone_percentage:.1f}% of alarms β€” highest-risk location."
 
1571
  else:
1572
  insight_lines.append("4. Alarm distribution appears relatively uniform across zones.")
1573
 
1574
+ # 5. Position 1 alarms
1575
  if pos1_alarm_count > 0:
1576
  insight_lines.append(
1577
  f"5. Position 1 recorded {pos1_alarm_count} Red/Amber alarms β€” highest of any wheel position."
 
1579
 
1580
  insight_text = "<br>".join(insight_lines)
1581
 
1582
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” SUMMARY (HARD-CODED) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1583
+ summary_text = """\
1584
  1. Front tyres are predominantly exposed to over-pressure risk, while rear tyres are prone to under-pressure events linked to ambient temperature variation, particularly during morning and evening operations.<br>
1585
+ 2. Overall, over-pressure alarms significantly exceed under-pressure alarms, and pressure behaviour is not correlated with vehicle speed, suggesting that temperature rise is driven by operational and environmental factors rather than driving behaviour.\
1586
  """
1587
 
1588
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” RECOMMENDATION (HARD-CODED, BUSINESS-READY) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1589
+ recommendation_text = """\
1590
  1. Adjust cold inflation pressures based on actual axle loads, particularly for front tyres.<br>
1591
  2. Apply shift-based pressure management to account for ambient temperature changes affecting rear tyres.<br>
1592
  3. Strengthen leak detection and valve inspections for tyres with recurring under-pressure events.<br>
1593
  4. Optimise TPMS alarm thresholds to reduce nuisance alarms and prevent alarm fatigue.<br>
1594
  5. Address non-speed temperature drivers through haul road improvement, payload control, and reduced idle under load.<br>
1595
+ 6. Use TPMS trends to enable proactive tyre health monitoring and maintenance planning.\
1596
  """
1597
 
1598
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” RENDER β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1599
  st.markdown('<h4 style="text-align:center; margin:10px 0 5px 0; font-weight:bold;">INSIGHTS</h4>', unsafe_allow_html=True)
1600
  st.markdown(f"""
1601
  <div class="insight-box">