Spaces:
Sleeping
Sleeping
Update app.py
Browse files
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("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 951 |
with gr.Row():
|
| 952 |
-
|
| 953 |
-
|
| 954 |
-
|
| 955 |
-
|
| 956 |
-
|
| 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:
|