SHELLAPANDIANGANHUNGING commited on
Commit
b985caa
Β·
verified Β·
1 Parent(s): 65db3aa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -56
app.py CHANGED
@@ -723,9 +723,9 @@ def generate_objective2_insights(df: pd.DataFrame) -> str:
723
 
724
  # === RENDER INSIGHT BOX ===
725
  # =============== INSIGHT 2 (Ringkas & Fokus ke Red & Amber) ===============
726
- # =============== INSIGHT 3 (Objective 3: Red & Amber Alarm Patterns by Time & Position) ===============
727
 
728
- # Pastikan alarm_data ada dan punya kolom wajib
729
  required_cols = {'Alarm Status', 'hour', 'Position'}
730
  if not required_cols.issubset(alarm_data.columns) or alarm_data.empty:
731
  insight_lines = [
@@ -733,7 +733,7 @@ if not required_cols.issubset(alarm_data.columns) or alarm_data.empty:
733
  "β€’ Ensure dataset includes 'Alarm Status', 'hour', and 'Position' columns."
734
  ]
735
  else:
736
- # Filter Red & Amber β€” case-insensitive, aman terhadap NaN
737
  mask_red = alarm_data['Alarm Status'].str.contains(r'\bRed\b', case=False, na=False)
738
  mask_amber = alarm_data['Alarm Status'].str.contains(r'\bAmber\b', case=False, na=False)
739
  red_amber_data = alarm_data[mask_red | mask_amber].copy()
@@ -743,82 +743,68 @@ else:
743
  else:
744
  insight_lines = []
745
 
746
- # Helper: count alarms per (position, hour, type)
747
- # β€” tanpa def, langsung vectorized
748
  red_amber_data['is_red'] = mask_red.loc[red_amber_data.index]
749
  red_amber_data['is_amber'] = mask_amber.loc[red_amber_data.index]
750
 
751
- # Loop position 1–4
752
  for pos in [1, 2, 3, 4]:
753
- # Filter by position
754
  pos_data = red_amber_data[red_amber_data['Position'] == pos]
755
  if pos_data.empty:
756
  continue
757
 
758
- # Band 1: 12:00–18:00 (12 ≀ hour ≀ 17)
759
  band1 = pos_data[(pos_data['hour'] >= 12) & (pos_data['hour'] <= 17)]
760
- # Band 2: 18:00–00:00 (18–23 atau 0–5)
761
  band2 = pos_data[(pos_data['hour'] >= 18) | (pos_data['hour'] <= 5)]
762
 
763
- # β€”β€”β€”β€”β€” Band 1: 12:00–18:00 β€”β€”β€”β€”β€”
764
  if not band1.empty:
765
- # Red peak hour in band1
766
- red_band1 = band1[band1['is_red']]
767
- if not red_band1.empty:
768
- hour_counts = red_band1['hour'].value_counts()
769
- peak_h = int(hour_counts.idxmax())
770
- count = int(hour_counts.max())
771
- insight_lines.append(f"β€’ Position {pos}, 12:00–18:00: Peak Red alarm at {peak_h:02d}:00 ({count} occurrences).")
772
-
773
- # Amber peak hour in band1
774
- amber_band1 = band1[band1['is_amber']]
775
- if not amber_band1.empty:
776
- hour_counts = amber_band1['hour'].value_counts()
777
- peak_h = int(hour_counts.idxmax())
778
- count = int(hour_counts.max())
779
- insight_lines.append(f"β€’ Position {pos}, 12:00–18:00: Peak Amber alarm at {peak_h:02d}:00 ({count} occurrences).")
780
-
781
- # β€”β€”β€”β€”β€” Band 2: 18:00–00:00 β€”β€”β€”β€”β€”
782
  if not band2.empty:
783
- red_band2 = band2[band2['is_red']]
784
- if not red_band2.empty:
785
- hour_counts = red_band2['hour'].value_counts()
786
- peak_h = int(hour_counts.idxmax())
787
- count = int(hour_counts.max())
788
- insight_lines.append(f"β€’ Position {pos}, 18:00–00:00: Peak Red alarm at {peak_h:02d}:00 ({count} occurrences).")
789
-
790
- amber_band2 = band2[band2['is_amber']]
791
- if not amber_band2.empty:
792
- hour_counts = amber_band2['hour'].value_counts()
793
- peak_h = int(hour_counts.idxmax())
794
- count = int(hour_counts.max())
795
- insight_lines.append(f"β€’ Position {pos}, 18:00–00:00: Peak Amber alarm at {peak_h:02d}:00 ({count} occurrences).")
796
-
797
- # Jika insight_lines masih kosong (misal: semua alarm di pos 5+), tambahkan fallback
798
  if not insight_lines:
799
  insight_lines = ["β€’ Red and Amber alarms occur outside standard wheel positions (1–4)."]
800
 
801
- # =============== TAMBAHKAN INSIGHT NARATIF (SESUAI YANG ANDA TULISKAN) ===============
802
- # Catatan: Ini bukan hasil komputasi, tapi insight profesional berdasarkan pola umum.
803
- # Disesuaikan dengan preferensi: business-ready, actionable, tanpa spekulasi root cause.
804
-
805
- # Cek apakah ada front tyre (pos 1/2) dan rear tyre (pos 3/4) Red/Amber
806
  has_front_red = not red_amber_data[(red_amber_data['Position'].isin([1, 2])) & (red_amber_data['is_red'])].empty
807
  has_rear_amber = not red_amber_data[(red_amber_data['Position'].isin([3, 4])) & (red_amber_data['is_amber'])].empty
808
 
809
  if has_front_red or has_rear_amber:
810
- # Tambahkan insight berbasis pola operasional (hard-coded, validasi berdasarkan pengalaman lapangan)
811
- insight_lines.append("")
812
- insight_lines.append("Front tyres:")
813
- insight_lines.append("1. The front tyre pressure is high, close to threshold, with a small number of Red alarms. However, as operating time increases, the number of Red alarms rises significantly. Furthermore, during 04:00–06:00, operational slowdown likely occurs β€” leading to a decrease in Red alarm notifications.")
814
-
815
- insight_lines.append("")
816
- insight_lines.append("Rear tyres:")
817
- insight_lines.append("2. When the unit begins operation, notifications often indicate pressure below the minimum threshold due to initially low rear tyre pressure. As a result, Amber alarms do not occur during operation since pressure remains within the defined threshold range.")
818
 
819
- # =============== DISPLAY ===============
820
  insight_text = "\n".join(insight_lines)
821
 
 
 
822
  st.markdown(f"""
823
  <div class="insight-box">
824
  <div class="content" style="
 
723
 
724
  # === RENDER INSIGHT BOX ===
725
  # =============== INSIGHT 2 (Ringkas & Fokus ke Red & Amber) ===============
726
+ # =============== INSIGHT 3 (Rata kiri, sesuai preferensi Aning) ===============
727
 
728
+ # β€”β€”β€”β€”β€” Validasi data β€”β€”β€”β€”β€”
729
  required_cols = {'Alarm Status', 'hour', 'Position'}
730
  if not required_cols.issubset(alarm_data.columns) or alarm_data.empty:
731
  insight_lines = [
 
733
  "β€’ Ensure dataset includes 'Alarm Status', 'hour', and 'Position' columns."
734
  ]
735
  else:
736
+ # Filter Red & Amber β€” case-insensitive, robust terhadap NaN
737
  mask_red = alarm_data['Alarm Status'].str.contains(r'\bRed\b', case=False, na=False)
738
  mask_amber = alarm_data['Alarm Status'].str.contains(r'\bAmber\b', case=False, na=False)
739
  red_amber_data = alarm_data[mask_red | mask_amber].copy()
 
743
  else:
744
  insight_lines = []
745
 
746
+ # Tambahkan flag
 
747
  red_amber_data['is_red'] = mask_red.loc[red_amber_data.index]
748
  red_amber_data['is_amber'] = mask_amber.loc[red_amber_data.index]
749
 
750
+ # Loop tiap posisi roda (1–4)
751
  for pos in [1, 2, 3, 4]:
 
752
  pos_data = red_amber_data[red_amber_data['Position'] == pos]
753
  if pos_data.empty:
754
  continue
755
 
756
+ # Band 1: 12:00–18:00 (siang/sore)
757
  band1 = pos_data[(pos_data['hour'] >= 12) & (pos_data['hour'] <= 17)]
758
+ # Band 2: 18:00–00:00 (sore/malam/dini hari)
759
  band2 = pos_data[(pos_data['hour'] >= 18) | (pos_data['hour'] <= 5)]
760
 
761
+ # β€” Band 1 β€”
762
  if not band1.empty:
763
+ red_sub = band1[band1['is_red']]
764
+ if not red_sub.empty:
765
+ h_peak = int(red_sub['hour'].value_counts().idxmax())
766
+ c_peak = int(red_sub['hour'].value_counts().max())
767
+ insight_lines.append(f"β€’ Position {pos}, 12:00–18:00: Peak Red alarm at {h_peak:02d}:00 ({c_peak} occurrences).")
768
+
769
+ amber_sub = band1[band1['is_amber']]
770
+ if not amber_sub.empty:
771
+ h_peak = int(amber_sub['hour'].value_counts().idxmax())
772
+ c_peak = int(amber_sub['hour'].value_counts().max())
773
+ insight_lines.append(f"β€’ Position {pos}, 12:00–18:00: Peak Amber alarm at {h_peak:02d}:00 ({c_peak} occurrences).")
774
+
775
+ # β€” Band 2 β€”
 
 
 
 
776
  if not band2.empty:
777
+ red_sub = band2[band2['is_red']]
778
+ if not red_sub.empty:
779
+ h_peak = int(red_sub['hour'].value_counts().idxmax())
780
+ c_peak = int(red_sub['hour'].value_counts().max())
781
+ insight_lines.append(f"β€’ Position {pos}, 18:00–00:00: Peak Red alarm at {h_peak:02d}:00 ({c_peak} occurrences).")
782
+
783
+ amber_sub = band2[band2['is_amber']]
784
+ if not amber_sub.empty:
785
+ h_peak = int(amber_sub['hour'].value_counts().idxmax())
786
+ c_peak = int(amber_sub['hour'].value_counts().max())
787
+ insight_lines.append(f"β€’ Position {pos}, 18:00–00:00: Peak Amber alarm at {h_peak:02d}:00 ({c_peak} occurrences).")
788
+
789
+ # Jika tidak ada alarm di posisi 1–4
 
 
790
  if not insight_lines:
791
  insight_lines = ["β€’ Red and Amber alarms occur outside standard wheel positions (1–4)."]
792
 
793
+ # β€”β€”β€”β€”β€” Tambahkan insight naratif (sesuai contoh Anda) β€”β€”β€”β€”β€”
 
 
 
 
794
  has_front_red = not red_amber_data[(red_amber_data['Position'].isin([1, 2])) & (red_amber_data['is_red'])].empty
795
  has_rear_amber = not red_amber_data[(red_amber_data['Position'].isin([3, 4])) & (red_amber_data['is_amber'])].empty
796
 
797
  if has_front_red or has_rear_amber:
798
+ insight_lines.extend([
799
+ "β€’ The front tyre pressure is high, close to threshold, with a small number of Red alarms. However, as operating time increases, the number of Red alarms rises significantly. Furthermore, during 04:00–06:00, operational slowdown likely occurs β€” leading to a decrease in Red alarm notifications.",
800
+ "β€’ When the unit begins operation, notifications often indicate pressure below the minimum threshold due to initially low rear tyre pressure. As a result, Amber alarms do not occur during operation since pressure remains within the defined threshold range."
801
+ ])
 
 
 
 
802
 
803
+ # Gabungkan jadi satu string, tiap baris baru β†’ \n
804
  insight_text = "\n".join(insight_lines)
805
 
806
+ # β€”β€”β€”β€”β€” TAMPILKAN (RATA KIRI) β€”β€”β€”β€”β€”
807
+ st.markdown('<h4 style="text-align:center; margin:10px 0 5px 0; font-weight:bold;">INSIGHTS</h4>', unsafe_allow_html=True)
808
  st.markdown(f"""
809
  <div class="insight-box">
810
  <div class="content" style="