Cosmographer commited on
Commit
fe084c4
Β·
verified Β·
1 Parent(s): f19eef2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +611 -19
app.py CHANGED
@@ -520,6 +520,195 @@ def transform_cols(df: pd.DataFrame, cols: List[str], method="log"):
520
  df[c] = df[c].apply(lambda x: np.sqrt(x) if x>=0 else x)
521
  return df
522
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
  # -----------------------------
524
  # Visualization NLP (very small parser)
525
  # -----------------------------
@@ -945,27 +1134,324 @@ with gr.Blocks(css=css, title="DataSynth β€” Analytics Hub") as demo:
945
  gr.Markdown("### Correlation Analysis")
946
  corr_plot = gr.Plot(label="Correlation Heatmap")
947
  gr.Markdown("*Note: Only numeric columns are included in correlation analysis*")
948
-
949
  with gr.TabItem("πŸ”§ Prepare"):
950
- gr.Markdown("Data cleaning and feature engineering options.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
951
  with gr.Row():
952
- with gr.Column(scale=1):
953
- impute_num = gr.Dropdown(choices=["mean","median","most_frequent"], value="mean", label="Numeric Imputation")
954
- impute_cat = gr.Dropdown(choices=["most_frequent","constant"], value="most_frequent", label="Categorical Imputation")
955
- const_fill = gr.Textbox(label="Constant fill value (if constant chosen)", value="missing")
956
- outlier_cols = gr.Textbox(label="Outlier numeric columns (comma separated) β€” leave blank for auto numeric")
957
- outlier_method = gr.Dropdown(choices=["cap","remove"], value="cap", label="Outlier treatment")
958
- date_col = gr.Dropdown(choices=[], label="Date column (auto-detected)", interactive=True)
959
- date_fmt = gr.Textbox(label="Date format (optional)", placeholder="%Y-%m-%d")
960
- text_cols = gr.Textbox(label="Text columns to clean (comma separated)")
961
- transform_cols_txt = gr.Textbox(label="Numeric columns to transform (comma separated)")
962
- transform_method = gr.Dropdown(choices=["log","sqrt"], value="log", label="Transform method")
963
- drop_highcard = gr.Checkbox(label="Auto-drop high-cardinality categorical columns", value=True)
964
- apply_prep = gr.Button("Apply Preparation")
965
- download_clean = gr.Button("Download Clean CSV")
966
- with gr.Column(scale=1):
967
- prep_output = gr.Markdown("Preparation preview will appear here.")
968
- preview_clean = gr.Dataframe(interactive=False)
969
  with gr.TabItem("πŸ“Š Visualize (NL)"):
970
  gr.Markdown("Type a natural-language request to create a plot (e.g., 'histogram of age', 'scatter income vs age', 'bar of country').")
971
  nl_input = gr.Textbox(label="Describe chart")
@@ -1107,6 +1593,112 @@ with gr.Blocks(css=css, title="DataSynth β€” Analytics Hub") as demo:
1107
 
1108
  download_clean.click(fn=lambda p: p if p else None, inputs=[clean_state], outputs=[download_clean])
1109
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1110
  # NLP visualize
1111
  def _nl_visualize(query, clean_path, df_path):
1112
  try:
 
520
  df[c] = df[c].apply(lambda x: np.sqrt(x) if x>=0 else x)
521
  return df
522
 
523
+ # -----------------------------
524
+ # Enhanced Data Preparation Functions
525
+ # -----------------------------
526
+
527
+ def update_column_lists(df):
528
+ """Update all column dropdowns and checkboxes based on current dataframe"""
529
+ if df is None or df.empty:
530
+ return [], [], [], [], [], [], [], [], []
531
+
532
+ cols = df.columns.tolist()
533
+ numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()
534
+ categorical_cols = df.select_dtypes(include=["object", "category"]).columns.tolist()
535
+ datetime_cols = df.select_dtypes(include=["datetime64"]).columns.tolist()
536
+
537
+ return (
538
+ gr.CheckboxGroup.update(choices=cols), # available_cols
539
+ gr.Dropdown.update(choices=cols), # rename_col_old
540
+ gr.Dropdown.update(choices=cols), # dtype_col
541
+ gr.CheckboxGroup.update(choices=numeric_cols), # num_impute_cols
542
+ gr.CheckboxGroup.update(choices=categorical_cols), # cat_impute_cols
543
+ gr.Dropdown.update(choices=cols), # replace_col
544
+ gr.Dropdown.update(choices=categorical_cols), # split_col
545
+ gr.Dropdown.update(choices=cols), # date_col_selected
546
+ gr.Dropdown.update(choices=cols), # pivot_index, groupby_cols, etc (simplified)
547
+ )
548
+
549
+ def apply_column_operations(df, selected_cols, rename_mapping, dtype_conversions):
550
+ """Apply column selection, renaming, and type conversions"""
551
+ df = df.copy()
552
+
553
+ # Column selection
554
+ if selected_cols:
555
+ df = df[selected_cols]
556
+
557
+ # Column renaming
558
+ for old_name, new_name in rename_mapping.items():
559
+ if old_name in df.columns:
560
+ df = df.rename(columns={old_name: new_name})
561
+
562
+ # Data type conversions
563
+ for col, target_dtype in dtype_conversions.items():
564
+ if col in df.columns:
565
+ try:
566
+ if target_dtype == "numeric":
567
+ df[col] = pd.to_numeric(df[col], errors='coerce')
568
+ elif target_dtype == "integer":
569
+ df[col] = pd.to_numeric(df[col], errors='coerce').astype('Int64')
570
+ elif target_dtype == "float":
571
+ df[col] = pd.to_numeric(df[col], errors='coerce').astype(float)
572
+ elif target_dtype == "datetime":
573
+ df[col] = pd.to_datetime(df[col], errors='coerce')
574
+ elif target_dtype == "category":
575
+ df[col] = df[col].astype('category')
576
+ elif target_dtype == "boolean":
577
+ df[col] = df[col].astype(bool)
578
+ # string is default, no conversion needed
579
+ except Exception as e:
580
+ print(f"Error converting {col} to {target_dtype}: {e}")
581
+
582
+ return df
583
+
584
+ def detect_high_cardinality_cols(df, threshold):
585
+ """Detect columns with high cardinality above threshold"""
586
+ high_card_cols = []
587
+ for col in df.columns:
588
+ if df[col].dtype in ['object', 'category']:
589
+ unique_count = df[col].nunique()
590
+ if unique_count > threshold:
591
+ high_card_cols.append((col, unique_count))
592
+ return high_card_cols
593
+
594
+ def advanced_imputation(df, num_cols, num_method, num_custom, cat_cols, cat_method, cat_custom):
595
+ """Apply advanced imputation methods"""
596
+ df = df.copy()
597
+
598
+ # Numeric imputation
599
+ if num_cols:
600
+ for col in num_cols:
601
+ if col in df.columns:
602
+ if num_method == "Mean":
603
+ df[col].fillna(df[col].mean(), inplace=True)
604
+ elif num_method == "Median":
605
+ df[col].fillna(df[col].median(), inplace=True)
606
+ elif num_method == "Mode":
607
+ df[col].fillna(df[col].mode()[0] if not df[col].mode().empty else 0, inplace=True)
608
+ elif num_method == "Zero":
609
+ df[col].fillna(0, inplace=True)
610
+ elif num_method == "Custom Value" and num_custom:
611
+ try:
612
+ custom_val = float(num_custom)
613
+ df[col].fillna(custom_val, inplace=True)
614
+ except ValueError:
615
+ df[col].fillna(0, inplace=True)
616
+ elif num_method in ["Forward Fill", "LOCF"]:
617
+ df[col].fillna(method='ffill', inplace=True)
618
+ elif num_method in ["Backward Fill", "NOCB"]:
619
+ df[col].fillna(method='bfill', inplace=True)
620
+ elif num_method == "Linear Interpolation":
621
+ df[col].interpolate(method='linear', inplace=True)
622
+ # Note: KNN and MICE would require more complex implementation
623
+
624
+ # Categorical imputation
625
+ if cat_cols:
626
+ for col in cat_cols:
627
+ if col in df.columns:
628
+ if cat_method == "Mode":
629
+ df[col].fillna(df[col].mode()[0] if not df[col].mode().empty else "Unknown", inplace=True)
630
+ elif cat_method == "Most Frequent":
631
+ df[col].fillna(df[col].value_counts().index[0] if len(df[col].value_counts()) > 0 else "Unknown", inplace=True)
632
+ elif cat_method == "Arbitrary ('Unknown')":
633
+ df[col].fillna("Unknown", inplace=True)
634
+ elif cat_method == "Constant Value" and cat_custom:
635
+ df[col].fillna(cat_custom, inplace=True)
636
+
637
+ return df
638
+
639
+ def apply_text_operations(df, text_cols, operations):
640
+ """Apply various text cleaning operations"""
641
+ df = df.copy()
642
+
643
+ for col in text_cols:
644
+ if col in df.columns:
645
+ # Convert to string first
646
+ df[col] = df[col].astype(str)
647
+
648
+ for op in operations:
649
+ if op == "Remove leading/trailing whitespace":
650
+ df[col] = df[col].str.strip()
651
+ elif op == "Convert to lowercase":
652
+ df[col] = df[col].str.lower()
653
+ elif op == "Convert to uppercase":
654
+ df[col] = df[col].str.upper()
655
+ elif op == "Remove special characters":
656
+ df[col] = df[col].str.replace(r'[^\w\s]', '', regex=True)
657
+ elif op == "Remove numbers":
658
+ df[col] = df[col].str.replace(r'\d+', '', regex=True)
659
+ elif op == "Remove extra spaces":
660
+ df[col] = df[col].str.replace(r'\s+', ' ', regex=True)
661
+
662
+ return df
663
+
664
+ def split_text_column(df, col, delimiter):
665
+ """Split text column into multiple columns"""
666
+ if col not in df.columns:
667
+ return df, "Column not found"
668
+
669
+ df = df.copy()
670
+ try:
671
+ # Split the column
672
+ split_df = df[col].str.split(delimiter, expand=True)
673
+
674
+ # Name new columns
675
+ new_col_names = [f"{col}_{i+1}" for i in range(split_df.shape[1])]
676
+ split_df.columns = new_col_names
677
+
678
+ # Add new columns to original dataframe
679
+ df = pd.concat([df, split_df], axis=1)
680
+
681
+ return df, f"Successfully split {col} into {len(new_col_names)} columns"
682
+ except Exception as e:
683
+ return df, f"Error splitting column: {str(e)}"
684
+
685
+ def create_formula_column(df, formula, new_col_name):
686
+ """Create new column based on formula expression"""
687
+ df = df.copy()
688
+
689
+ try:
690
+ # Simple formula evaluation (this is a basic implementation)
691
+ # In a real scenario, you'd want a more sophisticated formula parser
692
+ if "=" in formula:
693
+ # Remove the assignment part
694
+ formula = formula.split("=")[1].strip()
695
+
696
+ # Basic operations support
697
+ formula = formula.replace("CURRENT_YEAR", str(pd.Timestamp.now().year))
698
+
699
+ # Evaluate the formula (this is simplified)
700
+ # Note: In production, you'd want a safer evaluation method
701
+ try:
702
+ df[new_col_name] = df.eval(formula)
703
+ return df, f"Successfully created column '{new_col_name}'"
704
+ except:
705
+ # Fallback: try as string operation
706
+ df[new_col_name] = formula
707
+ return df, f"Created column '{new_col_name}' with constant value"
708
+
709
+ except Exception as e:
710
+ return df, f"Error creating formula column: {str(e)}"
711
+
712
  # -----------------------------
713
  # Visualization NLP (very small parser)
714
  # -----------------------------
 
1134
  gr.Markdown("### Correlation Analysis")
1135
  corr_plot = gr.Plot(label="Correlation Heatmap")
1136
  gr.Markdown("*Note: Only numeric columns are included in correlation analysis*")
1137
+
1138
  with gr.TabItem("πŸ”§ Prepare"):
1139
+ gr.Markdown("## πŸ› οΈ Advanced Data Preparation Studio")
1140
+
1141
+ with gr.Tabs() as prep_tabs:
1142
+ # Tab 1: Column Management
1143
+ with gr.TabItem("πŸ“‹ Column Management"):
1144
+ with gr.Row():
1145
+ with gr.Column(scale=1):
1146
+ gr.Markdown("### Column Selection & Operations")
1147
+ available_cols = gr.CheckboxGroup(
1148
+ label="Select Columns to Keep",
1149
+ choices=[],
1150
+ interactive=True
1151
+ )
1152
+ select_all_cols = gr.Button("Select All")
1153
+ deselect_all_cols = gr.Button("Deselect All")
1154
+
1155
+ gr.Markdown("### Column Renaming")
1156
+ rename_col_old = gr.Dropdown(
1157
+ label="Column to Rename",
1158
+ choices=[],
1159
+ interactive=True
1160
+ )
1161
+ rename_col_new = gr.Textbox(
1162
+ label="New Column Name",
1163
+ placeholder="Enter new column name"
1164
+ )
1165
+ rename_btn = gr.Button("Rename Column")
1166
+
1167
+ gr.Markdown("### Data Type Conversion")
1168
+ dtype_col = gr.Dropdown(
1169
+ label="Column to Convert",
1170
+ choices=[],
1171
+ interactive=True
1172
+ )
1173
+ dtype_target = gr.Dropdown(
1174
+ label="Target Data Type",
1175
+ choices=["string", "numeric", "integer", "float", "datetime", "category", "boolean"],
1176
+ value="string",
1177
+ interactive=True
1178
+ )
1179
+ convert_dtype_btn = gr.Button("Convert Data Type")
1180
+
1181
+ with gr.Column(scale=1):
1182
+ gr.Markdown("### High Cardinality Management")
1183
+ high_card_threshold = gr.Slider(
1184
+ minimum=1,
1185
+ maximum=100,
1186
+ value=50,
1187
+ step=1,
1188
+ label="High Cardinality Threshold (unique values)"
1189
+ )
1190
+ high_card_action = gr.Radio(
1191
+ choices=["Show only", "Drop columns"],
1192
+ value="Show only",
1193
+ label="Action for High Cardinality Columns"
1194
+ )
1195
+ high_card_btn = gr.Button("Apply High Cardinality Filter")
1196
+ high_card_results = gr.Markdown("High cardinality columns will appear here")
1197
+
1198
+ # Tab 2: Missing Value Treatment
1199
+ with gr.TabItem("🎯 Missing Values"):
1200
+ with gr.Row():
1201
+ with gr.Column(scale=1):
1202
+ gr.Markdown("### Numeric Imputation")
1203
+ num_impute_cols = gr.CheckboxGroup(
1204
+ label="Select Numeric Columns",
1205
+ choices=[],
1206
+ interactive=True
1207
+ )
1208
+ num_impute_method = gr.Dropdown(
1209
+ choices=[
1210
+ "Mean", "Median", "Mode", "KNN", "MICE",
1211
+ "LOCF", "NOCB", "Linear Interpolation",
1212
+ "Forward Fill", "Backward Fill", "Zero", "Custom Value"
1213
+ ],
1214
+ value="Median",
1215
+ label="Imputation Method"
1216
+ )
1217
+ num_custom_value = gr.Textbox(
1218
+ label="Custom Value (if selected)",
1219
+ visible=False
1220
+ )
1221
+
1222
+ gr.Markdown("### Categorical Imputation")
1223
+ cat_impute_cols = gr.CheckboxGroup(
1224
+ label="Select Categorical Columns",
1225
+ choices=[],
1226
+ interactive=True
1227
+ )
1228
+ cat_impute_method = gr.Dropdown(
1229
+ choices=[
1230
+ "Mode", "Most Frequent", "Arbitrary ('Unknown')",
1231
+ "KNN", "MICE", "Constant Value"
1232
+ ],
1233
+ value="Mode",
1234
+ label="Imputation Method"
1235
+ )
1236
+ cat_custom_value = gr.Textbox(
1237
+ label="Custom Value (if selected)",
1238
+ value="Unknown",
1239
+ visible=False
1240
+ )
1241
+
1242
+ impute_btn = gr.Button("Apply Imputation")
1243
+
1244
+ with gr.Column(scale=1):
1245
+ gr.Markdown("### Missing Value Analysis")
1246
+ missing_summary = gr.Markdown("Missing value summary will appear here")
1247
+ missing_heatmap = gr.Plot(label="Missing Value Heatmap")
1248
+
1249
+ # Tab 3: Feature Engineering
1250
+ with gr.TabItem("βš™οΈ Feature Engineering"):
1251
+ with gr.Row():
1252
+ with gr.Column(scale=1):
1253
+ gr.Markdown("### Text Operations")
1254
+ text_ops_cols = gr.CheckboxGroup(
1255
+ label="Select Text Columns",
1256
+ choices=[],
1257
+ interactive=True
1258
+ )
1259
+ text_operations = gr.CheckboxGroup(
1260
+ choices=[
1261
+ "Remove leading/trailing whitespace",
1262
+ "Convert to lowercase",
1263
+ "Convert to uppercase",
1264
+ "Remove special characters",
1265
+ "Remove numbers",
1266
+ "Remove extra spaces"
1267
+ ],
1268
+ label="Text Operations"
1269
+ )
1270
+
1271
+ gr.Markdown("### Value Replacement")
1272
+ replace_col = gr.Dropdown(
1273
+ label="Column for Value Replacement",
1274
+ choices=[],
1275
+ interactive=True
1276
+ )
1277
+ replace_old = gr.Textbox(
1278
+ label="Value to Replace",
1279
+ placeholder="Enter value to find"
1280
+ )
1281
+ replace_new = gr.Textbox(
1282
+ label="Replacement Value",
1283
+ placeholder="Enter new value"
1284
+ )
1285
+ replace_btn = gr.Button("Replace Values")
1286
+
1287
+ gr.Markdown("### Text to Columns")
1288
+ split_col = gr.Dropdown(
1289
+ label="Column to Split",
1290
+ choices=[],
1291
+ interactive=True
1292
+ )
1293
+ split_delimiter = gr.Textbox(
1294
+ label="Delimiter",
1295
+ value=",",
1296
+ placeholder="Enter delimiter (e.g., ',', ';', ' ')",
1297
+ max_lines=1
1298
+ )
1299
+ split_btn = gr.Button("Split Column")
1300
+
1301
+ with gr.Column(scale=1):
1302
+ gr.Markdown("### Formula-Based Columns")
1303
+ formula_expr = gr.Textbox(
1304
+ label="Formula Expression",
1305
+ placeholder="Example: Sales * Quantity, or LEFT(ProductName, 3)",
1306
+ lines=2
1307
+ )
1308
+ formula_new_col = gr.Textbox(
1309
+ label="New Column Name",
1310
+ placeholder="Enter name for new column"
1311
+ )
1312
+ formula_examples = gr.Markdown("""
1313
+ **Formula Examples:**
1314
+ - `Revenue = Sales * Price`
1315
+ - `FullName = FirstName + ' ' + LastName`
1316
+ - `Profit = Revenue - Cost`
1317
+ - `Category = LEFT(ProductCode, 3)`
1318
+ - `Age = CURRENT_YEAR - BirthYear`
1319
+ """)
1320
+ formula_btn = gr.Button("Create New Column")
1321
+
1322
+ gr.Markdown("### Date Operations")
1323
+ date_col_selected = gr.Dropdown(
1324
+ label="Date Column",
1325
+ choices=[],
1326
+ interactive=True
1327
+ )
1328
+ date_operations = gr.CheckboxGroup(
1329
+ choices=[
1330
+ "Extract Year", "Extract Month", "Extract Day",
1331
+ "Extract Quarter", "Extract Day of Week",
1332
+ "Calculate Age", "Calculate Days Difference"
1333
+ ],
1334
+ label="Date Operations"
1335
+ )
1336
+ date_btn = gr.Button("Apply Date Operations")
1337
+
1338
+ # Tab 4: Transformations
1339
+ with gr.TabItem("πŸ”„ Transformations"):
1340
+ with gr.Row():
1341
+ with gr.Column(scale=1):
1342
+ gr.Markdown("### Pivot/Unpivot")
1343
+ transform_type = gr.Radio(
1344
+ choices=["Pivot", "Unpivot", "Transpose"],
1345
+ label="Transformation Type"
1346
+ )
1347
+
1348
+ gr.Markdown("#### Pivot Options")
1349
+ pivot_index = gr.Dropdown(
1350
+ label="Index Columns",
1351
+ choices=[],
1352
+ interactive=True,
1353
+ multiselect=True
1354
+ )
1355
+ pivot_columns = gr.Dropdown(
1356
+ label="Columns to Pivot",
1357
+ choices=[],
1358
+ interactive=True,
1359
+ multiselect=True
1360
+ )
1361
+ pivot_values = gr.Dropdown(
1362
+ label="Values Columns",
1363
+ choices=[],
1364
+ interactive=True,
1365
+ multiselect=True
1366
+ )
1367
+
1368
+ gr.Markdown("#### Group By & Summarize")
1369
+ groupby_cols = gr.Dropdown(
1370
+ label="Group By Columns",
1371
+ choices=[],
1372
+ interactive=True,
1373
+ multiselect=True
1374
+ )
1375
+ agg_columns = gr.Dropdown(
1376
+ label="Columns to Aggregate",
1377
+ choices=[],
1378
+ interactive=True,
1379
+ multiselect=True
1380
+ )
1381
+ agg_functions = gr.Dropdown(
1382
+ choices=["sum", "mean", "median", "min", "max", "count", "std"],
1383
+ label="Aggregation Function",
1384
+ multiselect=True
1385
+ )
1386
+
1387
+ with gr.Column(scale=1):
1388
+ gr.Markdown("### Data Combination")
1389
+ combo_type = gr.Radio(
1390
+ choices=["Append (Union)", "Join", "Merge"],
1391
+ label="Combination Type"
1392
+ )
1393
+
1394
+ gr.Markdown("#### Join/Merge Options")
1395
+ join_type = gr.Dropdown(
1396
+ choices=["inner", "left", "right", "outer"],
1397
+ value="inner",
1398
+ label="Join Type",
1399
+ visible=False
1400
+ )
1401
+ join_key = gr.Dropdown(
1402
+ label="Join Key Column",
1403
+ choices=[],
1404
+ interactive=True,
1405
+ visible=False
1406
+ )
1407
+
1408
+ upload_second_df = gr.File(
1409
+ label="Upload Second Dataset for Combination",
1410
+ file_types=[".csv", ".xlsx", ".xls"],
1411
+ visible=False
1412
+ )
1413
+
1414
+ transform_btn = gr.Button("Apply Transformation")
1415
+ combo_btn = gr.Button("Combine Data", visible=False)
1416
+
1417
+ # Tab 5: Data Preview & Actions
1418
+ with gr.TabItem("πŸ‘οΈ Data Preview"):
1419
+ with gr.Row():
1420
+ with gr.Column(scale=1):
1421
+ gr.Markdown("### Interactive Data Grid")
1422
+ gr.Markdown("""
1423
+ **Features:**
1424
+ - Sort by clicking column headers
1425
+ - Filter using column menu
1426
+ - Copy values with Ctrl+C
1427
+ - Select multiple rows
1428
+ """)
1429
+
1430
+ row_count = gr.Slider(
1431
+ minimum=10,
1432
+ maximum=1000,
1433
+ value=100,
1434
+ step=10,
1435
+ label="Number of Rows to Display"
1436
+ )
1437
+
1438
+ duplicate_btn = gr.Button("πŸ“‹ Duplicate Current Dataset")
1439
+ reset_btn = gr.Button("πŸ”„ Reset to Original Data")
1440
+
1441
+ with gr.Column(scale=2):
1442
+ interactive_preview = gr.Dataframe(
1443
+ interactive=True,
1444
+ label="Interactive Data Preview",
1445
+ wrap=True
1446
+ )
1447
+
1448
+ # Global Actions at Bottom
1449
  with gr.Row():
1450
+ apply_all_btn = gr.Button("πŸš€ Apply All Changes", variant="primary", size="lg")
1451
+ download_clean_btn = gr.Button("πŸ“₯ Download Clean CSV", variant="secondary")
1452
+
1453
+ prep_output = gr.Markdown("Preparation status will appear here")
1454
+
 
 
 
 
 
 
 
 
 
 
 
 
1455
  with gr.TabItem("πŸ“Š Visualize (NL)"):
1456
  gr.Markdown("Type a natural-language request to create a plot (e.g., 'histogram of age', 'scatter income vs age', 'bar of country').")
1457
  nl_input = gr.Textbox(label="Describe chart")
 
1593
 
1594
  download_clean.click(fn=lambda p: p if p else None, inputs=[clean_state], outputs=[download_clean])
1595
 
1596
+ # Prepare tab callbacks
1597
+ def _update_prepare_ui(df_path):
1598
+ """Update all UI elements when prepare tab is loaded"""
1599
+ if not df_path:
1600
+ return update_column_lists(None)
1601
+
1602
+ df = pd.read_csv(df_path)
1603
+ return update_column_lists(df)
1604
+
1605
+ # When prepare tab is selected, update the UI
1606
+ prep_tabs.select(
1607
+ fn=_update_prepare_ui,
1608
+ inputs=[df_state],
1609
+ outputs=[
1610
+ available_cols, rename_col_old, dtype_col, num_impute_cols,
1611
+ cat_impute_cols, replace_col, split_col, date_col_selected, pivot_index
1612
+ ]
1613
+ )
1614
+
1615
+ def _apply_all_preparations(df_path, selected_cols, high_card_threshold, high_card_action):
1616
+ """Apply all preparation steps"""
1617
+ try:
1618
+ if not df_path:
1619
+ return "No dataset loaded", None
1620
+
1621
+ df = pd.read_csv(df_path)
1622
+
1623
+ # Apply column selection
1624
+ if selected_cols:
1625
+ df = df[selected_cols]
1626
+
1627
+ # Apply high cardinality filtering
1628
+ high_card_cols = detect_high_cardinality_cols(df, high_card_threshold)
1629
+ if high_card_action == "Drop columns" and high_card_cols:
1630
+ cols_to_drop = [col for col, count in high_card_cols]
1631
+ df = df.drop(columns=cols_to_drop)
1632
+
1633
+ # Save cleaned data
1634
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
1635
+ df.to_csv(tmp.name, index=False)
1636
+
1637
+ summary = f"""
1638
+ **Preparation Summary:**
1639
+ - Final dataset: {len(df)} rows, {len(df.columns)} columns
1640
+ - High cardinality columns found: {len(high_card_cols)}
1641
+ - Columns kept: {', '.join(df.columns.tolist())}
1642
+ """
1643
+
1644
+ return summary, tmp.name
1645
+
1646
+ except Exception as e:
1647
+ return f"Preparation failed: {str(e)}", None
1648
+
1649
+ apply_all_btn.click(
1650
+ fn=_apply_all_preparations,
1651
+ inputs=[df_state, available_cols, high_card_threshold, high_card_action],
1652
+ outputs=[prep_output, clean_state]
1653
+ )
1654
+
1655
+ # Column selection helpers
1656
+ def _select_all_cols(df_path):
1657
+ if not df_path:
1658
+ return gr.CheckboxGroup.update(choices=[])
1659
+ df = pd.read_csv(df_path)
1660
+ return gr.CheckboxGroup.update(value=df.columns.tolist())
1661
+
1662
+ def _deselect_all_cols():
1663
+ return gr.CheckboxGroup.update(value=[])
1664
+
1665
+ select_all_cols.click(fn=_select_all_cols, inputs=[df_state], outputs=[available_cols])
1666
+ deselect_all_cols.click(fn=_deselect_all_cols, outputs=[available_cols])
1667
+
1668
+ # Show/hide custom value inputs based on method selection
1669
+ def _toggle_custom_inputs(num_method, cat_method):
1670
+ num_visible = num_method == "Custom Value"
1671
+ cat_visible = cat_method == "Constant Value"
1672
+ return (
1673
+ gr.Textbox.update(visible=num_visible),
1674
+ gr.Textbox.update(visible=cat_visible)
1675
+ )
1676
+
1677
+ num_impute_method.change(
1678
+ fn=_toggle_custom_inputs,
1679
+ inputs=[num_impute_method, cat_impute_method],
1680
+ outputs=[num_custom_value, cat_custom_value]
1681
+ )
1682
+
1683
+ cat_impute_method.change(
1684
+ fn=_toggle_custom_inputs,
1685
+ inputs=[num_impute_method, cat_impute_method],
1686
+ outputs=[num_custom_value, cat_custom_value]
1687
+ )
1688
+
1689
+ # Data duplication
1690
+ def _duplicate_dataset(df_path):
1691
+ if not df_path:
1692
+ return None
1693
+ # Create a copy of the current dataset
1694
+ import shutil
1695
+ import tempfile
1696
+ new_path = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
1697
+ shutil.copy2(df_path, new_path.name)
1698
+ return new_path.name
1699
+
1700
+ duplicate_btn.click(fn=_duplicate_dataset, inputs=[df_state], outputs=[df_state])
1701
+
1702
  # NLP visualize
1703
  def _nl_visualize(query, clean_path, df_path):
1704
  try: