SHELLAPANDIANGANHUNGING commited on
Commit
1f4e27a
·
verified ·
1 Parent(s): 5356abb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -64
app.py CHANGED
@@ -725,24 +725,24 @@ st.markdown(f"""
725
  # """, unsafe_allow_html=True)
726
  st.markdown("""
727
  <h3 class="objective-title">OBJECTIVE 3: Alarm Frequency Analysis — When, Where, and Which Tyres Matter Most?</h3>
728
- <small>*Showing only Red High Pressure Alarms</small>
729
  """, unsafe_allow_html=True)
730
 
731
- # Filter hanya data alarm
732
- alarm_data = dff[dff['is_alarm'] == 1].copy()
733
 
734
  col_b, col_a = st.columns(2)
735
 
736
- # =============== COL B: Donut Charts (Distribusi Alarm per Position) ===============
737
  with col_b:
738
  st.markdown('<h5 style="text-align:center; margin-top: 0;">Alarm Distribution by Position</h5>', unsafe_allow_html=True)
739
 
740
  if alarm_data.empty:
741
- st.warning("No alarm data to display.")
742
  else:
743
  # Group alarm status
744
  alarm_data['Alarm_Category'] = alarm_data['Alarm Status'].apply(
745
- lambda x: 'No Alarm' if 'No Alarm' in x
746
  else 'Amber Alarm' if 'Amber' in x
747
  else 'Red Alarm'
748
  )
@@ -762,7 +762,7 @@ with col_b:
762
  labels = counts.index.tolist()
763
  values = counts.values.tolist()
764
 
765
- # Warna: Hijau (No), Kuning (Amber), Merah (Red)
766
  colors = ['#2E7D32', '#FFC107', '#D32F2F']
767
 
768
  fig_donut.add_trace(
@@ -791,73 +791,71 @@ with col_b:
791
  )
792
  st.plotly_chart(fig_donut, use_container_width=True)
793
 
794
- # =============== COL A: Radial Charts (Count Alarm per Jam) ===============
795
  with col_a:
796
  st.markdown('<h5 style="text-align:center; margin-top: 0;">Alarm Count by Hour (Radial)</h5>', unsafe_allow_html=True)
797
 
798
  if alarm_data.empty:
799
- st.warning("No alarm data to display.")
800
  else:
801
- # Ambil pressure max untuk warna gradasi
802
- max_pressure = alarm_data['Pressure (psi)'].max()
803
- min_pressure = alarm_data['Pressure (psi)'].min()
804
-
805
  # Buat 4 radial chart
806
  fig_radial = make_subplots(
807
  rows=2, cols=2,
808
  specs=[[{'type': 'polar'}, {'type': 'polar'}],
809
  [{'type': 'polar'}, {'type': 'polar'}]],
810
- subplot_titles=['Front Position 1 (06:00–18:00)', 'Front Position 2 (18:00–06:00)',
811
- 'Rear Position 3 (06:00–18:00)', 'Rear Position 4 (18:00–06:00)']
812
  )
813
 
814
  for i, pos in enumerate([1, 2, 3, 4], 1):
815
  pos_data = alarm_data[alarm_data['Position'] == pos].copy()
816
  if not pos_data.empty:
817
- # Kelompokkan jam berdasarkan periode
818
- if i in [1, 3]: # Pagi–sore
819
- pos_data = pos_data[pos_data['hour'].between(6, 17, inclusive='both')]
820
- else: # Sore–pagi
821
- pos_data = pos_data[~pos_data['hour'].between(6, 17, inclusive='both')]
822
-
823
- if not pos_data.empty:
824
- hourly_counts = pos_data.groupby('hour').size().reindex(range(24), fill_value=0)
825
- if i in [1, 3]: # 06:00–18:00
826
- hourly_counts = hourly_counts[6:18]
827
- else: # 18:00–06:00
828
- hourly_counts = pd.concat([hourly_counts[18:], hourly_counts[:6]]).reindex(range(12), fill_value=0)
829
-
830
- # Warna berdasarkan rata-rata pressure
831
- avg_pressure_per_hour = pos_data.groupby('hour')['Pressure (psi)'].mean().reindex(hourly_counts.index, fill_value=0)
832
- colorscale = avg_pressure_per_hour
833
-
834
- fig_radial.add_trace(
835
- go.Barpolar(
836
- r=hourly_counts.values,
837
- theta=hourly_counts.index * 30, # 12 jam * 30° = 360°
838
- name=f'Position {pos}',
839
- marker=dict(
840
- color=colorscale,
841
- colorscale='Reds',
842
- cmin=min_pressure,
843
- cmax=max_pressure
844
- ),
845
- opacity=0.8
846
- ),
847
- row=(i - 1) // 2 + 1,
848
- col=(i - 1) % 2 + 1
849
- )
850
 
851
  fig_radial.update_layout(
852
  height=600,
853
  showlegend=False,
854
- margin=dict(t=60, b=20, l=20, r=20)
 
 
 
 
 
 
 
855
  )
856
  st.plotly_chart(fig_radial, use_container_width=True)
857
 
858
  # =============== INSIGHT 3 ===============
859
  if alarm_data.empty:
860
- insight_text = "• No alarm data available for analysis."
861
  else:
862
  # Insight tetap sama
863
  alarm_hours = alarm_data['hour']
@@ -879,23 +877,15 @@ else:
879
  dominant_pct = (top_bands.iloc[0] / band_counts.sum() * 100) if len(top_bands) > 0 else 0
880
  second_pct = (top_bands.iloc[1] / band_counts.sum() * 100) if len(top_bands) > 1 else 0
881
 
882
- front_alarms = alarm_data[alarm_data['Position'].isin([1, 2])].shape[0]
883
- rear_alarms = alarm_data[alarm_data['Position'].isin([3, 4])].shape[0]
884
- total_alarms = front_alarms + rear_alarms
885
- front_pct = front_alarms / total_alarms * 100 if total_alarms > 0 else 0
886
-
887
- top_zone = alarm_data['Zone'].value_counts().index[0] if not alarm_data.empty else "N/A"
888
 
889
  insight_lines = [
890
- f"• {dominant_band} is the dominant alarm period ({dominant_pct:.1f}% of all alarms).",
891
- f"• {second_dominant_band} is the second-highest period ({second_pct:.1f}% of alarms)."
 
892
  ]
893
- if front_alarms > 0:
894
- insight_lines.append(f"• Front tyres (Pos 1 & 2) account for {front_pct:.1f}% of all alarms, indicating higher stress or usage intensity upfront.")
895
- if top_zone != "N/A":
896
- insight_lines.append(f"• Zone {top_zone} records the highest alarm frequency across all positions.")
897
- insight_lines.append("• Alarm clustering in specific hours and front positions suggests opportunity for targeted inspection scheduling.")
898
-
899
  insight_text = "\n".join(insight_lines)
900
 
901
  # =============== DISPLAY INSIGHT ===============
 
725
  # """, unsafe_allow_html=True)
726
  st.markdown("""
727
  <h3 class="objective-title">OBJECTIVE 3: Alarm Frequency Analysis — When, Where, and Which Tyres Matter Most?</h3>
728
+ <small>*Showing all alarm types: Normal, Amber, Red</small>
729
  """, unsafe_allow_html=True)
730
 
731
+ # Filter semua data (termasuk alarm normal)
732
+ alarm_data = dff.copy()
733
 
734
  col_b, col_a = st.columns(2)
735
 
736
+ # =============== COL B: Donut Charts (Distribusi Alarm per Position - Semua Jenis) ===============
737
  with col_b:
738
  st.markdown('<h5 style="text-align:center; margin-top: 0;">Alarm Distribution by Position</h5>', unsafe_allow_html=True)
739
 
740
  if alarm_data.empty:
741
+ st.warning("No data to display.")
742
  else:
743
  # Group alarm status
744
  alarm_data['Alarm_Category'] = alarm_data['Alarm Status'].apply(
745
+ lambda x: 'Normal' if 'No Alarm' in x
746
  else 'Amber Alarm' if 'Amber' in x
747
  else 'Red Alarm'
748
  )
 
762
  labels = counts.index.tolist()
763
  values = counts.values.tolist()
764
 
765
+ # Warna: Hijau (Normal), Kuning (Amber), Merah (Red)
766
  colors = ['#2E7D32', '#FFC107', '#D32F2F']
767
 
768
  fig_donut.add_trace(
 
791
  )
792
  st.plotly_chart(fig_donut, use_container_width=True)
793
 
794
+ # =============== COL A: Radial Charts (Count Alarm per Jam - Semua Jenis) ===============
795
  with col_a:
796
  st.markdown('<h5 style="text-align:center; margin-top: 0;">Alarm Count by Hour (Radial)</h5>', unsafe_allow_html=True)
797
 
798
  if alarm_data.empty:
799
+ st.warning("No data to display.")
800
  else:
 
 
 
 
801
  # Buat 4 radial chart
802
  fig_radial = make_subplots(
803
  rows=2, cols=2,
804
  specs=[[{'type': 'polar'}, {'type': 'polar'}],
805
  [{'type': 'polar'}, {'type': 'polar'}]],
806
+ subplot_titles=['Position 1', 'Position 2', 'Position 3', 'Position 4']
 
807
  )
808
 
809
  for i, pos in enumerate([1, 2, 3, 4], 1):
810
  pos_data = alarm_data[alarm_data['Position'] == pos].copy()
811
  if not pos_data.empty:
812
+ # Kelompokkan jam dan alarm
813
+ hourly_counts = pos_data.groupby(['hour', 'Alarm_Category']).size().unstack(fill_value=0)
814
+
815
+ # Ambil total alarm per jam
816
+ total_per_hour = hourly_counts.sum(axis=1).reindex(range(24), fill_value=0)
817
+
818
+ # Warna berdasarkan jenis alarm dominan per jam
819
+ dominant_alarm_per_hour = pos_data.groupby('hour')['Alarm_Category'].apply(
820
+ lambda x: x.mode().iloc[0] if not x.empty else 'Normal'
821
+ ).reindex(range(24), fill_value='Normal')
822
+
823
+ # Map warna
824
+ color_map = {'Normal': '#2E7D32', 'Amber Alarm': '#FFC107', 'Red Alarm': '#D32F2F'}
825
+ colorscale = [color_map.get(cat, '#2E7D32') for cat in dominant_alarm_per_hour]
826
+
827
+ # Sudut: jam 0 → 0° (atas), jam 6 → 90° (kanan), jam 12 → 180° (bawah), jam 18 → 270° (kiri)
828
+ theta = [h * 15 for h in range(24)] # 24 jam * 15° = 360°
829
+
830
+ fig_radial.add_trace(
831
+ go.Barpolar(
832
+ r=total_per_hour.values,
833
+ theta=theta,
834
+ name=f'Position {pos}',
835
+ marker_color=colorscale,
836
+ opacity=0.8
837
+ ),
838
+ row=(i - 1) // 2 + 1,
839
+ col=(i - 1) % 2 + 1
840
+ )
 
 
 
 
841
 
842
  fig_radial.update_layout(
843
  height=600,
844
  showlegend=False,
845
+ margin=dict(t=60, b=20, l=20, r=20),
846
+ polar=dict(
847
+ angularaxis=dict(
848
+ direction="clockwise",
849
+ period=24,
850
+ rotation=90 # Jam 0 di atas
851
+ )
852
+ )
853
  )
854
  st.plotly_chart(fig_radial, use_container_width=True)
855
 
856
  # =============== INSIGHT 3 ===============
857
  if alarm_data.empty:
858
+ insight_text = "• No data available for analysis."
859
  else:
860
  # Insight tetap sama
861
  alarm_hours = alarm_data['hour']
 
877
  dominant_pct = (top_bands.iloc[0] / band_counts.sum() * 100) if len(top_bands) > 0 else 0
878
  second_pct = (top_bands.iloc[1] / band_counts.sum() * 100) if len(top_bands) > 1 else 0
879
 
880
+ normal_alarms = alarm_data[alarm_data['Alarm_Status'] == 'No Alarm'].shape[0] if 'Alarm_Status' in alarm_data.columns else 0
881
+ amber_alarms = alarm_data[alarm_data['Alarm_Status'].str.contains('Amber', na=False)].shape[0] if 'Alarm_Status' in alarm_data.columns else 0
882
+ red_alarms = alarm_data[alarm_data['Alarm_Status'].str.contains('Red', na=False)].shape[0] if 'Alarm_Status' in alarm_data.columns else 0
 
 
 
883
 
884
  insight_lines = [
885
+ f"• {dominant_band} is the dominant period ({dominant_pct:.1f}% of all data).",
886
+ f"• {second_dominant_band} is the second-highest period ({second_pct:.1f}% of data).",
887
+ f"• Total: Normal={normal_alarms}, Amber={amber_alarms}, Red={red_alarms}"
888
  ]
 
 
 
 
 
 
889
  insight_text = "\n".join(insight_lines)
890
 
891
  # =============== DISPLAY INSIGHT ===============