Dun3Co commited on
Commit
2b789d9
·
verified ·
1 Parent(s): 95b3104

Upload 2 files

Browse files
pages/4_💃_Model_interpretation.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import shap
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ import joblib
6
+ import pandas as pd
7
+ from sklearn.metrics import roc_curve, roc_auc_score, precision_recall_curve, auc
8
+
9
+ st.set_page_config(page_title="Model Analysis Dashboard", layout="wide")
10
+ st.title("Model Analysis Dashboard")
11
+
12
+ st.markdown("""
13
+ This dashboard allows you to interactively explore the performance and interpretability of the trained logistic regression model.
14
+ - **Logistic Regression Coefficients:** See which features most strongly influence the model's predictions.
15
+ - **SHAP Analysis:** Understand both global and local feature importance using SHAP values.
16
+ - **ROC/PR Curves:** Evaluate the model's discrimination and precision-recall tradeoff.
17
+
18
+ Use the sidebar to select which plots to display and to adjust the number of features or the local sample for SHAP explanations.
19
+ """)
20
+
21
+ # --- Load model and test data ---
22
+ @st.cache_data
23
+ def load_model_and_data():
24
+ model = joblib.load("model_1mvp.pkl")
25
+ df_test = pd.read_csv("test_data.csv")
26
+ return model, df_test
27
+
28
+ model, df_test = load_model_and_data()
29
+
30
+ target = "y"
31
+ X_test = df_test.drop(columns=[target])
32
+ y_test = df_test[target]
33
+
34
+ preprocessor = model.named_steps["preprocessor"]
35
+ feature_names = preprocessor.get_feature_names_out()
36
+ X_test_transformed = preprocessor.transform(X_test)
37
+
38
+ # --- SHAP Explainer (precompute for efficiency) ---
39
+ explainer = shap.LinearExplainer(model.named_steps["classifier"], X_test_transformed, feature_names=feature_names)
40
+ shap_values = explainer.shap_values(X_test_transformed)
41
+ expected_value = explainer.expected_value
42
+
43
+ # --- Sidebar: Plot selection and controls ---
44
+ with st.sidebar.form("plot_selector"):
45
+ st.markdown("## Select plots to display")
46
+ show_coeff = st.checkbox("Logistic Regression Coefficients", value=True)
47
+ show_shap_global = st.checkbox("SHAP Global (summary plot)", value=True)
48
+ show_shap_local = st.checkbox("SHAP Local (waterfall plot)", value=False)
49
+ show_roc = st.checkbox("ROC/PR Curves", value=True)
50
+ top_n = st.slider("Number of top features for LogReg coeffecients", 5, 30, 15)
51
+ local_idx = st.number_input("Local SHAP sample index", min_value=0, max_value=len(X_test)-1, value=0)
52
+ submitted = st.form_submit_button("Update plots")
53
+
54
+ # --- Logistic Regression Coefficient Plot ---
55
+ if show_coeff and submitted:
56
+ st.header("Logistic Regression Coefficients")
57
+ logreg_model = model.named_steps["classifier"]
58
+ coefficients = logreg_model.coef_[0]
59
+ importance = pd.DataFrame({
60
+ "feature": feature_names,
61
+ "coefficient": coefficients
62
+ }).sort_values(by="coefficient", key=abs, ascending=False)
63
+ fig, ax = plt.subplots(figsize=(8, 6))
64
+ importance.head(top_n).set_index("feature")["coefficient"].plot(kind="barh", ax=ax, color="#4C72B0")
65
+ ax.set_title("Logistic Regression Feature Importance (Coefficients)")
66
+ ax.set_xlabel("Coefficient Value")
67
+ ax.set_ylabel("Feature")
68
+ st.pyplot(fig)
69
+ st.dataframe(importance.head(top_n).style.format({"coefficient": "{:.3f}"}))
70
+
71
+ # --- SHAP Analysis ---
72
+ if (show_shap_global or show_shap_local) and submitted:
73
+ st.header("SHAP Analysis")
74
+ if show_shap_global:
75
+ st.subheader("Global Feature Importance (SHAP Summary Plot)")
76
+ fig, ax = plt.subplots(figsize=(10, 6))
77
+ shap.summary_plot(shap_values, X_test_transformed, feature_names=feature_names, show=False)
78
+ st.pyplot(fig)
79
+ if show_shap_local:
80
+ st.subheader("Local Explanation (SHAP Waterfall Plot)")
81
+ fig2, ax2 = plt.subplots(figsize=(10, 6))
82
+ shap.plots.waterfall(
83
+ shap.Explanation(
84
+ values=shap_values[local_idx],
85
+ base_values=expected_value,
86
+ data=X_test_transformed[local_idx],
87
+ feature_names=feature_names
88
+ ),
89
+ max_display=15,
90
+ show=False
91
+ )
92
+ st.pyplot(fig2)
93
+
94
+ # --- ROC and PR Curves ---
95
+ if show_roc and submitted:
96
+ st.header("Model Performance Metrics (ROC / PR Curves)")
97
+ y_pred_proba = model.predict_proba(X_test)[:, 1]
98
+ roc_auc = roc_auc_score(y_test, y_pred_proba)
99
+ fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
100
+ precision, recall, _ = precision_recall_curve(y_test, y_pred_proba)
101
+ pr_auc = auc(recall, precision)
102
+
103
+ col1, col2 = st.columns(2)
104
+ with col1:
105
+ st.metric("ROC AUC", f"{roc_auc:.3f}")
106
+ with col2:
107
+ st.metric("PR AUC", f"{pr_auc:.3f}")
108
+
109
+ fig1, ax1 = plt.subplots(figsize=(5, 5))
110
+ ax1.plot(fpr, tpr, color="darkorange", lw=2, label=f"ROC curve (AUC = {roc_auc:.3f})")
111
+ ax1.plot([0, 1], [0, 1], color="navy", lw=2, linestyle="--", label="Random Guess")
112
+ ax1.set_xlabel("False Positive Rate")
113
+ ax1.set_ylabel("True Positive Rate")
114
+ ax1.set_title("ROC Curve")
115
+ ax1.legend()
116
+ st.pyplot(fig1)
117
+
118
+ fig2, ax2 = plt.subplots(figsize=(5, 5))
119
+ ax2.plot(recall, precision, color="#C44E52")
120
+ ax2.set_xlabel("Recall")
121
+ ax2.set_ylabel("Precision")
122
+ ax2.set_title("Precision-Recall Curve")
123
+ st.pyplot(fig2)
pages/5_💲_Magenment_deck.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import joblib
5
+
6
+ st.set_page_config(page_title="💲 Management Deck: Savings Analysis", layout="wide")
7
+ st.title("💲 Management Deck: Expected Savings of Model Decisions")
8
+
9
+ st.markdown("""
10
+ This page estimates the **expected savings** from using the model to decide which customers to call in a marketing campaign.
11
+
12
+ - **Savings (True Negative):** The amount saved for each customer the model correctly identifies as not worth calling (i.e., calls avoided).
13
+ - **Cost of missing a true subscriber (False Negative):** The loss incurred when the model fails to identify a customer who would have subscribed.
14
+
15
+ You can adjust these business parameters in the sidebar. The analysis shows the expected savings at the current threshold and how savings change as you vary the decision threshold. The optimal threshold is highlighted in the plot.
16
+ """)
17
+
18
+ # --- Load model and test data ---
19
+ @st.cache_data
20
+ def load_model_and_data():
21
+ model = joblib.load("model_1mvp.pkl")
22
+ df_test = pd.read_csv("test_data.csv")
23
+ return model, df_test
24
+
25
+ model, df_test = load_model_and_data()
26
+ target = "y"
27
+ X_test = df_test.drop(columns=[target])
28
+ y_test = df_test[target]
29
+
30
+ # --- Sidebar: Cost selection ---
31
+ with st.sidebar.form("cost_selector"):
32
+ st.markdown("## Set Business Parameters")
33
+ cost_per_call = st.number_input("Savings (True negative)", min_value=0.0, value=29.7, step=1.0)
34
+ cost_per_missed_subscriber = st.number_input("Cost of missing a true subscriber (FN)", min_value=0.0, value=300.0, step=1.0)
35
+ submitted = st.form_submit_button("Update Savings Analysis")
36
+
37
+ if submitted:
38
+ st.subheader("Expected Savings Based on Model Predictions")
39
+
40
+ # Predict probabilities
41
+ y_pred_proba = model.predict_proba(X_test)[:, 1]
42
+ threshold = 0.5
43
+
44
+ # Prepare a DataFrame for calculations
45
+ df_result = X_test.copy()
46
+ df_result["y"] = y_test.values
47
+ df_result["y_pred_proba"] = y_pred_proba
48
+ df_result["campaign"] = df_test["campaign"].values # Make sure campaign is present
49
+
50
+ # Customers NOT called (predicted negative): y_pred_proba < threshold
51
+ df_temp = df_result[df_result["y_pred_proba"] <= threshold].copy()
52
+ total_campaigns = df_temp["campaign"].sum()
53
+ missed_subscribers = (df_temp["y"] == 1).sum()
54
+
55
+ expected_savings = total_campaigns * cost_per_call - missed_subscribers * cost_per_missed_subscriber
56
+
57
+ st.markdown(f"""
58
+ **Threshold:** {threshold:.2f}
59
+ - **Calls avoided (sum of campaigns):** {total_campaigns} × {cost_per_call} = {total_campaigns * cost_per_call:.2f}
60
+ - **Missed subscribers:** {missed_subscribers} × {cost_per_missed_subscriber} = {missed_subscribers * cost_per_missed_subscriber:.2f}
61
+ ---
62
+ ## **Expected Savings: {expected_savings:,.2f}**
63
+ """)
64
+
65
+ # Show savings as a function of threshold
66
+ st.subheader("Expected Savings vs. Threshold")
67
+ thresholds = np.linspace(0, 1, 120)
68
+ savings = []
69
+ for t in thresholds:
70
+ df_temp_t = df_result[df_result["y_pred_proba"] <= t].copy()
71
+ total_campaigns_t = df_temp_t["campaign"].sum()
72
+ missed_subscribers_t = (df_temp_t["y"] == 1).sum()
73
+ savings.append(total_campaigns_t * cost_per_call - missed_subscribers_t * cost_per_missed_subscriber)
74
+ import matplotlib.pyplot as plt
75
+ fig, ax = plt.subplots(figsize=(8, 4))
76
+ ax.plot(thresholds, savings, label="Expected Savings")
77
+ ax.axvline(threshold, color="red", linestyle="--", label=f"Current threshold = {threshold:.2f}")
78
+ # Find and plot optimal threshold
79
+ t_star = float(thresholds[np.argmax(savings)])
80
+ ax.axvline(t_star, color="r", linestyle="--", label=f"Optimal Threshold = {t_star:.2f}")
81
+ ax.set_xlabel("Threshold")
82
+ ax.set_ylabel("Expected Savings")
83
+ ax.set_title("Expected Savings vs. Classification Threshold")
84
+ ax.legend()
85
+ st.pyplot(fig)
86
+
87
+ st.caption("You can adjust the business parameters in the sidebar to see their impact on expected savings and optimal threshold.")