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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -63
app.py CHANGED
@@ -308,15 +308,16 @@ df = load_data()
308
 
309
  # ================= HEADER =================
310
  st.markdown("""
311
- <div style="text-align:center; font-family:Arial, sans-serif;">
312
- <h1 style="color:#154D9C; font-weight:bold; margin-bottom:0;">Michelin Mining Tyre Analytics</h1>
313
- <p style="font-size:12px; color:#7d7d7d; margin-top:2px;">
 
 
314
  Daily trend insights derived from 13–16 December 2023 data
315
  </p>
316
- <p style="font-size:10px; color:#7d7d7d; margin-top:4px;">
317
- &copy; 2025 Bukit Technology. All rights reserved.
318
  </p>
319
- <hr style="border:0; height:1px; background-color:#e0e0e0; margin-top:8px; margin-bottom:8px;">
320
  </div>
321
  """, unsafe_allow_html=True)
322
 
@@ -1456,33 +1457,33 @@ else:
1456
  # ================= OBJECTIVE 6 =================
1457
  st.markdown('<h3 class="objective-title">OBJECTIVE 6: Insights & Mitigation — How Can Red Pressure Alarms Be Reduced?</h3>', unsafe_allow_html=True)
1458
 
1459
- # --- DATA PREP (dengan penanganan NaN/empty yang aman) ---
1460
  # Front tyre stats — fallback ke "—" jika NaN
1461
  front_pressure_avg = dff[dff['Position'].isin([1, 2])]['Pressure (psi)'].mean()
1462
  front_temp_avg = dff[dff['Position'].isin([1, 2])]['Temperature (°C)'].mean()
1463
  front_pressure_avg_str = f"{front_pressure_avg:.1f}" if pd.notna(front_pressure_avg) else "—"
1464
  front_temp_avg_str = f"{front_temp_avg:.1f}" if pd.notna(front_temp_avg) else "—"
1465
 
1466
- # Hourly alarm stats
1467
- hourly_counts = dff[dff['is_alarm'] == 1]['hour'].value_counts().reindex(range(24), fill_value=0)
 
1468
  total_alarms = hourly_counts.sum()
 
 
 
1469
  if total_alarms > 0 and not hourly_counts.empty:
1470
  dominant_hour = int(hourly_counts.idxmax())
1471
  dominant_percentage = (hourly_counts[dominant_hour] / total_alarms) * 100
1472
- else:
1473
- dominant_hour = None
1474
- dominant_percentage = 0.0
1475
 
1476
- # Zone alarm stats
1477
- zone_counts = dff[dff['is_alarm'] == 1]['Zone'].value_counts()
 
 
1478
  if not zone_counts.empty and total_alarms > 0:
1479
  top_zone = str(zone_counts.index[0])
1480
  top_zone_percentage = (zone_counts.iloc[0] / total_alarms) * 100
1481
- else:
1482
- top_zone = "—"
1483
- top_zone_percentage = 0.0
1484
 
1485
- # Correlation — pastikan tidak NaN
1486
  def safe_corr(x, y):
1487
  valid = x.notna() & y.notna()
1488
  if valid.sum() < 2:
@@ -1493,69 +1494,68 @@ def safe_corr(x, y):
1493
  front_df = dff[dff['Position'].isin([1, 2])]
1494
  rear_df = dff[dff['Position'].isin([3, 4])]
1495
 
1496
- corr_front = safe_corr(front_df['Pressure (psi)'], front_df['Temperature (°C)'])
1497
- corr_rear = safe_corr(rear_df['Speed (km/h)'], rear_df['Temperature (°C)'])
1498
 
1499
- # Format korelasi: batas 2 desimal & hindari -0.00 → 0.00
1500
- corr_front_str = f"{corr_front:+.2f}".replace("-0.00", "0.00")
1501
- corr_rear_str = f"{corr_rear:+.2f}".replace("-0.00", "0.00")
1502
 
1503
- # Insight text aman dari N/A/NaN
 
 
 
1504
  insight_lines = []
1505
 
1506
- line1 = f"1. Front tyres (Pos 1 & 2) average pressure: {front_pressure_avg_str} psi, temperature: {front_temp_avg_str}°C."
1507
- if pd.notna(front_pressure_avg) and front_pressure_avg > 125: # sesuaikan threshold jika perlu
1508
- line1 += " Values suggest risk of over-inflation under operational load (Objective 1)."
1509
  insight_lines.append(line1)
1510
 
1511
  if dominant_hour is not None:
1512
  insight_lines.append(
1513
- f"2. Peak alarm in Position 1 (238 occurrences)"
1514
  )
1515
  else:
1516
- insight_lines.append("2. No clear hourly alarm peak detected.")
1517
 
1518
  insight_lines.append(
1519
- f"3. Front tyres show pressure–temperature correlation r = {corr_front_str}; "
1520
- f" Temperaturespeed correlation to pressure is weak."
1521
  )
1522
 
1523
- if top_zone != "—":
1524
- insight_lines.append(f"4. Zone Parking 2 for {top_zone_percentage:.1f}% of alarms, confirmed as high-risk zone.")
 
 
1525
  else:
1526
- insight_lines.append("4. No dominant alarm zone identified.")
1527
 
1528
- insight_text = "<br>".join(insight_lines)
1529
-
1530
- # ============== ACTIONS (Recommendation & Risk Mitigation Digabung - Berdasarkan Data & Logika) ==============
1531
- action_lines = []
1532
 
1533
- # 1. Front tyre pressure & temp — fokus anomali
1534
- if pd.notna(front_pressure_avg):
1535
- # Jika pressure jauh dari ideal (misal 100-110 psi), tambahkan warning
1536
- ideal_low = 100
1537
- ideal_high = 110
1538
- if front_pressure_avg < ideal_low or front_pressure_avg > ideal_high:
1539
- action_lines.append(f"1. Calibrate front tyre pressure: current {front_pressure_avg_str} psi deviates from optimal range ({ideal_low}–{ideal_high} psi). Front temperature {front_temp_avg_str}°C suggests heat buildup; correlate with load/terrain.")
1540
- else:
1541
- action_lines.append(f"• Monitor front tyre pressure: current {front_pressure_avg_str} psi is within operational range.")
1542
- # 2. Peak alarm di Position 1 — fokus mekanikal
1543
- action_lines.append(
1544
- f"2. Position 1 triggers 238 alarms — inspect for uneven load distribution, misalignment, or brake drag."
1545
- )
1546
 
1547
- # 3. Correlation: front tinggi (r=+0.99), rear rendah (r=+0.01)
1548
- action_lines.append(
1549
- f"3. Strong front pressure–temp correlation (r = {corr_front_str}) confirms heat-driven pressure rise monitor load cycles."
1550
- )
1551
- action_lines.append(
1552
- f"4. Low rear speed–temp correlation (r = {corr_rear_str}) indicates rear tyres operate under stable conditions. {top_zone_percentage:.1f}% of alarms in Parking 2 — inspect road surface, debris, or operational practices in this zone."
1553
- )
1554
 
1555
- action_text = "<br>".join(action_lines)
 
 
 
 
 
 
 
 
1556
 
1557
- # ============== RENDER ==============
1558
- st.markdown('<h4 style="text-align:center; margin:10px 0 5px 0; font-weight:bold;">SUMMARY</h4>', unsafe_allow_html=True)
1559
  st.markdown(f"""
1560
  <div class="insight-box">
1561
  <div class="content" style="text-align:left;">
@@ -1564,11 +1564,20 @@ st.markdown(f"""
1564
  </div>
1565
  """, unsafe_allow_html=True)
1566
 
1567
- st.markdown('<h4 style="text-align:center; margin:15px 0 5px 0; font-weight:bold;">RECOMMENDATION</h4>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
1568
  st.markdown(f"""
1569
  <div class="insight-box">
1570
  <div class="content" style="text-align:left;">
1571
- {action_text}
1572
  </div>
1573
  </div>
1574
  """, unsafe_allow_html=True)
 
308
 
309
  # ================= HEADER =================
310
  st.markdown("""
311
+ <div style="text-align:center; font-family:Arial, sans-serif; margin-bottom:16px;">
312
+ <h1 style="color:#154D9C; font-weight:bold; margin:0;">
313
+ Tyre Pressure Monitoring System (TPMS) Analytics for Mining Equipments
314
+ </h1>
315
+ <p style="font-size:12px; color:#7d7d7d; margin:4px 0 2px 0;">
316
  Daily trend insights derived from 13–16 December 2023 data
317
  </p>
318
+ <p style="font-size:10px; color:#7d7d7d; margin:2px 0 0 0;">
319
+ &copy; 2025 Bukit Technology Digital. All rights reserved.
320
  </p>
 
321
  </div>
322
  """, unsafe_allow_html=True)
323
 
 
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
1473
+ dominant_percentage = 0.0
1474
  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:
 
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)."
1518
  )
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 speedtemperature 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."
1530
+ )
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."
1537
+ )
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">
1561
  <div class="content" style="text-align:left;">
 
1564
  </div>
1565
  """, unsafe_allow_html=True)
1566
 
1567
+ st.markdown('<h4 style="text-align:center; margin:15px 0 5px 0; font-weight:bold;">SUMMARY</h4>', unsafe_allow_html=True)
1568
+ st.markdown(f"""
1569
+ <div class="insight-box">
1570
+ <div class="content" style="text-align:left;">
1571
+ {summary_text}
1572
+ </div>
1573
+ </div>
1574
+ """, unsafe_allow_html=True)
1575
+
1576
+ st.markdown('<h4 style="text-align:center; margin:15px 0 5px 0; font-weight:bold;">RECOMMENDATION & RISK MITIGATION</h4>', unsafe_allow_html=True)
1577
  st.markdown(f"""
1578
  <div class="insight-box">
1579
  <div class="content" style="text-align:left;">
1580
+ {recommendation_text}
1581
  </div>
1582
  </div>
1583
  """, unsafe_allow_html=True)