Spaces:
Sleeping
Sleeping
Download pages/4_Machine_Learning.py from Kedhareswer/DataVisl: direct link, hf CLI and curl.
- Browser
- Download file 64.1 kB
-
https://huggingface.co/spaces/Kedhareswer/DataVisl/resolve/main/pages/4_Machine_Learning.py
- Command line
-
hf download hf://spaces/Kedhareswer/DataVisl/pages/4_Machine_Learning.py
-
curl -L -o 4_Machine_Learning.py https://huggingface.co/spaces/Kedhareswer/DataVisl/resolve/main/pages/4_Machine_Learning.py
64.1 kB
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| import seaborn as sns | |
| from sklearn.model_selection import train_test_split, cross_val_score | |
| from sklearn.preprocessing import StandardScaler, LabelEncoder | |
| from sklearn.metrics import ( | |
| accuracy_score, precision_score, recall_score, f1_score, | |
| mean_squared_error, r2_score, silhouette_score, confusion_matrix, | |
| classification_report, mean_absolute_error | |
| ) | |
| from sklearn.linear_model import LinearRegression, LogisticRegression | |
| from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor | |
| from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor | |
| from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor | |
| from sklearn.cluster import KMeans, DBSCAN | |
| from sklearn.decomposition import PCA | |
| from utils import get_numeric_columns, get_categorical_columns, explain_machine_learning | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="Machine Learning", | |
| page_icon="🤖", | |
| layout="wide" | |
| ) | |
| # Check if data is loaded | |
| if 'df' not in st.session_state or st.session_state.df is None: | |
| st.error("Please load a dataset first! Go back to the Home page.") | |
| st.stop() | |
| df = st.session_state.df | |
| # Page title | |
| st.title("🤖 Machine Learning") | |
| st.markdown(""" | |
| Apply machine learning algorithms to your data for prediction, classification, and clustering. | |
| This page helps you build and evaluate basic machine learning models. | |
| """) | |
| # Create tabs for different ML tasks | |
| tab1, tab2, tab3, tab4 = st.tabs([ | |
| "Classification", | |
| "Regression", | |
| "Clustering", | |
| "Dimensionality Reduction" | |
| ]) | |
| # Get column lists by type | |
| numeric_columns = get_numeric_columns(df) | |
| categorical_columns = get_categorical_columns(df) | |
| # Helper functions for ML | |
| def prepare_data(dataframe, target, features, test_size=0.2, random_state=42): | |
| """ | |
| Prepare data for machine learning by handling categorical features and splitting into train/test sets | |
| Parameters: | |
| ----------- | |
| dataframe : pandas.DataFrame | |
| The input dataframe | |
| target : str | |
| The target column name | |
| features : list | |
| List of feature column names | |
| test_size : float | |
| Proportion of data to use for testing | |
| random_state : int | |
| Random seed for reproducibility | |
| Returns: | |
| -------- | |
| X_train, X_test, y_train, y_test, encoders | |
| """ | |
| # Create a copy of the dataframe to avoid modifying the original | |
| data = dataframe.copy() | |
| # Drop rows with missing values in the target or features | |
| data = data.dropna(subset=[target] + features) | |
| # Separate features and target | |
| X = data[features] | |
| y = data[target] | |
| # Handle categorical features | |
| encoders = {} | |
| for col in X.select_dtypes(include=['object', 'category']).columns: | |
| encoder = LabelEncoder() | |
| X[col] = encoder.fit_transform(X[col]) | |
| encoders[col] = encoder | |
| # Encode target if it's categorical | |
| if y.dtype == 'object' or y.dtype.name == 'category': | |
| encoder = LabelEncoder() | |
| y = encoder.fit_transform(y) | |
| encoders['target'] = encoder | |
| # Split data into train and test sets | |
| X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state) | |
| return X_train, X_test, y_train, y_test, encoders | |
| def plot_feature_importance(model, feature_names, top_n=10): | |
| """ | |
| Plot feature importance for tree-based models | |
| """ | |
| if hasattr(model, 'feature_importances_'): | |
| importances = model.feature_importances_ | |
| indices = np.argsort(importances)[-top_n:] | |
| plt.figure(figsize=(10, 6)) | |
| plt.title('Feature Importance') | |
| plt.barh(range(len(indices)), importances[indices], align='center') | |
| plt.yticks(range(len(indices)), [feature_names[i] for i in indices]) | |
| plt.xlabel('Relative Importance') | |
| return plt.gcf() | |
| else: | |
| return None | |
| def plot_confusion_matrix(y_true, y_pred, labels=None): | |
| """ | |
| Plot confusion matrix for classification models | |
| """ | |
| cm = confusion_matrix(y_true, y_pred) | |
| plt.figure(figsize=(8, 6)) | |
| sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=labels, yticklabels=labels) | |
| plt.ylabel('True Label') | |
| plt.xlabel('Predicted Label') | |
| plt.title('Confusion Matrix') | |
| return plt.gcf() | |
| def evaluate_classification(y_true, y_pred, labels=None): | |
| """ | |
| Evaluate classification model and return metrics | |
| """ | |
| metrics = { | |
| 'Accuracy': accuracy_score(y_true, y_pred), | |
| 'Precision': precision_score(y_true, y_pred, average='weighted', zero_division=0), | |
| 'Recall': recall_score(y_true, y_pred, average='weighted', zero_division=0), | |
| 'F1 Score': f1_score(y_true, y_pred, average='weighted', zero_division=0) | |
| } | |
| return metrics | |
| def evaluate_regression(y_true, y_pred): | |
| """ | |
| Evaluate regression model and return metrics | |
| """ | |
| metrics = { | |
| 'Mean Absolute Error (MAE)': mean_absolute_error(y_true, y_pred), | |
| 'Mean Squared Error (MSE)': mean_squared_error(y_true, y_pred), | |
| 'Root Mean Squared Error (RMSE)': np.sqrt(mean_squared_error(y_true, y_pred)), | |
| 'R² Score': r2_score(y_true, y_pred) | |
| } | |
| return metrics | |
| with tab1: | |
| st.header("Classification") | |
| st.markdown(""" | |
| Classification algorithms predict categorical outcomes (like yes/no, spam/not spam, or product categories). | |
| Select your target variable (what you want to predict) and the features (predictors) to build a classification model. | |
| """) | |
| # Educational content | |
| with st.expander("Learn about classification"): | |
| st.markdown(""" | |
| ### Classification in Machine Learning | |
| Classification is a supervised learning technique where the model learns from labeled data to predict discrete categories. | |
| **Common classification algorithms:** | |
| 1. **Logistic Regression**: Despite its name, it's used for binary classification. It models the probability of a binary outcome. | |
| 2. **Decision Trees**: Creates a tree-like model of decisions. Easy to understand but prone to overfitting. | |
| 3. **Random Forest**: Builds multiple decision trees and merges their predictions. More robust than a single tree. | |
| 4. **K-Nearest Neighbors (KNN)**: Classifies based on the majority class of k nearest data points. | |
| **When to use classification:** | |
| - Email spam detection (spam/not spam) | |
| - Medical diagnosis (disease present/absent) | |
| - Customer churn prediction (will churn/won't churn) | |
| - Sentiment analysis (positive/negative/neutral) | |
| **Evaluation metrics:** | |
| - **Accuracy**: Proportion of correct predictions (can be misleading with imbalanced classes) | |
| - **Precision**: How many selected items are relevant (TP/(TP+FP)) | |
| - **Recall**: How many relevant items are selected (TP/(TP+FN)) | |
| - **F1 Score**: Harmonic mean of precision and recall | |
| - **Confusion Matrix**: Table showing correct and incorrect predictions by class | |
| """) | |
| # Check if there are categorical columns for classification | |
| if not categorical_columns: | |
| st.warning("No categorical columns found in the dataset. Classification typically requires a categorical target variable.") | |
| # Input settings for classification | |
| st.subheader("Model Settings") | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| # Select target variable | |
| target_options = categorical_columns + numeric_columns | |
| target_variable = st.selectbox( | |
| "Select target variable (what to predict)", | |
| target_options, | |
| key="classification_target" | |
| ) | |
| # For numeric targets, allow user to convert to categorical | |
| if target_variable in numeric_columns: | |
| convert_target = st.checkbox("Convert numeric target to categorical bins") | |
| if convert_target: | |
| num_bins = st.slider("Number of bins", 2, 10, 3, key="classification_bins") | |
| # Create bins | |
| df[f"{target_variable}_binned"] = pd.cut( | |
| df[target_variable].dropna(), | |
| bins=num_bins, | |
| labels=[f"Bin {i+1}" for i in range(num_bins)] | |
| ) | |
| target_variable = f"{target_variable}_binned" | |
| st.success(f"Created categorical variable '{target_variable}' from numeric data.") | |
| with col2: | |
| # Select features | |
| available_features = [col for col in df.columns if col != target_variable] | |
| selected_features = st.multiselect( | |
| "Select features (predictors)", | |
| available_features, | |
| default=available_features[:min(5, len(available_features))], | |
| key="classification_features" | |
| ) | |
| # Additional settings | |
| test_size = st.slider("Test set size (%)", 10, 50, 20, key="classification_test_size") | |
| # Select classification algorithm | |
| algorithm = st.selectbox( | |
| "Select classification algorithm", | |
| ["Logistic Regression", "Decision Tree", "Random Forest", "K-Nearest Neighbors"], | |
| key="classification_algorithm" | |
| ) | |
| # Algorithm-specific parameters | |
| if algorithm == "Logistic Regression": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| c_param = st.slider("Regularization strength (C)", 0.01, 10.0, 1.0, key="logistic_c") | |
| with col2: | |
| max_iter = st.slider("Maximum iterations", 100, 1000, 100, step=100, key="logistic_iter") | |
| model_params = { | |
| "C": c_param, | |
| "max_iter": max_iter, | |
| "random_state": 42 | |
| } | |
| elif algorithm == "Decision Tree": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| max_depth = st.slider("Maximum depth", 1, 20, 5, key="dt_depth") | |
| with col2: | |
| min_samples_split = st.slider("Minimum samples to split", 2, 20, 2, key="dt_split") | |
| model_params = { | |
| "max_depth": max_depth, | |
| "min_samples_split": min_samples_split, | |
| "random_state": 42 | |
| } | |
| elif algorithm == "Random Forest": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| n_estimators = st.slider("Number of trees", 10, 200, 100, step=10, key="rf_trees") | |
| with col2: | |
| max_depth = st.slider("Maximum depth", 1, 20, 5, key="rf_depth") | |
| model_params = { | |
| "n_estimators": n_estimators, | |
| "max_depth": max_depth, | |
| "random_state": 42 | |
| } | |
| elif algorithm == "K-Nearest Neighbors": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| n_neighbors = st.slider("Number of neighbors (k)", 1, 20, 5, key="knn_k") | |
| with col2: | |
| weights = st.selectbox( | |
| "Weight function", | |
| ["uniform", "distance"], | |
| key="knn_weights" | |
| ) | |
| model_params = { | |
| "n_neighbors": n_neighbors, | |
| "weights": weights | |
| } | |
| # Train model button | |
| if st.button("Train Classification Model", key="train_classification"): | |
| if not selected_features: | |
| st.error("Please select at least one feature.") | |
| else: | |
| # Create progress bar | |
| progress_bar = st.progress(0) | |
| status_text = st.empty() | |
| try: | |
| # Progress update | |
| status_text.text("Preparing data...") | |
| progress_bar.progress(10) | |
| # Prepare data | |
| X_train, X_test, y_train, y_test, encoders = prepare_data( | |
| df, target_variable, selected_features, | |
| test_size=test_size/100, | |
| random_state=42 | |
| ) | |
| # Progress update | |
| status_text.text("Data prepared. Initializing model...") | |
| progress_bar.progress(30) | |
| # Initialize the model based on selected algorithm | |
| if algorithm == "Logistic Regression": | |
| model = LogisticRegression(**model_params) | |
| elif algorithm == "Decision Tree": | |
| model = DecisionTreeClassifier(**model_params) | |
| elif algorithm == "Random Forest": | |
| model = RandomForestClassifier(**model_params) | |
| elif algorithm == "K-Nearest Neighbors": | |
| model = KNeighborsClassifier(**model_params) | |
| # Progress update | |
| status_text.text("Training model...") | |
| progress_bar.progress(50) | |
| # Train model | |
| model.fit(X_train, y_train) | |
| # Progress update | |
| status_text.text("Evaluating model...") | |
| progress_bar.progress(70) | |
| # Make predictions | |
| y_pred_train = model.predict(X_train) | |
| y_pred_test = model.predict(X_test) | |
| # Evaluate model | |
| train_metrics = evaluate_classification(y_train, y_pred_train) | |
| test_metrics = evaluate_classification(y_test, y_pred_test) | |
| # Progress update | |
| status_text.text("Generating visualizations...") | |
| progress_bar.progress(90) | |
| # Display results | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| st.subheader("Training Set Performance") | |
| st.dataframe(pd.DataFrame({ | |
| 'Metric': train_metrics.keys(), | |
| 'Value': [f"{v:.4f}" for v in train_metrics.values()] | |
| })) | |
| with col2: | |
| st.subheader("Test Set Performance") | |
| st.dataframe(pd.DataFrame({ | |
| 'Metric': test_metrics.keys(), | |
| 'Value': [f"{v:.4f}" for v in test_metrics.values()] | |
| })) | |
| # Check if target was encoded and get the class labels | |
| if 'target' in encoders: | |
| class_labels = list(encoders['target'].classes_) | |
| else: | |
| class_labels = list(set(y_test)) | |
| # Display confusion matrix | |
| st.subheader("Confusion Matrix (Test Set)") | |
| cm_fig = plot_confusion_matrix(y_test, y_pred_test, labels=class_labels) | |
| st.pyplot(cm_fig) | |
| # Display feature importance for tree-based models | |
| if algorithm in ["Decision Tree", "Random Forest"]: | |
| st.subheader("Feature Importance") | |
| imp_fig = plot_feature_importance(model, selected_features) | |
| if imp_fig: | |
| st.pyplot(imp_fig) | |
| # Display classification report | |
| st.subheader("Detailed Classification Report") | |
| if 'target' in encoders: | |
| report = classification_report(y_test, y_pred_test, output_dict=True) | |
| report_df = pd.DataFrame(report).transpose() | |
| st.dataframe(report_df.style.format("{:.4f}")) | |
| else: | |
| report = classification_report(y_test, y_pred_test, output_dict=True) | |
| report_df = pd.DataFrame(report).transpose() | |
| st.dataframe(report_df.style.format("{:.4f}")) | |
| # Feature explanation | |
| st.subheader("Understanding Your Model") | |
| if algorithm == "Logistic Regression": | |
| st.markdown(""" | |
| **Logistic Regression** models the probability of the target belonging to a particular category. | |
| - **Strengths**: Simple, interpretable, and efficient for linearly separable data | |
| - **Limitations**: Cannot capture complex non-linear relationships | |
| """) | |
| elif algorithm == "Decision Tree": | |
| st.markdown(""" | |
| **Decision Trees** make predictions by following a series of if-then rules learned from the data. | |
| - **Strengths**: Intuitive, handles both numerical and categorical data, captures non-linear patterns | |
| - **Limitations**: Can create overly complex trees that don't generalize well (overfitting) | |
| The feature importance chart above shows which features were most influential in the model's decisions. | |
| """) | |
| elif algorithm == "Random Forest": | |
| st.markdown(""" | |
| **Random Forest** combines multiple decision trees to improve prediction accuracy and control overfitting. | |
| - **Strengths**: High accuracy, handles large datasets well, resistant to overfitting | |
| - **Limitations**: Less interpretable than a single decision tree, computationally intensive | |
| The feature importance chart above shows which features were most influential across all trees in the forest. | |
| """) | |
| elif algorithm == "K-Nearest Neighbors": | |
| st.markdown(""" | |
| **K-Nearest Neighbors (KNN)** classifies based on the majority class among the k closest training examples. | |
| - **Strengths**: Simple, effective, no training phase, adaptable to new data | |
| - **Limitations**: Computationally expensive for large datasets, sensitive to irrelevant features | |
| KNN doesn't have traditional feature importance, but features that create clear separation between classes are more useful. | |
| """) | |
| # Progress complete | |
| progress_bar.progress(100) | |
| status_text.text("Model training and evaluation complete!") | |
| except Exception as e: | |
| st.error(f"An error occurred during model training: {e}") | |
| progress_bar.empty() | |
| status_text.empty() | |
| with tab2: | |
| st.header("Regression") | |
| st.markdown(""" | |
| Regression algorithms predict continuous numerical values (like prices, temperatures, or ages). | |
| Select your target variable (what you want to predict) and the features (predictors) to build a regression model. | |
| """) | |
| # Educational content | |
| with st.expander("Learn about regression"): | |
| st.markdown(""" | |
| ### Regression in Machine Learning | |
| Regression is a supervised learning technique that predicts continuous numerical values, as opposed to discrete categories. | |
| **Common regression algorithms:** | |
| 1. **Linear Regression**: Models the relationship between features and target using a linear equation. | |
| 2. **Decision Tree Regression**: Similar to classification trees, but predicts continuous values at the leaves. | |
| 3. **Random Forest Regression**: Ensemble of decision trees for regression tasks. | |
| 4. **K-Nearest Neighbors Regression**: Predicts based on the average of k nearest data points. | |
| **When to use regression:** | |
| - Price prediction (houses, stocks, products) | |
| - Sales forecasting | |
| - Temperature prediction | |
| - Age estimation | |
| **Evaluation metrics:** | |
| - **Mean Absolute Error (MAE)**: Average of absolute differences between predictions and actual values | |
| - **Mean Squared Error (MSE)**: Average of squared differences (penalizes larger errors more heavily) | |
| - **Root Mean Squared Error (RMSE)**: Square root of MSE, in the same units as the target | |
| - **R² Score**: Proportion of variance explained by the model (1 is perfect, 0 is no better than the mean) | |
| """) | |
| # Check if there are numeric columns for regression | |
| if not numeric_columns: | |
| st.warning("No numeric columns found in the dataset. Regression requires a numeric target variable.") | |
| # Input settings for regression | |
| st.subheader("Model Settings") | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| # Select target variable | |
| regression_target = st.selectbox( | |
| "Select target variable (what to predict)", | |
| numeric_columns, | |
| key="regression_target" | |
| ) | |
| with col2: | |
| # Select features | |
| available_features = [col for col in df.columns if col != regression_target] | |
| regression_features = st.multiselect( | |
| "Select features (predictors)", | |
| available_features, | |
| default=available_features[:min(5, len(available_features))], | |
| key="regression_features" | |
| ) | |
| # Additional settings | |
| regression_test_size = st.slider("Test set size (%)", 10, 50, 20, key="regression_test_size") | |
| # Select regression algorithm | |
| regression_algorithm = st.selectbox( | |
| "Select regression algorithm", | |
| ["Linear Regression", "Decision Tree", "Random Forest", "K-Nearest Neighbors"], | |
| key="regression_algorithm" | |
| ) | |
| # Algorithm-specific parameters | |
| if regression_algorithm == "Linear Regression": | |
| # Linear regression doesn't have many tunable parameters | |
| st.info("Linear Regression has few hyperparameters to tune. Fit intercept is enabled by default.") | |
| elif regression_algorithm == "Decision Tree": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| dt_reg_depth = st.slider("Maximum depth", 1, 20, 5, key="dt_reg_depth") | |
| with col2: | |
| dt_reg_split = st.slider("Minimum samples to split", 2, 20, 2, key="dt_reg_split") | |
| reg_model_params = { | |
| "max_depth": dt_reg_depth, | |
| "min_samples_split": dt_reg_split, | |
| "random_state": 42 | |
| } | |
| elif regression_algorithm == "Random Forest": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| rf_reg_trees = st.slider("Number of trees", 10, 200, 100, step=10, key="rf_reg_trees") | |
| with col2: | |
| rf_reg_depth = st.slider("Maximum depth", 1, 20, 5, key="rf_reg_depth") | |
| reg_model_params = { | |
| "n_estimators": rf_reg_trees, | |
| "max_depth": rf_reg_depth, | |
| "random_state": 42 | |
| } | |
| elif regression_algorithm == "K-Nearest Neighbors": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| knn_reg_k = st.slider("Number of neighbors (k)", 1, 20, 5, key="knn_reg_k") | |
| with col2: | |
| knn_reg_weights = st.selectbox( | |
| "Weight function", | |
| ["uniform", "distance"], | |
| key="knn_reg_weights" | |
| ) | |
| reg_model_params = { | |
| "n_neighbors": knn_reg_k, | |
| "weights": knn_reg_weights | |
| } | |
| # Train model button | |
| if st.button("Train Regression Model", key="train_regression"): | |
| if not regression_features: | |
| st.error("Please select at least one feature.") | |
| else: | |
| # Create progress bar | |
| reg_progress_bar = st.progress(0) | |
| reg_status_text = st.empty() | |
| try: | |
| # Progress update | |
| reg_status_text.text("Preparing data...") | |
| reg_progress_bar.progress(10) | |
| # Prepare data | |
| X_train, X_test, y_train, y_test, _ = prepare_data( | |
| df, regression_target, regression_features, | |
| test_size=regression_test_size/100, | |
| random_state=42 | |
| ) | |
| # Progress update | |
| reg_status_text.text("Data prepared. Initializing model...") | |
| reg_progress_bar.progress(30) | |
| # Initialize the model based on selected algorithm | |
| if regression_algorithm == "Linear Regression": | |
| reg_model = LinearRegression() | |
| elif regression_algorithm == "Decision Tree": | |
| reg_model = DecisionTreeRegressor(**reg_model_params) | |
| elif regression_algorithm == "Random Forest": | |
| reg_model = RandomForestRegressor(**reg_model_params) | |
| elif regression_algorithm == "K-Nearest Neighbors": | |
| reg_model = KNeighborsRegressor(**reg_model_params) | |
| # Progress update | |
| reg_status_text.text("Training model...") | |
| reg_progress_bar.progress(50) | |
| # Train model | |
| reg_model.fit(X_train, y_train) | |
| # Progress update | |
| reg_status_text.text("Evaluating model...") | |
| reg_progress_bar.progress(70) | |
| # Make predictions | |
| y_pred_train = reg_model.predict(X_train) | |
| y_pred_test = reg_model.predict(X_test) | |
| # Evaluate model | |
| train_metrics = evaluate_regression(y_train, y_pred_train) | |
| test_metrics = evaluate_regression(y_test, y_pred_test) | |
| # Progress update | |
| reg_status_text.text("Generating visualizations...") | |
| reg_progress_bar.progress(90) | |
| # Display results | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| st.subheader("Training Set Performance") | |
| st.dataframe(pd.DataFrame({ | |
| 'Metric': train_metrics.keys(), | |
| 'Value': [f"{v:.4f}" for v in train_metrics.values()] | |
| })) | |
| with col2: | |
| st.subheader("Test Set Performance") | |
| st.dataframe(pd.DataFrame({ | |
| 'Metric': test_metrics.keys(), | |
| 'Value': [f"{v:.4f}" for v in test_metrics.values()] | |
| })) | |
| # Scatter plot of predicted vs actual values | |
| st.subheader("Predicted vs Actual Values (Test Set)") | |
| fig, ax = plt.subplots(figsize=(10, 6)) | |
| ax.scatter(y_test, y_pred_test, alpha=0.5) | |
| ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--') | |
| ax.set_xlabel('Actual Values') | |
| ax.set_ylabel('Predicted Values') | |
| ax.set_title('Predicted vs Actual Values') | |
| # Add a text box with R² score | |
| textstr = f'R² Score: {test_metrics["R² Score"]:.4f}' | |
| props = dict(boxstyle='round', facecolor='white', alpha=0.5) | |
| ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=12, | |
| verticalalignment='top', bbox=props) | |
| st.pyplot(fig) | |
| # Residual plot | |
| st.subheader("Residual Plot (Test Set)") | |
| residuals = y_test - y_pred_test | |
| fig, ax = plt.subplots(figsize=(10, 6)) | |
| ax.scatter(y_pred_test, residuals, alpha=0.5) | |
| ax.axhline(y=0, color='r', linestyle='--') | |
| ax.set_xlabel('Predicted Values') | |
| ax.set_ylabel('Residuals') | |
| ax.set_title('Residual Plot') | |
| st.pyplot(fig) | |
| # Display feature importance for tree-based models | |
| if regression_algorithm in ["Decision Tree", "Random Forest"]: | |
| st.subheader("Feature Importance") | |
| reg_imp_fig = plot_feature_importance(reg_model, regression_features) | |
| if reg_imp_fig: | |
| st.pyplot(reg_imp_fig) | |
| # Feature explanation | |
| st.subheader("Understanding Your Model") | |
| if regression_algorithm == "Linear Regression": | |
| # Get coefficients | |
| coefficients = pd.DataFrame({ | |
| 'Feature': regression_features, | |
| 'Coefficient': reg_model.coef_ | |
| }) | |
| coefficients = coefficients.sort_values('Coefficient', key=abs, ascending=False) | |
| st.markdown("**Model Coefficients:**") | |
| st.dataframe(coefficients) | |
| st.markdown(f"**Intercept:** {reg_model.intercept_:.4f}") | |
| st.markdown(""" | |
| **Linear Regression** models the target as a linear combination of the features. | |
| - **Coefficients**: Show the change in the target for a one-unit change in the feature | |
| - **Intercept**: The predicted value when all features are zero | |
| - **Strengths**: Simple, interpretable, computationally efficient | |
| - **Limitations**: Assumes linear relationship, sensitive to outliers | |
| """) | |
| elif regression_algorithm == "Decision Tree": | |
| st.markdown(""" | |
| **Decision Tree Regression** predicts by dividing the feature space into regions and using the mean target value in each region. | |
| - **Strengths**: Captures non-linear relationships, handles both numerical and categorical data | |
| - **Limitations**: Prone to overfitting with deep trees | |
| The feature importance chart above shows which features were most influential in creating the splits in the tree. | |
| """) | |
| elif regression_algorithm == "Random Forest": | |
| st.markdown(""" | |
| **Random Forest Regression** combines multiple decision trees for better prediction and reduced overfitting. | |
| - **Strengths**: High accuracy, handles large datasets well, good with non-linear data | |
| - **Limitations**: Less interpretable, computationally intensive | |
| The feature importance chart above shows which features were most influential across all trees in the forest. | |
| """) | |
| elif regression_algorithm == "K-Nearest Neighbors": | |
| st.markdown(""" | |
| **K-Nearest Neighbors Regression** predicts the average target value of the k nearest training examples. | |
| - **Strengths**: Simple, effective for small datasets, no training phase | |
| - **Limitations**: Computationally expensive for large datasets, sensitive to irrelevant features and the scale of the data | |
| For KNN regression, the predicted value is the average of the target values for the k nearest neighbors. | |
| """) | |
| # Progress complete | |
| reg_progress_bar.progress(100) | |
| reg_status_text.text("Model training and evaluation complete!") | |
| except Exception as e: | |
| st.error(f"An error occurred during model training: {e}") | |
| reg_progress_bar.empty() | |
| reg_status_text.empty() | |
| with tab3: | |
| st.header("Clustering") | |
| st.markdown(""" | |
| Clustering algorithms group similar data points together based on their features. | |
| These are unsupervised learning techniques that find natural patterns in your data. | |
| """) | |
| # Educational content | |
| with st.expander("Learn about clustering"): | |
| st.markdown(""" | |
| ### Clustering in Machine Learning | |
| Clustering is an unsupervised learning technique that groups similar data points together without predefined labels. | |
| **Common clustering algorithms:** | |
| 1. **K-Means**: Partitions data into k clusters by minimizing the distance from data points to cluster centers. | |
| 2. **DBSCAN (Density-Based Spatial Clustering of Applications with Noise)**: Groups points in high-density regions, identifies outliers, and can find clusters of arbitrary shapes. | |
| 3. **Hierarchical Clustering**: Builds a tree of clusters by either starting with all points as individual clusters and merging (agglomerative) or starting with one cluster and dividing (divisive). | |
| **When to use clustering:** | |
| - Customer segmentation | |
| - Document categorization | |
| - Anomaly detection | |
| - Image segmentation | |
| - Recommender systems | |
| **Evaluation metrics:** | |
| - **Silhouette Score**: Measures how similar an object is to its own cluster compared to other clusters (-1 to 1, higher is better) | |
| - **Inertia**: Sum of squared distances to the nearest cluster center (lower is better, but decreases with more clusters) | |
| - **Davies-Bouldin Index**: Average similarity of each cluster with its most similar cluster (lower is better) | |
| """) | |
| # Input settings for clustering | |
| st.subheader("Model Settings") | |
| # Select features for clustering | |
| clustering_features = st.multiselect( | |
| "Select features for clustering", | |
| numeric_columns, | |
| default=numeric_columns[:min(3, len(numeric_columns))], | |
| key="clustering_features" | |
| ) | |
| # Select clustering algorithm | |
| clustering_algorithm = st.selectbox( | |
| "Select clustering algorithm", | |
| ["K-Means", "DBSCAN"], | |
| key="clustering_algorithm" | |
| ) | |
| # Algorithm-specific parameters | |
| if clustering_algorithm == "K-Means": | |
| n_clusters = st.slider("Number of clusters (k)", 2, 10, 3, key="kmeans_k") | |
| # Option to use elbow method | |
| use_elbow = st.checkbox("Use elbow method to find optimal k", value=False) | |
| if use_elbow: | |
| max_k = st.slider("Maximum k to try", 2, 15, 10, key="elbow_max_k") | |
| elif clustering_algorithm == "DBSCAN": | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| eps = st.slider("Epsilon (neighborhood size)", 0.1, 2.0, 0.5, 0.1, key="dbscan_eps") | |
| with col2: | |
| min_samples = st.slider("Minimum samples in neighborhood", 2, 20, 5, key="dbscan_min_samples") | |
| # Standardize data option | |
| standardize = st.checkbox("Standardize features (recommended)", value=True) | |
| # Run clustering button | |
| if st.button("Run Clustering", key="run_clustering"): | |
| if len(clustering_features) < 2: | |
| st.error("Please select at least two features for clustering.") | |
| else: | |
| # Create progress bar | |
| cluster_progress_bar = st.progress(0) | |
| cluster_status_text = st.empty() | |
| try: | |
| # Progress update | |
| cluster_status_text.text("Preparing data...") | |
| cluster_progress_bar.progress(10) | |
| # Extract features | |
| X = df[clustering_features].dropna() | |
| # Standardize if selected | |
| if standardize: | |
| scaler = StandardScaler() | |
| X_scaled = scaler.fit_transform(X) | |
| else: | |
| X_scaled = X.values | |
| # Progress update | |
| cluster_status_text.text("Data prepared. Running clustering algorithm...") | |
| cluster_progress_bar.progress(30) | |
| # Run clustering | |
| if clustering_algorithm == "K-Means": | |
| if use_elbow: | |
| # Calculate inertia for different k values | |
| inertia = [] | |
| k_values = range(1, max_k + 1) | |
| for k in k_values: | |
| kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) | |
| kmeans.fit(X_scaled) | |
| inertia.append(kmeans.inertia_) | |
| # Plot elbow method | |
| st.subheader("Elbow Method") | |
| fig, ax = plt.subplots(figsize=(10, 6)) | |
| ax.plot(k_values, inertia, 'bo-') | |
| ax.set_xlabel('Number of Clusters (k)') | |
| ax.set_ylabel('Inertia (Sum of Squared Distances)') | |
| ax.set_title('Elbow Method for Optimal k') | |
| ax.grid(True) | |
| st.pyplot(fig) | |
| st.markdown(""" | |
| **How to interpret:** Look for the "elbow" point where the rate of decrease sharply changes. | |
| This suggests a good balance between cluster quality and number of clusters. | |
| """) | |
| # Fit final K-Means model | |
| kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10) | |
| clusters = kmeans.fit_predict(X_scaled) | |
| cluster_centers = kmeans.cluster_centers_ | |
| if standardize: | |
| # Transform cluster centers back to original scale | |
| cluster_centers = scaler.inverse_transform(cluster_centers) | |
| # Evaluate clustering | |
| silhouette_avg = silhouette_score(X_scaled, clusters) | |
| inertia = kmeans.inertia_ | |
| # Add cluster labels to data | |
| X['Cluster'] = clusters | |
| elif clustering_algorithm == "DBSCAN": | |
| # Fit DBSCAN | |
| dbscan = DBSCAN(eps=eps, min_samples=min_samples) | |
| clusters = dbscan.fit_predict(X_scaled) | |
| # Count number of clusters | |
| n_clusters = len(set(clusters)) - (1 if -1 in clusters else 0) | |
| # Evaluate clustering if more than one cluster is found | |
| if n_clusters > 1: | |
| # Filter out noise points (-1) for silhouette score | |
| if -1 in clusters: | |
| mask = clusters != -1 | |
| silhouette_avg = silhouette_score(X_scaled[mask], clusters[mask]) | |
| else: | |
| silhouette_avg = silhouette_score(X_scaled, clusters) | |
| else: | |
| silhouette_avg = "N/A (need at least 2 clusters)" | |
| # Add cluster labels to data | |
| X['Cluster'] = clusters | |
| # Progress update | |
| cluster_status_text.text("Generating visualizations...") | |
| cluster_progress_bar.progress(70) | |
| # Display results | |
| st.subheader("Clustering Results") | |
| # Display metrics | |
| col1, col2 = st.columns([1, 1]) | |
| with col1: | |
| st.metric("Number of Clusters", n_clusters) | |
| if clustering_algorithm == "K-Means": | |
| st.metric("Inertia", f"{inertia:.2f}") | |
| with col2: | |
| if isinstance(silhouette_avg, str): | |
| st.metric("Silhouette Score", silhouette_avg) | |
| else: | |
| st.metric("Silhouette Score", f"{silhouette_avg:.4f}") | |
| if clustering_algorithm == "DBSCAN": | |
| noise_count = np.sum(clusters == -1) | |
| noise_percent = noise_count / len(clusters) * 100 | |
| st.metric("Noise Points", f"{noise_count} ({noise_percent:.1f}%)") | |
| # Summary of clusters | |
| st.subheader("Cluster Summary") | |
| cluster_summary = X.groupby('Cluster').size().reset_index() | |
| cluster_summary.columns = ['Cluster', 'Count'] | |
| cluster_summary['Percentage'] = cluster_summary['Count'] / len(X) * 100 | |
| st.dataframe(cluster_summary) | |
| # Cluster profiles | |
| st.subheader("Cluster Profiles") | |
| cluster_profiles = X.groupby('Cluster')[clustering_features].mean() | |
| if clustering_algorithm == "K-Means": | |
| st.dataframe(cluster_profiles) | |
| else: | |
| st.dataframe(cluster_profiles) | |
| # Visualize clusters (select 2 features for 2D plot or 3 for 3D) | |
| st.subheader("Cluster Visualization") | |
| if len(clustering_features) >= 2: | |
| # Let user select features to visualize | |
| col1, col2, col3 = st.columns([1, 1, 1]) | |
| with col1: | |
| x_feature = st.selectbox("X-axis", clustering_features, index=0, key="cluster_viz_x") | |
| with col2: | |
| y_feature = st.selectbox("Y-axis", clustering_features, index=min(1, len(clustering_features)-1), key="cluster_viz_y") | |
| with col3: | |
| if len(clustering_features) >= 3: | |
| viz_type = st.radio("Visualization type", ["2D", "3D"], key="cluster_viz_type") | |
| if viz_type == "3D": | |
| z_feature = st.selectbox("Z-axis", clustering_features, index=min(2, len(clustering_features)-1), key="cluster_viz_z") | |
| else: | |
| viz_type = "2D" | |
| # Create the visualization | |
| if viz_type == "2D": | |
| fig, ax = plt.subplots(figsize=(10, 8)) | |
| # Create a color map that handles -1 (noise) differently | |
| cmap = plt.cm.get_cmap('tab10', n_clusters) | |
| scatter = ax.scatter(X[x_feature], X[y_feature], c=X['Cluster'], cmap=cmap, alpha=0.7) | |
| # Add cluster centers for K-Means | |
| if clustering_algorithm == "K-Means": | |
| ax.scatter( | |
| cluster_centers[:, clustering_features.index(x_feature)], | |
| cluster_centers[:, clustering_features.index(y_feature)], | |
| s=200, marker='X', c='red', label='Cluster Centers' | |
| ) | |
| ax.set_xlabel(x_feature) | |
| ax.set_ylabel(y_feature) | |
| ax.set_title(f'Clusters by {x_feature} and {y_feature}') | |
| if clustering_algorithm == "K-Means": | |
| ax.legend() | |
| plt.colorbar(scatter, label='Cluster') | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| elif viz_type == "3D" and len(clustering_features) >= 3: | |
| fig = plt.figure(figsize=(10, 8)) | |
| ax = fig.add_subplot(111, projection='3d') | |
| scatter = ax.scatter( | |
| X[x_feature], X[y_feature], X[z_feature], | |
| c=X['Cluster'], cmap='tab10', alpha=0.7 | |
| ) | |
| # Add cluster centers for K-Means | |
| if clustering_algorithm == "K-Means": | |
| ax.scatter( | |
| cluster_centers[:, clustering_features.index(x_feature)], | |
| cluster_centers[:, clustering_features.index(y_feature)], | |
| cluster_centers[:, clustering_features.index(z_feature)], | |
| s=200, marker='X', c='red', label='Cluster Centers' | |
| ) | |
| ax.set_xlabel(x_feature) | |
| ax.set_ylabel(y_feature) | |
| ax.set_zlabel(z_feature) | |
| ax.set_title(f'3D Visualization of Clusters') | |
| if clustering_algorithm == "K-Means": | |
| ax.legend() | |
| plt.colorbar(scatter, label='Cluster') | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| # Feature explanation | |
| st.subheader("Understanding Your Clustering Results") | |
| if clustering_algorithm == "K-Means": | |
| st.markdown(f""" | |
| **K-Means Clustering with k={n_clusters}** | |
| - **How it works**: Divides data into k clusters by minimizing the distance from points to cluster centers | |
| - **Strengths**: Simple, efficient, works well with globular clusters | |
| - **Limitations**: Assumes spherical clusters, sensitive to outliers, requires specifying k in advance | |
| **Interpreting the results**: | |
| - Each cluster represents a group of similar data points | |
| - Cluster centers show the "typical" values for each feature in that cluster | |
| - Higher silhouette score (closer to 1) indicates better-defined clusters | |
| """) | |
| elif clustering_algorithm == "DBSCAN": | |
| st.markdown(f""" | |
| **DBSCAN Clustering with epsilon={eps}, min_samples={min_samples}** | |
| - **How it works**: Groups points in dense regions, marks points in low-density regions as noise | |
| - **Strengths**: Doesn't require specifying number of clusters, can find arbitrary shapes, identifies outliers | |
| - **Limitations**: Sensitive to parameter choices, struggles with varying densities | |
| **Interpreting the results**: | |
| - Cluster -1 represents noise points that don't belong to any cluster | |
| - Each other cluster represents a dense region of similar data points | |
| - The silhouette score indicates how well-separated the clusters are | |
| """) | |
| # Progress complete | |
| cluster_progress_bar.progress(100) | |
| cluster_status_text.text("Clustering complete!") | |
| except Exception as e: | |
| st.error(f"An error occurred during clustering: {e}") | |
| cluster_progress_bar.empty() | |
| cluster_status_text.empty() | |
| with tab4: | |
| st.header("Dimensionality Reduction") | |
| st.markdown(""" | |
| Dimensionality reduction techniques reduce the number of features while preserving important information. | |
| This helps with visualization, addressing the curse of dimensionality, and improving model performance. | |
| """) | |
| # Educational content | |
| with st.expander("Learn about dimensionality reduction"): | |
| st.markdown(""" | |
| ### Dimensionality Reduction in Machine Learning | |
| Dimensionality reduction transforms high-dimensional data into a lower-dimensional representation while preserving important information. | |
| **Common dimensionality reduction techniques:** | |
| 1. **Principal Component Analysis (PCA)**: Creates new uncorrelated features (principal components) that capture maximum variance in the data. | |
| 2. **t-SNE (t-Distributed Stochastic Neighbor Embedding)**: Focuses on preserving local structures, good for visualization. | |
| 3. **UMAP (Uniform Manifold Approximation and Projection)**: Similar to t-SNE but better preserves global structure. | |
| **When to use dimensionality reduction:** | |
| - Visualizing high-dimensional data in 2D or 3D | |
| - Reducing computational cost for subsequent algorithms | |
| - Addressing multicollinearity in features | |
| - Removing noise from data | |
| **Benefits:** | |
| - Easier visualization | |
| - Faster training of machine learning models | |
| - Reduced risk of overfitting | |
| - Removal of multicollinearity | |
| """) | |
| # Input settings for dimensionality reduction | |
| st.subheader("Model Settings") | |
| # Select features | |
| dim_red_features = st.multiselect( | |
| "Select numeric features", | |
| numeric_columns, | |
| default=numeric_columns[:min(5, len(numeric_columns))], | |
| key="dim_red_features" | |
| ) | |
| # Select technique (currently only PCA is implemented) | |
| dim_red_technique = st.selectbox( | |
| "Select technique", | |
| ["PCA (Principal Component Analysis)"], | |
| key="dim_red_technique" | |
| ) | |
| # Select number of components | |
| if dim_red_technique == "PCA (Principal Component Analysis)": | |
| max_components = min(len(dim_red_features), 10) | |
| n_components = st.slider("Number of components", 2, max_components, 2, key="pca_components") | |
| # Standardize data option | |
| standardize_dim_red = st.checkbox("Standardize features (recommended for PCA)", value=True, key="standardize_dim_red") | |
| # Optional: color points by category | |
| color_by = st.selectbox( | |
| "Color points by (optional)", | |
| ["None"] + categorical_columns, | |
| key="dim_red_color" | |
| ) | |
| # Run dimensionality reduction button | |
| if st.button("Run Dimensionality Reduction", key="run_dim_red"): | |
| if len(dim_red_features) < 2: | |
| st.error("Please select at least two features.") | |
| else: | |
| # Create progress bar | |
| dim_red_progress_bar = st.progress(0) | |
| dim_red_status_text = st.empty() | |
| try: | |
| # Progress update | |
| dim_red_status_text.text("Preparing data...") | |
| dim_red_progress_bar.progress(10) | |
| # Extract features | |
| if color_by != "None": | |
| X_dim_red = df[dim_red_features + [color_by]].dropna() | |
| color_values = X_dim_red[color_by] | |
| X_dim_red = X_dim_red[dim_red_features] | |
| else: | |
| X_dim_red = df[dim_red_features].dropna() | |
| color_values = None | |
| # Standardize if selected | |
| if standardize_dim_red: | |
| scaler_dim_red = StandardScaler() | |
| X_scaled_dim_red = scaler_dim_red.fit_transform(X_dim_red) | |
| else: | |
| X_scaled_dim_red = X_dim_red.values | |
| # Progress update | |
| dim_red_status_text.text("Running dimensionality reduction...") | |
| dim_red_progress_bar.progress(30) | |
| # Run PCA | |
| if dim_red_technique == "PCA (Principal Component Analysis)": | |
| pca = PCA(n_components=n_components) | |
| principal_components = pca.fit_transform(X_scaled_dim_red) | |
| # Create DataFrame with principal components | |
| pc_df = pd.DataFrame( | |
| data=principal_components, | |
| columns=[f'PC{i+1}' for i in range(n_components)] | |
| ) | |
| # Add color column if specified | |
| if color_values is not None: | |
| pc_df[color_by] = color_values.reset_index(drop=True) | |
| # Get explained variance | |
| explained_variance = pca.explained_variance_ratio_ | |
| cumulative_variance = np.cumsum(explained_variance) | |
| # Get component loadings | |
| loadings = pca.components_ | |
| loading_df = pd.DataFrame( | |
| loadings.T, | |
| columns=[f'PC{i+1}' for i in range(n_components)], | |
| index=dim_red_features | |
| ) | |
| # Progress update | |
| dim_red_status_text.text("Generating visualizations...") | |
| dim_red_progress_bar.progress(70) | |
| # Display results | |
| st.subheader("Dimensionality Reduction Results") | |
| # Display explained variance | |
| if dim_red_technique == "PCA (Principal Component Analysis)": | |
| st.subheader("Explained Variance") | |
| # Create bar chart of explained variance | |
| fig, ax = plt.subplots(figsize=(10, 6)) | |
| ax.bar(range(1, n_components + 1), explained_variance * 100) | |
| ax.plot(range(1, n_components + 1), cumulative_variance * 100, 'ro-') | |
| ax.set_xlabel('Principal Component') | |
| ax.set_ylabel('Explained Variance (%)') | |
| ax.set_title('Explained Variance by Principal Component') | |
| ax.set_xticks(range(1, n_components + 1)) | |
| # Add cumulative variance as text | |
| for i, (var, cum_var) in enumerate(zip(explained_variance, cumulative_variance)): | |
| ax.text(i + 1, var * 100 + 2, f'{var:.1%}', ha='center') | |
| ax.text(i + 1, cum_var * 100 + 2, f'Cum: {cum_var:.1%}', ha='center') | |
| st.pyplot(fig) | |
| # Display component loadings | |
| st.subheader("Component Loadings") | |
| st.markdown("These show how much each original feature contributes to each principal component.") | |
| # Format loadings for display | |
| st.dataframe(loading_df.style.format("{:.3f}").background_gradient(cmap='coolwarm'), use_container_width=True) | |
| # Create heatmap of loadings | |
| fig, ax = plt.subplots(figsize=(12, 8)) | |
| sns.heatmap(loading_df, annot=True, cmap='coolwarm', fmt=".2f", ax=ax) | |
| ax.set_title('PCA Component Loadings') | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| # Visualize reduced dimensions | |
| st.subheader("Visualizing Data in Reduced Dimensions") | |
| if n_components >= 2: | |
| # 2D visualization | |
| fig, ax = plt.subplots(figsize=(10, 8)) | |
| if color_values is not None: | |
| # If categorical, use discrete colors | |
| if pc_df[color_by].dtype == 'object' or pc_df[color_by].dtype.name == 'category': | |
| categories = pc_df[color_by].unique() | |
| cmap = plt.cm.get_cmap('tab10', len(categories)) | |
| # Create a color map | |
| color_dict = {cat: i for i, cat in enumerate(categories)} | |
| colors = [color_dict[val] for val in pc_df[color_by]] | |
| scatter = ax.scatter(pc_df['PC1'], pc_df['PC2'], c=colors, cmap=cmap, alpha=0.7) | |
| # Create legend | |
| handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=cmap(i), | |
| markersize=10, label=cat) for i, cat in enumerate(categories)] | |
| ax.legend(handles=handles, title=color_by, loc='best') | |
| # If numeric, use continuous colormap | |
| else: | |
| scatter = ax.scatter(pc_df['PC1'], pc_df['PC2'], c=pc_df[color_by], cmap='viridis', alpha=0.7) | |
| plt.colorbar(scatter, label=color_by) | |
| else: | |
| ax.scatter(pc_df['PC1'], pc_df['PC2'], alpha=0.7) | |
| # Add axis labels with explained variance | |
| ax.set_xlabel(f'PC1 ({explained_variance[0]:.1%} variance explained)') | |
| ax.set_ylabel(f'PC2 ({explained_variance[1]:.1%} variance explained)') | |
| ax.set_title('Data in 2D Principal Component Space') | |
| st.pyplot(fig) | |
| # 3D visualization if we have at least 3 components | |
| if n_components >= 3: | |
| st.subheader("3D Visualization") | |
| fig = plt.figure(figsize=(10, 8)) | |
| ax = fig.add_subplot(111, projection='3d') | |
| if color_values is not None: | |
| # If categorical, use discrete colors | |
| if pc_df[color_by].dtype == 'object' or pc_df[color_by].dtype.name == 'category': | |
| categories = pc_df[color_by].unique() | |
| cmap = plt.cm.get_cmap('tab10', len(categories)) | |
| # Create a color map | |
| color_dict = {cat: i for i, cat in enumerate(categories)} | |
| colors = [color_dict[val] for val in pc_df[color_by]] | |
| scatter = ax.scatter(pc_df['PC1'], pc_df['PC2'], pc_df['PC3'], c=colors, cmap=cmap, alpha=0.7) | |
| # Create legend | |
| handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=cmap(i), | |
| markersize=10, label=cat) for i, cat in enumerate(categories)] | |
| ax.legend(handles=handles, title=color_by, loc='best') | |
| # If numeric, use continuous colormap | |
| else: | |
| scatter = ax.scatter(pc_df['PC1'], pc_df['PC2'], pc_df['PC3'], c=pc_df[color_by], cmap='viridis', alpha=0.7) | |
| plt.colorbar(scatter, label=color_by) | |
| else: | |
| ax.scatter(pc_df['PC1'], pc_df['PC2'], pc_df['PC3'], alpha=0.7) | |
| # Add axis labels with explained variance | |
| ax.set_xlabel(f'PC1 ({explained_variance[0]:.1%})') | |
| ax.set_ylabel(f'PC2 ({explained_variance[1]:.1%})') | |
| ax.set_zlabel(f'PC3 ({explained_variance[2]:.1%})') | |
| ax.set_title('Data in 3D Principal Component Space') | |
| st.pyplot(fig) | |
| # Display transformed data | |
| st.subheader("Transformed Data") | |
| st.dataframe(pc_df.head(10), use_container_width=True) | |
| # Feature explanation | |
| st.subheader("Understanding PCA Results") | |
| st.markdown(""" | |
| **Principal Component Analysis (PCA)** | |
| - **How it works**: PCA transforms the original features into a new set of uncorrelated features (principal components) that capture the maximum variance in the data. | |
| - **Explained variance**: Shows how much information (variance) each principal component captures from the original data. Higher values mean the component is more important. | |
| - **Component loadings**: Show how each original feature contributes to each principal component. Higher absolute values (positive or negative) indicate stronger influence. | |
| - **Interpreting the visualization**: | |
| - Points that are close together in the reduced space are similar in the original high-dimensional space | |
| - Clusters in the reduced space suggest natural groupings in the data | |
| - The axes represent the directions of maximum variance in the data | |
| - **Applications**: | |
| - Data visualization | |
| - Noise reduction | |
| - Feature extraction | |
| - Preprocessing for other algorithms | |
| """) | |
| # Progress complete | |
| dim_red_progress_bar.progress(100) | |
| dim_red_status_text.text("Dimensionality reduction complete!") | |
| except Exception as e: | |
| st.error(f"An error occurred during dimensionality reduction: {e}") | |
| dim_red_progress_bar.empty() | |
| dim_red_status_text.empty() | |
| # Educational sidebar | |
| with st.sidebar: | |
| st.title("📚 Learning Corner") | |
| st.markdown(""" | |
| ### Machine Learning Fundamentals | |
| **Types of Machine Learning:** | |
| 1. **Supervised Learning** | |
| - Uses labeled data | |
| - Examples: Classification, Regression | |
| 2. **Unsupervised Learning** | |
| - Uses unlabeled data | |
| - Examples: Clustering, Dimensionality Reduction | |
| 3. **Reinforcement Learning** | |
| - Learns through interaction with an environment | |
| - Examples: Game playing, Robotics | |
| **ML Workflow:** | |
| 1. Data Collection | |
| 2. Data Preparation (cleaning, feature engineering) | |
| 3. Model Selection | |
| 4. Training | |
| 5. Evaluation | |
| 6. Deployment | |
| **Avoiding Overfitting:** | |
| - Use cross-validation | |
| - Regularization | |
| - More training data | |
| - Simpler models | |
| - Early stopping | |
| **Feature Selection:** | |
| Good features make models more accurate and efficient. Look for: | |
| - Relevance to the target | |
| - Non-redundancy | |
| - Good coverage of the problem space | |
| """) | |