RavindranadhM commited on
Commit
7466d26
·
verified ·
1 Parent(s): 6aee0de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -160
app.py CHANGED
@@ -1,176 +1,73 @@
1
- # Gradio App
2
- #Imports
3
  import gradio as gr
4
- import joblib
5
  import pandas as pd
6
- import numpy as np
7
  import shap
8
- import plotly.graph_objects as go
9
- from sklearn.preprocessing import StandardScaler
10
- # We'll ask user for human-friendly original features and then transform into the encoded features
11
- # Input fields based on original Telco columns (commonly present)
12
- def preprocess_user_input(user_inputs):
13
- """
14
- user_inputs: dict with original columns (human-friendly)
15
- returns: DataFrame encoded to match feature_cols order
16
- """
17
- # Build single-row DataFrame with original categorical values
18
- row = pd.DataFrame([user_inputs])
19
- # same preprocessing as training: get_dummies with drop_first
20
- row_enc = pd.get_dummies(row, drop_first=True)
21
- # reindex to feature_cols with 0 fill for missing dummies
22
- row_enc = row_enc.reindex(columns=feature_cols, fill_value=0)
23
- # scale numeric columns
24
- if numeric_cols:
25
- row_enc[numeric_cols] = scaler.transform(row_enc[numeric_cols])
26
- return row_enc
27
-
28
- def create_gauge(prob):
29
- # prob is in [0,1]; create a Plotly gauge
30
- fig = go.Figure(go.Indicator(
31
- mode="gauge+number",
32
- value=prob * 100,
33
- number={'suffix': "%"},
34
- title={'text': "Churn Probability"},
35
- gauge={
36
- 'axis': {'range': [0, 100]},
37
- 'bar': {'color': "red" if prob > 0.6 else ("orange" if prob > 0.3 else "green")},
38
- 'steps': [
39
- {'range': [0, 30], 'color': "lightgreen"},
40
- {'range': [30, 60], 'color': "yellow"},
41
- {'range': [60, 100], 'color': "lightcoral"}
42
- ],
43
- }
44
- ))
45
- fig.update_layout(height=300, margin=dict(l=20, r=20, t=40, b=20))
46
- return fig
47
-
48
- def top_shap_barchart(shap_vals, feat_names, top_n=5):
49
- # shap_vals: 1D array of shap values for the single prediction
50
- abs_vals = np.abs(shap_vals)
51
- idx = np.argsort(abs_vals)[::-1][:top_n]
52
- top_feats = [feat_names[i] for i in idx]
53
- top_vals = shap_vals[idx]
54
- # Plotly horizontal bar
55
- fig = go.Figure(go.Bar(
56
- x=top_vals[::-1],
57
- y=[f for f in top_feats[::-1]],
58
- orientation='h'
59
- ))
60
- fig.update_layout(title=f"Top {top_n} SHAP feature contributions (positive increases churn)", height=300, margin=dict(l=100))
61
- return fig
62
 
63
- # The actual predict function wired to Gradio
64
- def predict_and_explain(
65
- gender, SeniorCitizen, Partner, Dependents,
66
- tenure, PhoneService, MultipleLines, InternetService,
67
- OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport,
68
- StreamingTV, StreamingMovies, Contract, PaperlessBilling, PaymentMethod,
69
- MonthlyCharges, TotalCharges,
70
- example_choice=None
71
- ):
72
- # If example button used, ignore other inputs and load preset
73
- if example_choice == "High churn risk example":
74
- inputs = {
75
- "gender": "Male", "SeniorCitizen": "No", "Partner":"No", "Dependents":"No",
76
- "tenure": 4, "PhoneService":"Yes", "MultipleLines":"No", "InternetService":"Fiber optic",
77
- "OnlineSecurity":"No","OnlineBackup":"No","DeviceProtection":"No","TechSupport":"No",
78
- "StreamingTV":"Yes","StreamingMovies":"Yes","Contract":"Month-to-month","PaperlessBilling":"Yes",
79
- "PaymentMethod":"Electronic check","MonthlyCharges":90.0,"TotalCharges":360.0
80
- }
81
- elif example_choice == "Low churn risk example":
82
- inputs = {
83
- "gender": "Female", "SeniorCitizen": "No", "Partner":"Yes", "Dependents":"Yes",
84
- "tenure": 48, "PhoneService":"Yes", "MultipleLines":"Yes", "InternetService":"DSL",
85
- "OnlineSecurity":"Yes","OnlineBackup":"Yes","DeviceProtection":"Yes","TechSupport":"Yes",
86
- "StreamingTV":"No","StreamingMovies":"No","Contract":"Two year","PaperlessBilling":"No",
87
- "PaymentMethod":"Mailed check","MonthlyCharges":40.0,"TotalCharges":1920.0
88
- }
89
- else:
90
- inputs = {
91
- "gender": gender, "SeniorCitizen": SeniorCitizen, "Partner": Partner, "Dependents": Dependents,
92
- "tenure": tenure, "PhoneService": PhoneService, "MultipleLines": MultipleLines, "InternetService": InternetService,
93
- "OnlineSecurity": OnlineSecurity, "OnlineBackup": OnlineBackup, "DeviceProtection": DeviceProtection, "TechSupport": TechSupport,
94
- "StreamingTV": StreamingTV, "StreamingMovies": StreamingMovies, "Contract": Contract, "PaperlessBilling": PaperlessBilling,
95
- "PaymentMethod": PaymentMethod, "MonthlyCharges": MonthlyCharges, "TotalCharges": TotalCharges
96
- }
97
 
98
- # Preprocess -> encoded row matching training features
99
- row_enc = preprocess_user_input(inputs)
 
 
 
100
 
101
- # Predict
102
- prob = float(model.predict_proba(row_enc)[0, 1])
103
- pred_label = "⚠️ High Risk of Churn" if prob > 0.5 else "✅ Low Risk (likely to stay)"
104
 
105
- # SHAP explanation
106
- shap_vals = explainer.shap_values(row_enc) # might return array shape (1, n_features) or list
107
- # normalize into array
108
- if isinstance(shap_vals, list):
109
- shap_arr = np.array(shap_vals)[0]
110
- else:
111
- shap_arr = np.array(shap_vals).reshape(-1)
112
- # Prepare top contributors text
113
- abs_idx = np.argsort(np.abs(shap_arr))[::-1][:3]
114
- top_contribs = []
115
- for i in abs_idx:
116
- sign = "+" if shap_arr[i] > 0 else "-"
117
- top_contribs.append(f"{feature_cols[i]}: {sign}{abs(shap_arr[i]):.3f}")
118
- top_text = "\n".join(top_contribs)
119
 
120
- # Create visuals
121
- gauge_fig = create_gauge(prob)
122
- shap_fig = top_shap_barchart(shap_arr, feature_cols, top_n=5)
123
 
124
- # Business insight text
125
- insight = f"{pred_label}\nChurn Probability: {prob:.2%}\n\nTop factors:\n{top_text}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
- return insight, gauge_fig, shap_fig
128
 
129
- # UI layout with Gradio Blocks
130
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
131
- gr.Markdown("# 📊 Customer Churn Prediction Explainable Demo")
132
- gr.Markdown("Fill in customer info or choose an example. The app returns a churn probability (gauge) and the top feature contributions (SHAP).")
133
-
134
  with gr.Row():
135
- with gr.Column(scale=1):
136
- gender = gr.Dropdown(["Female","Male"], value="Male", label="Gender")
137
- SeniorCitizen = gr.Dropdown(["No","Yes"], value="No", label="Senior Citizen")
138
- Partner = gr.Dropdown(["No","Yes"], value="No", label="Partner")
139
- Dependents = gr.Dropdown(["No","Yes"], value="No", label="Dependents")
140
- tenure = gr.Slider(minimum=0, maximum=72, step=1, value=12, label="Tenure (months)")
141
- MonthlyCharges = gr.Slider(minimum=0, maximum=200, step=0.5, value=70, label="Monthly Charges (USD)")
142
- TotalCharges = gr.Number(value=700, label="Total Charges (USD)")
143
- PhoneService = gr.Dropdown(["No","Yes"], value="Yes", label="Phone Service")
144
- MultipleLines = gr.Dropdown(["No","Yes","No phone service"], value="No", label="Multiple Lines")
145
- InternetService = gr.Dropdown(["DSL","Fiber optic","No"], value="Fiber optic", label="Internet Service")
146
- OnlineSecurity = gr.Dropdown(["No","Yes","No internet service"], value="No", label="Online Security")
147
- OnlineBackup = gr.Dropdown(["No","Yes","No internet service"], value="No", label="Online Backup")
148
- DeviceProtection = gr.Dropdown(["No","Yes","No internet service"], value="No", label="Device Protection")
149
- TechSupport = gr.Dropdown(["No","Yes","No internet service"], value="No", label="Tech Support")
150
- StreamingTV = gr.Dropdown(["No","Yes","No internet service"], value="No", label="Streaming TV")
151
- StreamingMovies = gr.Dropdown(["No","Yes","No internet service"], value="No", label="Streaming Movies")
152
- Contract = gr.Dropdown(["Month-to-month","One year","Two year"], value="Month-to-month", label="Contract")
153
- PaperlessBilling = gr.Dropdown(["No","Yes"], value="Yes", label="Paperless Billing")
154
- PaymentMethod = gr.Dropdown(["Electronic check","Mailed check","Bank transfer (automatic)","Credit card (automatic)"],
155
- value="Electronic check", label="Payment Method")
156
- example_choice = gr.Radio(["None","High churn risk example","Low churn risk example"], value="None", label="Load example?")
157
- submit_btn = gr.Button("Predict & Explain", variant="primary")
158
-
159
- with gr.Column(scale=1):
160
- out_text = gr.Textbox(label="Prediction & Business Insight", lines=7)
161
- out_gauge = gr.Plot(label="Churn Probability Gauge")
162
- out_shap = gr.Plot(label="Top SHAP Contributors")
163
-
164
- # Hook up the button
165
  submit_btn.click(
166
- fn=predict_and_explain,
167
- inputs=[gender, SeniorCitizen, Partner, Dependents,
168
- tenure, PhoneService, MultipleLines, InternetService,
169
- OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport,
170
- StreamingTV, StreamingMovies, Contract, PaperlessBilling, PaymentMethod,
171
- MonthlyCharges, TotalCharges, example_choice],
172
- outputs=[out_text, out_gauge, out_shap]
173
  )
174
 
175
- # Launch demo (share=True gives public link)
176
- demo.launch(share=True)
 
 
 
 
1
  import gradio as gr
 
2
  import pandas as pd
3
+ import joblib
4
  import shap
5
+ import numpy as np
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ # Load model, scaler, and feature list
8
+ model = joblib.load("churn_model.pkl")
9
+ scaler = joblib.load("scaler.pkl")
10
+ feature_cols = joblib.load("feature_cols.pkl") # list of feature names
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # If you saved an explainer, load it; otherwise create new one
13
+ try:
14
+ explainer = joblib.load("explainer.pkl")
15
+ except:
16
+ explainer = shap.Explainer(model, feature_names=feature_cols)
17
 
 
 
 
18
 
19
+ # Preprocessing Function
20
+ def preprocess_user_input(user_inputs):
21
+ """
22
+ user_inputs: list of values in correct order
23
+ returns: DataFrame ready for prediction
24
+ """
25
+ row = pd.DataFrame([user_inputs], columns=feature_cols)
26
+ row_scaled = scaler.transform(row)
27
+ return pd.DataFrame(row_scaled, columns=feature_cols)
 
 
 
 
 
28
 
 
 
 
29
 
30
+ # Prediction + SHAP Explanation
31
+ def predict_and_explain(*inputs):
32
+ # Preprocess
33
+ row_scaled = preprocess_user_input(inputs)
34
+
35
+ # Predict probability and class
36
+ pred_prob = model.predict_proba(row_scaled)[0][1]
37
+ pred_class = model.predict(row_scaled)[0]
38
+
39
+ # SHAP values
40
+ shap_values = explainer(row_scaled)
41
+
42
+ # Explanation as bar chart
43
+ shap_fig = shap.plots.bar(shap_values[0], show=False)
44
+
45
+ result_text = f"**Prediction:** {'Churn' if pred_class==1 else 'No Churn'}\n"
46
+ result_text += f"**Probability of Churn:** {pred_prob:.2%}"
47
+
48
+ return result_text, shap_fig
49
 
 
50
 
51
+ # Build Gradio UI
52
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
53
+ gr.Markdown("## 📊 Customer Churn Prediction with SHAP Explanations")
54
+
 
55
  with gr.Row():
56
+ inputs = []
57
+ for col in feature_cols:
58
+ inputs.append(gr.Number(label=col))
59
+
60
+ output_text = gr.Markdown()
61
+ output_plot = gr.Plot()
62
+
63
+ submit_btn = gr.Button("Predict")
64
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  submit_btn.click(
66
+ predict_and_explain,
67
+ inputs=inputs,
68
+ outputs=[output_text, output_plot]
 
 
 
 
69
  )
70
 
71
+ # Launch
72
+ if __name__ == "__main__":
73
+ demo.launch()