ncardian commited on
Commit
55b21fd
·
verified ·
1 Parent(s): 3863030

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -805
app.py DELETED
@@ -1,805 +0,0 @@
1
- import io
2
- import streamlit as st
3
- import pandas as pd
4
- import csv
5
- import re
6
- import plotly.express as px
7
- from scipy.stats import skew
8
- from prompt_models import *
9
- from execution_programs import *
10
- from llm_models import text_llm, code_llm
11
-
12
- st.set_page_config(layout="wide")
13
-
14
- # Custom style
15
- st.markdown("""
16
- <style>
17
- .block-container {
18
- padding-top: 1rem;
19
- padding-bottom: 1rem;
20
- padding-left: 0rem;
21
- padding-right: 0rem;
22
- }
23
- .element-container:has(.stColumn) {
24
- gap: 0rem !important;
25
- }
26
- div[data-testid="column"] {
27
- padding-left: 0rem !important;
28
- padding-right: 0rem !important;
29
- }
30
- .stApp {
31
- max-width: 1200px;
32
- margin: 0 auto;
33
- padding: 1rem;
34
- }
35
- .cell {
36
- border: 1px solid #e0e0e0;
37
- border-radius: 4px;
38
- padding: 1rem;
39
- margin-bottom: 1.5rem;
40
- background-color: #f9f9f9;
41
- }
42
- .output {
43
- border-left: 3px solid #4285f4;
44
- padding-left: 1rem;
45
- margin-top: 0.5rem;
46
- }
47
- .input-area {
48
- background-color: #f5f5f5;
49
- padding: 1rem;
50
- border-radius: 4px;
51
- margin-bottom: 1rem;
52
- }
53
- .stButton > button {
54
- padding: 0rem 0.5rem;
55
- border-radius: 5px;
56
- font-size: 16px;
57
- font-weight: bold;
58
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
59
- transition: all 0.3s ease;
60
- }
61
-
62
- .stButton > button:hover {
63
- background-color: #357ae8;
64
- border-color: #357ae8;
65
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
66
- }
67
- .history-item {
68
- margin-bottom: 2rem;
69
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
70
- }
71
- .chat-message {
72
- padding: 1rem;
73
- border-radius: 0.5rem;
74
- margin-bottom: 1rem;
75
- display: flex;
76
- }
77
- .chat-message.user {
78
- background-color: #262626;
79
- align-self: flex-start;
80
- }
81
- .chat-message.assistant {
82
- background-color: #262626;
83
- align-self: flex-end;
84
- }
85
- .chat-message {
86
- font-size: 14px;
87
- }
88
- .chat-message.user {
89
- font-size: 16px;
90
- }
91
- .chat-message.assistant {
92
- font-size: 14px;
93
- }
94
- .sidebar {
95
- width: 300px;
96
- padding-right: 2rem;
97
- }
98
- .delete-btn {
99
- background-color: #ff4b4b !important;
100
- color: white !important;
101
- border: none !important;
102
- padding: 0.25rem 0.5rem !important;
103
- font-size: 0.8rem !important;
104
- margin-top: 0.5rem;
105
- }
106
- .delete-btn:hover {
107
- background-color: #ff0000 !important;
108
- }
109
- </style>
110
- """, unsafe_allow_html=True)
111
-
112
- # Initialize session state
113
- if 'execution_history' not in st.session_state:
114
- st.session_state.execution_history = []
115
- if 'df' not in st.session_state:
116
- st.session_state.df = None
117
-
118
-
119
- # Main app layout
120
- col1a, col2a = st.columns([4, 7], gap="small")
121
- st.markdown("<div style='margin-top: 5px'></div>", unsafe_allow_html=True)
122
- show_col1 = st.toggle("Ask chat bot", value=True)
123
-
124
- if show_col1:
125
- col1a, col2a = st.columns([4,7])
126
- else:
127
- _ , col2a = st.columns([0.0001, 2])
128
-
129
- with col1a:
130
- if show_col1:
131
- with st.container(height=570):
132
- st.header("💬 Chatbot Assistant")
133
-
134
- # Initial state
135
- if 'chatbot' not in st.session_state:
136
- st.session_state.chatbot = {
137
- 'chat_history': [],
138
- 'prev_df': None,
139
- 'prev_raw_dataframes': None
140
- }
141
-
142
- # button clear all chat
143
- if st.button("🗑️ Clear all chat", key="clear_all_chat"):
144
- st.session_state.chatbot['chat_history'] = []
145
-
146
- # show history chatbot
147
- for i in range(0, len(st.session_state.chatbot['chat_history']), 2):
148
- user_msg = st.session_state.chatbot['chat_history'][i]
149
- assistant_msg = st.session_state.chatbot['chat_history'][i+1] if i+1 < len(st.session_state.chatbot['chat_history']) else None
150
-
151
- with st.expander(f"Chat {i//2 + 1}", expanded=False):
152
- with st.chat_message("user"):
153
- st.markdown(user_msg["content"])
154
- if assistant_msg:
155
- with st.chat_message("assistant"):
156
- st.markdown(assistant_msg["content"])
157
-
158
-
159
- # Input chat
160
- user_query = st.chat_input("Ask coding questions...", key="chatbot_input")
161
-
162
- if user_query:
163
- # save previous state
164
- st.session_state.chatbot['prev_df'] = st.session_state.get('df')
165
- st.session_state.chatbot['prev_raw_dataframes'] = st.session_state.get('raw_dataframes')
166
-
167
- # show chat user
168
- with st.chat_message("user"):
169
- st.markdown(user_query)
170
-
171
- # add to history chat
172
- st.session_state.chatbot['chat_history'].append({"role": "user", "content": user_query})
173
-
174
- with st.spinner("Thinking..."):
175
- if "df" in st.session_state:
176
- try:
177
- df = st.session_state.df
178
- columns = df.columns
179
- shape = df.shape
180
- dtypes = df.dtypes
181
- response = text_llm(prompt_chatbot(user_query,st.session_state.df.columns, st.session_state.df.shape, st.session_state.df.dtypes))
182
- clean_response = re.sub(r'<\s*think\s*>.*?<\s*/\s*think\s*>', '', response, flags=re.DOTALL | re.IGNORECASE)
183
-
184
- # show and save chatbot response
185
- with st.chat_message("assistant"):
186
- st.markdown(clean_response)
187
- st.session_state.chatbot['chat_history'].append({"role": "assistant", "content": clean_response})
188
- except AttributeError as e:
189
- st.error(f"Please confirm dataset before starting the chat: {e}")
190
- else:
191
- st.write('You need to upload the data first')
192
-
193
- with col2a: # Sidebar for chatbot
194
- with st.container(height=570):
195
- st.header("📊 DataCoder AI")
196
- st.markdown("Analyze your CSV data with LLM Models")
197
-
198
- def reset_data_state():
199
- st.session_state.df = None
200
- st.session_state.raw_dataframes = None
201
- st.session_state.preview_df = None
202
- st.session_state.file_names = [f.name for f in uploaded_files] if uploaded_files else []
203
-
204
- # Initialize session state if not already present
205
- if 'df' not in st.session_state:
206
- st.session_state.df = None
207
-
208
- uploaded_files = st.file_uploader(
209
- "Upload CSV file(s)",
210
- type=["csv"],
211
- accept_multiple_files=True,
212
- help="Upload one or more CSV files to combine"
213
- )
214
-
215
- if uploaded_files:
216
- # Reset processing if files change
217
- if 'prev_uploaded_files' not in st.session_state or st.session_state.prev_uploaded_files != [f.name for f in uploaded_files]:
218
- st.session_state.df = None
219
- st.session_state.raw_dataframes = None
220
- st.session_state.preview_df = None
221
- st.session_state.prev_uploaded_files = [f.name for f in uploaded_files]
222
-
223
- # Process files but don't merge yet
224
- if st.session_state.raw_dataframes is None:
225
- dataframes = []
226
- processed_files = 0
227
-
228
- for uploaded_file in uploaded_files:
229
- with st.expander(f"📄 File: {uploaded_file.name}", expanded=False):
230
- try:
231
- # Try to detect delimiter
232
- raw_data = uploaded_file.getvalue().decode("utf-8")
233
- sniffer = csv.Sniffer()
234
-
235
- try:
236
- dialect = sniffer.sniff(raw_data.splitlines()[0])
237
- delimiter = dialect.delimiter
238
- except (csv.Error, IndexError):
239
- # Try common delimiters if sniffing fails
240
- for test_delim in [',', ';', '\t', '|']:
241
- if test_delim in raw_data:
242
- delimiter = test_delim
243
- break
244
- else:
245
- delimiter = ',' # default
246
-
247
- uploaded_file.seek(0) # Reset file pointer
248
-
249
- # Read CSV with error handling
250
- try:
251
- df = pd.read_csv(uploaded_file, delimiter=delimiter, on_bad_lines='warn')
252
- except Exception as e:
253
- st.warning(f"Using Python engine for {uploaded_file.name} due to parsing issues")
254
- df = pd.read_csv(uploaded_file, delimiter=delimiter, engine='python')
255
-
256
- # Display file info
257
- st.write(f"Shape: {df.shape}")
258
- st.write(f"Detected delimiter: '{delimiter}'")
259
- st.dataframe(df.head(5), use_container_width=True)
260
-
261
- dataframes.append(df)
262
- processed_files += 1
263
-
264
- except Exception as e:
265
- st.error(f"Error processing {uploaded_file.name}: {str(e)}")
266
- continue
267
-
268
- if processed_files == 0:
269
- st.error("No files were successfully processed")
270
- st.stop()
271
-
272
- # Store raw dataframes in session state
273
- st.session_state.raw_dataframes = dataframes
274
-
275
- # Only proceed if we have raw dataframes
276
- if st.session_state.raw_dataframes:
277
- dataframes = st.session_state.raw_dataframes
278
-
279
- col1, col2 = st.columns([3, 1])
280
-
281
- with col1:
282
- merge_option = st.radio(
283
- "Merge method:",
284
- ("single data","Horizontal (concat columns)", "Vertical (concat rows)", "Horizontal (merge on common columns)"),
285
- index=0,
286
- horizontal=True,
287
- key="merge_option"
288
- )
289
-
290
- # Additional merge parameters
291
- if merge_option == "Horizontal (merge on common columns)":
292
- if len(dataframes) > 0:
293
- common_cols = set(dataframes[0].columns)
294
- for df in dataframes[1:]:
295
- common_cols.intersection_update(df.columns)
296
-
297
- if common_cols:
298
- selected_cols = st.multiselect(
299
- "Select columns to merge on:",
300
- options=list(common_cols),
301
- default=list(common_cols),
302
- key="merge_columns"
303
- )
304
- how_merge = st.selectbox(
305
- "Merge type:",
306
- ["inner", "outer", "left", "right"],
307
- index=1,
308
- key="how_merge"
309
- )
310
- else:
311
- None
312
-
313
- with col2:
314
- preview_button = st.button("🔄 Preview")
315
- confirm_button = st.button("✅ Confirm")
316
- reset_btn = st.button("🔄 Reset")
317
-
318
- # Reset logic
319
- if reset_btn:
320
- reset_data_state()
321
- st.rerun()
322
-
323
- # Preview logic
324
- if preview_button:
325
- with st.spinner("Generating preview..."):
326
- try:
327
- if merge_option == "single data":
328
- preview_df = dataframes[0]
329
- elif merge_option == "Horizontal (concat columns)":
330
- # Find common indices across ALL dataframes
331
- common_indices = dataframes[0].index
332
- for df in dataframes[1:]:
333
- common_indices = common_indices.intersection(df.index)
334
-
335
- if len(common_indices) == 0:
336
- st.error("No common indices found across all dataframes")
337
- st.stop()
338
-
339
- # Filter all dataframes to only common indices
340
- filtered_dfs = [df.loc[common_indices] for df in dataframes]
341
-
342
- # Handle duplicate column names
343
- all_columns = []
344
- duplicate_counter = {}
345
-
346
- for i, df in enumerate(filtered_dfs):
347
- new_columns = []
348
- for col in df.columns:
349
- if col in all_columns:
350
- if col not in duplicate_counter:
351
- duplicate_counter[col] = 1
352
- duplicate_counter[col] += 1
353
- new_col = f"{col}_df{duplicate_counter[col]}"
354
- new_columns.append(new_col)
355
- else:
356
- new_columns.append(col)
357
- all_columns.extend(new_columns)
358
- filtered_dfs[i].columns = new_columns
359
-
360
- # Concatenate horizontally
361
- preview_df = pd.concat(filtered_dfs, axis=1)
362
-
363
- elif merge_option == "Vertical (concat rows)":
364
- preview_df = pd.concat(dataframes, axis=0, ignore_index=True)
365
-
366
- elif merge_option == "Horizontal (merge on common columns)":
367
- def auto_merge_many(dfs):
368
- if not dfs:
369
- return None
370
-
371
- merged_df = dfs[0]
372
-
373
- for next_df in dfs[1:]:
374
- common_keys = list(set(merged_df.columns) & set(next_df.columns))
375
- if common_keys:
376
- merged_df = pd.merge(merged_df, next_df, on=common_keys)
377
- else:
378
- print(f"[WARNING] There is no matching key between:\n{merged_df.columns}\n&\n{next_df.columns}")
379
-
380
- return merged_df
381
-
382
- preview_df = auto_merge_many(dataframes)
383
-
384
- st.session_state.preview_df = preview_df
385
- st.success("Preview generated!")
386
-
387
- # Show preview stats
388
- st.subheader("Preview Results")
389
- st.write(f"Shape: {preview_df.shape}")
390
-
391
- # Show sample data with tabs
392
- tab1, tab2 = st.tabs(["First Rows", "Last Rows"])
393
- with tab1:
394
- st.dataframe(preview_df.head(20), use_container_width=True)
395
- with tab2:
396
- st.dataframe(preview_df.tail(20), use_container_width=True)
397
-
398
- # Show quick stats
399
- with st.expander("Preview Statistics"):
400
- st.write("Column types:")
401
- st.dataframe(preview_df.dtypes.astype(str).reset_index().rename(
402
- columns={'index': 'Column', 0: 'DataType'}
403
- ))
404
-
405
- st.write("Missing values:")
406
- missing = preview_df.isnull().sum()
407
- missing = missing[missing > 0].reset_index().rename(
408
- columns={'index': 'Column', 0: 'Missing Count'}
409
- )
410
- if len(missing) > 0:
411
- st.dataframe(missing)
412
- else:
413
- st.success("No missing values found!")
414
-
415
- except Exception as e:
416
- st.error(f"Preview failed: {str(e)}")
417
- st.stop()
418
-
419
- # Confirm merge logic
420
- if confirm_button and st.session_state.preview_df is not None:
421
- st.session_state.df = st.session_state.preview_df
422
- st.session_state.preview_df = None # Clear preview after confirmation
423
- st.success("Data is now available for analysis.")
424
- st.balloons()
425
- st.rerun()
426
-
427
- # Show raw data info
428
- with st.expander("📦 Raw Data Summary", expanded=False):
429
- st.write(f"Total files loaded: {len(dataframes)}")
430
- for i, df in enumerate(dataframes, 1):
431
- st.write(f"### Dataframe {i}")
432
- st.write(f"- Shape: {df.shape}")
433
- st.write("- Columns:")
434
- st.dataframe(pd.DataFrame({
435
- 'Column': df.columns,
436
- 'Type': df.dtypes.values,
437
- 'Missing %': (df.isnull().mean() * 100).round(2)
438
- }), hide_index=True)
439
-
440
- if st.checkbox(f"Show sample data for Dataframe {i}", key=f"show_raw_{i}"):
441
- st.dataframe(df.head(5), use_container_width=True)
442
-
443
- # If merge is already confirmed, show the final data
444
- if st.session_state.df is not None:
445
-
446
- # Data cleaning options (only after merge is confirmed)
447
- with st.expander("**🧹 Data Cleaning**", expanded=True):
448
- # Inisialisasi df_cleaned di session state jika belum ada
449
-
450
- st.session_state.df_cleaned = st.session_state.df.copy()
451
-
452
- clean_options = st.columns(4)
453
-
454
- with clean_options[0]:
455
- clean_nan = st.selectbox(
456
- "Handle NaN values:",
457
- ["Keep", "Remove rows", "Fill with zero", "Fill with mean"],
458
- index=0,
459
- key="clean_nan"
460
- )
461
-
462
- if st.button("Apply NaN Handling"):
463
- if clean_nan == "Fill with zero":
464
- st.session_state.df_cleaned = st.session_state.df_cleaned.fillna(0)
465
- st.success("Filled NaN with zeros!")
466
- elif clean_nan == "Fill with mean":
467
- st.session_state.df_cleaned = st.session_state.df_cleaned.fillna(st.session_state.df_cleaned.mean())
468
- st.success("Filled NaN with mean values!")
469
- elif clean_nan == "Remove rows":
470
- initial_rows = len(st.session_state.df_cleaned)
471
- st.session_state.df_cleaned = st.session_state.df_cleaned.dropna()
472
- removed_nan = initial_rows - len(st.session_state.df_cleaned)
473
- st.success(f"Removed {removed_nan} rows with NaN values!")
474
-
475
- with clean_options[1]:
476
- clean_duplicates = st.selectbox(
477
- "Handle duplicates:",
478
- ["Keep", "Remove all", "Keep first", "Keep last"],
479
- index=0,
480
- key="clean_duplicates"
481
- )
482
-
483
- if st.button("Apply Deduplication"):
484
- if clean_duplicates != "Keep":
485
- initial_rows = len(st.session_state.df_cleaned)
486
- if clean_duplicates == "Remove all":
487
- st.session_state.df_cleaned = st.session_state.df_cleaned.drop_duplicates(keep=False)
488
- elif clean_duplicates == "Keep first":
489
- st.session_state.df_cleaned = st.session_state.df_cleaned.drop_duplicates(keep='first')
490
- elif clean_duplicates == "Keep last":
491
- st.session_state.df_cleaned = st.session_state.df_cleaned.drop_duplicates(keep='last')
492
-
493
- removed_dups = initial_rows - len(st.session_state.df_cleaned)
494
- st.success(f"Removed {removed_dups} duplicate rows!")
495
-
496
- with clean_options[2]:
497
- col_rename = st.text_input("Rename ('old:new')")
498
- if st.button("Rename Column") and col_rename and ":" in col_rename:
499
- old, new = col_rename.split(":", 1)
500
- if old in st.session_state.df_cleaned.columns:
501
- st.session_state.df_cleaned = st.session_state.df_cleaned.rename(columns={old: new})
502
- st.success(f"Renamed column '{old}' to '{new}'")
503
- else:
504
- st.warning(f"Column '{old}' not found")
505
-
506
- with clean_options[3]:
507
- # Fitur Drop Columns
508
- cols_to_drop = st.text_input(
509
- "Drop columns",
510
- placeholder="column1, column2, ...",
511
- key="drop_cols_input"
512
- )
513
-
514
- if st.button("Drop Columns"):
515
- if cols_to_drop:
516
- # Proses input: hilangkan spasi, split by comma, dan bersihkan
517
- columns_list = [col.strip() for col in cols_to_drop.split(",") if col.strip()]
518
-
519
- # Fungsi untuk mencocokkan nama kolom dengan fleksibilitas
520
- def find_matching_column(col_name, df_columns):
521
- col_name = col_name.lower().strip()
522
- for df_col in df_columns:
523
- if df_col.lower().strip() == col_name:
524
- return df_col
525
- return None
526
-
527
- # Cari kolom yang cocok
528
- matched_cols = []
529
- not_found = []
530
-
531
- for col in columns_list:
532
- matched = find_matching_column(col, st.session_state.df_cleaned.columns)
533
- if matched:
534
- matched_cols.append(matched)
535
- else:
536
- not_found.append(col)
537
-
538
- # Drop kolom yang ditemukan
539
- if matched_cols:
540
- initial_cols = st.session_state.df_cleaned.columns.tolist()
541
- st.session_state.df_cleaned = st.session_state.df_cleaned.drop(columns=matched_cols)
542
- st.success(f"Successfully dropped columns: {', '.join(matched_cols)}")
543
-
544
- # Tampilkan warning untuk kolom yang tidak ditemukan
545
- if not_found:
546
- st.warning(f"Columns not found: {', '.join(not_found)}")
547
-
548
- # Tampilkan perubahan
549
- st.write(f"Remaining columns: {len(st.session_state.df_cleaned.columns)}")
550
- else:
551
- st.error("No matching columns found to drop")
552
- else:
553
- st.warning("Please enter column names to drop")
554
-
555
- # Tampilkan info missing values setelah cleaning
556
- missing_values = st.session_state.df_cleaned.isnull().sum().sum()
557
- duplicate_values = st.session_state.df_cleaned.duplicated().sum().sum()
558
- if missing_values > 0 or duplicate_values > 0:
559
- st.warning(f"⚠️ Warning: There are still {missing_values} missing values and {duplicate_values} ducplicate rows in the data")
560
- else:
561
- st.success("✅ No missing values and duplicate rows remaining!")
562
-
563
- # Final data display
564
- st.session_state.df = st.session_state.df_cleaned
565
- st.subheader("📊 Final Data")
566
- st.write(f"Shape: {st.session_state.df.shape}")
567
-
568
- # Interactive data explorer
569
- tab1, tab2, tab3, tab4 = st.tabs(["Data View", "Statistics", "Column Analysis", 'Type Data'])
570
-
571
- with tab1:
572
- num_rows = st.slider(
573
- "Number of rows to display:",
574
- min_value=5,
575
- max_value=100,
576
- value=5,
577
- key="num_rows_view"
578
- )
579
- st.dataframe(st.session_state.df.head(num_rows), use_container_width=True)
580
-
581
- with tab2:
582
- st.write("### Descriptive Statistics")
583
- st.dataframe(st.session_state.df.describe(include='all'), use_container_width=True)
584
-
585
-
586
- with tab3:
587
- selected_col = st.selectbox(
588
- "Select column to analyze:",
589
- st.session_state.df.columns,
590
- key="col_analyze"
591
- )
592
-
593
- col_data = st.session_state.df[selected_col]
594
- st.write(f"**Type:** {col_data.dtype}")
595
- st.write(f"**Unique values:** {len(col_data.unique())}")
596
- st.write(f"**Missing values:** {col_data.isnull().sum()} ({col_data.isnull().mean()*100:.2f}%)")
597
-
598
-
599
- if col_data.nunique() < 20 and not pd.api.types.is_numeric_dtype(col_data):
600
- ascending = st.checkbox("Ascending", value=False)
601
- st.write("**Distribution**")
602
- column_name = col_data.name
603
-
604
- # sum and sort data
605
- counts = col_data.value_counts(ascending=ascending)
606
- ordered_categories = counts.index.tolist()
607
-
608
- # create dataframe and category for data
609
- df_temp = pd.DataFrame({column_name: col_data})
610
- df_temp[column_name] = pd.Categorical(df_temp[column_name], categories=ordered_categories, ordered=True)
611
-
612
- # create histogram
613
- fig = px.histogram(
614
- df_temp,
615
- x=column_name,
616
- opacity=0.7,
617
- color_discrete_sequence=["#d06200"],
618
- )
619
-
620
- fig.update_traces(marker_line_color='gray', marker_line_width=1)
621
-
622
- # Show x axis based on category sorting
623
- fig.update_layout(
624
- template='seaborn',
625
- title=f"Histogram of {column_name}",
626
- xaxis_title=column_name,
627
- yaxis_title="Count",
628
- bargap=0.1,
629
- font=dict(family="Verdana", size=13),
630
- xaxis=dict(
631
- categoryorder="array",
632
- categoryarray=ordered_categories,
633
- )
634
- )
635
-
636
- st.plotly_chart(fig, use_container_width=True)
637
-
638
- elif pd.api.types.is_numeric_dtype(col_data):
639
- st.write("**Distribution**")
640
- column_name = col_data.name
641
-
642
- df_temp = pd.DataFrame({column_name: col_data.dropna()})
643
-
644
- fig = px.histogram(
645
- df_temp,
646
- x=column_name,
647
- nbins=30,
648
- opacity=0.85,
649
- color_discrete_sequence=["#d06200"],
650
- )
651
-
652
- fig.update_traces(marker_line_color='gray', marker_line_width=0.9)
653
-
654
- fig.update_layout(
655
- template='seaborn',
656
- title=f"Histogram of {column_name}",
657
- xaxis_title=column_name,
658
- yaxis_title="Count",
659
- bargap=0.1,
660
- font=dict(family="Verdana", size=13),
661
- )
662
- fig.update_layout(
663
- annotations=[
664
- dict(
665
- text=f"Skewness: {skew(col_data):.2f}",
666
- showarrow=False,
667
- xref="paper", yref="paper",
668
- x=0.95, y=0.95,
669
- bordercolor='black',
670
- borderwidth=1
671
- )
672
- ]
673
- )
674
-
675
- st.plotly_chart(fig, use_container_width=True)
676
-
677
- else:
678
- st.write("**Value counts (top 20):**")
679
- st.dataframe(col_data.value_counts().head(20))
680
-
681
-
682
- with tab4:
683
- buffer = io.StringIO()
684
- st.session_state.df.info(buf=buffer)
685
- st.text(buffer.getvalue())
686
-
687
- st.markdown("**Data Types:**")
688
- st.json(st.session_state.df.dtypes.astype(str).to_dict())
689
-
690
- st.markdown("**Missing Values:**")
691
- st.json(st.session_state.df.isnull().sum().to_dict())
692
-
693
- st.markdown("**Duplicate:**")
694
- duplicate_count = st.session_state.df.duplicated().sum()
695
- st.write(f"Total duplicate row: {duplicate_count}")
696
-
697
- # Menampilkan jumlah duplikat dalam format JSON yang benar
698
- st.json({"duplicate_count": duplicate_count})
699
-
700
-
701
- # Export options
702
- with st.expander("💾 Export Data", expanded=False):
703
- export_format = st.radio(
704
- "Export format:",
705
- ["CSV", "Excel", "JSON"],
706
- horizontal=True
707
- )
708
-
709
- export_filename = st.text_input("Filename (without extension)", "clean_data")
710
-
711
- if st.button("Export Data"):
712
- try:
713
- if export_format == "CSV":
714
- csv = st.session_state.df.to_csv(index=False).encode('utf-8')
715
- st.download_button(
716
- label="Download CSV",
717
- data=csv,
718
- file_name=f"{export_filename}.csv",
719
- mime='text/csv'
720
- )
721
- elif export_format == "Excel":
722
- excel_buffer = io.BytesIO()
723
- st.session_state.df.to_excel(excel_buffer, index=False)
724
- st.download_button(
725
- label="Download Excel",
726
- data=excel_buffer,
727
- file_name=f"{export_filename}.xlsx",
728
- mime='application/vnd.ms-excel'
729
- )
730
- elif export_format == "JSON":
731
- json_str = st.session_state.df.to_json(orient='records', indent=2)
732
- st.download_button(
733
- label="Download JSON",
734
- data=json_str,
735
- file_name=f"{export_filename}.json",
736
- mime='application/json'
737
- )
738
- st.success("Export ready!")
739
- except Exception as e:
740
- st.error(f"Export failed: {str(e)}")
741
-
742
- # Input area
743
- input_method = st.radio("Input method:", ["Auto run code","Manual run code"])
744
-
745
- user_input = st.text_area("Enter your Python code or question:", height=150,
746
- placeholder="Write code here or ask a question about your data...",
747
- key="user_input")
748
-
749
-
750
-
751
- # Mode handling
752
- if input_method == "Auto run code":
753
- col1, col2, col3 = st.columns(3)
754
- with col1: execute_btn = st.button("▶️ Execute")
755
- with col2: clear_btn = st.button("🗑️ Clear History")
756
- with col3: show_vars = st.button("📊 Show Variables")
757
- # Common button handling
758
- if clear_btn:
759
- st.session_state.execution_history = []
760
-
761
- if show_vars and st.session_state.execution_history:
762
- latest_vars = st.session_state.execution_history[-1].get('variables', {})
763
- st.json({k: str(type(v)) for k, v in latest_vars.items()})
764
- if execute_btn and user_input:
765
- with st.spinner("Execute code..."):
766
- generated_code = code_llm(generate_prompt(user_input, st.session_state.df.columns.tolist()))
767
- st.markdown("**Generated Code:**")
768
- st.code(generated_code, language='python')
769
-
770
- result = execute_code(generated_code, st.session_state.df)
771
- add_to_history(result)
772
- st.rerun()
773
-
774
- elif input_method == "Manual run code":
775
- col1, col2, col3, col4 = st.columns(4)
776
- with col1: code_btn = st.button("▶️ Code")
777
- with col2: execute_btn = st.button("▶️ Execute")
778
- with col3: clear_btn = st.button("🗑️ Clear History")
779
- with col4: show_vars = st.button("📊 Show Variables")
780
-
781
- if clear_btn:
782
- st.session_state.execution_history = []
783
-
784
- if show_vars and st.session_state.execution_history:
785
- latest_vars = st.session_state.execution_history[-1].get('variables', {})
786
- st.json({k: str(type(v)) for k, v in latest_vars.items()})
787
-
788
- if code_btn and user_input:
789
- with st.spinner("Generate Code"):
790
- generated_code = code_llm(generate_prompt(user_input, st.session_state.df.columns.tolist()))
791
- st.session_state.generated_code = generated_code
792
- st.markdown("**Generated Code:**")
793
- st.code(generated_code, language='python')
794
-
795
- if execute_btn:
796
- if 'generated_code' in st.session_state:
797
- result = execute_code(st.session_state.generated_code, st.session_state.df)
798
- add_to_history(result)
799
- elif user_input:
800
- result = execute_code(user_input, st.session_state.df)
801
- add_to_history(result)
802
- st.rerun()
803
-
804
- # Display execution history
805
- display_history()