Spaces:
Runtime error
Runtime error
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| import gradio as gr | |
| import re | |
| custom_theme = gr.themes.Base().set( | |
| body_background_fill="linear-gradient(120deg, #f9fcff 0%, #fef6ff 100%)", | |
| block_background_fill="white", | |
| block_shadow="0px 5px 15px rgba(0,0,0,0.06)", | |
| block_border_width="1px", | |
| block_border_color="#e6e6e6", | |
| ) | |
| # Run predictions | |
| results_df = run_batch_predictions(df, pipe) | |
| # Create a new DataFrame without 'Clusters' and 'Recommendation' | |
| export_df = results_df.drop(columns=[col for col in ['Clusters', 'Recommendation'] if col in results_df.columns]) | |
| # Save to CSV | |
| export_df.to_csv('results_summary.csv', index=False) | |
| # Add the constant column | |
| results_df["RecordCount"] = 1 | |
| # Extract actions | |
| '''' | |
| def extract_actions(html_text): | |
| if not isinstance(html_text, str): | |
| return ["No action"] | |
| text = html_text.replace("<br>", "\n") | |
| actions = re.findall(r"• (.+)", text) | |
| return actions if actions else ["No action"] | |
| ''' | |
| # Drilldown DataFrame | |
| drilldown_data = [] | |
| for _, row in results_df.iterrows(): | |
| churn_pred = row["Churn Prediction"] | |
| prob= row["Probability"] | |
| clusters = row["Clusters"] | |
| if isinstance(clusters, dict): | |
| for cluster_name, rec_list in clusters.items(): | |
| for rec in rec_list: | |
| drilldown_data.append({ | |
| "Churn Prediction": churn_pred, | |
| "Cluster": cluster_name, | |
| "Recommendation": rec, | |
| "Probability": prob | |
| }) | |
| drilldown_df = pd.DataFrame(drilldown_data) | |
| # Chart defaults | |
| px.defaults.template = "plotly_white" | |
| px.defaults.color_discrete_sequence = px.colors.qualitative.Set2 | |
| # Helper for numeric KPI cards | |
| def make_numeric_card(title, value, suffix=""): | |
| fig = go.Figure(go.Indicator( | |
| mode="number", | |
| value=value, | |
| number={'font': {'size': 36}, 'suffix': suffix}, | |
| title={'text': title, 'font': {'size': 16}} | |
| )) | |
| fig.update_layout( | |
| height=140, width=220, | |
| margin=dict(l=10, r=10, t=25, b=10), | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| # Build Analytics Figures | |
| def generate_analytics_figs(df): | |
| figs = {} | |
| figs["avg_tenure"] = make_numeric_card("Avg Tenure (Months)", df["Tenure in Months"].mean()) | |
| figs["avg_charge"] = make_numeric_card("Avg Monthly Charge ($)", df["Monthly Charge"].mean(), suffix="$") | |
| figs["avg_data"] = make_numeric_card("Avg Monthly GB Download", df["Avg Monthly GB Download"].mean()) | |
| figs["tenure_hist"] = px.histogram(df, x="Tenure in Months", nbins=20, title="Tenure Distribution") | |
| figs["age_hist"] = px.histogram(df, x="Age", nbins=20, title="Age Distribution") | |
| figs["contract_pie"] = px.pie(df, names="Contract", hole=0.4, title="Contract Types") | |
| figs["dependents_donut"] = px.pie(df, names="Number of Dependents", hole=0.4, title="Dependents Distribution") | |
| figs["senior_pie"] = px.pie(df, names="Senior Citizen", hole=0.4, title="Senior Citizen Ratio") | |
| figs["streaming_pie"] = px.pie(df, names="Streaming Movies", hole=0.4, title="Streaming Movies Adoption") | |
| figs["security_pie"] = px.pie(df, names="Online Security", hole=0.4, title="Online Security Subscription") | |
| figs["unlimited_pie"] = px.pie(df, names="Unlimited Data", hole=0.4, title="Unlimited Data Adoption") | |
| referrals_df = df["Number of Referrals"].value_counts().reset_index() | |
| referrals_df.columns = ["Number of Referrals", "count"] | |
| figs["referrals_bar"] = px.bar( | |
| referrals_df, | |
| x="Number of Referrals", | |
| y="count", | |
| title="Referrals Count Distribution", | |
| labels={"Number of Referrals": "Number of Referrals", "count": "Count"} | |
| ) | |
| for f in figs.values(): | |
| f.update_layout(margin=dict(l=20, r=20, t=40, b=20)) | |
| return figs | |
| analytics_figs = generate_analytics_figs(results_df) | |
| # Drilldown Chart | |
| def generate_drilldown_chart(): | |
| if drilldown_df.empty: | |
| fig = px.treemap(names=["No data"], parents=[""], values=[1], title="No recommendations yet") | |
| return fig | |
| fig = px.treemap( | |
| drilldown_df, | |
| path=['Churn Prediction', 'Cluster', 'Recommendation'], | |
| values=None, | |
| title="Recommendations by Churn Prediction & Cluster", | |
| ) | |
| fig.update_traces( | |
| textinfo='label+percent parent', | |
| hovertemplate="<b>%{label}</b><br>Customers: %{value}<br>Percentage of parent: %{percentParent:.1%}<extra></extra>" | |
| ) | |
| fig.update_layout( | |
| margin=dict(l=20, r=20, t=50, b=20), | |
| title_font=dict(size=18) | |
| ) | |
| return fig | |
| # Customer Details | |
| def get_customer_details(customer_id): | |
| row = results_df[results_df["Customer ID"] == customer_id].iloc[0] | |
| prediction = row["Churn Prediction"] | |
| clusters = row["Clusters"] | |
| prob= row["Probability"] | |
| # Convert decimal probability → percentage | |
| pct_value = float(prob) * 100 # numeric percentage, e.g. 82.3 | |
| pct = f"{pct_value:.0f}%" # string percentage, e.g. "82%" | |
| # Categorize risk | |
| if pct_value < 30: | |
| risk_level = "Low" | |
| elif pct_value < 60: | |
| risk_level = "Medium" | |
| else: | |
| risk_level = "High" | |
| # Corrected line | |
| churn_risk = f"{risk_level} ({pct})" | |
| # Build recommendations with cluster names | |
| if isinstance(clusters, dict): | |
| rec_lines = [] | |
| for cluster_name, rec_list in clusters.items(): | |
| for rec in rec_list: | |
| rec_lines.append(f"<b>{cluster_name}:</b> {rec}") | |
| recommendations_html = "<br>• ".join(rec_lines) if rec_lines else "No recommendations" | |
| else: | |
| recommendations_html = "No recommendations" | |
| selected_features = [ | |
| 'Contract', 'Tenure in Months', 'Age', 'Number of Referrals', | |
| 'Monthly Charge', 'Number of Dependents', 'Senior Citizen', | |
| 'Streaming Movies', 'Online Security', 'Avg Monthly GB Download', | |
| 'Unlimited Data' | |
| ] | |
| # Table with customer features | |
| table_html = "<table style='width:100%; border-collapse: collapse;'>" | |
| table_html += "<tbody>" | |
| for col in selected_features: | |
| table_html += f"<tr><td style='padding:8px; border:1px solid #ddd; font-weight:600;'>{col}</td>" | |
| table_html += f"<td style='padding:8px; border:1px solid #ddd;'>{row[col]}</td></tr>" | |
| table_html += "</tbody></table>" | |
| # Build full HTML with recommendations **outside** the table | |
| full_html = ( | |
| f"<b>Churn Risk:</b> {churn_risk}<br><br>" | |
| f"<b>Recommendations:</b><br>• {recommendations_html}<br><br>" | |
| f"<b>Customer Details:</b><br>{table_html}" | |
| ) | |
| return full_html | |
| # Gradio App | |
| with gr.Blocks( | |
| theme=custom_theme, | |
| title="CRM Churn Dashboard", | |
| css=""" | |
| /* FORCE LIGHT MODE */ | |
| :root { | |
| --color-background: #ffffff !important; | |
| --color-border: #e6e6e6 !important; | |
| --color-text: #000000 !important; | |
| --body-background-fill: linear-gradient(120deg, #f9fcff 0%, #fef6ff 100%) !important; | |
| /* Override dark mode variables */ | |
| --dark-mode: 0 !important; | |
| --neutral-900: #000000 !important; | |
| --neutral-800: #1a1a1a !important; | |
| --neutral-700: #2a2a2a !important; | |
| --neutral-600: #3a3a3a !important; | |
| --neutral-500: #777 !important; | |
| --neutral-400: #aaa !important; | |
| --neutral-300: #ccc !important; | |
| --neutral-200: #eee !important; | |
| --neutral-100: #fafafa !important; | |
| } | |
| body { | |
| background: linear-gradient(120deg, #f9fcff 0%, #fef6ff 100%) !important; | |
| } | |
| /* Force blocks to be light */ | |
| .gr-block, .gr-panel, .gr-box, .gr-container { | |
| background: white !important; | |
| color: black !important; | |
| } | |
| /* Dropdowns, inputs, etc. */ | |
| input, select, textarea { | |
| background: white !important; | |
| color: black !important; | |
| border: 1px solid #ddd !important; | |
| } | |
| """ | |
| ) as demo: | |
| gr.Markdown("## 📊 CRM Analytics Dashboard") | |
| with gr.Tabs(): | |
| # Actions Insights tab | |
| with gr.TabItem("Actions Overview"): | |
| gr.Markdown("### Drilldown of Recommendations by Cluster") | |
| drilldown_plot = gr.Plot(value=generate_drilldown_chart()) | |
| # Analytics Dashboard tab (donut style, original font) | |
| with gr.TabItem("Analytics Dashboard"): | |
| gr.Markdown("### Customer Base Analytics Overview") | |
| with gr.Row(): | |
| gr.Plot(value=analytics_figs["avg_tenure"]) | |
| gr.Plot(value=analytics_figs["avg_charge"]) | |
| gr.Plot(value=analytics_figs["avg_data"]) | |
| with gr.Row(): | |
| gr.Plot(value=analytics_figs["tenure_hist"]) | |
| gr.Plot(value=analytics_figs["age_hist"]) | |
| with gr.Row(): | |
| gr.Plot(value=analytics_figs["contract_pie"]) | |
| gr.Plot(value=analytics_figs["dependents_donut"]) | |
| with gr.Row(): | |
| gr.Plot(value=analytics_figs["senior_pie"]) | |
| gr.Plot(value=analytics_figs["streaming_pie"]) | |
| with gr.Row(): | |
| gr.Plot(value=analytics_figs["security_pie"]) | |
| gr.Plot(value=analytics_figs["unlimited_pie"]) | |
| gr.Plot(value=analytics_figs["referrals_bar"]) | |
| # Customer Insights tab | |
| with gr.TabItem("CRM Details"): | |
| gr.Markdown("### Customer-Level Predictions & Recommendations") | |
| customer_dropdown = gr.Dropdown( | |
| choices=results_df["Customer ID"].tolist(), | |
| value=results_df["Customer ID"].iloc[0], | |
| label="Select Customer" | |
| ) | |
| customer_output = gr.HTML() | |
| customer_dropdown.change( | |
| fn=get_customer_details, | |
| inputs=customer_dropdown, | |
| outputs=customer_output | |
| ) | |
| demo.launch(share=True) | |