ncardian commited on
Commit
0ef2579
·
verified ·
1 Parent(s): 408bf8c

Upload 4 files

Browse files
Files changed (4) hide show
  1. app.py +0 -0
  2. execution_programs.py +224 -0
  3. llm_models.py +78 -0
  4. prompt_models.py +263 -0
app.py CHANGED
The diff for this file is too large to render. See raw diff
 
execution_programs.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import contextlib
3
+ import streamlit as st
4
+ import pandas as pd
5
+ import matplotlib.pyplot as plt
6
+ import seaborn as sns
7
+ from datetime import datetime
8
+ import re
9
+ from io import StringIO
10
+ import plotly.graph_objects as go
11
+ from llm_models import text_llm
12
+ from prompt_models import prompt_analyze
13
+
14
+
15
+ def analyze_code_execution(code: str, execution_result: dict, df: pd.DataFrame) -> str:
16
+ """
17
+ Analyze only the execution results (output and variables) to provide focused insights.
18
+
19
+ Args:
20
+ code: The Python code that was executed
21
+ execution_result: The dictionary containing execution results
22
+ df: The DataFrame being analyzed (for context only)
23
+
24
+ Returns:
25
+ str: Natural language analysis focused on the execution results
26
+ """
27
+ # Extract relevant information from execution_result
28
+ output = execution_result.get('stdout', 'Tidak ada output teks')
29
+ variables = execution_result.get('variables', {})
30
+ error = execution_result.get('error', None)
31
+
32
+ # Prepare variable summaries
33
+ var_summaries = []
34
+ for var_name, var_value in variables.items():
35
+ if isinstance(var_value, (pd.DataFrame, pd.Series)):
36
+ var_summaries.append(f"- {var_name}: {type(var_value).__name__} dengan bentuk {var_value.shape}")
37
+ elif isinstance(var_value, (plt.Figure, sns.axisgrid.Grid)):
38
+ var_summaries.append(f"- {var_name}: Visualisasi plot")
39
+ else:
40
+ var_summaries.append(f"- {var_name}: {type(var_value).__name__}")
41
+
42
+
43
+ prompt = prompt_analyze(output, var_summaries, df, error)
44
+ # Get analysis from the text model
45
+ response = text_llm(prompt)
46
+
47
+ # Clear thinking output
48
+ clean_response = re.sub(r'<\s*think\s*>.*?<\s*/\s*think\s*>', '', response, flags=re.DOTALL | re.IGNORECASE)
49
+
50
+ return clean_response
51
+
52
+ # =======================
53
+ def execute_code(code: str, df: pd.DataFrame):
54
+ """Execute Python code safely and return outputs"""
55
+ # Extract Python code from markdown blocks
56
+ if "```python" in code:
57
+ clean_code = code.split("```python")[1].split("```")[0].strip()
58
+ else:
59
+ clean_code = code.strip()
60
+
61
+ # Add required imports if missing
62
+ required_imports = [
63
+ "import pandas as pd",
64
+ "import matplotlib.pyplot as plt",
65
+ "import seaborn as sns",
66
+ "import plotly.express as px",
67
+ "import plotly.graph_objects as go"
68
+ ]
69
+
70
+ for imp in required_imports:
71
+ if imp not in clean_code:
72
+ clean_code = imp + "\n" + clean_code
73
+
74
+ # Prepare execution environment
75
+ local_vars = {
76
+ 'df': df,
77
+ 'plt': plt,
78
+ 'sns': sns,
79
+ 'pd': pd
80
+ }
81
+
82
+ # Capture outputs
83
+ stdout = io.StringIO()
84
+ stderr = io.StringIO()
85
+
86
+ try:
87
+ with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
88
+ exec(clean_code, local_vars)
89
+
90
+ # Deteksi plot Plotly
91
+ plotly_figs = []
92
+ for var_name, var_value in local_vars.items():
93
+ if isinstance(var_value, go.Figure):
94
+ plotly_figs.append(var_value)
95
+
96
+ return {
97
+ 'code': clean_code,
98
+ 'stdout': stdout.getvalue(),
99
+ 'stderr': stderr.getvalue(),
100
+ 'figure': plotly_figs,
101
+ 'success': True,
102
+ 'variables': {k: v for k, v in local_vars.items()
103
+ if not k.startswith('_') and k not in ['df', 'plt', 'sns', 'pd']}
104
+ }
105
+
106
+ except Exception as e:
107
+ return {
108
+ 'code': clean_code,
109
+ 'error': str(e),
110
+ 'stderr': stderr.getvalue(),
111
+ 'success': False
112
+ }
113
+
114
+ def add_to_history(execution_result):
115
+ """Add execution result to history with timestamp"""
116
+ history_item = {
117
+ **execution_result,
118
+ 'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
119
+ }
120
+ st.session_state.execution_history.append(history_item)
121
+
122
+ # Generate explanation for successful executions
123
+ if execution_result['success']:
124
+ with st.spinner("Analyzing results..."):
125
+ df_to_analyze = st.session_state.df_cleaned if 'df_cleaned' in st.session_state else st.session_state.df
126
+
127
+ analysis = analyze_code_execution(
128
+ code=execution_result['code'],
129
+ execution_result=execution_result,
130
+ df=df_to_analyze # Only used for context, not analyzed
131
+ )
132
+
133
+ history_item['explanation'] = f"""
134
+ **Analisis Hasil Eksekusi:**
135
+ {analysis}
136
+ """
137
+
138
+
139
+
140
+ # =======================
141
+ # Display Functions
142
+ # =======================
143
+
144
+ def display_code_with_highlight(code: str):
145
+ """Display formatted Python code using Streamlit's built-in code block"""
146
+ st.code(code, language='python')
147
+
148
+ def display_history():
149
+ """Display all execution history items"""
150
+ st.markdown("### Execution History")
151
+
152
+ if not st.session_state.execution_history:
153
+ st.info("No executions yet. Run some code to see results here.")
154
+ return
155
+
156
+ for i, item in enumerate(reversed(st.session_state.execution_history)):
157
+ with st.container():
158
+ st.markdown(f"### Execution #{len(st.session_state.execution_history)-i}")
159
+ st.caption(f"Executed at {item['timestamp']}")
160
+
161
+ # Display code
162
+ with st.expander("View Code", expanded=False):
163
+ display_code_with_highlight(item['code'])
164
+
165
+ # Display outputs
166
+ if item['success']:
167
+ if 'explanation' in item:
168
+ with st.expander("See Explanation"):
169
+ st.markdown("**Explanation:**")
170
+ st.info(item['explanation'])
171
+
172
+ if item['stdout']:
173
+ with st.expander("View Output", expanded=False):
174
+ st.markdown("**Output:**")
175
+ lines = item['stdout'].split('\n')
176
+ text_content = []
177
+ table_data = []
178
+ header_detected = False
179
+
180
+ for idx, line in enumerate(lines):
181
+ cleaned_line = line.strip()
182
+ if not cleaned_line:
183
+ continue
184
+
185
+ if not header_detected:
186
+ if re.match(r"^[\w\s_]+$", cleaned_line) and len(cleaned_line.split()) > 1:
187
+ if (idx + 1 < len(lines)) and re.match(r"^\d+\s+[\d\.e+-]+", lines[idx+1].strip()):
188
+ header_detected = True
189
+ table_data = lines[idx:]
190
+ break
191
+ else:
192
+ text_content.append(cleaned_line)
193
+ else:
194
+ text_content.append(cleaned_line)
195
+
196
+ if text_content:
197
+ st.write("\n".join(text_content))
198
+
199
+ if table_data:
200
+ try:
201
+ clean_table = [line.strip() for line in table_data if line.strip()]
202
+ df = pd.read_csv(StringIO("\n".join(clean_table)), sep=r"\s+", engine="python")
203
+ st.dataframe(df)
204
+ except Exception as e:
205
+ st.write("\n".join(table_data))
206
+
207
+
208
+ if item['figure']:
209
+ st.markdown("**Visualisasi Interaktif:**")
210
+ for fig in item['figure']:
211
+ st.plotly_chart(fig, use_container_width=True)
212
+
213
+
214
+ if item['variables']:
215
+ with st.expander("Created Variables"):
216
+ st.json({k: str(type(v)) for k, v in item['variables'].items()})
217
+ else:
218
+ st.error("Execution failed")
219
+ st.error(item['error'])
220
+ if item['stderr']:
221
+ st.text("Error details:")
222
+ st.text(item['stderr'])
223
+
224
+ st.markdown("---")
llm_models.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ from langchain.llms.base import LLM
4
+ from typing import Optional, List
5
+ import os
6
+ from execution_programs import *
7
+
8
+ # API key for llm model
9
+ GROQ_API_KEY = os.environ.get("GROQ_TOKENS")
10
+ class GroqLLM(LLM):
11
+ model: str = "qwen-2.5-coder-32b"
12
+ temperature: float = 0.3
13
+ api_key: str = GROQ_API_KEY
14
+
15
+ @property
16
+ def _llm_type(self) -> str:
17
+ return "groq-llm"
18
+
19
+ def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
20
+ headers = {
21
+ "Authorization": f"Bearer {self.api_key}",
22
+ "Content-Type": "application/json",
23
+ }
24
+
25
+ payload = {
26
+ "messages": [{"role": "user", "content": prompt}],
27
+ "model": self.model,
28
+ "temperature": self.temperature,
29
+ }
30
+
31
+ try:
32
+ response = requests.post(
33
+ "https://api.groq.com/openai/v1/chat/completions",
34
+ headers=headers,
35
+ json=payload
36
+ )
37
+ response.raise_for_status()
38
+ return response.json()["choices"][0]["message"]["content"]
39
+ except Exception as e:
40
+ st.error(f"Error calling Groq API: {str(e)}")
41
+ return "Sorry, there was an error processing your request."
42
+
43
+ class GroqTextLLM(LLM):
44
+ model: str = "deepseek-r1-distill-llama-70b"
45
+ temperature: float = 0.7
46
+ api_key: str = GROQ_API_KEY
47
+
48
+ @property
49
+ def _llm_type(self) -> str:
50
+ return "groq-text-llm"
51
+
52
+ def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
53
+ headers = {
54
+ "Authorization": f"Bearer {self.api_key}",
55
+ "Content-Type": "application/json",
56
+ }
57
+
58
+ payload = {
59
+ "messages": [{"role": "user", "content": prompt}],
60
+ "model": self.model,
61
+ "temperature": self.temperature,
62
+ }
63
+
64
+ try:
65
+ response = requests.post(
66
+ "https://api.groq.com/openai/v1/chat/completions",
67
+ headers=headers,
68
+ json=payload
69
+ )
70
+ response.raise_for_status()
71
+ return response.json()["choices"][0]["message"]["content"]
72
+ except Exception as e:
73
+ st.error(f"Error calling Groq API: {str(e)}")
74
+ return "Sorry, there was an error processing your request."
75
+
76
+ # Initialize LLMs
77
+ code_llm = GroqLLM()
78
+ text_llm = GroqTextLLM()
prompt_models.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def generate_prompt(user_input, df_columns):
2
+ """Generate optimized prompt for code generation"""
3
+ return f"""
4
+ Generate Python code to analyze pandas DataFrame 'df' according to request:
5
+ {user_input}
6
+
7
+ STRICT RULES (NUMBERED PRIORITY):
8
+ 1. CODE STRUCTURE:
9
+ - Return complete executable code in ```python block
10
+ - All new variables with len(var) same lenght with len(df.shape[0]) MUST be added as new columns to existing 'df' using df['new_column'] syntax
11
+ - Never create new DataFrames or use merge/join operations
12
+ - Print calculation result (such as a list, Series, or array) in console
13
+ - Define all temporary variables explicitly
14
+
15
+ 2. DATA HANDLING:
16
+ - Use only columns from: {list(df_columns)}
17
+ - For new data storage:
18
+ if isinstance(result, (pd.Series, list, np.ndarray)):
19
+ if len(result) == len(df): # Check length match
20
+ df[new_col_name] = result
21
+ else:
22
+ print("\\nHasil kalkulasi (ukuran {{}}):".format(len(result)))
23
+ print(result)
24
+ elif isinstance(result, (int, float, str, dict, pd.DataFrame, np.ndarray)):
25
+ print("\\nHasil skalar/metrik:")
26
+ print(result)
27
+ else:
28
+ print("\\nHasil tidak tersimpan:", type(result))
29
+
30
+ 3. COLUMN CREATION RULES:
31
+ a. Untuk hasil operasi matematika:
32
+ df['hasil_kalkulasi'] = df['col1'] + df['col2']
33
+
34
+ b. Untuk transformasi data:
35
+ df['kategori_baru'] = df['col_existing'].apply(lambda x: x*2)
36
+
37
+ c. Untuk agregasi grup:
38
+ df['rata_grup'] = df.groupby('kolom_grup')['target'].transform('mean')
39
+
40
+ d. Contoh khusus untuk statistik:
41
+ # Untuk metrik seperti R2/korelasi
42
+ corr_matrix = df.corr()
43
+ print("\\nMatriks korelasi:")
44
+ print(corr_matrix)
45
+
46
+ # Untuk nilai skalar
47
+ r2_score = calculate_r2()
48
+ print("\\nR-squared:", r2_score)
49
+ e. contoh untuk hasil prediksi
50
+ df['Predict data'] = model.predict(X)
51
+
52
+ 4. OUTPUT HANDLING:
53
+ - Untuk hasil non-kolom (skalar/matriks/agregat):
54
+ print("\\nHasil analisis:")
55
+ print(result)
56
+
57
+ - Untuk visualisasi WAJIB gunakan Plotly dengan contoh template:
58
+ # JANGAN gunakan fig.show() atau renderer apapun
59
+ Contoh untuk histogram:
60
+ fig = px.histogram(
61
+ df_temp,
62
+ x=column_name,
63
+ opacity=0.8,
64
+ color_discrete_sequence=["#d06200"],
65
+ )
66
+
67
+ fig.update_traces(marker_line_color='gray', marker_line_width=0.4)
68
+
69
+ fig.update_layout(
70
+ template='seaborn',
71
+ title=f"Histogram of {{column_name}}",
72
+ xaxis_title=column_name,
73
+ yaxis_title="Count",
74
+ bargap=0.1
75
+ )
76
+ )
77
+
78
+ - Berikut adalah contoh bar chart namun pastikan dulu unique value yang akan diplot kurang dari 100
79
+ top_10_customers['Customer_ID'] = top_10_customers['Customer_ID'].astype(str)
80
+ # selalu cek dulu jumlah unique value data
81
+ if top_10_customers['Customer_ID'].unique() < 100:
82
+ # Plot bar chart
83
+ fig = px.bar(
84
+ top_10_customers,
85
+ x='Customer_ID',
86
+ y='CLV',
87
+ color='CLV',
88
+ template='plotly_dark',
89
+ title='Top 10 Customers by CLV',
90
+ category_orders={{"Customer_ID": top_10_customers['Customer_ID'].tolist()}}
91
+ )
92
+
93
+ # Pastikan sumbu X bertipe kategori (bukan numeric)
94
+ fig.update_xaxes(type='category')
95
+
96
+ fig.update_layout(
97
+ bargap=0.1,
98
+ xaxis_title='Customer ID',
99
+ yaxis_title='CLV',
100
+ hovermode='x unified'
101
+ )
102
+
103
+ Jika if top_10_customers['Customer_ID'].unique() > 100 maka buat bar chart seperti biasa, perinta ini tidak berlaku untuk chart lain seperti pie chart
104
+
105
+
106
+ # Contoh scatter plot
107
+ fig = px.scatter(
108
+ df,
109
+ x='col1',
110
+ y='col2',
111
+ color='col4',
112
+ trendline='ols'
113
+ )
114
+
115
+ # Contoh pie chart
116
+ # Tentukan pull untuk bagian terbesar agar sedikit terpisah
117
+ feedback_counts = df[feedback_col].value_counts().reset_index()
118
+ feedback_counts.columns = [feedback_col, 'count']
119
+
120
+ # Print feedback counts
121
+ print("\nFeedback counts:")
122
+ print(feedback_counts)
123
+
124
+ # Create pie chart for feedback
125
+ fig = px.pie(
126
+ feedback_counts,
127
+ names=feedback_col,
128
+ values='count',
129
+ hole=0.3,
130
+ title='Feedback Distribution'
131
+ )
132
+
133
+ # Determine the largest segment to pull out
134
+ max_segment = feedback_counts[feedback_counts['count'] == feedback_counts['count'].max()][feedback_col].values[0]
135
+ pull = [0.2 if feedback == max_segment else 0 for feedback in feedback_counts[feedback_col]]
136
+
137
+ # Update traces for pulling out the largest segment
138
+ fig.update_traces(
139
+ pull=pull
140
+ )
141
+
142
+ # Update layout for better visualization
143
+ fig.update_layout(
144
+ margin=dict(t=50, b=50, l=50, r=50),
145
+ title_x=0.5
146
+ )
147
+
148
+ # Add border to the chart
149
+ fig.update_xaxes(showline=True, linewidth=1, linecolor='black', mirror=True)
150
+ fig.update_yaxes(showline=True, linewidth=1, linecolor='black', mirror=True)
151
+
152
+
153
+ - Selalu gunakan border untuk chart
154
+ - Jangan gunakan fig.show()
155
+ - Simpan objek figure sebagai variabel
156
+
157
+
158
+ - Untuk kolom baru:
159
+ print(df[['kolom_baru_1', 'kolom_baru_2']].head())
160
+
161
+ - Untuk operasi yang menghasilkan array ukuran berbeda:
162
+ # Langsung print jangan simpan ke df
163
+ conf_matrix = confusion_matrix(...)
164
+ print("Confusion Matrix:", conf_matrix)
165
+
166
+ 5. ERROR PREVENTION:
167
+ - Cek konflik nama kolom:
168
+ if 'nama_kolom' in df.columns:
169
+ df['nama_kolom_rev'] = ... # tambahkan suffix jika sudah ada
170
+
171
+
172
+ FINAL CHECK:
173
+ 1. Pastikan tidak ada operasi merge/join/concat
174
+ 2. Hasil dengan ukuran new_var != len(df) harus langsung di-print
175
+ 3. Skalar dan matriks tidak boleh disimpan sebagai kolom
176
+ 4. Print statement harus menunjukkan tipe hasil yang jelas
177
+ 5. Jangan menampilkan penjelasan apapun dari kode
178
+ """
179
+
180
+ def prompt_chatbot(user_query, df, df1, df2):
181
+ prompt = f"""
182
+ Anda adalah seorang analyst. Jawab pertanyaan berikut dengan jelas
183
+
184
+ Pertanyaan: {user_query}
185
+
186
+ jika user menanyakan data jawablah sesuai data user
187
+ Data user: {df}, dimensinya: {df1}, tipe datanya {df2}
188
+
189
+ Ketentuan jawaban:
190
+
191
+ 1. Gunakan bahasa sesuai input user
192
+ 2. Jangan sertakan contoh kode jika tidak diminta 'tampilkan kode', 'show code', 'write code'
193
+ 3. Format kode dalam blok code
194
+ 4. Jelaskan istilah teknis dengan analogi sederhana
195
+ 5. Selalu berikan tag untuk proses berpikir anda
196
+ 6. Langsung jawab pada intinya
197
+ 7. Hanya tuliskan poin-poinnya saja
198
+ 8 Jawablah sesingkat mungkin
199
+ 9. Jangan gunakan simbol yg tidak perlu di awal kalimat misal '#'
200
+ """
201
+ return prompt
202
+
203
+ def prompt_analyze(output, var_summaries, df, error):
204
+ prompt = f"""
205
+ Kamu adalah analis data yang menjelaskan hasil eksekusi kode Python kepada pemangku kepentingan non-teknis.
206
+
207
+
208
+ Hasil eksekusi yang perlu dianalisis:
209
+ 1. Output teks:
210
+ {output}
211
+
212
+ 2. Variabel yang dihasilkan:
213
+ {chr(10).join(var_summaries) if var_summaries else 'Tidak ada variabel baru dibuat'}
214
+
215
+ 3. Error (jika ada):
216
+ {error if error else 'Tidak ada error'}
217
+
218
+ Format analisis yang diharapkan:
219
+ A. Visualisasi Grafik:
220
+ - Jelaskan jenis visualisasi yang dihasilkan
221
+ - Identifikasi pola atau tren utama yang terlihat
222
+ - Berikan interpretasi bisnis dari visual tersebut
223
+
224
+ B. Output Konsol:
225
+ - Terjemahkan nilai numerik/metrik ke dalam konteks bisnis
226
+ - Jelaskan signifikansi statistik dari nilai yang ditampilkan
227
+ - Highlight angka-angka kunci yang penting untuk pengambilan keputusan
228
+
229
+ C. Variabel Baru:
230
+ - Untuk kolom baru di DataFrame: jelaskan hubungannya dengan kolom lain
231
+ - Untuk variabel statistik: jelaskan implikasi analitisnya
232
+ - Klasifikasikan tipe variabel (numerik/kategorikal/deret waktu)
233
+
234
+ D. Rekomendasi Lanjutan:
235
+ - Sarankan teknik analisis tambahan yang relevan (Contoh: "Untuk memahami hubungan non-linear, bisa digunakan regresi polinomial")
236
+ - Rekomendasikan jenis visualisasi pendukung lainnya
237
+ - Identifikasi potensi masalah data yang perlu diperhatikan
238
+
239
+ Aturan ketat:
240
+ 1. JANGAN menyebutkan detail teknis kode atau library
241
+ 2. Fokus pada interpretasi bisnis/bisnis
242
+ 3. Gunakan analogi sehari-hari untuk konsep statistik kompleks
243
+ 4. Prioritaskan insight yang actionable
244
+ 5. Batasi analisis maksimal 3 poin utama per kategori
245
+ 6. Gunakan format poin-poin dengan penomoran jelas
246
+
247
+ Contoh struktur jawaban:
248
+ 'Analisis menunjukkan:
249
+ 1. Pada visualisasi terlihat... [interpretasi grafik]
250
+ 2. Nilai R-squared sebesar 0.85 menunjukkan... [penjelasan metrik]
251
+ 3. Kolom baru "prediksi" memiliki... [analisis variabel]
252
+ Rekomendasi: [saran spesifik]'
253
+
254
+ Data pendukung:
255
+ - Dimensi DataFrame: {df.shape} baris x {len(df.columns)} kolom
256
+ - Kolom tersedia: {list(df.columns)}
257
+ - Statistik deskriptif terakhir: {df.describe().to_string() if not df.empty else 'Tidak tersedia'}
258
+
259
+ Important
260
+ - Gunakan bahasa inggris untuk hasil text summarizenya
261
+ """
262
+
263
+ return prompt