tututz commited on
Commit
5735bd9
Β·
verified Β·
1 Parent(s): 1dc34bc

Update explanation_builder.py

Browse files
Files changed (1) hide show
  1. explanation_builder.py +142 -48
explanation_builder.py CHANGED
@@ -1,96 +1,179 @@
 
 
 
 
1
  import random
2
  from typing import List, Dict, Any
3
 
4
  # ======================================================================
5
- # 1. "BANK TEMPLATE"
6
  # ======================================================================
7
  EXPLANATION_TEMPLATES = {
8
  "pembuka": {
9
- "Resiko Tinggi": "Saat ini profil Anda termasuk dalam kategori 'Risiko Tinggi'. Beberapa faktor utama teridentifikasi memberikan pengaruh besar terhadap hasil ini:",
10
- "Resiko Sedang": "Profil Anda berada pada kategori 'Risiko Sedang'. Berikut beberapa hal yang menjadi pertimbangan utama dari hasil penilaian:",
11
- "Resiko Rendah": "Profil Anda tergolong 'Risiko Rendah'. Secara umum hasil ini cukup baik, walau ada beberapa hal yang masih bisa ditingkatkan:",
12
- "Aman": "Profil Anda diprediksi 'Aman', menunjukkan performa akademik yang stabil dan konsisten sejauh ini. Berikut beberapa faktor pendukungnya:",
13
- "default": "Berikut adalah faktor-faktor yang menjadi pertimbangan sistem untuk kategori '{prediction_val}':"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  },
15
  "fitur": {
 
16
  "IPS_Terakhir": {
17
  "rendah": [
18
- "Nilai IPS di semester terakhir masih sedikit di bawah target dan ikut memengaruhi hasil prediksi.",
19
- "Performa di semester terakhir belum sepenuhnya optimal sehingga menjadi salah satu faktor pertimbangan."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  ],
21
  "tinggi": [
22
- "IPS di semester terakhir menunjukkan hasil yang baik dan menggambarkan peningkatan performa.",
23
- "Semester terakhir Anda berjalan dengan cukup baik, menjadi sinyal positif dalam penilaian ini."
 
24
  ]
25
  },
 
 
26
  "Jumlah_MK_Gagal": {
27
  "rendah": [
28
- "Jumlah mata kuliah gagal yang sangat sedikit (atau bahkan tidak ada) menjadi poin positif penting.",
29
- "Anda memiliki sedikit atau tidak ada MK gagal β€” hal ini menunjukkan konsistensi belajar yang bagus."
 
30
  ],
31
  "tinggi": [
32
- "Beberapa mata kuliah belum lulus dan menjadi salah satu faktor yang meningkatkan risiko.",
33
- "Terdeteksi adanya MK gagal di atas batas wajar, yang memberi pengaruh cukup besar terhadap hasil ini."
 
34
  ]
35
  },
36
- "IPK_Terakhir": {
 
 
 
 
 
 
 
 
 
 
 
 
37
  "rendah": [
38
- "IPK kumulatif Anda saat ini masih bisa ditingkatkan agar hasil akademik lebih stabil.",
39
- "Nilai IPK terakhir sedikit di bawah kisaran ideal dan menjadi salah satu pertimbangan sistem."
40
  ],
41
  "tinggi": [
42
- "IPK kumulatif Anda sudah tergolong baik dan menjadi dasar yang kuat untuk hasil prediksi ini.",
43
- "Nilai IPK Anda berada di kisaran aman dan mendukung hasil yang positif."
44
  ]
45
  },
 
 
46
  "Tren_IPS_Slope": {
47
  "rendah": [
48
- "Perubahan nilai IPS belum menunjukkan peningkatan yang stabil dari waktu ke waktu.",
49
- "Tren performa Anda masih cenderung datar atau sedikit menurun, sehingga perlu perhatian lebih."
50
  ],
51
  "tinggi": [
52
- "Performa akademik Anda menunjukkan tren peningkatan yang konsisten dari waktu ke waktu.",
53
- "Tren IPS yang meningkat menandakan adanya perkembangan positif dalam performa akademik Anda."
54
  ]
55
  },
 
 
 
 
 
 
 
 
 
 
 
 
56
  "Tren_Menaik": {
57
- "ya": "Performa Anda menunjukkan tren yang meningkat β€” ini pertanda sangat baik.",
58
- "tidak": "Belum terlihat pola peningkatan yang konsisten sejauh ini."
59
  },
60
  "Tren_Menurun": {
61
- "ya": "Terlihat adanya penurunan tren performa, yang sebaiknya segera diperhatikan.",
62
- "tidak": "Tidak ada tanda penurunan performa yang berarti β€” ini hal yang positif."
63
  },
64
  "Tren_Stabil": {
65
- "ya": "Performa akademik Anda cukup stabil selama beberapa semester terakhir.",
66
- "tidak": "Belum terlihat kestabilan nilai yang konsisten dalam beberapa periode terakhir."
67
  },
 
 
68
  "default": {
69
- "rendah": "Nilai {feature_name} berada di bawah kisaran aman ({value:.2f} <= {threshold:.2f}).",
70
- "tinggi": "Nilai {feature_name} berada di atas kisaran yang diharapkan ({value:.2f} > {threshold:.2f})."
71
  }
72
  }
73
  }
74
 
 
75
  # ======================================================================
76
- # 2. FUNGSI BUILDER
77
  # ======================================================================
78
 
79
  def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: str) -> Dict[str, Any]:
80
  """
81
- Merakit penjelasan yang dinamis dalam format terstruktur (Dict).
82
  """
83
  try:
84
- # 1. Pilih kalimat pembuka
85
- opening_line = EXPLANATION_TEMPLATES["pembuka"].get(prediction_val, EXPLANATION_TEMPLATES["pembuka"]["default"].format(prediction_val=prediction_val))
 
 
 
 
 
 
 
 
 
 
86
 
87
  explained_rules_list = []
88
- features_explained = set()
89
-
90
- # 2. Iterasi aturan SECARA TERBALIK (dari leaf ke root)
 
91
  for rule in reversed(structured_rules):
92
  feature = rule["feature"]
93
 
 
 
94
  if feature in features_explained:
95
  continue
96
 
@@ -99,20 +182,30 @@ def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: s
99
  condition = rule["condition"]
100
  chosen_template = None
101
 
102
- # Logika untuk Fitur OHE
103
  if feature in ["Tren_Menaik", "Tren_Menurun", "Tren_Stabil"]:
104
  if condition == "tinggi":
105
  chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["ya"]
106
  else:
107
  chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["tidak"]
108
 
109
- # Logika untuk Fitur Normal
110
  else:
 
111
  feature_templates = EXPLANATION_TEMPLATES["fitur"].get(feature, EXPLANATION_TEMPLATES["fitur"]["default"])
 
 
112
  condition_templates = feature_templates.get(condition)
113
 
 
114
  if isinstance(condition_templates, list):
115
- chosen_template = random.choice(condition_templates)
 
 
 
 
 
 
116
  elif isinstance(condition_templates, str):
117
  chosen_template = condition_templates.format(
118
  feature_name=feature,
@@ -123,19 +216,20 @@ def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: s
123
  if chosen_template:
124
  explained_rules_list.append(chosen_template)
125
 
126
- # 3. Balikkan list agar urutannya logis (root ke leaf)
127
- explained_rules_list.reverse()
 
 
 
 
128
 
129
- # 4. Kembalikan sebagai DICT alih-alih string
130
  return {
131
  "opening_line": opening_line,
132
  "factors": explained_rules_list
133
  }
134
 
135
  except Exception as e:
136
- # Kembalikan error dalam format yang sama
137
  return {
138
- "opening_line": f"Gagal membuat penjelasan dinamis: {str(e)}",
139
  "factors": []
140
- }
141
-
 
1
+ # ======================================================================
2
+ # --- explanation_builder.py ---
3
+ # ======================================================================
4
+
5
  import random
6
  from typing import List, Dict, Any
7
 
8
  # ======================================================================
9
+ # 1. BANK TEMPLATE (Natural Language & Emoji)
10
  # ======================================================================
11
  EXPLANATION_TEMPLATES = {
12
  "pembuka": {
13
+ "Resiko Tinggi": [
14
+ "⚠️ **Perhatian Serius Diperlukan:** Sistem mendeteksi indikator risiko tinggi pada profil akademik Anda. Berikut adalah faktor krusial yang memicunya:",
15
+ "🚨 **Peringatan Dini:** Berdasarkan pola data historis, performa Anda saat ini berada dalam zona 'Resiko Tinggi'. Hal ini didorong oleh faktor-faktor berikut:",
16
+ "πŸ›‘ **Analisis Kritis:** Terdapat akumulasi faktor yang menempatkan Anda pada kategori 'Resiko Tinggi'. Mohon perhatikan poin-poin evaluasi ini:"
17
+ ],
18
+ "Resiko Sedang": [
19
+ "⚠️ **Waspada:** Profil Anda menunjukkan tanda-tanda 'Resiko Sedang'. Belum kritis, namun perlu perbaikan segera pada aspek berikut:",
20
+ "πŸ’‘ **Perlu Evaluasi:** Sistem mendeteksi adanya ketidakstabilan yang memicu status 'Resiko Sedang'. Berikut adalah area yang perlu mendapat perhatian:",
21
+ "🚩 **Zona Kuning:** Anda berada di kategori 'Resiko Sedang'. Ada keseimbangan antara faktor positif dan negatif, namun poin berikut perlu diwaspadai:"
22
+ ],
23
+ "Resiko Rendah": [
24
+ "βœ… **Cukup Aman:** Profil akademik Anda tergolong 'Resiko Rendah', namun tetap ada beberapa catatan kecil untuk menjaga konsistensi:",
25
+ "πŸ“ˆ **Progres Baik:** Secara umum performa Anda stabil di zona aman. Sistem hanya menyoroti beberapa hal minor berikut:",
26
+ "πŸ›‘οΈ **Terkendali:** Prediksi risiko Anda rendah. Pertahankan momentum ini, sambil memperhatikan sedikit catatan berikut:"
27
+ ],
28
+ "Aman": [
29
+ "🌟 **Sangat Baik:** Selamat! Rekam jejak akademik Anda sangat solid sehingga dikategorikan 'Aman'. Faktor pendukung utamanya adalah:",
30
+ "πŸš€ **Performa Unggul:** Sistem tidak mendeteksi masalah berarti. Prediksi 'Aman' ini didukung oleh pondasi akademik yang kuat berikut ini:",
31
+ "πŸ† **Top Performance:** Data menunjukkan stabilitas yang sangat baik. Berikut adalah poin-poin kekuatan profil Anda:"
32
+ ],
33
+ "default": "πŸ” Berikut adalah faktor-faktor analisis sistem untuk kategori '{prediction_val}':"
34
  },
35
  "fitur": {
36
+ # --- 1. PERFORMA TERKINI (IPS) ---
37
  "IPS_Terakhir": {
38
  "rendah": [
39
+ "πŸ“‰ **Penurunan Terkini:** IPS semester terakhir Anda tercatat **{value:.2f}**, yang berada di bawah ambang batas ideal.",
40
+ "⚠️ **Warning Semester Lalu:** Performa semester terakhir (**{value:.2f}**) menjadi kontributor utama risiko karena belum memenuhi standar aman.",
41
+ "πŸ”» **Butuh Boost:** Capaian IPS terakhir (**{value:.2f}**) terindikasi rendah, menyeret turun profil risiko Anda secara keseluruhan."
42
+ ],
43
+ "tinggi": [
44
+ "βœ… **Momentum Positif:** IPS semester terakhir Anda (**{value:.2f}**) sangat solid, menunjukkan Anda sedang dalam performa yang baik.",
45
+ "πŸ“ˆ **Finish Kuat:** Capaian semester terakhir yang tinggi (**{value:.2f}**) menjadi sinyal kuat bahwa Anda mampu mengatasi beban studi.",
46
+ "🌟 **Nilai Kompetitif:** IPS terakhir di angka **{value:.2f}** memberikan bobot positif yang signifikan pada prediksi ini."
47
+ ]
48
+ },
49
+
50
+ # --- 2. PERFORMA KUMULATIF (IPK) ---
51
+ "IPK_Terakhir": {
52
+ "rendah": [
53
+ "πŸ—οΈ **Pondasi Rapuh:** IPK kumulatif saat ini (**{value:.2f}**) terdeteksi di zona yang memerlukan perbaikan segera.",
54
+ "πŸ“‰ **Akumulasi Nilai:** Secara keseluruhan, IPK Anda (**{value:.2f}**) masih di bawah ambang batas aman sistem.",
55
+ "πŸ†˜ **Perhatian:** IPK Terakhir (**{value:.2f}**) adalah faktor dominan yang menempatkan Anda pada risiko ini."
56
  ],
57
  "tinggi": [
58
+ "πŸ›οΈ **Pondasi Kokoh:** IPK kumulatif Anda (**{value:.2f}**) sangat baik dan menjadi penyangga utama profil akademik Anda.",
59
+ "πŸ›‘οΈ **Rekam Jejak Solid:** Konsistensi nilai yang tercermin dari IPK (**{value:.2f}**) menjauhkan Anda dari risiko akademik.",
60
+ "πŸŽ“ **Prestasi Stabil:** IPK di angka **{value:.2f}** menunjukkan pemahaman materi yang konsisten di atas rata-rata."
61
  ]
62
  },
63
+
64
+ # --- 3. KEGAGALAN MATA KULIAH ---
65
  "Jumlah_MK_Gagal": {
66
  "rendah": [
67
+ "✨ **Rekam Jejak Bersih:** Anda memiliki sedikit/tanpa mata kuliah gagal (Total: {value}), yang sangat bagus untuk kelancaran studi.",
68
+ "βœ… **Efisiensi Studi:** Minimnya mata kuliah yang harus diulang (Total: {value}) adalah indikator positif yang kuat.",
69
+ "πŸ›‘οΈ **Bebas Hambatan:** Tidak adanya beban mata kuliah gagal yang signifikan menjaga risiko Anda tetap rendah."
70
  ],
71
  "tinggi": [
72
+ "πŸŽ’ **Beban Mengulang:** Terdapat **{value}** mata kuliah gagal. Tumpukan beban ini meningkatkan risiko akademik secara signifikan.",
73
+ "🚧 **Hambatan Studi:** Jumlah mata kuliah gagal yang tinggi (**{value} MK**) terdeteksi sebagai 'red flag' utama dalam profil ini.",
74
+ "🚨 **Perlu Perbaikan:** Akumulasi **{value}** mata kuliah yang belum lulus menuntut perhatian ekstra untuk semester depan."
75
  ]
76
  },
77
+ "Total_SKS_Gagal": {
78
+ "rendah": [
79
+ "βœ… **Minim SKS Hangus:** Total SKS dari mata kuliah gagal sangat minim.",
80
+ "✨ **Efektif:** Hampir seluruh SKS yang diambil berhasil lulus."
81
+ ],
82
+ "tinggi": [
83
+ "⚠️ **SKS Terbuang:** Total SKS gagal yang besar membebani rasio kelulusan Anda.",
84
+ "πŸ›‘ **Warning SKS:** Banyak kredit SKS yang harus diulang."
85
+ ]
86
+ },
87
+
88
+ # --- 4. PROGRES SKS ---
89
+ "Total_SKS": {
90
  "rendah": [
91
+ "⏳ **Progres Lambat:** Total SKS yang berhasil dikumpulkan (**{value}**) masih tertinggal dari target ideal tahap ini.",
92
+ "🐒 **Perlu Akselerasi:** Jumlah SKS lulus (**{value}**) tergolong sedikit, mengindikasikan perlunya strategi pengambilan SKS yang lebih optimal."
93
  ],
94
  "tinggi": [
95
+ "πŸƒ **On-Track:** Tabungan SKS Anda (**{value}**) sudah cukup banyak, menandakan progres studi yang lancar.",
96
+ "🎯 **Target Tercapai:** Jumlah SKS lulus (**{value}**) sudah memenuhi standar progres yang diharapkan."
97
  ]
98
  },
99
+
100
+ # --- 5. TREN & KONSISTENSI ---
101
  "Tren_IPS_Slope": {
102
  "rendah": [
103
+ "πŸ“‰ **Tren Menurun:** Analisis regresi menunjukkan grafik performa Anda cenderung melandai/turun belakangan ini.",
104
+ "⚠️ **Kehilangan Momentum:** Nilai *slope* tren akademik Anda negatif/rendah, waspadai penurunan semangat belajar."
105
  ],
106
  "tinggi": [
107
+ "πŸš€ **Tren Menanjak:** Grafik nilai Anda menunjukkan tren kenaikan (slope positif). Pertahankan semangat ini!",
108
+ "πŸ“ˆ **Perbaikan Konsisten:** Sistem mendeteksi pola kenaikan performa yang konsisten dari waktu ke waktu."
109
  ]
110
  },
111
+ "Rentang_IPS": {
112
+ "rendah": [
113
+ "βš–οΈ **Performa Stabil:** Fluktuasi nilai Anda kecil, menunjukkan konsistensi belajar yang baik.",
114
+ "πŸ”Ή **Konsisten:** Tidak ada lonjakan atau penurunan drastis pada sejarah nilai."
115
+ ],
116
+ "tinggi": [
117
+ "🎒 **Nilai Fluktuatif:** Terdeteksi rentang nilai yang lebar (tidak stabil). Performa Anda cenderung naik-turun drastis.",
118
+ "⚠️ **Inkonsistensi:** Ada semester di mana nilai sangat tinggi dan sangat rendah. Konsistensi perlu ditingkatkan."
119
+ ]
120
+ },
121
+
122
+ # --- 6. FITUR ONE-HOT (TREN KATEGORIKAL) ---
123
  "Tren_Menaik": {
124
+ "ya": "πŸ“ˆ **Grafik Positif:** Pola data Anda secara eksplisit dikategorikan sebagai tren 'Menaik'.",
125
+ "tidak": "πŸ”Έ **Tidak Ada Kenaikan:** Pola data saat ini tidak menunjukkan tren kenaikan yang signifikan."
126
  },
127
  "Tren_Menurun": {
128
+ "ya": "πŸ“‰ **Peringatan Penurunan:** Pola data Anda secara eksplisit dikategorikan sebagai tren 'Menurun'. Segera lakukan evaluasi.",
129
+ "tidak": "βœ… **Aman dari Penurunan:** Untungnya, profil Anda tidak menunjukkan pola tren 'Menurun'."
130
  },
131
  "Tren_Stabil": {
132
+ "ya": "➑️ **Stagnan/Stabil:** Pola nilai Anda cenderung datar (Stabil). Ini bisa baik atau buruk tergantung nilai rata-ratanya.",
133
+ "tidak": "πŸ”€ **Dinamis:** Profil nilai Anda tidak stagnan, ada pergerakan naik atau turun."
134
  },
135
+
136
+ # --- DEFAULT FALLBACK ---
137
  "default": {
138
+ "rendah": "πŸ”Ή Nilai **{feature_name}** tercatat **{value:.2f}**, lebih rendah dari batas acuan ({threshold:.2f}).",
139
+ "tinggi": "πŸ”Έ Nilai **{feature_name}** tercatat **{value:.2f}**, lebih tinggi dari batas acuan ({threshold:.2f})."
140
  }
141
  }
142
  }
143
 
144
+
145
  # ======================================================================
146
+ # 2. FUNGSI BUILDER (With Deduplication & Random Logic)
147
  # ======================================================================
148
 
149
  def build_explanation_from_rules(structured_rules: List[Dict], prediction_val: str) -> Dict[str, Any]:
150
  """
151
+ Merakit penjelasan dinamis dengan emoji, anti-duplikat, dan variasi kalimat.
152
  """
153
  try:
154
+ # 1. Pilih Kalimat Pembuka (Random Choice dari List)
155
+ opening_templates = EXPLANATION_TEMPLATES["pembuka"].get(prediction_val)
156
+
157
+ if not opening_templates:
158
+ # Fallback jika key prediksi tidak ditemukan di template
159
+ opening_line = EXPLANATION_TEMPLATES["pembuka"]["default"].format(prediction_val=prediction_val)
160
+ elif isinstance(opening_templates, list):
161
+ # Pilih salah satu variasi secara acak
162
+ opening_line = random.choice(opening_templates)
163
+ else:
164
+ # Jika ternyata cuma string tunggal
165
+ opening_line = opening_templates
166
 
167
  explained_rules_list = []
168
+ features_explained = set() # Set untuk mencegah duplikasi faktor
169
+
170
+ # 2. Iterasi aturan SECARA TERBALIK (dari Leaf -> Root)
171
+ # Tujuannya: Mengambil aturan yang paling spesifik (di ujung pohon) terlebih dahulu.
172
  for rule in reversed(structured_rules):
173
  feature = rule["feature"]
174
 
175
+ # LOGIKA ANTI-DUPLIKAT
176
+ # Jika fitur sudah dijelaskan oleh node yang lebih spesifik, skip node induknya.
177
  if feature in features_explained:
178
  continue
179
 
 
182
  condition = rule["condition"]
183
  chosen_template = None
184
 
185
+ # A. Logika untuk Fitur One-Hot (Tren) -> Ya/Tidak
186
  if feature in ["Tren_Menaik", "Tren_Menurun", "Tren_Stabil"]:
187
  if condition == "tinggi":
188
  chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["ya"]
189
  else:
190
  chosen_template = EXPLANATION_TEMPLATES["fitur"][feature]["tidak"]
191
 
192
+ # B. Logika untuk Fitur Numerik -> Rendah/Tinggi
193
  else:
194
+ # Ambil template fitur, fallback ke default jika nama fitur tidak ada di bank template
195
  feature_templates = EXPLANATION_TEMPLATES["fitur"].get(feature, EXPLANATION_TEMPLATES["fitur"]["default"])
196
+
197
+ # Ambil template kondisi (rendah/tinggi)
198
  condition_templates = feature_templates.get(condition)
199
 
200
+ # Jika template berupa LIST (ada variasi kalimat), pilih satu acak
201
  if isinstance(condition_templates, list):
202
+ template_str = random.choice(condition_templates)
203
+ chosen_template = template_str.format(
204
+ feature_name=feature,
205
+ value=rule["value"],
206
+ threshold=rule["threshold"]
207
+ )
208
+ # Jika template berupa STRING tunggal
209
  elif isinstance(condition_templates, str):
210
  chosen_template = condition_templates.format(
211
  feature_name=feature,
 
216
  if chosen_template:
217
  explained_rules_list.append(chosen_template)
218
 
219
+ # 3. Balikkan list agar urutannya logis (Penting -> Pendukung)
220
+ # Karena tadi kita iterasi dari bawah (reversed), hasilnya jadi terbalik.
221
+ # Sebenarnya urutan "Leaf first" (Paling spesifik dulu) seringkali lebih baik untuk dibaca.
222
+ # Namun jika ingin urutan flow Decision Tree (Root -> Leaf), uncomment baris di bawah:
223
+ # explained_rules_list.reverse()
224
+
225
 
 
226
  return {
227
  "opening_line": opening_line,
228
  "factors": explained_rules_list
229
  }
230
 
231
  except Exception as e:
 
232
  return {
233
+ "opening_line": f"⚠️ Maaf, terjadi kesalahan saat menyusun penjelasan: {str(e)}",
234
  "factors": []
235
+ }