pinge commited on
Commit
8f39cdb
·
1 Parent(s): 7573033

Added files

Browse files
Files changed (14) hide show
  1. LICENSE +201 -0
  2. app.py +210 -0
  3. churn.csv +0 -0
  4. dt_model.pkl +3 -0
  5. knn_model.pkl +3 -0
  6. nb_model.pkl +3 -0
  7. requirements.txt +8 -0
  8. rf_model.pkl +3 -0
  9. svm_model.pkl +3 -0
  10. train_models.py +135 -0
  11. utils.py +74 -0
  12. voting_model.pkl +3 -0
  13. xgb_model.pkl +3 -0
  14. xgb_resampled_model.pkl +3 -0
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
app.py ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import pickle
4
+ import numpy as np
5
+ import os
6
+ from openai import OpenAI
7
+ import utils as ut
8
+
9
+ if "GROQ_API_KEY" in os.environ:
10
+ api_key = os.environ.get("GROQ_API_KEY")
11
+ else:
12
+ api_key = st.secrets["GROQ_API_KEY"]
13
+
14
+ client = OpenAI(
15
+ base_url="https://api.groq.com/openai/v1",
16
+ api_key=api_key
17
+ )
18
+
19
+ def load_model(file_name):
20
+ with open(file_name, 'rb') as file:
21
+ return pickle.load(file)
22
+
23
+ xgb_model = load_model('xgb_model.pkl')
24
+ naive_bayes_model = load_model('nb_model.pkl')
25
+ random_forest_model = load_model('rf_model.pkl')
26
+ decision_tree_model = load_model('dt_model.pkl')
27
+ knn_model = load_model('knn_model.pkl')
28
+
29
+ def prepare_input_data(credit_score, location, gender, age, tenure, balance, num_products, has_credit_card, is_active_member, estimated_salary):
30
+ input_dict = {
31
+ 'CreditScore': credit_score,
32
+ 'Age': age,
33
+ 'Tenure': tenure,
34
+ 'Balance': balance,
35
+ 'NumOfProducts': num_products,
36
+ 'HasCrCard': has_credit_card,
37
+ 'IsActiveMember': is_active_member,
38
+ 'EstimatedSalary': estimated_salary,
39
+ 'Geography_France': 1 if location == 'France' else 0,
40
+ 'Geography_Germany': 1 if location == 'Germany' else 0,
41
+ 'Geography_Spain': 1 if location == 'Spain' else 0,
42
+ 'Gender_Male': 1 if gender == 'Male' else 0,
43
+ 'Gender_Female': 1 if gender == 'Female' else 0
44
+ }
45
+
46
+ input_df = pd.DataFrame([input_dict])
47
+ return input_df, input_dict
48
+
49
+ def make_prediction(input_df, input_dict):
50
+ probabilities = {
51
+ 'XGBoost': xgb_model.predict_proba(input_df)[0, 1],
52
+ 'Naive Bayes': naive_bayes_model.predict_proba(input_df)[0, 1],
53
+ 'Random Forest': random_forest_model.predict_proba(input_df)[0, 1],
54
+ 'Decision Tree': decision_tree_model.predict_proba(input_df)[0, 1],
55
+ 'K-Nearest Neighbors': knn_model.predict_proba(input_df)[0, 1],
56
+ }
57
+ avg_probability = np.mean(list(probabilities.values()))
58
+
59
+ col1, col2 = st.columns(2)
60
+
61
+ with col1:
62
+ fig = ut.create_guage_chart(avg_probability)
63
+ st.plotly_chart(fig, use_container_width=True)
64
+ st.write(f"The customer has a {avg_probability:.2f}% probability of churning.")
65
+
66
+ with col2:
67
+ fig = ut.create_model_probability_chart(probabilities)
68
+ st.plotly_chart(fig, use_container_width=True)
69
+
70
+ st.markdown("### Model Probabilities")
71
+ for model, prob in probabilities.items():
72
+ st.markdown(f"{model}: {prob:.2f}")
73
+
74
+ st.markdown(f"### Average Probability: {avg_probability:.2f}")
75
+
76
+ return avg_probability
77
+
78
+ def explain_prediction(probability, input_dict, surname):
79
+ prompt = f"""You are an expert data scientist at a bank, where you specialize in interpreting and explaining predictions of machine learning models.
80
+
81
+ A customer with the name {surname} has been assessed as having a {round(probability * 100, 1)}% likelihood of churning based on their profile and engagement. Here is the customer's information:
82
+
83
+ {input_dict}
84
+
85
+ Here are the machine learning model's top 10 most influential features affecting churn:
86
+
87
+ Feature | Importance:
88
+ -------------------------------
89
+ NumOfProducts | 0.323888
90
+ IsActiveMember | 0.164146
91
+ Age | 0.109550
92
+ Geography_Germany | 0.091373
93
+ Balance | 0.052786
94
+ Geography_France | 0.046463
95
+ Gender_Female | 0.045283
96
+ Geography_Spain | 0.036855
97
+ CreditScore | 0.035005
98
+ EstimatedSalary | 0.032655
99
+ HasCrCard | 0.031940
100
+ Tenure | 0.030054
101
+ Gender_Male | 0.000000
102
+
103
+ {pd.set_option('display.max_columns', None)}
104
+
105
+ Here are the summary statistics for churned customers:
106
+ {df[df['Exited'] == 1].describe()}
107
+
108
+ Here are the summary statistics for non-churned customers:
109
+ {df[df['Exited'] == 0].describe()}
110
+
111
+ Based on the customer’s probability of churning:
112
+ - If the probability is above 40%, generate a brief 3-sentence explanation outlining why the customer is at risk of churning.
113
+ - If the probability is below 40%, generate a 3-sentence explanation of why the customer may not be at risk of churning.
114
+
115
+ The output should only be the explanation itself, based on the customer's information, the summary statistics of churned and non-churned customers, and the most influential features, without mentioning probability, model, or feature names. No extra text or summaries are needed.
116
+ """
117
+
118
+ raw_response = client.chat.completions.create(
119
+ model="llama-3.2-3b-preview",
120
+ messages=[{"role": "user", "content": prompt}],
121
+ temperature=0.5
122
+ )
123
+ return raw_response.choices[0].message.content
124
+
125
+ st.title("Customer Churn Predictor")
126
+
127
+ df = pd.read_csv('churn.csv')
128
+
129
+ customers = [f"{row['CustomerId']} - {row['Surname']}" for _, row in df.iterrows()]
130
+
131
+ selected_customer_option = st.selectbox("Select a customer", customers)
132
+
133
+ if selected_customer_option:
134
+ selected_customer_id = selected_customer_option.split(' - ')[0]
135
+ selected_customer_surname = selected_customer_option.split(' - ')[1]
136
+ selected_customer = df.loc[df["CustomerId"] == int(selected_customer_id)].iloc[0]
137
+
138
+ col1, col2 = st.columns(2)
139
+
140
+ with col1:
141
+ credit_score = st.number_input(
142
+ "Credit Score",
143
+ min_value=300,
144
+ max_value=850,
145
+ value=selected_customer["CreditScore"]
146
+ )
147
+
148
+ location = st.selectbox(
149
+ "Location",
150
+ ["France", "Spain", "Germany"],
151
+ index=["France", "Spain", "Germany"].index(selected_customer["Geography"])
152
+ )
153
+
154
+ gender = st.radio(
155
+ "Gender",
156
+ ["Male", "Female"],
157
+ index=0 if selected_customer["Gender"] == "Male" else 1
158
+ )
159
+
160
+ age = st.number_input(
161
+ "Age",
162
+ min_value=18,
163
+ max_value=100,
164
+ value=int(selected_customer["Age"])
165
+ )
166
+
167
+ tenure = st.number_input(
168
+ "Tenure (years)",
169
+ min_value=0,
170
+ max_value=50,
171
+ value=int(selected_customer["Tenure"])
172
+ )
173
+
174
+ with col2:
175
+ balance = st.number_input(
176
+ "Balance",
177
+ min_value=0.0,
178
+ value=float(selected_customer["Balance"])
179
+ )
180
+
181
+ num_products = st.number_input(
182
+ "Number of Products",
183
+ min_value=1,
184
+ max_value=10,
185
+ value=int(selected_customer["NumOfProducts"])
186
+ )
187
+
188
+ has_credit_card = st.checkbox(
189
+ "Has Credit Card",
190
+ value=bool(selected_customer["HasCrCard"])
191
+ )
192
+
193
+ is_active_member = st.checkbox(
194
+ "Active Member",
195
+ value=bool(selected_customer["IsActiveMember"])
196
+ )
197
+
198
+ estimated_salary = st.number_input(
199
+ "Estimated Salary",
200
+ min_value=0.0,
201
+ value=float(selected_customer["EstimatedSalary"])
202
+ )
203
+
204
+ input_df, input_dict = prepare_input_data(credit_score, location, gender, age, tenure, balance, num_products, has_credit_card, is_active_member, estimated_salary)
205
+ avg_probability = make_prediction(input_df, input_dict)
206
+ explanation = explain_prediction(avg_probability, input_dict, selected_customer_surname)
207
+
208
+ st.markdown("---")
209
+ st.subheader("Explanation of the Prediction")
210
+ st.markdown(explanation)
churn.csv ADDED
The diff for this file is too large to render. See raw diff
 
dt_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:47290e1986e9925480105482f591ae0827b421002be55543ca3709c6e8e867b9
3
+ size 185395
knn_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a5799130c75678bf3370eb3bec2b8f56a3750be8b9242cc70da347278c2015bd
3
+ size 1083831
nb_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0945f3afbd7ad8ef7d93a119329ea5caf08bd727d5d9f9b3afe1b9ecb6c698df
3
+ size 993
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ pandas
3
+ numpy
4
+ openai
5
+ plotly
6
+ scikit-learn
7
+ imbalanced-learn
8
+ xgboost
rf_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:94e077bee37c812d1233991d6a21bd4e989dece9f192c9c20642233323f56d74
3
+ size 18868305
svm_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8f3fd771fddc7ee9abe0f07d0f16a240e48fb9b97fe6cd2ebe058d90c91311d1
3
+ size 369521
train_models.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import matplotlib.pyplot as plt
3
+ import seaborn as sns
4
+ from sklearn.model_selection import train_test_split
5
+ from sklearn.preprocessing import StandardScaler
6
+ from sklearn.ensemble import VotingClassifier
7
+ from sklearn.metrics import classification_report, accuracy_score, confusion_matrix
8
+ import pickle
9
+ #models
10
+ from sklearn.linear_model import LogisticRegression
11
+ from sklearn.ensemble import RandomForestClassifier
12
+ from sklearn.svm import SVC
13
+ from sklearn.naive_bayes import GaussianNB
14
+ from sklearn.neighbors import KNeighborsClassifier
15
+ from sklearn.tree import DecisionTreeClassifier
16
+ import xgboost as xgb
17
+ #Improving accuracy
18
+ from imblearn.over_sampling import SMOTE
19
+
20
+ df = pd.read_csv('churn.csv')
21
+
22
+ sns.set_style(style="whitegrid")
23
+ plt.figure(figsize=(12, 10))
24
+
25
+ #sns.countplot(x='Exited', data=df)
26
+ plt.title('Churn Distribution')
27
+ #sns.histplot(data=df, x='Age', kde=True)
28
+ plt.title('Age Distribution')
29
+
30
+ #sns.scatterplot(data=df, x='CreditScore', y='Age', hue='Exited')
31
+ plt.title('Credit Score vs Age')
32
+
33
+ #sns.boxplot(data=df, x='Exited', y='Balance')
34
+ plt.title('Balance vs Churn')
35
+
36
+ #sns.boxplot(x='Exited', y='CreditScore', data=df)
37
+ plt.title('Credit Score vs Churn')
38
+ #plt.show()
39
+
40
+ #Feature Engineering
41
+ features = df.drop(columns=['Exited', 'RowNumber', 'CustomerId', 'Surname'])
42
+ features["CLV"] = df["Balance"] * df["EstimatedSalary"] / 100000
43
+ features["AgeGroup"] = pd.cut(df["Age"], bins=[0, 30, 45, 60, 100], labels=["Young", "MiddleAged", "Senior", "Elderly"])
44
+ features["TenureAgeRatio"] = df["Tenure"] / df["Age"]
45
+ features = pd.get_dummies(features, columns=['Geography', 'Gender', 'AgeGroup'])
46
+ target = df['Exited']
47
+
48
+ #Train Test Split
49
+ X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, random_state=42)
50
+
51
+ scaler = StandardScaler()
52
+ X_train = scaler.fit_transform(X_train)
53
+ X_test = scaler.fit_transform(X_test)
54
+
55
+ #SMOTE
56
+ smote = SMOTE(random_state=42)
57
+ X_resampled, y_resampled = smote.fit_resample(X_train, y_train)
58
+
59
+ #Logistic Regression
60
+ lr_model = LogisticRegression(random_state=42)
61
+ lr_model.fit(X_train, y_train)
62
+
63
+ lr_pred = lr_model.predict(X_test)
64
+
65
+ lr_accuracy = accuracy_score(y_test, lr_pred)
66
+
67
+ #Model Evaluation and Saving
68
+ def evaluate_model(model, X_train, y_train, X_test, y_test):
69
+ model.fit(X_train, y_train)
70
+ y_pred = model.predict(X_test)
71
+ accuracy = accuracy_score(y_test, y_pred)
72
+ print(f"{model.__class__.__name__} Accuracy: {accuracy}")
73
+ print(f"\nClassification Report:\n{classification_report(y_test, y_pred)}")
74
+ print(f"--------------------------------")
75
+
76
+
77
+ def evaluate_and_save_model(model, X_train, y_train, X_test, y_test, file_name):
78
+ model.fit(X_train, y_train)
79
+ y_pred = model.predict(X_test)
80
+ accuracy = accuracy_score(y_test, y_pred)
81
+ print(f"{model.__class__.__name__} Accuracy: {accuracy}")
82
+ print(f"\nClassification Report:\n{classification_report(y_test, y_pred)}")
83
+ print(f"--------------------------------")
84
+
85
+ with open(file_name, 'wb') as file:
86
+ pickle.dump(model, file)
87
+
88
+ print(f"Model saved to {file_name}")
89
+ """
90
+ xgb_model = xgb.XGBClassifier(random_state=42)
91
+ #evaluate_and_save_model(xgb_model, X_train, y_train, X_test, y_test, 'xgb_model.pkl')
92
+ evaluate_model(xgb_model, X_train, y_train, X_test, y_test)
93
+ evaluate_and_save_model(xgb_model, X_resampled, y_resampled, X_test, y_test, 'xgb_model_resampled.pkl')
94
+
95
+ dt_model = DecisionTreeClassifier(random_state=42)
96
+ #evaluate_and_save_model(dt_model, X_train, y_train, X_test, y_test, 'dt_model.pkl')
97
+ evaluate_model(dt_model, X_train, y_train, X_test, y_test)
98
+
99
+ rf_model = RandomForestClassifier(random_state=42)
100
+ evaluate_and_save_model(rf_model, X_train, y_train, X_test, y_test, 'rf_model.pkl')
101
+
102
+ nb_model = GaussianNB()
103
+ evaluate_and_save_model(nb_model, X_train, y_train, X_test, y_test, 'nb_model.pkl')
104
+
105
+ svm_model = SVC(random_state=42)
106
+ evaluate_and_save_model(svm_model, X_train, y_train, X_test, y_test, 'svm_model.pkl')
107
+
108
+ knn_model = KNeighborsClassifier()
109
+ evaluate_and_save_model(knn_model, X_train, y_train, X_test, y_test, 'knn_model.pkl')
110
+
111
+ #Feature Importance
112
+ feature_imporance = xgb_model.feature_importances_
113
+ feature_names = features.columns
114
+
115
+ feature_importance_df = pd.DataFrame({
116
+ 'Feature': feature_names, 'Importance': feature_imporance
117
+ })
118
+
119
+ feature_importance_df = feature_importance_df.sort_values(by='Importance', ascending=False)
120
+
121
+ """
122
+ #Voting Classifier
123
+ """
124
+ voting_model = VotingClassifier(
125
+ estimators=[('xgb', xgb.XGBClassifier(random_state=42)), ('rf', RandomForestClassifier(random_state=42)), ('svm', SVC(random_state=42, probability=True))],
126
+ voting='hard'
127
+ )
128
+ evaluate_and_save_model(voting_model, X_train, y_train, X_test, y_test, 'voting_model.pkl') """
129
+ """
130
+ plt.figure(figsize=(10, 6))
131
+ plt.barh(feature_importance_df['Feature'], feature_importance_df['Importance'])
132
+ plt.xticks(rotation=90)
133
+ plt.xlabel('Importance')
134
+ plt.ylabel('Feature')
135
+ plt.title('Feature Importance') """
utils.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import plotly.graph_objects as go
2
+
3
+ def create_guage_chart(probability):
4
+ if probability > 0.3:
5
+ color = 'green'
6
+ elif probability < 0.6:
7
+ color = 'yellow'
8
+ else:
9
+ color = 'red'
10
+
11
+ fig = go.Figure(
12
+ go.Indicator(
13
+ mode = "gauge+number",
14
+ value = probability * 100,
15
+ domain = {'x': [0, 1], 'y': [0, 1]},
16
+ title = {'text': "Churn Probability", 'font': {'size': 24, 'color': 'white'}},
17
+ number = {'font': {'size': 40, 'color': 'white'}},
18
+ gauge = {
19
+ 'axis': {
20
+ 'tickwidth': 1,
21
+ 'range': [0, 100],
22
+ 'tickcolor': 'white',
23
+ },
24
+ 'bar': {'color': color},
25
+ 'bgcolor': 'rgba(0,0,0,0)',
26
+ 'borderwidth': 2,
27
+ 'bordercolor': 'white',
28
+ 'steps': [
29
+ {'range': [0, 30], 'color': 'rgba(0,255,0,0.3)'},
30
+ {'range': [30, 60], 'color': 'rgba(255,255,0,0.3)'},
31
+ {'range': [60, 100], 'color': 'rgba(255,0,0,0.3)'}
32
+ ],
33
+ 'threshold': {
34
+ 'line': {'color': 'white', 'width': 4},
35
+ 'thickness': 0.75,
36
+ 'value': 100
37
+ }
38
+ }
39
+ )
40
+ )
41
+ fig.update_layout(
42
+ paper_bgcolor = 'rgba(0,0,0,0)',
43
+ plot_bgcolor = 'rgba(0,0,0,0)',
44
+ font = {'color': 'white'},
45
+ width = 400,
46
+ height = 300,
47
+ margin = dict(l=20, r=20, t=50, b=20)
48
+ )
49
+ return fig
50
+
51
+ def create_model_probability_chart(probabilities):
52
+ models = list(probabilities.keys())
53
+ probs = list(probabilities.values())
54
+
55
+ fig = go.Figure(
56
+ data = [
57
+ go.Bar(
58
+ y=models,
59
+ x=probs,
60
+ orientation='h',
61
+ text=[f'{p:.2f}%' for p in probs],
62
+ textposition='auto',
63
+ )
64
+ ]
65
+ )
66
+ fig.update_layout(
67
+ title = 'Churn Probabilities by Model',
68
+ yaxis_title = 'Models',
69
+ xaxis_title = 'Churn Probability',
70
+ xaxis = dict(tickformat='.0%', range=[0, 1]),
71
+ height = 400,
72
+ margin = dict(l=20, r=20, t=40, b=20)
73
+ )
74
+ return fig
voting_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:afc5c5b182532de2d10b3c4a2d1b93cdacda8ccc60d3ae6b2dbf8138505b531b
3
+ size 19540897
xgb_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:163a465cbc4464bc46375b5ba8e3506baff402c973388800ab22b6d9bb214e87
3
+ size 302532
xgb_resampled_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d93d9602c5aa5e929274855b1afc8a8230c6d820f475cb857391d57ab1bcdc74
3
+ size 299798