CoolFace
Apppublic

lschrand/RM

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
app.py203 linesDownload Raw Back to root
1import pandas as pd2import numpy as np3import plotly.express as px4import plotly.graph_objects as go5import gradio as gr6from sklearn.cluster import KMeans7from sklearn.preprocessing import StandardScaler8from sklearn.metrics import silhouette_score9import os10 11# --- CONSTANTS ---12FEATURES = ['Fresh', 'Milk', 'Grocery', 'Frozen', 'Detergents_Paper', 'Delicassen']13CATEGORICAL = ['Channel', 'Region']14FILE_PATH = "R_Dataset.csv"15 16# --- CORE FUNCTIONS ---17 18def load_data():19    if not os.path.exists(FILE_PATH):20        return None, f"Error: {FILE_PATH} not found. Please ensure the dataset is in the root directory."21    try:22        df = pd.read_csv(FILE_PATH)23        return df, None24    except Exception as e:25        return None, f"Error loading data: {str(e)}"26 27def validate_data(df):28    required = FEATURES + CATEGORICAL29    missing = [col for col in required if col not in df.columns]30    if missing:31        return False, f"Missing columns: {', '.join(missing)}"32    return True, None33 34def preprocess_data(df):35    df_clean = df.copy().dropna(subset=FEATURES)36    scaler = StandardScaler()37    X_scaled = scaler.fit_transform(df_clean[FEATURES])38    return df_clean, X_scaled39 40def run_kmeans(df_clean, X_scaled, n_clusters):41    kmeans = KMeans(n_clusters=n_clusters, init='k-means++', n_init=10, random_state=42)42    df_model = df_clean.copy()43    df_model['Cluster'] = kmeans.fit_predict(X_scaled)44    # Ensure Cluster is treated as a discrete category for plotting45    df_model['Cluster'] = df_model['Cluster'].astype(str)46    return df_model47 48def create_diagnostics():49    df, err = load_data()50    if err: return gr.update(visible=False), err51    52    valid, v_err = validate_data(df)53    if not valid: return gr.update(visible=False), v_err54    55    df_clean, X_scaled = preprocess_data(df)56    57    wcss = []58    sil_scores = []59    k_range = range(2, 11)60    61    for k in k_range:62        km = KMeans(n_clusters=k, init='k-means++', n_init=10, random_state=42)63        km.fit(X_scaled)64        wcss.append(km.inertia_)65        sil_scores.append(silhouette_score(X_scaled, km.labels_))66    67    fig_elbow = px.line(x=list(k_range), y=wcss, title="Elbow Method (WCSS)",68                        labels={'x': 'Number of Clusters', 'y': 'WCSS'}, markers=True)69    70    fig_sil = px.line(x=list(k_range), y=sil_scores, title="Silhouette Score Analysis",71                      labels={'x': 'Number of Clusters', 'y': 'Score'}, markers=True)72    73    return fig_elbow, fig_sil, None74 75def create_3d_plot(n_clusters):76    df, err = load_data()77    if err: return None78    df_clean, X_scaled = preprocess_data(df)79    df_model = run_kmeans(df_clean, X_scaled, n_clusters)80    81    fig = px.scatter_3d(82        df_model,83        x='Grocery',84        y='Detergents_Paper',85        z='Milk',86        color='Cluster',87        symbol='Channel',88        title=f"3D Cluster Segmentation (K={n_clusters})",89        opacity=0.8,90        height=700,91        category_orders={"Cluster": [str(i) for i in range(n_clusters)]}92    )93    fig.update_layout(legend_title_text='Cluster ID')94    return fig95 96def create_cluster_profiles(n_clusters):97    df, err = load_data()98    if err: return None, None, None, None99    df_clean, X_scaled = preprocess_data(df)100    df_model = run_kmeans(df_clean, X_scaled, n_clusters)101    102    # 1. Cluster Sizes103    sizes = df_model.groupby('Cluster').size().reset_index(name='Count')104    fig_sizes = px.bar(sizes, x='Cluster', y='Count', title="Customer Count per Cluster", color='Cluster')105    106    # 2. Radar Chart / Means107    means = df_model.groupby('Cluster')[FEATURES].mean().reset_index()108    fig_radar = go.Figure()109    for i in range(n_clusters):110        cluster_id = str(i)111        row = means[means['Cluster'] == cluster_id]112        if not row.empty:113            fig_radar.add_trace(go.Scatterpolar(114                r=row[FEATURES].values[0],115                theta=FEATURES,116                fill='toself',117                name=f'Cluster {cluster_id}'118            ))119    fig_radar.update_layout(polar=dict(radialaxis=dict(visible=True)), title="Feature Means by Cluster")120    121    # 3. Channel Distribution122    channel_dist = df_model.groupby(['Cluster', 'Channel']).size().reset_index(name='Count')123    fig_channel = px.bar(channel_dist, x='Cluster', y='Count', color='Channel', barmode='group', title="Channel distribution per Cluster")124    125    # 4. Region Distribution126    region_dist = df_model.groupby(['Cluster', 'Region']).size().reset_index(name='Count')127    fig_region = px.bar(region_dist, x='Cluster', y='Count', color='Region', barmode='group', title="Region distribution per Cluster")128    129    return fig_sizes, fig_radar, fig_channel, fig_region130 131# --- GRADIO INTERFACE ---132 133with gr.Blocks(theme=gr.themes.Soft(), title="Recheio Customer Segmentation") as demo:134    gr.Markdown("# ๐Ÿ“Š Recheio Customer Segmentation Dashboard")135    gr.Markdown("Production-grade K-Means clustering for wholesale customer behavior analysis.")136    137    with gr.Row():138        cluster_slider = gr.Slider(minimum=2, maximum=10, value=4, step=1, label="Select number of segments")139    140    with gr.Tabs():141        # TAB 1: DIAGNOSTICS142        with gr.TabItem("Clustering Diagnostics"):143            with gr.Row():144                diag_elbow = gr.Plot()145                diag_sil = gr.Plot()146            diag_err = gr.Markdown(visible=False)147            148        # TAB 2: 3D VISUALIZATION149        with gr.TabItem("3D Cluster Visualization"):150            plot_3d = gr.Plot()151            152        # TAB 3: PROFILING153        with gr.TabItem("Cluster Profiling"):154            with gr.Row():155                profile_sizes = gr.Plot()156                profile_radar = gr.Plot()157            with gr.Row():158                profile_channel = gr.Plot()159                profile_region = gr.Plot()160                161        # TAB 4: DATA DICTIONARY162        with gr.TabItem("Data Dictionary"):163            dict_data = {164                "Feature": FEATURES + CATEGORICAL,165                "Description": [166                    "Annual spending on fresh products",167                    "Annual spending on milk products",168                    "Annual spending on grocery products",169                    "Annual spending on frozen products",170                    "Annual spending on detergents and paper products",171                    "Annual spending on delicatessen products",172                    "Customer Channel (1: Horeca, 2: Retail)",173                    "Customer Region (1: Lisbon, 2: Oporto, 3: Other)"174                ]175            }176            gr.Table(value=pd.DataFrame(dict_data))177 178    # --- LOGIC BINDING ---179    180    # Static Load for Diagnostics181    demo.load(create_diagnostics, outputs=[diag_elbow, diag_sil, diag_err])182    183    # Dynamic Updates184    def update_dynamic_tabs(n):185        fig3d = create_3d_plot(n)186        p1, p2, p3, p4 = create_cluster_profiles(n)187        return fig3d, p1, p2, p3, p4188 189    cluster_slider.change(190        fn=update_dynamic_tabs,191        inputs=cluster_slider,192        outputs=[plot_3d, profile_sizes, profile_radar, profile_channel, profile_region]193    )194    195    # Initial load for dynamic components196    demo.load(197        fn=update_dynamic_tabs,198        inputs=cluster_slider,199        outputs=[plot_3d, profile_sizes, profile_radar, profile_channel, profile_region]200    )201 202if __name__ == "__main__":203    demo.launch()