diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -9,1237 +9,1152 @@ import warnings warnings.filterwarnings('ignore') def install_package(package): - """Install a package using pip""" try: - print(f"๐Ÿ“ฆ Installing {package}...") subprocess.check_call([sys.executable, "-m", "pip", "install", package, "--quiet", "--no-warn-script-location"]) - print(f"โœ… Successfully installed {package}") return True except Exception as e: - print(f"โŒ Failed to install {package}: {e}") + print(f"Failed to install {package}: {e}") return False def install_all_packages(): - """Install all required packages""" packages = [ - "numpy>=1.21.0", - "pandas>=1.3.0", - "matplotlib>=3.4.0", - "seaborn>=0.11.0", - "plotly>=5.0.0", - "scikit-learn>=1.0.0", - "tensorflow>=2.8.0", - "keras>=2.8.0", - "xgboost>=1.5.0", - "lightgbm>=3.3.0", - "catboost>=1.0.0", - "requests>=2.25.0", - "openpyxl>=3.0.0", - "gradio>=4.0.0" + "numpy>=1.21.0", "pandas>=1.3.0", "matplotlib>=3.4.0", + "seaborn>=0.11.0", "plotly>=5.0.0", "scikit-learn>=1.0.0", + "xgboost>=1.5.0", "lightgbm>=3.3.0", "catboost>=1.0.0", + "requests>=2.25.0", "openpyxl>=3.0.0", "gradio>=4.0.0", + "scipy>=1.7.0" ] - - print("๐Ÿš€ Starting installation of all required packages...") - print(f"๐Ÿ“‹ Total packages to install: {len(packages)}") - - success_count = 0 - for i, package in enumerate(packages, 1): - print(f"\n[{i}/{len(packages)}] Processing {package}") - if install_package(package): - success_count += 1 - - print(f"\n๐ŸŽ‰ Installation completed! {success_count}/{len(packages)} packages installed successfully.") - return success_count == len(packages) - -# Install all packages at startup -install_all_packages() + for package in packages: + install_package(package) -# Import all packages -print("\n๐Ÿ“ฅ Importing all packages...") +install_all_packages() +import gradio as gr +import pandas as pd +import numpy as np +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import seaborn as sns +from sklearn.model_selection import train_test_split, cross_val_score, RandomizedSearchCV +from sklearn.preprocessing import StandardScaler, LabelEncoder +from sklearn.ensemble import (RandomForestClassifier, RandomForestRegressor, + GradientBoostingClassifier, GradientBoostingRegressor, + AdaBoostClassifier, AdaBoostRegressor, + ExtraTreesClassifier, ExtraTreesRegressor) +from sklearn.linear_model import (LogisticRegression, LinearRegression, + Ridge, Lasso, ElasticNet) +from sklearn.svm import SVC, SVR +from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor +from sklearn.naive_bayes import GaussianNB +from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor +from sklearn.cluster import KMeans +from sklearn.metrics import (accuracy_score, f1_score, precision_score, + recall_score, mean_squared_error, + mean_absolute_error, r2_score, + classification_report, confusion_matrix, + roc_auc_score, silhouette_score) +from scipy import stats + +# Optional imports try: - import gradio as gr - import pandas as pd - import numpy as np - print("โœ… Core packages imported") -except ImportError as e: - print(f"โŒ Core packages import failed: {e}") + import xgboost as xgb + XGBOOST_AVAILABLE = True +except: + XGBOOST_AVAILABLE = False try: - import matplotlib - matplotlib.use('Agg') - import matplotlib.pyplot as plt - import seaborn as sns - import plotly.graph_objects as go - import plotly.express as px - from plotly.subplots import make_subplots - print("โœ… Visualization packages imported") -except ImportError as e: - print(f"โŒ Visualization packages import failed: {e}") + import lightgbm as lgb + LIGHTGBM_AVAILABLE = True +except: + LIGHTGBM_AVAILABLE = False try: - from sklearn.model_selection import train_test_split, cross_val_score - from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor - from sklearn.linear_model import LogisticRegression, LinearRegression - from sklearn.svm import SVC, SVR - from sklearn.metrics import accuracy_score, classification_report, mean_squared_error, r2_score - from sklearn.preprocessing import StandardScaler, LabelEncoder - from sklearn.cluster import KMeans - print("โœ… Scikit-learn imported") -except ImportError as e: - print(f"โŒ Scikit-learn import failed: {e}") + import catboost as cb + CATBOOST_AVAILABLE = True +except: + CATBOOST_AVAILABLE = False -try: - import tensorflow as tf - from tensorflow import keras - from tensorflow.keras.models import Sequential - from tensorflow.keras.layers import Dense, LSTM, Conv2D - print("โœ… TensorFlow and Keras imported") -except ImportError as e: - print(f"โš ๏ธ TensorFlow/Keras import failed (optional): {e}") -try: - import xgboost as xgb - print("โœ… XGBoost imported") -except ImportError as e: - print(f"โš ๏ธ XGBoost import failed (optional): {e}") +# ============================================================ +# REAL DATA LOADER AGENT +# ============================================================ +class DataLoaderAgent: + def load_data(self, source, source_type='csv', **kwargs): + try: + if source_type == 'csv': + data = pd.read_csv(source) + elif source_type == 'json': + data = pd.read_json(source) + elif source_type == 'excel': + data = pd.read_excel(source) + else: + raise ValueError(f"Unsupported source type: {source_type}") -try: - import lightgbm as lgb - print("โœ… LightGBM imported") -except ImportError as e: - print(f"โš ๏ธ LightGBM import failed (optional): {e}") + # Auto-detect datetime columns + for col in data.columns: + if data[col].dtype == 'object': + try: + data[col] = pd.to_datetime(data[col]) + except: + pass -try: - import catboost as cb - from catboost import CatBoostClassifier, CatBoostRegressor - print("โœ… CatBoost imported") -except ImportError as e: - print(f"โš ๏ธ CatBoost import failed (optional): {e}") + return { + 'status': 'success', + 'data': data, + 'info': { + 'shape': data.shape, + 'columns': list(data.columns), + 'dtypes': data.dtypes.astype(str).to_dict(), + 'memory_usage': f"{data.memory_usage(deep=True).sum() / 1024**2:.2f} MB" + } + } + except Exception as e: + return {'status': 'error', 'error': str(e), 'data': None} + + +# ============================================================ +# REAL DATA CLEANING AGENT +# ============================================================ +class DataCleaningAgent: + def clean_data(self, data, aggressive_cleaning=False): + cleaned = data.copy() + report = {'original_shape': data.shape, 'cleaning_steps': []} + + # Handle missing values smartly + missing_info = {} + for col in cleaned.columns: + missing_count = cleaned[col].isnull().sum() + if missing_count > 0: + missing_info[col] = missing_count + if cleaned[col].dtype == 'object': + mode_val = cleaned[col].mode() + fill_val = mode_val[0] if len(mode_val) > 0 else 'Unknown' + cleaned[col].fillna(fill_val, inplace=True) + elif 'datetime' in str(cleaned[col].dtype): + cleaned[col].fillna(method='ffill', inplace=True) + else: + skewness = abs(cleaned[col].skew()) + if skewness > 1: + cleaned[col].fillna(cleaned[col].median(), inplace=True) + else: + cleaned[col].fillna(cleaned[col].mean(), inplace=True) + + report['missing_values'] = missing_info + report['cleaning_steps'].append('Missing values handled') + + # Remove duplicates + initial_count = len(cleaned) + cleaned.drop_duplicates(inplace=True) + cleaned.reset_index(drop=True, inplace=True) + dups_removed = initial_count - len(cleaned) + report['duplicates_removed'] = dups_removed + + # Handle outliers if aggressive + outlier_info = {} + if aggressive_cleaning: + for col in cleaned.select_dtypes(include=[np.number]).columns: + Q1 = cleaned[col].quantile(0.25) + Q3 = cleaned[col].quantile(0.75) + IQR = Q3 - Q1 + if IQR == 0: + continue + lower = Q1 - 1.5 * IQR + upper = Q3 + 1.5 * IQR + outlier_count = ((cleaned[col] < lower) | (cleaned[col] > upper)).sum() + if outlier_count > 0: + outlier_info[col] = outlier_count + cleaned[col] = cleaned[col].clip(lower, upper) + + report['outliers_handled'] = outlier_info + + # Handle infinite values + for col in cleaned.select_dtypes(include=[np.number]).columns: + if np.isinf(cleaned[col]).any(): + cleaned[col].replace([np.inf, -np.inf], np.nan, inplace=True) + cleaned[col].fillna(cleaned[col].median(), inplace=True) + + report['final_shape'] = cleaned.shape + report['rows_removed'] = data.shape[0] - cleaned.shape[0] + + return {'status': 'success', 'data': cleaned, 'cleaning_report': report} + + +# ============================================================ +# REAL EDA AGENT +# ============================================================ +class EDAAgent: + def analyze_data(self, data, target_column=None): + try: + analysis = {} -try: - import requests - import openpyxl - print("โœ… Utility packages imported") -except ImportError as e: - print(f"โŒ Utility packages import failed: {e}") - -print("๐ŸŽ‰ All package imports completed!") - -class SafeDataAnalyzer: - """Safe data analyzer that handles datetime and other special data types""" - - @staticmethod - def detect_column_types(df): - """Detect and categorize column types safely""" - column_types = { - 'numeric': [], - 'categorical': [], - 'datetime': [], - 'boolean': [], - 'text': [] - } - - for col in df.columns: - dtype = str(df[col].dtype).lower() - - if 'datetime' in dtype or 'timestamp' in dtype: - column_types['datetime'].append(col) - elif 'bool' in dtype: - column_types['boolean'].append(col) - elif 'int' in dtype or 'float' in dtype: - column_types['numeric'].append(col) - elif 'object' in dtype: - if df[col].nunique() < len(df) * 0.5 and df[col].nunique() < 50: - column_types['categorical'].append(col) + # Column type detection + column_types = { + 'numeric': list(data.select_dtypes(include=[np.number]).columns), + 'categorical': list(data.select_dtypes(include=['object', 'category']).columns), + 'datetime': list(data.select_dtypes(include=['datetime64']).columns), + } + analysis['column_types'] = column_types + + # Basic statistics + numeric_df = data[column_types['numeric']] + analysis['basic_stats'] = {} + if not numeric_df.empty: + analysis['basic_stats']['describe'] = numeric_df.describe().to_dict() + analysis['basic_stats']['skewness'] = numeric_df.skew().to_dict() + analysis['basic_stats']['kurtosis'] = numeric_df.kurtosis().to_dict() + + # Correlation matrix + analysis['correlations'] = {} + if len(column_types['numeric']) > 1: + corr_matrix = data[column_types['numeric']].corr() + analysis['correlations']['matrix'] = corr_matrix.to_dict() + + # Strong correlations + strong = [] + for i in range(len(corr_matrix.columns)): + for j in range(i+1, len(corr_matrix.columns)): + val = corr_matrix.iloc[i, j] + if not np.isnan(val) and abs(val) > 0.7: + strong.append({ + 'var1': corr_matrix.columns[i], + 'var2': corr_matrix.columns[j], + 'correlation': round(val, 3) + }) + analysis['correlations']['strong'] = strong + + # Missing values + analysis['missing_values'] = data.isnull().sum().to_dict() + + # Duplicate count + analysis['duplicates'] = int(data.duplicated().sum()) + + # Data quality score + total_cells = data.shape[0] * data.shape[1] + missing_cells = data.isnull().sum().sum() + analysis['data_quality_score'] = round( + (1 - missing_cells / total_cells) * 100, 1 + ) + + # Target analysis + if target_column and target_column in data.columns: + target = data[target_column] + is_classification = ( + target.dtype == 'object' or target.nunique() < 20 + ) + analysis['target_analysis'] = { + 'type': 'classification' if is_classification else 'regression', + 'unique_values': int(target.nunique()), + 'missing': int(target.isnull().sum()) + } + if is_classification: + analysis['target_analysis']['class_distribution'] = ( + target.value_counts().to_dict() + ) else: - column_types['text'].append(col) + analysis['target_analysis']['stats'] = { + 'mean': round(float(target.mean()), 4), + 'median': round(float(target.median()), 4), + 'std': round(float(target.std()), 4), + 'skewness': round(float(target.skew()), 4) + } + + return {'status': 'success', 'analysis': analysis} + + except Exception as e: + return {'status': 'error', 'error': str(e), 'analysis': {}} + + +# ============================================================ +# REAL MODEL BUILDING AGENT +# ============================================================ +class ModelBuildingAgent: + def __init__(self): + self.scaler = StandardScaler() + self.label_encoders = {} + + def _preprocess(self, X): + X_proc = X.copy() + + # Drop datetime columns + datetime_cols = X_proc.select_dtypes(include=['datetime64']).columns + X_proc.drop(columns=datetime_cols, inplace=True) + + # Encode categoricals + for col in X_proc.select_dtypes(include=['object']).columns: + if X_proc[col].nunique() <= 10: + dummies = pd.get_dummies(X_proc[col], prefix=col, drop_first=True) + X_proc = pd.concat([X_proc, dummies], axis=1) + X_proc.drop(columns=[col], inplace=True) else: - column_types['categorical'].append(col) - - return column_types - - @staticmethod - def safe_describe(df): - """Safely describe dataframe without breaking on datetime columns""" + le = LabelEncoder() + X_proc[col] = le.fit_transform(X_proc[col].astype(str)) + self.label_encoders[col] = le + + # Fill missing + X_proc.fillna(X_proc.median(numeric_only=True), inplace=True) + X_proc.replace([np.inf, -np.inf], np.nan, inplace=True) + X_proc.fillna(0, inplace=True) + + return X_proc + + def _detect_problem_type(self, y): + if y.dtype == 'object' or y.nunique() < 20: + return 'classification' + return 'regression' + + def build_models(self, data, target_column): try: - column_types = SafeDataAnalyzer.detect_column_types(df) - - description = {} - - if column_types['numeric']: - numeric_df = df[column_types['numeric']] - description['numeric'] = numeric_df.describe() + X = data.drop(columns=[target_column]) + y = data[target_column] + + problem_type = self._detect_problem_type(y) + + X_proc = self._preprocess(X) + + # Encode target if classification + if problem_type == 'classification' and y.dtype == 'object': + le = LabelEncoder() + y = le.fit_transform(y) + self.label_encoders['target'] = le + + # Train-test split + X_train, X_test, y_train, y_test = train_test_split( + X_proc, y, test_size=0.2, random_state=42, + stratify=y if problem_type == 'classification' else None + ) + + # Scale + X_train_scaled = self.scaler.fit_transform(X_train) + X_test_scaled = self.scaler.transform(X_test) + + # Define models + if problem_type == 'classification': + models = { + 'Logistic Regression': LogisticRegression(max_iter=1000, random_state=42), + 'Random Forest': RandomForestClassifier(n_estimators=100, random_state=42), + 'Gradient Boosting': GradientBoostingClassifier(random_state=42), + 'Extra Trees': ExtraTreesClassifier(n_estimators=100, random_state=42), + 'SVM': SVC(probability=True, random_state=42), + 'KNN': KNeighborsClassifier(n_neighbors=5), + 'Naive Bayes': GaussianNB(), + 'Decision Tree': DecisionTreeClassifier(random_state=42), + 'AdaBoost': AdaBoostClassifier(random_state=42), + } + else: + models = { + 'Linear Regression': LinearRegression(), + 'Ridge': Ridge(random_state=42), + 'Lasso': Lasso(random_state=42), + 'Random Forest': RandomForestRegressor(n_estimators=100, random_state=42), + 'Gradient Boosting': GradientBoostingRegressor(random_state=42), + 'Extra Trees': ExtraTreesRegressor(n_estimators=100, random_state=42), + 'SVR': SVR(), + 'KNN': KNeighborsRegressor(n_neighbors=5), + 'Decision Tree': DecisionTreeRegressor(random_state=42), + 'AdaBoost': AdaBoostRegressor(random_state=42), + } + + # Optional boosting models + if XGBOOST_AVAILABLE: + if problem_type == 'classification': + models['XGBoost'] = xgb.XGBClassifier( + random_state=42, eval_metric='logloss', verbosity=0 + ) + else: + models['XGBoost'] = xgb.XGBRegressor(random_state=42, verbosity=0) + + if LIGHTGBM_AVAILABLE: + if problem_type == 'classification': + models['LightGBM'] = lgb.LGBMClassifier(random_state=42, verbose=-1) + else: + models['LightGBM'] = lgb.LGBMRegressor(random_state=42, verbose=-1) + + if CATBOOST_AVAILABLE: + if problem_type == 'classification': + models['CatBoost'] = cb.CatBoostClassifier(random_state=42, verbose=False) + else: + models['CatBoost'] = cb.CatBoostRegressor(random_state=42, verbose=False) + + # Train and evaluate all models + results = {} + for name, model in models.items(): try: - description['skewness'] = numeric_df.skew() - except Exception as e: - print(f"Warning: Could not calculate skewness: {e}") - description['skewness'] = pd.Series() - - if column_types['categorical']: - categorical_df = df[column_types['categorical']] - description['categorical'] = categorical_df.describe() - - if column_types['datetime']: - datetime_df = df[column_types['datetime']] - description['datetime'] = {} - for col in column_types['datetime']: - try: - description['datetime'][col] = { - 'min': datetime_df[col].min(), - 'max': datetime_df[col].max(), - 'unique_count': datetime_df[col].nunique() + model.fit(X_train_scaled, y_train) + y_pred = model.predict(X_test_scaled) + + if problem_type == 'classification': + metrics = { + 'accuracy': round(accuracy_score(y_test, y_pred), 4), + 'f1_score': round(f1_score(y_test, y_pred, average='weighted', zero_division=0), 4), + 'precision': round(precision_score(y_test, y_pred, average='weighted', zero_division=0), 4), + 'recall': round(recall_score(y_test, y_pred, average='weighted', zero_division=0), 4), } - except Exception as e: - print(f"Warning: Could not analyze datetime column {col}: {e}") - - return description, column_types - except Exception as e: - print(f"Error in safe_describe: {e}") - return {}, {'numeric': [], 'categorical': [], 'datetime': [], 'boolean': [], 'text': []} - - @staticmethod - def safe_correlation(df): - """Safely calculate correlation matrix for numeric columns only""" - try: - column_types = SafeDataAnalyzer.detect_column_types(df) - numeric_cols = column_types['numeric'] - - if len(numeric_cols) > 1: - return df[numeric_cols].corr() + # ROC AUC for binary + if len(np.unique(y_test)) == 2 and hasattr(model, 'predict_proba'): + try: + y_prob = model.predict_proba(X_test_scaled)[:, 1] + metrics['roc_auc'] = round(roc_auc_score(y_test, y_prob), 4) + except: + pass + else: + metrics = { + 'rmse': round(float(mean_squared_error(y_test, y_pred, squared=False)), 4), + 'mae': round(float(mean_absolute_error(y_test, y_pred)), 4), + 'r2_score': round(float(r2_score(y_test, y_pred)), 4), + } + + results[name] = {**metrics, 'model': model, 'status': 'success'} + + except Exception as e: + results[name] = {'status': 'error', 'error': str(e)} + + # Select best model + valid = {k: v for k, v in results.items() if v['status'] == 'success'} + if not valid: + return {'status': 'error', 'error': 'No models trained successfully'} + + if problem_type == 'classification': + best_name = max(valid.keys(), key=lambda x: valid[x]['accuracy']) else: - return pd.DataFrame() + best_name = min(valid.keys(), key=lambda x: valid[x]['rmse']) + + # Feature importance + best_model = valid[best_name]['model'] + feature_importance = {} + try: + if hasattr(best_model, 'feature_importances_'): + fi = dict(zip(X_proc.columns, best_model.feature_importances_)) + feature_importance = dict( + sorted(fi.items(), key=lambda x: x[1], reverse=True) + ) + elif hasattr(best_model, 'coef_'): + coef = best_model.coef_ if len(best_model.coef_.shape) == 1 else best_model.coef_[0] + fi = dict(zip(X_proc.columns, np.abs(coef))) + feature_importance = dict( + sorted(fi.items(), key=lambda x: x[1], reverse=True) + ) + except: + pass + + return { + 'status': 'success', + 'problem_type': problem_type, + 'results': results, + 'best_model': best_name, + 'feature_importance': feature_importance, + 'n_features': X_proc.shape[1], + 'train_size': len(X_train), + 'test_size': len(X_test), + } + except Exception as e: - print(f"Warning: Could not calculate correlation: {e}") - return pd.DataFrame() + return {'status': 'error', 'error': str(e)} + + +# ============================================================ +# REAL DOMAIN EXPERT AGENT +# ============================================================ +class DomainExpertAgent: + def __init__(self): + self.domain_keywords = { + 'finance': ['price', 'revenue', 'profit', 'cost', 'sales', 'income', + 'expense', 'balance', 'rate', 'return', 'amount'], + 'healthcare': ['age', 'bmi', 'weight', 'height', 'diagnosis', 'treatment', + 'patient', 'doctor', 'medication', 'symptoms', 'blood'], + 'retail': ['product', 'category', 'quantity', 'discount', 'store', + 'customer', 'purchase', 'inventory', 'brand', 'order'], + 'marketing': ['campaign', 'clicks', 'impressions', 'conversion', + 'engagement', 'audience', 'channel', 'ctr', 'budget'], + } + + def detect_domain(self, data): + col_text = ' '.join(data.columns).lower() + scores = {} + for domain, keywords in self.domain_keywords.items(): + score = sum(1 for kw in keywords if kw in col_text) + scores[domain] = score + + if max(scores.values()) > 0: + return max(scores, key=scores.get) + return 'general' + + def provide_insights(self, data, domain=None, target_column=None): + if not domain: + domain = self.detect_domain(data) + + insights = [ + f"Dataset contains {data.shape[0]:,} records with {data.shape[1]} features", + f"Detected domain: {domain.title()}", + ] + + numeric_cols = data.select_dtypes(include=[np.number]).columns + categorical_cols = data.select_dtypes(include=['object']).columns + + if len(numeric_cols) > 1: + insights.append(f"{len(numeric_cols)} numeric features available for analysis") + if len(categorical_cols) > 0: + insights.append(f"{len(categorical_cols)} categorical features detected") + + missing = data.isnull().sum().sum() + if missing > 0: + insights.append(f"Found {missing:,} missing values โ€” handled during cleaning") + + recommendations = [ + "Use cross-validation for robust model evaluation", + "Monitor for data drift in production", + ] + + if len(numeric_cols) > 10: + recommendations.append("Consider dimensionality reduction (PCA)") + if len(categorical_cols) > 0: + recommendations.append("Apply proper encoding for categorical features") + + return { + 'detected_domain': domain, + 'insights': insights, + 'recommendations': recommendations, + } -class SupervisorAgentMock: - """Enhanced mock supervisor with safe data handling""" - + +# ============================================================ +# REAL SUPERVISOR AGENT (No Mock!) +# ============================================================ +class SupervisorAgent: def __init__(self): - self.analyzer = SafeDataAnalyzer() - - def execute_pipeline(self, data_source, source_type='csv', target_column=None, domain=None, **kwargs): + self.data_loader = DataLoaderAgent() + self.data_cleaner = DataCleaningAgent() + self.eda_agent = EDAAgent() + self.domain_expert = DomainExpertAgent() + self.model_builder = ModelBuildingAgent() + + def execute_pipeline(self, data_source, source_type='csv', + target_column=None, domain=None, **kwargs): try: - if source_type == 'csv': - df = pd.read_csv(data_source) - elif source_type == 'json': - df = pd.read_json(data_source) - else: - raise ValueError(f"Unsupported file type: {source_type}") - - for col in df.columns: - if df[col].dtype == 'object': - try: - pd.to_datetime(df[col], infer_datetime_format=True) - df[col] = pd.to_datetime(df[col]) - except: - pass - - description, column_types = self.analyzer.safe_describe(df) - correlation_matrix = self.analyzer.safe_correlation(df) - + # Step 1: Load data + load_result = self.data_loader.load_data(data_source, source_type) + if load_result['status'] != 'success': + return {'status': 'error', 'error': load_result['error']} + + data = load_result['data'] + + # Step 2: Clean data + clean_result = self.data_cleaner.clean_data(data) + if clean_result['status'] != 'success': + return {'status': 'error', 'error': clean_result['error']} + + cleaned_data = clean_result['data'] + cleaning_report = clean_result['cleaning_report'] + + # Step 3: EDA + eda_result = self.eda_agent.analyze_data(cleaned_data, target_column) + + # Step 4: Domain insights + domain_result = self.domain_expert.provide_insights( + cleaned_data, domain, target_column + ) + + # Step 5: Model building (only if target specified) + model_result = {} + if target_column and target_column in cleaned_data.columns: + model_result = self.model_builder.build_models(cleaned_data, target_column) + return { 'status': 'success', 'pipeline_results': { 'data_loading': { 'status': 'success', - 'info': { - 'shape': df.shape, - 'columns': list(df.columns), - 'dtypes': df.dtypes.astype(str).to_dict(), - 'column_types': column_types, - 'memory_usage': f"{df.memory_usage(deep=True).sum() / 1024**2:.2f} MB" - } + 'info': load_result['info'] }, 'data_cleaning': { 'status': 'success', - 'cleaning_report': { - 'duplicates_removed': df.duplicated().sum(), - 'missing_values': df.isnull().sum().to_dict(), - 'outliers_handled': self._safe_outlier_detection(df, column_types['numeric']) - } + 'cleaning_report': cleaning_report }, - 'eda': { - 'status': 'success', - 'analysis': { - 'basic_stats': description, - 'column_types': column_types, - 'correlations': { - 'correlation_matrix': correlation_matrix.to_dict() if not correlation_matrix.empty else {} - } - } - }, - 'domain_insights': { - 'detected_domain': domain or 'general', - 'insights': self._generate_domain_insights(df, domain, column_types), - 'recommendations': self._generate_recommendations(df, column_types, target_column) - }, - 'modeling': self._safe_modeling_results(df, target_column, column_types) if target_column else {} + 'eda': eda_result, + 'domain_insights': domain_result, + 'modeling': model_result, }, - 'summary': { - 'key_insights': self._generate_key_insights(df, column_types, target_column), - 'recommendations': self._generate_final_recommendations(df, column_types, domain) - } + 'data': cleaned_data, } + except Exception as e: - return { - 'status': 'error', - 'error': str(e), - 'pipeline_results': {}, - 'summary': {'key_insights': [], 'recommendations': []} - } - - def _safe_outlier_detection(self, df, numeric_cols): - """Safely detect outliers in numeric columns""" - outliers = {} - for col in numeric_cols: - try: - Q1 = df[col].quantile(0.25) - Q3 = df[col].quantile(0.75) - IQR = Q3 - Q1 - lower_bound = Q1 - 1.5 * IQR - upper_bound = Q3 + 1.5 * IQR - outliers[col] = len(df[(df[col] < lower_bound) | (df[col] > upper_bound)]) - except Exception as e: - outliers[col] = 0 - return outliers - - def _generate_domain_insights(self, df, domain, column_types): - """Generate domain-specific insights""" - insights = [ - f"Dataset contains {df.shape[0]:,} records with {df.shape[1]} features", - f"Data types: {len(column_types['numeric'])} numeric, {len(column_types['categorical'])} categorical, {len(column_types['datetime'])} datetime" - ] - - if domain: - insights.append(f"Dataset optimized for {domain.title()} domain analysis") - - if column_types['datetime']: - insights.append(f"Time series analysis possible with {len(column_types['datetime'])} datetime columns") - - return insights - - def _generate_recommendations(self, df, column_types, target_column): - """Generate recommendations based on data analysis""" - recommendations = [] - - if len(column_types['numeric']) > 1: - recommendations.append("Consider feature scaling for numeric variables") - - if column_types['datetime']: - recommendations.append("Extract time-based features (day, month, seasonality)") - - if len(column_types['categorical']) > 0: - recommendations.append("Apply appropriate encoding for categorical variables") - - if target_column and target_column in column_types['categorical']: - recommendations.append("Classification problem detected - consider ensemble methods") - elif target_column and target_column in column_types['numeric']: - recommendations.append("Regression problem detected - evaluate feature importance") - - return recommendations - - def _safe_modeling_results(self, df, target_column, column_types): - """Generate safe modeling results""" - if not target_column or target_column not in df.columns: - return {} - - is_classification = target_column in column_types['categorical'] or df[target_column].nunique() < 20 - - return { - 'status': 'success', - 'problem_type': 'classification' if is_classification else 'regression', - 'best_model': 'Random Forest', - 'results': { - 'Random Forest': {'accuracy': 0.87, 'f1_score': 0.85} if is_classification else {'rmse': 0.45, 'r2_score': 0.82}, - 'SVM': {'accuracy': 0.82, 'f1_score': 0.80} if is_classification else {'rmse': 0.52, 'r2_score': 0.78}, - 'LogisticRegression': {'accuracy': 0.78, 'f1_score': 0.76} if is_classification else {'rmse': 0.58, 'r2_score': 0.74} - }, - 'feature_importance': {col: np.random.random() for col in df.columns if col != target_column and col in column_types['numeric']} - } - - def _generate_key_insights(self, df, column_types, target_column): - """Generate key insights from the analysis""" - insights = [ - f"Dataset contains {df.shape[0]:,} samples with {df.shape[1]} features", - f"Data quality is {(1 - df.isnull().sum().sum() / (df.shape[0] * df.shape[1])) * 100:.1f}% complete" - ] - - if len(column_types['numeric']) > 1: - insights.append("Multiple numeric features available for correlation analysis") - - if column_types['datetime']: - insights.append("Time-based patterns can be analyzed for temporal insights") - - return insights - - def _generate_final_recommendations(self, df, column_types, domain): - """Generate final recommendations""" - recommendations = [ - "Consider cross-validation for robust model evaluation", - "Monitor data drift in production environment" - ] - - if len(column_types['numeric']) > 10: - recommendations.append("Consider dimensionality reduction techniques") - - if domain in ['finance', 'healthcare']: - recommendations.append("Implement additional validation for regulatory compliance") - - return recommendations + return {'status': 'error', 'error': str(e)} -class DataSciencePipelineUI: - """Advanced UI for the comprehensive data science pipeline with safe data handling""" +# ============================================================ +# UI CLASS +# ============================================================ +class DataSciencePipelineUI: def __init__(self): - self.supervisor = SupervisorAgentMock() - self.analyzer = SafeDataAnalyzer() + self.supervisor = SupervisorAgent() # REAL agent now! self.current_data = None self.pipeline_results = None - self.processing_step = 0 - self.total_steps = 6 - self.plot_images = {} # Store base64 images for report - - self.custom_css = """ - .main-container { - max-width: 1400px; - margin: 0 auto; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - } - .step-container { - margin: 15px 0; - padding: 20px; - border-radius: 12px; - border-left: 5px solid #3498db; - background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); - box-shadow: 0 4px 6px rgba(0,0,0,0.1); - } - .step-header { - display: flex; - align-items: center; - margin-bottom: 10px; - } - .step-icon { - font-size: 24px; - margin-right: 15px; - } - .progress-bar { - background: linear-gradient(90deg, #4CAF50, #45a049); - height: 6px; - border-radius: 3px; - margin: 10px 0; - } - """ + self.plot_images = {} def create_plot_html(self, fig, plot_id=None): - """Convert matplotlib figure to HTML and store for report""" buf = BytesIO() fig.savefig(buf, format='png', dpi=100, bbox_inches='tight', facecolor='white') buf.seek(0) img_str = base64.b64encode(buf.getvalue()).decode('utf-8') buf.close() plt.close(fig) - if plot_id: self.plot_images[plot_id] = img_str - - return f'' + return f'' def process_file_upload(self, file_obj, learning_type): - """Enhanced file processing with safe datetime handling""" if file_obj is None: return "โŒ No file uploaded", "", [], gr.update(visible=False), "" try: file_path = file_obj.name - file_name = os.path.basename(file_path) - file_extension = os.path.splitext(file_name)[1].lower() + ext = os.path.splitext(file_path)[1].lower() - if file_extension == '.csv': + if ext == '.csv': df = pd.read_csv(file_path) - file_type = 'csv' - elif file_extension == '.json': + elif ext == '.json': df = pd.read_json(file_path) - file_type = 'json' + elif ext in ['.xlsx', '.xls']: + df = pd.read_excel(file_path) else: - return "โŒ Unsupported file type. Please upload CSV or JSON files only.", "", [], gr.update(visible=False), "" - - for col in df.columns: - if df[col].dtype == 'object': - try: - pd.to_datetime(df[col], infer_datetime_format=True, errors='raise') - df[col] = pd.to_datetime(df[col]) - except: - pass + return "โŒ Unsupported file type. Use CSV, JSON, or Excel.", "", [], gr.update(visible=False), "" self.current_data = df - description, column_types = self.analyzer.safe_describe(df) - - file_size = os.path.getsize(file_path) / 1024 - memory_usage = df.memory_usage(deep=True).sum() / 1024**2 - missing_count = df.isnull().sum().sum() - duplicate_count = df.duplicated().sum() - - preview_html = self._create_safe_data_preview(df) + missing = df.isnull().sum().sum() + dups = df.duplicated().sum() + numeric_cols = df.select_dtypes(include=[np.number]).columns + cat_cols = df.select_dtypes(include=['object']).columns file_info = f""" -
-

๐Ÿ“Š File Upload Successful!

-
-
-

๐Ÿ“ File Details

-

Name: {file_name}

-

Type: {file_type.upper()}

-

Size: {file_size:.2f} KB

+
+

๐Ÿ“Š File Loaded Successfully!

+
+
+ Shape
+ {df.shape[0]:,} rows ร— {df.shape[1]} cols
-
-

๐Ÿ“ Dimensions

-

Rows: {df.shape[0]:,}

-

Columns: {df.shape[1]}

-

Memory: {memory_usage:.2f} MB

+
+ Data Quality
+ Missing: {missing:,} | Dups: {dups:,}
-
-

๐Ÿ” Data Quality

-

Missing: {missing_count:,} values

-

Duplicates: {duplicate_count:,} rows

-

Quality: {((1 - (missing_count + duplicate_count) / (df.shape[0] * df.shape[1])) * 100):.1f}%

-
-
-

๐Ÿ“Š Column Types

-

Numeric: {len(column_types['numeric'])}

-

Categorical: {len(column_types['categorical'])}

-

DateTime: {len(column_types['datetime'])}

+
+ Column Types
+ Num: {len(numeric_cols)} | Cat: {len(cat_cols)}
""" + # Data preview + preview = df.head(10) + table_html = "
" + table_html += "" + for col in preview.columns: + table_html += f"" + table_html += "" + for i, row in preview.iterrows(): + bg = '#f9f9f9' if i % 2 == 0 else 'white' + table_html += f"" + for val in row: + if pd.isna(val): + cell = "NaN" + elif isinstance(val, pd.Timestamp): + cell = val.strftime('%Y-%m-%d') + elif isinstance(val, float): + cell = f"{val:.3f}" + else: + s = str(val) + cell = s[:40] + '...' if len(s) > 40 else s + table_html += f"" + table_html += "" + table_html += "
{col}
{cell}
" + columns = df.columns.tolist() - if learning_type == "Supervised": - target_update = gr.update(visible=True, choices=columns, value=columns[0] if columns else None) + target_update = gr.update(visible=True, choices=columns, value=columns[-1]) else: target_update = gr.update(visible=False, choices=columns, value=None) - return ( - file_info, - file_type, - columns, - target_update, - preview_html - ) + return file_info, ext.replace('.', ''), columns, target_update, table_html except Exception as e: - return f"โŒ Error processing file: {str(e)}", "", [], gr.update(visible=False), "" - - def _create_safe_data_preview(self, df): - """Create HTML preview of the data with safe datetime handling""" - preview_df = df.head(10) - - html = """ -
-

๐Ÿ“‹ Data Preview (First 10 rows)

-
- - - - """ - - for col in preview_df.columns: - html += f"" - html += "" - - for idx, row in preview_df.iterrows(): - html += f"" - for value in row: - if pd.isna(value): - cell_value = "NaN" - elif isinstance(value, pd.Timestamp): - cell_value = value.strftime('%Y-%m-%d %H:%M:%S') - elif isinstance(value, (int, float)): - cell_value = f"{value:.3f}" if isinstance(value, float) else str(value) - else: - cell_value = str(value)[:50] + "..." if len(str(value)) > 50 else str(value) + return f"โŒ Error: {str(e)}", "", [], gr.update(visible=False), "" - html += f"" - html += "" - - html += "
{col}
{cell_value}
" - return html - - def update_target_column_visibility(self, learning_type, columns): - """Update target column visibility based on learning type""" - if learning_type == "Supervised": - return gr.update(visible=True, choices=columns, value=columns[0] if columns else "") - else: - return gr.update(visible=False, value=None, choices=columns) - - def run_comprehensive_pipeline(self, file_obj, learning_type, target_column, domain, enable_deep_learning, enable_automl): - """Run the complete comprehensive pipeline with safe data handling""" + def run_pipeline(self, file_obj, learning_type, target_column, domain, + enable_deep_learning, enable_automl): if file_obj is None: - return self._create_error_html("Please upload a file first."), None + return self._error_html("Please upload a file first."), None - if learning_type == "Unsupervised": - target_column = None - elif learning_type == "Supervised" and not target_column: - return self._create_error_html("Please select a target column for supervised learning."), None + if learning_type == "Supervised" and not target_column: + return self._error_html("Please select a target column."), None try: - self.plot_images = {} # Reset plot images - progress_html = self._create_progress_header() - + self.plot_images = {} file_path = file_obj.name - file_extension = os.path.splitext(file_path)[1].lower().replace('.', '') + ext = os.path.splitext(file_path)[1].lower().replace('.', '') + if ext in ['xlsx', 'xls']: + ext = 'excel' + + tc = target_column if learning_type == "Supervised" else None result = self.supervisor.execute_pipeline( data_source=file_path, - source_type=file_extension, - target_column=target_column, + source_type=ext, + target_column=tc, domain=domain.lower() if domain else 'general' ) if result['status'] != 'success': - return self._create_error_html(f"Pipeline failed: {result.get('error', 'Unknown error')}"), None + return self._error_html(f"Pipeline failed: {result.get('error')}"), None self.pipeline_results = result['pipeline_results'] - summary = result['summary'] - - progress_html += self._create_all_steps_html(self.pipeline_results, summary, learning_type, target_column, domain, enable_deep_learning, enable_automl) + self.current_data = result.get('data', self.current_data) - return progress_html, gr.update(visible=True) + html = self._render_pipeline( + self.pipeline_results, learning_type, tc, domain + ) + return html, gr.update(visible=True) except Exception as e: - return self._create_error_html(f"Pipeline execution failed: {str(e)}"), None + return self._error_html(f"Error: {str(e)}"), None + + def _render_pipeline(self, results, learning_type, target_column, domain): + html = self._header_html() + html += self._step_html(1, "๐Ÿ“ Data Loading", + self._format_loading(results.get('data_loading', {}))) + html += self._step_html(2, "๐Ÿงน Data Cleaning", + self._format_cleaning(results.get('data_cleaning', {}))) + html += self._step_html(3, "๐Ÿ“Š Exploratory Data Analysis", + self._format_eda(results.get('eda', {}), target_column)) + html += self._step_html(4, "๐ŸŽ“ Domain Insights", + self._format_domain(results.get('domain_insights', {}))) + + modeling = results.get('modeling', {}) + if modeling and modeling.get('status') == 'success': + html += self._step_html(5, "๐Ÿค– Model Training & Evaluation", + self._format_modeling(modeling)) + elif learning_type == "Unsupervised": + html += self._step_html(5, "๐Ÿ” Cluster Analysis", + self._format_clustering()) + else: + html += self._step_html(5, "๐Ÿค– Modeling", + "

No target column โ€” skipped.

") - def _create_error_html(self, message): - return f""" -
-

โŒ Error

-

{message}

-
- """ + html += self._completion_html(domain) + return html - def _create_progress_header(self): - """Create the main progress header""" + def _header_html(self): return f""" -
-
-

๐Ÿ”ฌ Advanced Data Science Pipeline

-

End-to-end automated machine learning pipeline with comprehensive analysis

-
-

Started: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}

-
-
+
+

๐Ÿ”ฌ Advanced Data Science Pipeline

+

+ Real multi-agent automated ML โ€” {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} +

""" - def _create_all_steps_html(self, pipeline_results, summary, learning_type, target_column, domain, enable_deep_learning, enable_automl): - """Create HTML for all pipeline steps""" - html = "" - - html += self._create_step_html(1, "๐Ÿ“ Data Loading", "completed", - self._format_data_loading_results(pipeline_results.get('data_loading', {}))) - - html += self._create_step_html(2, "๐Ÿงน Data Cleaning", "completed", - self._format_data_cleaning_results(pipeline_results.get('data_cleaning', {}))) - - html += self._create_step_html(3, "๐Ÿ“Š Exploratory Data Analysis", "completed", - self._format_eda_results(pipeline_results.get('eda', {}), self.current_data, learning_type, target_column)) - - html += self._create_step_html(4, "โš™๏ธ Feature Engineering & Domain Analysis", "completed", - self._format_domain_results(pipeline_results.get('domain_insights', {}))) - - if learning_type == "Supervised" and pipeline_results.get('modeling'): - html += self._create_step_html(5, "๐Ÿค– Model Training & Evaluation", "completed", - self._format_modeling_results(pipeline_results.get('modeling', {}), enable_deep_learning)) - else: - html += self._create_step_html(5, "๐Ÿ” Unsupervised Analysis", "completed", - self._format_unsupervised_results(self.current_data)) - - html += self._create_step_html(6, "๐Ÿ“ˆ Results & Recommendations", "completed", - self._format_final_results(summary, pipeline_results)) - - html += self._create_completion_footer(learning_type, domain, enable_deep_learning, enable_automl) - - return html - - def _create_step_html(self, step_num, title, status, content): - """Create HTML for individual pipeline steps""" - status_config = { - 'loading': {'color': '#f39c12', 'icon': 'โณ', 'bg': '#fff3cd'}, - 'completed': {'color': '#27ae60', 'icon': 'โœ…', 'bg': '#d4edda'}, - 'error': {'color': '#e74c3c', 'icon': 'โŒ', 'bg': '#f8d7da'} - } - - config = status_config.get(status, status_config['loading']) - + def _step_html(self, num, title, content): return f""" -
-
- {config['icon']} -
-

Step {step_num}: {title}

-
-
-
-
-
-
- {content} -
+
+

โœ… Step {num}: {title}

+
+ {content}
""" - def _format_data_loading_results(self, results): - """Format data loading results with safe handling""" - if not results or results.get('status') != 'success': - return "

Data loading information not available

" - - info = results.get('info', {}) + def _format_loading(self, result): + if not result: + return "

No loading info.

" + info = result.get('info', {}) shape = info.get('shape', (0, 0)) - column_types = info.get('column_types', {}) - return f""" -
-
-

๐Ÿ“Š Dataset Dimensions

-

Rows: {shape[0]:,}

-

Columns: {shape[1]}

-

Memory: {info.get('memory_usage', 'Unknown')}

+
+
+ Rows
{shape[0]:,} +
+
+ Columns
{shape[1]}
-
-

๐Ÿท๏ธ Column Types

-

Numeric: {len(column_types.get('numeric', []))}

-

Categorical: {len(column_types.get('categorical', []))}

-

DateTime: {len(column_types.get('datetime', []))}

+
+ Memory
{info.get('memory_usage','N/A')}
-

โœ… Data loaded and column types detected successfully!

""" - def _format_data_cleaning_results(self, results): - """Format data cleaning results""" - if not results or results.get('status') != 'success': - return "

Data cleaning information not available

" - - report = results.get('cleaning_report', {}) - duplicates = report.get('duplicates_removed', 0) - missing_values = report.get('missing_values', {}) + def _format_cleaning(self, result): + if not result: + return "

No cleaning info.

" + report = result.get('cleaning_report', {}) + missing = report.get('missing_values', {}) + total_missing = sum(missing.values()) if isinstance(missing, dict) else 0 + dups = report.get('duplicates_removed', 0) outliers = report.get('outliers_handled', {}) - - total_missing = sum(missing_values.values()) if isinstance(missing_values, dict) else 0 total_outliers = sum(outliers.values()) if isinstance(outliers, dict) else 0 return f""" -
-
-

๐Ÿ”ง Cleaning Actions

-

Duplicates Removed: {duplicates}

-

Missing Values: {total_missing}

-

Outliers Handled: {total_outliers}

+
+
+ Missing Handled
{total_missing:,} values +
+
+ Duplicates Removed
{dups:,} rows +
+
+ Outliers Capped
{total_outliers:,} values
-

โœ… Data cleaning completed successfully!

+

+ Final shape: {report.get('final_shape','N/A')} +

""" - def _create_dynamic_histogram(self, data, column): - """Create a dynamic histogram for a numeric column""" - try: - values = data[column].dropna() - if len(values) == 0: - return "

No valid data for histogram

" - - # Dynamically adjust number of bins based on data size and spread - n_bins = min(max(int(np.sqrt(len(values))), 10), 50) - plt.figure(figsize=(8, 6)) - sns.histplot(values, bins=n_bins, kde=True, color='skyblue') - plt.title(f'Distribution of {column}', fontsize=14) - plt.xlabel(column, fontsize=12) - plt.ylabel('Count', fontsize=12) - - # Add range and stats annotations - stats_text = f'Min: {values.min():.2f}\nMax: {values.max():.2f}\nMean: {values.mean():.2f}' - plt.text(0.95, 0.95, stats_text, transform=plt.gca().transAxes, ha='right', va='top', - bbox=dict(facecolor='white', alpha=0.8)) - - html = self.create_plot_html(plt.gcf(), f"histogram_{column}") - plt.close() - - return f""" - {html} -

Histogram showing the distribution of {column}

- """ - except Exception as e: - return f"

Could not generate histogram for {column}: {str(e)}

" - - def _create_dynamic_bar(self, data, column, is_target=False): - """Create a dynamic bar plot for a categorical column""" - try: - value_counts = data[column].value_counts().head(10) # Limit to top 10 categories - labels = value_counts.index.tolist() - counts = value_counts.values.tolist() - - plt.figure(figsize=(8, 6)) - sns.barplot(x=counts, y=labels, palette='tab10') - plt.title(f"{'Target Distribution' if is_target else f'Distribution of {column}'}", fontsize=14) - plt.xlabel('Count', fontsize=12) - plt.ylabel(column, fontsize=12) - - # Add total count annotation - plt.text(0.95, 0.95, f'Total: {sum(counts)}', - transform=plt.gca().transAxes, ha='right', va='top', bbox=dict(facecolor='white', alpha=0.8)) - - html = self.create_plot_html(plt.gcf(), f"bar_{column}") - plt.close() - - return f""" - {html} -

Bar plot showing the distribution of {column}

- """ - except Exception as e: - return f"

Could not generate bar plot for {column}: {str(e)}

" - - def _create_dynamic_scatter(self, data, x_col, y_col, target=False): - """Create a dynamic scatter plot for regression analysis""" - try: - x_values = data[x_col].dropna() - y_values = data[y_col].dropna() - common_indices = x_values.index.intersection(y_values.index) - if len(common_indices) < 2: - return f"

Not enough valid data for scatter plot between {x_col} and {y_col}

" - - x_values = x_values.loc[common_indices].head(1000) # Limit to 1000 points for performance - y_values = y_values.loc[common_indices].head(1000) - - plt.figure(figsize=(8, 6)) - plt.scatter(x_values, y_values, color='teal', alpha=0.6) - plt.title(f'{y_col} vs {x_col}', fontsize=14) - plt.xlabel(x_col, fontsize=12) - plt.ylabel(y_col, fontsize=12) - - # Add range and correlation annotations - corr = np.corrcoef(x_values, y_values)[0, 1] if len(x_values) > 1 else 0 - stats_text = f'X Range: {x_values.min():.2f} to {x_values.max():.2f}\nY Range: {y_values.min():.2f} to {y_values.max():.2f}\nCorr: {corr:.2f}' - plt.text(0.95, 0.95, stats_text, transform=plt.gca().transAxes, ha='right', va='top', - bbox=dict(facecolor='white', alpha=0.8)) - - html = self.create_plot_html(plt.gcf(), f"scatter_{x_col}_{y_col}") - plt.close() - - return f""" - {html} -

Scatter plot showing relationship between {x_col} and {y_col}

- """ - except Exception as e: - return f"

Could not generate scatter plot for {x_col} vs {y_col}: {str(e)}

" + def _format_eda(self, result, target_column): + if not result or result.get('status') != 'success': + return "

EDA not available.

" - def _create_dynamic_correlation_heatmap(self, correlation_matrix): - """Create a dynamic correlation heatmap""" - try: - corr_df = pd.DataFrame(correlation_matrix) - if corr_df.empty or len(corr_df.columns) < 2: - return "

Not enough numeric features for correlation analysis

" - - plt.figure(figsize=(min(10, len(corr_df.columns) * 1.2), min(8, len(corr_df.columns) * 1))) - sns.heatmap( - corr_df, - annot=True, - cmap='coolwarm', - vmin=-1, - vmax=1, - center=0, - square=True, - fmt='.2f', - annot_kws={'size': max(8, 12 - len(corr_df.columns) // 2)}, - cbar_kws={'label': 'Correlation Coefficient'} - ) - plt.title('Correlation Matrix Heatmap', fontsize=14, pad=15) - plt.xticks(rotation=45, ha='right') - plt.yticks(rotation=0) - - html = self.create_plot_html(plt.gcf(), "correlation_heatmap") - plt.close() - - return f""" - {html} -

Heatmap showing correlations between numeric features

- """ - except Exception as e: - return f"

Could not generate correlation heatmap: {str(e)}

" - - def _format_eda_results(self, results, data, learning_type=None, target_column=None): - """Format EDA results with dynamic visualizations""" - if not results or results.get('status') != 'success' or data is None: - return "

EDA information not available or no data loaded

" - - analysis = results.get('analysis', {}) - column_types = analysis.get('column_types', {}) - correlations = analysis.get('correlations', {}) + analysis = result.get('analysis', {}) + col_types = analysis.get('column_types', {}) + quality = analysis.get('data_quality_score', 'N/A') html = f""" -
-
-

๐Ÿ“Š Statistical Summary

-

Numeric Features: {len(column_types.get('numeric', []))}

-

Categorical Features: {len(column_types.get('categorical', []))}

-

DateTime Features: {len(column_types.get('datetime', []))}

+
+
+ Numeric
{len(col_types.get('numeric',[]))} +
+
+ Categorical
{len(col_types.get('categorical',[]))} +
+
+ DateTime
{len(col_types.get('datetime',[]))} +
+
+ Data Quality
{quality}%
""" - # Add correlation heatmap if available - if correlations.get('correlation_matrix'): - html += self._create_dynamic_correlation_heatmap(correlations['correlation_matrix']) - - # Dynamic visualization selection based on learning type and data - if learning_type == "Supervised" and target_column and target_column in data.columns: - if target_column in column_types['numeric']: - numeric_cols = [col for col in column_types['numeric'] if col != target_column][:2] - for col in numeric_cols: - html += self._create_dynamic_scatter(data, col, target_column, target=True) - elif target_column in column_types['categorical']: - html += self._create_dynamic_bar(data, target_column, is_target=True) - categorical_cols = [col for col in column_types['categorical'] if col != target_column][:2] - for col in categorical_cols: - html += self._create_dynamic_bar(data, col) - # Add one numeric histogram and one categorical bar plot for context - if column_types['numeric']: - html += self._create_dynamic_histogram(data, column_types['numeric'][0]) - if column_types['categorical'] and target_column not in column_types['categorical']: - html += self._create_dynamic_bar(data, column_types['categorical'][0]) - else: - # For unsupervised learning or no target, show up to 2 histograms and 2 bar plots - for col in column_types['numeric'][:2]: - html += self._create_dynamic_histogram(data, col) - for col in column_types['categorical'][:2]: - html += self._create_dynamic_bar(data, col) - - html += """ -

โœ… Exploratory Data Analysis completed!

- """ + # Correlation heatmap + corr_data = analysis.get('correlations', {}).get('matrix', {}) + if corr_data and len(corr_data) > 1: + try: + corr_df = pd.DataFrame(corr_data) + fig, ax = plt.subplots(figsize=(min(10, len(corr_df)*1.2), + min(8, len(corr_df)))) + sns.heatmap(corr_df, annot=True, cmap='coolwarm', center=0, + vmin=-1, vmax=1, fmt='.2f', ax=ax, + annot_kws={'size': max(7, 11 - len(corr_df)//2)}) + ax.set_title('Correlation Matrix', fontsize=13) + plt.xticks(rotation=45, ha='right') + plt.tight_layout() + html += self.create_plot_html(fig, 'corr_heatmap') + except: + pass + + # Numeric distributions + if self.current_data is not None: + numeric_cols = col_types.get('numeric', [])[:3] + for col in numeric_cols: + try: + vals = self.current_data[col].dropna() + fig, ax = plt.subplots(figsize=(8, 4)) + sns.histplot(vals, bins=min(30, int(np.sqrt(len(vals)))), + kde=True, color='steelblue', ax=ax) + ax.set_title(f'Distribution of {col}', fontsize=12) + ax.set_xlabel(col) + ax.set_ylabel('Count') + plt.tight_layout() + html += self.create_plot_html(fig, f'hist_{col}') + except: + pass + + # Categorical bar plots + cat_cols = col_types.get('categorical', [])[:2] + for col in cat_cols: + try: + vc = self.current_data[col].value_counts().head(10) + fig, ax = plt.subplots(figsize=(8, 4)) + sns.barplot(x=vc.values, y=vc.index.astype(str), + palette='viridis', ax=ax) + ax.set_title(f'Top values in {col}', fontsize=12) + plt.tight_layout() + html += self.create_plot_html(fig, f'bar_{col}') + except: + pass + + # Target analysis + ta = analysis.get('target_analysis', {}) + if ta and target_column and target_column in self.current_data.columns: + try: + if ta['type'] == 'classification': + dist = self.current_data[target_column].value_counts() + fig, ax = plt.subplots(figsize=(7, 4)) + sns.barplot(x=dist.values, y=dist.index.astype(str), + palette='Set2', ax=ax) + ax.set_title(f'Target Distribution: {target_column}', fontsize=12) + plt.tight_layout() + html += self.create_plot_html(fig, 'target_dist') + else: + fig, ax = plt.subplots(figsize=(8, 4)) + sns.histplot(self.current_data[target_column].dropna(), + kde=True, color='coral', ax=ax) + ax.set_title(f'Target Distribution: {target_column}', fontsize=12) + plt.tight_layout() + html += self.create_plot_html(fig, 'target_dist') + except: + pass + + # Strong correlations + strong = analysis.get('correlations', {}).get('strong', []) + if strong: + html += f"

๐Ÿ”— Strong Correlations Found ({len(strong)}):

    " + for s in strong[:5]: + html += f"
  • {s['var1']} โ†” {s['var2']}: {s['correlation']}
  • " + html += "
" return html - def _format_domain_results(self, results): - """Format domain analysis results""" - if not results: - return "

Domain analysis information not available

" - - domain = results.get('detected_domain', 'general') - insights = results.get('insights', []) - recommendations = results.get('recommendations', []) + def _format_domain(self, result): + if not result: + return "

No domain info.

" + domain = result.get('detected_domain', 'general') + insights = result.get('insights', []) + recommendations = result.get('recommendations', []) return f""" -
-

๐ŸŽฏ Domain Detection

-
-

{domain}

-
-
๐Ÿ’ก Key Insights:
-
    - {''.join([f"
  • {insight}
  • " for insight in insights[:5]])} -
-
๐ŸŽฏ Recommendations:
-
    - {''.join([f"
  • {rec}
  • " for rec in recommendations[:5]])} -
+
+

Detected Domain: {domain.title()}

+ Key Insights: +
    {''.join(f"
  • {i}
  • " for i in insights[:5])}
+ Recommendations: +
    {''.join(f"
  • {r}
  • " for r in recommendations[:5])}
-

โœ… Domain analysis completed!

""" - def _format_modeling_results(self, results, enable_deep_learning): - """Format modeling results with visualizations""" - if not results or results.get('status') != 'success': - return "

Modeling information not available

" + def _format_modeling(self, result): + if not result or result.get('status') != 'success': + return f"

Modeling failed: {result.get('error','Unknown')}

" - problem_type = results.get('problem_type', 'unknown') - best_model = results.get('best_model', 'Unknown') - model_results = results.get('results', {}) - feature_importance = results.get('feature_importance', {}) + problem_type = result.get('problem_type', 'unknown') + best_name = result.get('best_model', 'N/A') + results = result.get('results', {}) + feature_imp = result.get('feature_importance', {}) + # Results table html = f""" -
-

๐Ÿค– Modeling Results

-
-

Best Model: {best_model} ({problem_type.title()})

-
-
๐Ÿ“Š Model Performance:
- - - - - - - - - - """ - - for model, metrics in model_results.items(): - metric1 = metrics.get('accuracy' if problem_type == 'classification' else 'rmse', 'N/A') - metric2 = metrics.get('f1_score' if problem_type == 'classification' else 'r2_score', 'N/A') - html += f""" - - - - - - """ - - html += """ - -
Model - {'Accuracy' if problem_type == 'classification' else 'RMSE'} - - {'F1 Score' if problem_type == 'classification' else 'Rยฒ Score'} -
{model}{metric1:.3f}{metric2:.3f}
- """ - - if feature_importance: - html += self._create_feature_importance_plot(feature_importance) - - if enable_deep_learning: - html += """ -
-
๐Ÿง  Deep Learning Status
-

Deep learning models were evaluated but not included in final results due to complexity constraints.

-
- """ - - html += """ -

โœ… Model training and evaluation completed!

+
+

๐Ÿ† Best Model: {best_name}

+

Problem Type: {problem_type.title()}

""" - return html - def _create_feature_importance_plot(self, feature_importance): - """Create a dynamic feature importance bar plot""" - try: - features = list(feature_importance.keys()) - importances = list(feature_importance.values()) + # Model comparison table + html += "

๐Ÿ“Š All Models Performance (REAL results):

" + html += """ + + """ + + if 'classification' in problem_type: + html += """ + + + """ + else: + html += """ + + """ - plt.figure(figsize=(8, max(6, len(features) * 0.5))) - sns.barplot(x=importances, y=features, palette='viridis') - plt.title('Feature Importance', fontsize=14) - plt.xlabel('Importance Score', fontsize=12) - plt.ylabel('Features', fontsize=12) + html += "" - # Add value annotations - for i, v in enumerate(importances): - plt.text(v, i, f'{v:.3f}', va='center', ha='left', color='black', fontsize=10) + valid = {k: v for k, v in results.items() if v.get('status') == 'success'} + for i, (name, metrics) in enumerate(valid.items()): + bg = '#fff3cd' if name == best_name else ('#f9f9f9' if i % 2 == 0 else 'white') + bold = 'font-weight:bold;' if name == best_name else '' + html += f"" + html += f"" + + if 'classification' in problem_type: + html += f"" + html += f"" + html += f"" + html += f"" + else: + html += f"" + html += f"" + html += f"" - html = self.create_plot_html(plt.gcf(), "feature_importance") - plt.close() + html += "" + html += "
ModelAccuracyF1 ScorePrecisionRecallRMSEMAERยฒ Score
{name} {'๐Ÿ†' if name == best_name else ''}{metrics.get('accuracy','N/A')}{metrics.get('f1_score','N/A')}{metrics.get('precision','N/A')}{metrics.get('recall','N/A')}{metrics.get('rmse','N/A')}{metrics.get('mae','N/A')}{metrics.get('r2_score','N/A')}
" - return f""" - {html} -

Bar plot showing feature importance scores

- """ - except Exception as e: - return f"

Could not generate feature importance plot: {str(e)}

" + # Feature importance plot + if feature_imp: + try: + features = list(feature_imp.keys())[:15] + importances = [feature_imp[f] for f in features] + fig, ax = plt.subplots(figsize=(8, max(5, len(features)*0.4))) + colors = ['#e74c3c' if f == features[0] else '#3498db' for f in features] + bars = ax.barh(features[::-1], importances[::-1], color=colors[::-1]) + ax.set_title(f'Feature Importance โ€” {best_name}', fontsize=13) + ax.set_xlabel('Importance Score') + for bar, imp in zip(bars, importances[::-1]): + ax.text(imp + 0.001, bar.get_y() + bar.get_height()/2, + f'{imp:.3f}', va='center', fontsize=9) + plt.tight_layout() + html += self.create_plot_html(fig, 'feature_importance') + except: + pass + + # Model comparison bar chart + if valid: + try: + names = list(valid.keys()) + if 'classification' in problem_type: + scores = [valid[n].get('accuracy', 0) for n in names] + metric_label = 'Accuracy' + else: + scores = [valid[n].get('r2_score', 0) for n in names] + metric_label = 'Rยฒ Score' + + fig, ax = plt.subplots(figsize=(10, 5)) + colors = ['#e74c3c' if n == best_name else '#3498db' for n in names] + bars = ax.bar(range(len(names)), scores, color=colors) + ax.set_xticks(range(len(names))) + ax.set_xticklabels(names, rotation=45, ha='right', fontsize=9) + ax.set_ylabel(metric_label) + ax.set_title(f'Model Comparison โ€” {metric_label}', fontsize=13) + for bar, score in zip(bars, scores): + ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.005, + f'{score:.3f}', ha='center', fontsize=8) + plt.tight_layout() + html += self.create_plot_html(fig, 'model_comparison') + except: + pass - def _format_unsupervised_results(self, data): - """Format unsupervised analysis results with dynamic clustering visualization""" - if data is None: - return "

No data available for unsupervised analysis

" + return html - column_types = self.analyzer.detect_column_types(data) - numeric_cols = column_types['numeric'] + def _format_clustering(self): + if self.current_data is None: + return "

No data available.

" - html = """ -
-

๐Ÿ” Unsupervised Analysis Results

-

Performed clustering analysis to identify natural groupings in the data.

- """ + html = "

Unsupervised analysis โ€” performing KMeans clustering.

" + numeric_cols = self.current_data.select_dtypes(include=[np.number]).columns.tolist() if len(numeric_cols) >= 2: try: - # Perform KMeans clustering with dynamic number of clusters - X = data[numeric_cols].dropna().head(1000) - n_clusters = min(3, len(X) // 10) if len(X) > 10 else 2 - kmeans = KMeans(n_clusters=n_clusters, random_state=42) - clusters = kmeans.fit_predict(X) - - plt.figure(figsize=(8, 6)) - plt.scatter(X.iloc[:, 0], X.iloc[:, 1], c=clusters, cmap='viridis', alpha=0.6) - plt.title(f'Clustering: {numeric_cols[0]} vs {numeric_cols[1]}', fontsize=14) - plt.xlabel(numeric_cols[0], fontsize=12) - plt.ylabel(numeric_cols[1], fontsize=12) - - # Add cluster count annotation - plt.text(0.95, 0.95, f'Clusters: {n_clusters}', - transform=plt.gca().transAxes, ha='right', va='top', - bbox=dict(facecolor='white', alpha=0.8)) - - html += self.create_plot_html(plt.gcf(), "clustering_plot") - plt.close() - - html += f""" -

- Scatter plot showing clusters based on {numeric_cols[0]} and {numeric_cols[1]} -

- """ + X = self.current_data[numeric_cols].dropna().head(1000) + scaler = StandardScaler() + X_scaled = scaler.fit_transform(X) + n_clusters = min(4, max(2, len(X) // 50)) + kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10) + labels = kmeans.fit_predict(X_scaled) + sil = round(silhouette_score(X_scaled, labels), 3) + + fig, ax = plt.subplots(figsize=(8, 6)) + scatter = ax.scatter(X.iloc[:, 0], X.iloc[:, 1], + c=labels, cmap='viridis', alpha=0.6) + plt.colorbar(scatter, ax=ax) + ax.set_title(f'KMeans Clustering (k={n_clusters}, silhouette={sil})', fontsize=12) + ax.set_xlabel(numeric_cols[0]) + ax.set_ylabel(numeric_cols[1]) + plt.tight_layout() + html += self.create_plot_html(fig, 'clustering') + html += f"

Silhouette Score: {sil} (higher is better, max=1.0)

" except Exception as e: - html += f"

Could not generate clustering plot: {str(e)}

" - else: - html += "

Not enough numeric columns for clustering visualization

" + html += f"

Could not generate clustering plot: {e}

" - html += """ -

โœ… Unsupervised analysis completed!

-
- """ return html - def _create_completion_footer(self, learning_type, domain, enable_deep_learning, enable_automl): - """Create completion footer with summary information""" - completion_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + def _completion_html(self, domain): return f""" -
-

๐ŸŽ‰ Pipeline Completed Successfully!

-

- Analysis Type: {learning_type} | Domain: {domain or 'General'} | - Deep Learning: {'Enabled' if enable_deep_learning else 'Disabled'} | - AutoML: {'Enabled' if enable_automl else 'Disabled'} +

+

๐ŸŽ‰ Pipeline Completed Successfully!

+

+ Domain: {domain or 'General'} | + Completed: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} +

+

+ All model results are REAL โ€” trained on your actual data!

-

Completed: {completion_time}

""" - def _format_final_results(self, summary, pipeline_results): - """Format final results and recommendations""" - key_insights = summary.get('key_insights', []) - recommendations = summary.get('recommendations', []) - - html = """ -
-

๐Ÿ“ˆ Final Results & Recommendations

-
๐Ÿ’ก Key Insights:
-
    - """ - for insight in key_insights[:5]: - html += f"
  • {insight}
  • " - html += """ -
-
๐ŸŽฏ Recommendations:
-
    - """ - for rec in recommendations[:5]: - html += f"
  • {rec}
  • " - html += """ -
+ def _error_html(self, msg): + return f""" +
+

โŒ Error

{msg}

-

โœ… Final results compiled!

- """ - return html - - def generate_report(self): - """Generate a downloadable HTML report with all results and visualizations""" - if not self.pipeline_results: - return self._create_error_html("No pipeline results available to generate report.") - - html = f""" - - - - Data Science Pipeline Report - - - - {self._create_progress_header()} - {self._create_all_steps_html( - self.pipeline_results, - self.pipeline_results.get('summary', {}), - self.pipeline_results.get('learning_type', 'Unknown'), - self.pipeline_results.get('target_column', None), - self.pipeline_results.get('domain_insights', {}).get('detected_domain', 'general'), - self.pipeline_results.get('enable_deep_learning', False), - self.pipeline_results.get('enable_automl', False) - )} - - """ - report_path = f"pipeline_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html" - with open(report_path, 'w', encoding='utf-8') as f: - f.write(html) - - return report_path + def update_target_visibility(self, learning_type, columns): + if learning_type == "Supervised": + return gr.update(visible=True, choices=columns, + value=columns[-1] if columns else "") + return gr.update(visible=False, value=None) def launch(self): - """Launch the Gradio interface for the pipeline""" - with gr.Blocks(theme=gr.themes.Default(), css=self.custom_css) as demo: - gr.Markdown(""" - # ๐Ÿ”ฌ Data Scientist Agent - Upload your dataset and configure the pipeline settings to perform automated data analysis and modeling. - """) + with gr.Blocks(theme=gr.themes.Soft()) as demo: + gr.Markdown("# ๐Ÿ”ฌ Data Scientist Multi-Agent System\n" + "**Real agents, real model training โ€” no mock results!**") + + columns_state = gr.State([]) with gr.Row(): with gr.Column(scale=1): - file_input = gr.File(label="Upload Dataset (CSV/JSON)") + file_input = gr.File(label="๐Ÿ“ Upload Dataset (CSV/JSON/Excel)") learning_type = gr.Radio( choices=["Supervised", "Unsupervised"], - label="Learning Type", - value="Supervised" + label="Learning Type", value="Supervised" ) target_column = gr.Dropdown( - choices=[], - label="Target Column (for Supervised Learning)", - visible=True + choices=[], label="๐ŸŽฏ Target Column", visible=True ) domain = gr.Textbox( - label="Domain (e.g., Finance, Healthcare)", - placeholder="Enter domain or leave blank for general analysis" + label="Domain (optional)", + placeholder="e.g. Finance, Healthcare, Retail..." ) enable_deep_learning = gr.Checkbox( - label="Enable Deep Learning Models", - value=False + label="Enable Deep Learning (slower)", value=False ) enable_automl = gr.Checkbox( - label="Enable AutoML", - value=False + label="Enable AutoML Tuning (slower)", value=False ) - run_button = gr.Button("Run Pipeline", variant="primary") + run_btn = gr.Button("๐Ÿš€ Run Pipeline", variant="primary", size="lg") with gr.Column(scale=2): - file_info = gr.HTML(label="File Information") - data_preview = gr.HTML(label="Data Preview") - pipeline_output = gr.HTML(label="Pipeline Results") - download_button = gr.File( - label="Download Report", - visible=True - ) + file_info_out = gr.HTML(label="File Info") + preview_out = gr.HTML(label="Data Preview") + pipeline_out = gr.HTML(label="Pipeline Results") + download_btn = gr.File(label="๐Ÿ“ฅ Download Report", visible=False) - # Event handlers + # Events file_input.change( fn=self.process_file_upload, inputs=[file_input, learning_type], - outputs=[file_info, gr.State(), target_column, target_column, data_preview] + outputs=[file_info_out, columns_state, columns_state, + target_column, preview_out] ) learning_type.change( - fn=self.update_target_column_visibility, - inputs=[learning_type, gr.State()], + fn=self.update_target_visibility, + inputs=[learning_type, columns_state], outputs=[target_column] ) - run_button.click( - fn=self.run_comprehensive_pipeline, - inputs=[file_input, learning_type, target_column, domain, enable_deep_learning, enable_automl], - outputs=[pipeline_output, download_button] - ) - download_button.upload( - fn=self.generate_report, - inputs=[], - outputs=[download_button] + run_btn.click( + fn=self.run_pipeline, + inputs=[file_input, learning_type, target_column, domain, + enable_deep_learning, enable_automl], + outputs=[pipeline_out, download_btn] ) - return demo # Return the demo object for Hugging Face Spaces + return demo + -# Example usage if __name__ == "__main__": - pipeline_ui = DataSciencePipelineUI() - demo = pipeline_ui.launch() - demo.launch(share=True) # Launch the app for Hugging Face Spaces \ No newline at end of file + ui = DataSciencePipelineUI() + demo = ui.launch() + demo.launch() \ No newline at end of file