SHELLAPANDIANGANHUNGING commited on
Commit
85eab2b
Β·
verified Β·
1 Parent(s): 9f04e44

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -169
app.py CHANGED
@@ -197,19 +197,20 @@ def load_data():
197
 
198
  df = load_data()
199
 
 
200
  # ================= HEADER β€” FIX FINAL (LOGO PASTI MUNCUL) =================
201
  with st.container():
202
  col1, col2, col3 = st.columns([1.2, 6, 1.2])
203
 
204
  # LEFT β€” Michelin Logo
205
  with col1:
206
- if os.path.isfile("logo.png"):
207
- st.image("logo.png", use_container_width=True)
 
 
 
208
  else:
209
- st.markdown(
210
- "<h4 style='color:#003A8F; text-align:center;'>Michelin</h4>",
211
- unsafe_allow_html=True
212
- )
213
 
214
  # CENTER β€” Title
215
  with col2:
@@ -226,14 +227,13 @@ with st.container():
226
 
227
  # RIGHT β€” BTech Logo
228
  with col3:
229
- if os.path.isfile("btech.png"):
230
- st.image("btech.png", use_container_width=True)
 
 
 
231
  else:
232
- st.markdown(
233
- "<h4 style='color:#7d7d7d; text-align:center;'>BTech</h4>",
234
- unsafe_allow_html=True
235
- )
236
-
237
  # # ================= HEADER =================
238
  # st.markdown("""
239
  # <div class="main-header" style="text-align:center;">
@@ -248,45 +248,45 @@ with st.container():
248
 
249
  # ================= LOGO (Perbaikan: Base64 Embed - Selalu Muncul) =================
250
  # ================= LOGO (Pakai File Lokal - Selalu Muncul di Pojok Kanan Atas) =================
251
- import os
252
- import streamlit as st
253
-
254
- # ================= PATH AMAN (WAJIB) =================
255
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
256
 
257
- LOGO_MICHELIN = os.path.join(BASE_DIR, "logo.png")
258
- LOGO_BTECH = os.path.join(BASE_DIR, "btech.png")
259
 
260
- # ================= HEADER =================
261
- with st.container():
262
- col1, col2, col3 = st.columns([1.5, 6, 1.5])
263
 
264
- # LEFT β€” Michelin Logo
265
- with col1:
266
- if os.path.exists(LOGO_MICHELIN):
267
- st.image(LOGO_MICHELIN, width=140)
268
- else:
269
- st.markdown("**Michelin**")
270
-
271
- # CENTER β€” Title
272
- with col2:
273
- st.markdown("""
274
- <div style="text-align:center;">
275
- <h1 style="margin:0; color:#154D9C; font-weight:800;">
276
- Tyre Pressure Monitoring System (TPMS) Analytics for Mining Equipments
277
- </h1>
278
- <p style="font-size:12px; color:#7d7d7d; margin-top:6px;">
279
- Daily trend insights derived from 13–16 December 2023 data
280
- </p>
281
- </div>
282
- """, unsafe_allow_html=True)
283
-
284
- # RIGHT β€” BTech Logo
285
- with col3:
286
- if os.path.exists(LOGO_BTECH):
287
- st.image(LOGO_BTECH, width=140)
288
- else:
289
- st.markdown("**BTech**")
 
 
 
 
290
 
291
  # ================= SIDEBAR FILTERS =================
292
  with st.sidebar:
@@ -1382,118 +1382,118 @@ import numpy as np
1382
  st.markdown('<h3 class="objective-title">OBJECTIVE 6: Insights & Mitigation β€” How Can Red Pressure Alarms Be Reduced?</h3>', unsafe_allow_html=True)
1383
 
1384
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” DATA PREP (HARD-CODED, NO DEF) β€”β€”β€”β€”β€”οΏ½οΏ½β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1385
- # Cek apakah kolom 'Alarm Type' ada
1386
- has_alarm_type = 'Alarm Type' in dff.columns
1387
-
1388
- # Front tyre stats β€” fallback ke "β€”" jika NaN/empty/error
1389
- try:
1390
- front_pressure_avg = dff[dff['Position'].isin([1, 2])]['Pressure (psi)'].mean()
1391
- front_temp_avg = dff[dff['Position'].isin([1, 2])]['Temperature (Β°C)'].mean()
1392
- except Exception:
1393
- front_pressure_avg = np.nan
1394
- front_temp_avg = np.nan
1395
-
1396
- front_pressure_avg_str = f"{front_pressure_avg:.1f}" if pd.notna(front_pressure_avg) else "β€”"
1397
- front_temp_avg_str = f"{front_temp_avg:.1f}" if pd.notna(front_temp_avg) else "β€”"
1398
-
1399
- # Alarm filtering β€” hanya Red & Amber, jika 'Alarm Type' tidak ada β†’ anggap 0 alarm
1400
- if has_alarm_type:
1401
- alarm_mask = dff['Alarm Type'].isin(['Red', 'Amber'])
1402
- alarm_df = dff[alarm_mask].copy()
1403
- else:
1404
- alarm_df = dff.iloc[0:0] # empty DataFrame
1405
-
1406
- 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))
1407
- total_alarms = hourly_counts.sum()
1408
-
1409
- dominant_hour = None
1410
- dominant_percentage = 0.0
1411
- if total_alarms > 0 and not hourly_counts.empty:
1412
- dominant_hour = int(hourly_counts.idxmax())
1413
- dominant_percentage = (hourly_counts[dominant_hour] / total_alarms) * 100
1414
-
1415
- # Zone alarm stats
1416
- if 'Zone' in alarm_df.columns and not alarm_df.empty:
1417
- zone_counts = alarm_df['Zone'].value_counts()
1418
- if not zone_counts.empty:
1419
- top_zone = str(zone_counts.index[0])
1420
- top_zone_percentage = (zone_counts.iloc[0] / total_alarms) * 100 if total_alarms > 0 else 0.0
1421
- else:
1422
- top_zone = "β€”"
1423
- top_zone_percentage = 0.0
1424
- else:
1425
- top_zone = "β€”"
1426
- top_zone_percentage = 0.0
1427
-
1428
- # Korelasi β€” aman tanpa fungsi
1429
- corr_pressure_temp_front = 0.0
1430
- corr_speed_temp_rear = 0.0
1431
-
1432
- try:
1433
- front_df = dff[dff['Position'].isin([1, 2])]
1434
- rear_df = dff[dff['Position'].isin([3, 4])]
1435
-
1436
- # Pressure–temp (front)
1437
- x1, y1 = front_df['Pressure (psi)'], front_df['Temperature (Β°C)']
1438
- valid1 = x1.notna() & y1.notna()
1439
- if valid1.sum() >= 2:
1440
- c1 = np.corrcoef(x1[valid1], y1[valid1])[0, 1]
1441
- corr_pressure_temp_front = c1 if np.isfinite(c1) else 0.0
1442
-
1443
- # Speed–temp (rear)
1444
- x2, y2 = rear_df['Speed (km/h)'], rear_df['Temperature (Β°C)']
1445
- valid2 = x2.notna() & y2.notna()
1446
- if valid2.sum() >= 2:
1447
- c2 = np.corrcoef(x2[valid2], y2[valid2])[0, 1]
1448
- corr_speed_temp_rear = c2 if np.isfinite(c2) else 0.0
1449
- except Exception:
1450
- pass # tetap 0.0 jika error
1451
-
1452
- # Format Β±0.xx, hindari -0.00
1453
- corr_pressure_temp_front_str = f"{corr_pressure_temp_front:+.2f}".replace("-0.00", "0.00")
1454
- corr_speed_temp_rear_str = f"{corr_speed_temp_rear:+.2f}".replace("-0.00", "0.00")
1455
-
1456
- # Position 1 alarm count
1457
- pos1_alarm_count = alarm_df[alarm_df['Position'] == 1].shape[0] if 'Position' in alarm_df.columns else 0
1458
-
1459
- # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” INSIGHTS β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1460
- insight_lines = []
1461
-
1462
- # 1. Front tyre avg
1463
- line1 = f"1. Front tyres (Pos 1 & 2): avg pressure {front_pressure_avg_str} psi, avg temperature {front_temp_avg_str}Β°C."
1464
- if pd.notna(front_pressure_avg) and front_pressure_avg > 115:
1465
- line1 += " Exceeds ideal inflation range (100–110 psi), indicating over-pressure risk."
1466
- insight_lines.append(line1)
1467
-
1468
- # 2. Dominant hour
1469
- if dominant_hour is not None:
1470
- insight_lines.append(
1471
- f"2. Peak Red/Amber alarm concentration at {dominant_hour:02d}:00 ({dominant_percentage:.1f}% of total)."
1472
- )
1473
- else:
1474
- insight_lines.append("2. No statistically dominant hourly alarm peak.")
1475
-
1476
- # 3. Correlations
1477
- insight_lines.append(
1478
- f"3. Front pressure–temperature correlation: r = {corr_pressure_temp_front_str}; "
1479
- f"Rear speed–temperature correlation: r = {corr_speed_temp_rear_str}."
1480
- )
1481
-
1482
- # 4. Top zone
1483
- if top_zone != "β€”" and top_zone_percentage >= 10.0:
1484
- insight_lines.append(
1485
- f"4. Zone {top_zone} accounts for {top_zone_percentage:.1f}% of alarms β€” highest-risk location."
1486
- )
1487
- else:
1488
- insight_lines.append("4. Alarm distribution appears relatively uniform across zones.")
1489
-
1490
- # 5. Position 1 alarms
1491
- if pos1_alarm_count > 0:
1492
- insight_lines.append(
1493
- f"5. Position 1 recorded {pos1_alarm_count} Red/Amber alarms β€” highest of any wheel position."
1494
- )
1495
-
1496
- insight_text = "<br>".join(insight_lines)
1497
 
1498
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” SUMMARY (HARD-CODED) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1499
  summary_text = """\
@@ -1512,14 +1512,14 @@ recommendation_text = """\
1512
  """
1513
 
1514
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” RENDER β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1515
- st.markdown('<h4 style="text-align:center; margin:10px 0 5px 0; font-weight:bold;">INSIGHTS</h4>', unsafe_allow_html=True)
1516
- st.markdown(f"""
1517
- <div class="insight-box">
1518
- <div class="content" style="text-align:left;">
1519
- {insight_text}
1520
- </div>
1521
- </div>
1522
- """, unsafe_allow_html=True)
1523
 
1524
  st.markdown('<h4 style="text-align:center; margin:15px 0 5px 0; font-weight:bold;">SUMMARY</h4>', unsafe_allow_html=True)
1525
  st.markdown(f"""
 
197
 
198
  df = load_data()
199
 
200
+ # ================= HEADER β€” FIX FINAL (LOGO PASTI MUNCUL) =================
201
  # ================= HEADER β€” FIX FINAL (LOGO PASTI MUNCUL) =================
202
  with st.container():
203
  col1, col2, col3 = st.columns([1.2, 6, 1.2])
204
 
205
  # LEFT β€” Michelin Logo
206
  with col1:
207
+ if os.path.exists("logo.png"):
208
+ try:
209
+ st.image("logo.png", use_container_width=True)
210
+ except Exception as e:
211
+ st.markdown("<h4 style='color:#003A8F; text-align:center;'>Michelin</h4>", unsafe_allow_html=True)
212
  else:
213
+ st.markdown("<h4 style='color:#003A8F; text-align:center;'>Michelin</h4>", unsafe_allow_html=True)
 
 
 
214
 
215
  # CENTER β€” Title
216
  with col2:
 
227
 
228
  # RIGHT β€” BTech Logo
229
  with col3:
230
+ if os.path.exists("btech.png"):
231
+ try:
232
+ st.image("btech.png", use_container_width=True)
233
+ except Exception as e:
234
+ st.markdown("<h4 style='color:#7d7d7d; text-align:center;'>BTech</h4>", unsafe_allow_html=True)
235
  else:
236
+ st.markdown("<h4 style='color:#7d7d7d; text-align:center;'>BTech</h4>", unsafe_allow_html=True)
 
 
 
 
237
  # # ================= HEADER =================
238
  # st.markdown("""
239
  # <div class="main-header" style="text-align:center;">
 
248
 
249
  # ================= LOGO (Perbaikan: Base64 Embed - Selalu Muncul) =================
250
  # ================= LOGO (Pakai File Lokal - Selalu Muncul di Pojok Kanan Atas) =================
251
+ # import os
252
+ # import streamlit as st
 
 
 
253
 
254
+ # # ================= PATH AMAN (WAJIB) =================
255
+ # BASE_DIR = os.path.dirname(os.path.abspath(__file__))
256
 
257
+ # LOGO_MICHELIN = os.path.join(BASE_DIR, "logo.png")
258
+ # LOGO_BTECH = os.path.join(BASE_DIR, "btech.png")
 
259
 
260
+ # # ================= HEADER =================
261
+ # with st.container():
262
+ # col1, col2, col3 = st.columns([1.5, 6, 1.5])
263
+
264
+ # # LEFT β€” Michelin Logo
265
+ # with col1:
266
+ # if os.path.exists(LOGO_MICHELIN):
267
+ # st.image(LOGO_MICHELIN, width=140)
268
+ # else:
269
+ # st.markdown("**Michelin**")
270
+
271
+ # # CENTER β€” Title
272
+ # with col2:
273
+ # st.markdown("""
274
+ # <div style="text-align:center;">
275
+ # <h1 style="margin:0; color:#154D9C; font-weight:800;">
276
+ # Tyre Pressure Monitoring System (TPMS) Analytics for Mining Equipments
277
+ # </h1>
278
+ # <p style="font-size:12px; color:#7d7d7d; margin-top:6px;">
279
+ # Daily trend insights derived from 13–16 December 2023 data
280
+ # </p>
281
+ # </div>
282
+ # """, unsafe_allow_html=True)
283
+
284
+ # # RIGHT β€” BTech Logo
285
+ # with col3:
286
+ # if os.path.exists(LOGO_BTECH):
287
+ # st.image(LOGO_BTECH, width=140)
288
+ # else:
289
+ # st.markdown("**BTech**")
290
 
291
  # ================= SIDEBAR FILTERS =================
292
  with st.sidebar:
 
1382
  st.markdown('<h3 class="objective-title">OBJECTIVE 6: Insights & Mitigation β€” How Can Red Pressure Alarms Be Reduced?</h3>', unsafe_allow_html=True)
1383
 
1384
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” DATA PREP (HARD-CODED, NO DEF) β€”β€”β€”β€”β€”οΏ½οΏ½β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1385
+ # # Cek apakah kolom 'Alarm Type' ada
1386
+ # has_alarm_type = 'Alarm Type' in dff.columns
1387
+
1388
+ # # Front tyre stats β€” fallback ke "β€”" jika NaN/empty/error
1389
+ # try:
1390
+ # front_pressure_avg = dff[dff['Position'].isin([1, 2])]['Pressure (psi)'].mean()
1391
+ # front_temp_avg = dff[dff['Position'].isin([1, 2])]['Temperature (Β°C)'].mean()
1392
+ # except Exception:
1393
+ # front_pressure_avg = np.nan
1394
+ # front_temp_avg = np.nan
1395
+
1396
+ # front_pressure_avg_str = f"{front_pressure_avg:.1f}" if pd.notna(front_pressure_avg) else "β€”"
1397
+ # front_temp_avg_str = f"{front_temp_avg:.1f}" if pd.notna(front_temp_avg) else "β€”"
1398
+
1399
+ # # Alarm filtering β€” hanya Red & Amber, jika 'Alarm Type' tidak ada β†’ anggap 0 alarm
1400
+ # if has_alarm_type:
1401
+ # alarm_mask = dff['Alarm Type'].isin(['Red', 'Amber'])
1402
+ # alarm_df = dff[alarm_mask].copy()
1403
+ # else:
1404
+ # alarm_df = dff.iloc[0:0] # empty DataFrame
1405
+
1406
+ # 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))
1407
+ # total_alarms = hourly_counts.sum()
1408
+
1409
+ # dominant_hour = None
1410
+ # dominant_percentage = 0.0
1411
+ # if total_alarms > 0 and not hourly_counts.empty:
1412
+ # dominant_hour = int(hourly_counts.idxmax())
1413
+ # dominant_percentage = (hourly_counts[dominant_hour] / total_alarms) * 100
1414
+
1415
+ # # Zone alarm stats
1416
+ # if 'Zone' in alarm_df.columns and not alarm_df.empty:
1417
+ # zone_counts = alarm_df['Zone'].value_counts()
1418
+ # if not zone_counts.empty:
1419
+ # top_zone = str(zone_counts.index[0])
1420
+ # top_zone_percentage = (zone_counts.iloc[0] / total_alarms) * 100 if total_alarms > 0 else 0.0
1421
+ # else:
1422
+ # top_zone = "β€”"
1423
+ # top_zone_percentage = 0.0
1424
+ # else:
1425
+ # top_zone = "β€”"
1426
+ # top_zone_percentage = 0.0
1427
+
1428
+ # # Korelasi β€” aman tanpa fungsi
1429
+ # corr_pressure_temp_front = 0.0
1430
+ # corr_speed_temp_rear = 0.0
1431
+
1432
+ # try:
1433
+ # front_df = dff[dff['Position'].isin([1, 2])]
1434
+ # rear_df = dff[dff['Position'].isin([3, 4])]
1435
+
1436
+ # # Pressure–temp (front)
1437
+ # x1, y1 = front_df['Pressure (psi)'], front_df['Temperature (Β°C)']
1438
+ # valid1 = x1.notna() & y1.notna()
1439
+ # if valid1.sum() >= 2:
1440
+ # c1 = np.corrcoef(x1[valid1], y1[valid1])[0, 1]
1441
+ # corr_pressure_temp_front = c1 if np.isfinite(c1) else 0.0
1442
+
1443
+ # # Speed–temp (rear)
1444
+ # x2, y2 = rear_df['Speed (km/h)'], rear_df['Temperature (Β°C)']
1445
+ # valid2 = x2.notna() & y2.notna()
1446
+ # if valid2.sum() >= 2:
1447
+ # c2 = np.corrcoef(x2[valid2], y2[valid2])[0, 1]
1448
+ # corr_speed_temp_rear = c2 if np.isfinite(c2) else 0.0
1449
+ # except Exception:
1450
+ # pass # tetap 0.0 jika error
1451
+
1452
+ # # Format Β±0.xx, hindari -0.00
1453
+ # corr_pressure_temp_front_str = f"{corr_pressure_temp_front:+.2f}".replace("-0.00", "0.00")
1454
+ # corr_speed_temp_rear_str = f"{corr_speed_temp_rear:+.2f}".replace("-0.00", "0.00")
1455
+
1456
+ # # Position 1 alarm count
1457
+ # pos1_alarm_count = alarm_df[alarm_df['Position'] == 1].shape[0] if 'Position' in alarm_df.columns else 0
1458
+
1459
+ # # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” INSIGHTS β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1460
+ # insight_lines = []
1461
+
1462
+ # # 1. Front tyre avg
1463
+ # line1 = f"1. Front tyres (Pos 1 & 2): avg pressure {front_pressure_avg_str} psi, avg temperature {front_temp_avg_str}Β°C."
1464
+ # if pd.notna(front_pressure_avg) and front_pressure_avg > 115:
1465
+ # line1 += " Exceeds ideal inflation range (100–110 psi), indicating over-pressure risk."
1466
+ # insight_lines.append(line1)
1467
+
1468
+ # # 2. Dominant hour
1469
+ # if dominant_hour is not None:
1470
+ # insight_lines.append(
1471
+ # f"2. Peak Red/Amber alarm concentration at {dominant_hour:02d}:00 ({dominant_percentage:.1f}% of total)."
1472
+ # )
1473
+ # else:
1474
+ # insight_lines.append("2. No statistically dominant hourly alarm peak.")
1475
+
1476
+ # # 3. Correlations
1477
+ # insight_lines.append(
1478
+ # f"3. Front pressure–temperature correlation: r = {corr_pressure_temp_front_str}; "
1479
+ # f"Rear speed–temperature correlation: r = {corr_speed_temp_rear_str}."
1480
+ # )
1481
+
1482
+ # # 4. Top zone
1483
+ # if top_zone != "β€”" and top_zone_percentage >= 10.0:
1484
+ # insight_lines.append(
1485
+ # f"4. Zone {top_zone} accounts for {top_zone_percentage:.1f}% of alarms β€” highest-risk location."
1486
+ # )
1487
+ # else:
1488
+ # insight_lines.append("4. Alarm distribution appears relatively uniform across zones.")
1489
+
1490
+ # # 5. Position 1 alarms
1491
+ # if pos1_alarm_count > 0:
1492
+ # insight_lines.append(
1493
+ # f"5. Position 1 recorded {pos1_alarm_count} Red/Amber alarms β€” highest of any wheel position."
1494
+ # )
1495
+
1496
+ # insight_text = "<br>".join(insight_lines)
1497
 
1498
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” SUMMARY (HARD-CODED) β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1499
  summary_text = """\
 
1512
  """
1513
 
1514
  # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€” RENDER β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
1515
+ # st.markdown('<h4 style="text-align:center; margin:10px 0 5px 0; font-weight:bold;">INSIGHTS</h4>', unsafe_allow_html=True)
1516
+ # st.markdown(f"""
1517
+ # <div class="insight-box">
1518
+ # <div class="content" style="text-align:left;">
1519
+ # {insight_text}
1520
+ # </div>
1521
+ # </div>
1522
+ # """, unsafe_allow_html=True)
1523
 
1524
  st.markdown('<h4 style="text-align:center; margin:15px 0 5px 0; font-weight:bold;">SUMMARY</h4>', unsafe_allow_html=True)
1525
  st.markdown(f"""