InĀ [1]:
# Importing libraries
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.svm import SVC
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report, accuracy_score, ConfusionMatrixDisplay, confusion_matrix, roc_curve, auc
import matplotlib.pyplot as plt
InĀ [2]:
# Read csv file into data variable
data = pd.read_csv('Telco-Customer-Churn.csv')
# Splitting the data into training and testing, using Churn as the label
train_df, test_df = train_test_split(
data,
test_size=0.3,
stratify=data['Churn'])
# Displays the top rows of each new data set
display(train_df.head())
display(test_df.head())
| customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | ... | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 313 | 2799-ARNLO | Female | 1 | Yes | No | 5 | Yes | No | Fiber optic | No | ... | No | No | No | No | Month-to-month | Yes | Electronic check | 69.35 | 341.6 | No |
| 911 | 7508-SMHXL | Female | 1 | No | No | 15 | Yes | Yes | Fiber optic | No | ... | No | No | Yes | No | Month-to-month | No | Credit card (automatic) | 89.00 | 1288.3 | No |
| 5332 | 4565-EVZMJ | Female | 0 | No | No | 47 | Yes | No | DSL | Yes | ... | No | Yes | No | No | One year | Yes | Mailed check | 58.90 | 2813.05 | No |
| 4062 | 1915-IOFGU | Female | 0 | No | No | 1 | Yes | No | Fiber optic | No | ... | No | No | No | No | Month-to-month | No | Electronic check | 70.50 | 70.5 | Yes |
| 2629 | 5028-GZLDO | Male | 0 | No | No | 2 | Yes | No | Fiber optic | No | ... | No | No | No | No | Month-to-month | No | Credit card (automatic) | 70.70 | 140.7 | Yes |
5 rows Ć 21 columns
| customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | ... | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1281 | 9531-NSBMR | Female | 0 | No | No | 36 | Yes | No | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | One year | Yes | Bank transfer (automatic) | 19.25 | 679.8 | No |
| 3159 | 5150-ITWWB | Male | 1 | No | No | 3 | Yes | Yes | Fiber optic | No | ... | No | No | Yes | Yes | Month-to-month | Yes | Electronic check | 94.85 | 335.75 | No |
| 7024 | 7398-LXGYX | Male | 0 | Yes | No | 44 | Yes | Yes | Fiber optic | Yes | ... | Yes | No | No | No | Month-to-month | Yes | Credit card (automatic) | 84.80 | 3626.35 | No |
| 3110 | 6569-KTMDU | Female | 0 | No | No | 1 | Yes | No | No | No internet service | ... | No internet service | No internet service | No internet service | No internet service | Month-to-month | No | Mailed check | 19.10 | 19.1 | No |
| 1067 | 7030-NJVDP | Male | 0 | Yes | No | 24 | Yes | No | Fiber optic | Yes | ... | No | Yes | No | Yes | Month-to-month | Yes | Electronic check | 89.25 | 2210.2 | No |
5 rows Ć 21 columns
InĀ [3]:
# Handle missing values
train_df.fillna(value=0, inplace=True)
test_df.fillna(value=0, inplace=True)
# Check missing values have been handled
display("Training set columns:")
display(train_df.isnull().sum())
display("Testing set columns:")
display(test_df.isnull().sum())
'Training set columns:'
customerID 0 gender 0 SeniorCitizen 0 Partner 0 Dependents 0 tenure 0 PhoneService 0 MultipleLines 0 InternetService 0 OnlineSecurity 0 OnlineBackup 0 DeviceProtection 0 TechSupport 0 StreamingTV 0 StreamingMovies 0 Contract 0 PaperlessBilling 0 PaymentMethod 0 MonthlyCharges 0 TotalCharges 0 Churn 0 dtype: int64
'Testing set columns:'
customerID 0 gender 0 SeniorCitizen 0 Partner 0 Dependents 0 tenure 0 PhoneService 0 MultipleLines 0 InternetService 0 OnlineSecurity 0 OnlineBackup 0 DeviceProtection 0 TechSupport 0 StreamingTV 0 StreamingMovies 0 Contract 0 PaperlessBilling 0 PaymentMethod 0 MonthlyCharges 0 TotalCharges 0 Churn 0 dtype: int64
InĀ [4]:
#https://www.geeksforgeeks.org/machine-learning/label-encoding-across-multiple-columns-in-scikit-learn/
# Columns to encode (change to numerical values)
columns_to_encode = ['Churn', 'gender', 'Partner', 'Dependents', 'PhoneService', 'PaperlessBilling', 'MultipleLines', 'InternetService', 'OnlineSecurity',
'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies', 'Contract', 'PaymentMethod']
# for loop to work through each column and display the object to integer conversions
for column in columns_to_encode:
label_encoder = LabelEncoder()
train_df[column] = label_encoder.fit_transform(train_df[column])
test_df[column] = label_encoder.transform(test_df[column])
print(column, list(zip(label_encoder.classes_, label_encoder.transform(label_encoder.classes_))))
# display updated training and testing data sets
display(train_df.head())
display(test_df.head())
Churn [('No', 0), ('Yes', 1)]
gender [('Female', 0), ('Male', 1)]
Partner [('No', 0), ('Yes', 1)]
Dependents [('No', 0), ('Yes', 1)]
PhoneService [('No', 0), ('Yes', 1)]
PaperlessBilling [('No', 0), ('Yes', 1)]
MultipleLines [('No', 0), ('No phone service', 1), ('Yes', 2)]
InternetService [('DSL', 0), ('Fiber optic', 1), ('No', 2)]
OnlineSecurity [('No', 0), ('No internet service', 1), ('Yes', 2)]
OnlineBackup [('No', 0), ('No internet service', 1), ('Yes', 2)]
DeviceProtection [('No', 0), ('No internet service', 1), ('Yes', 2)]
TechSupport [('No', 0), ('No internet service', 1), ('Yes', 2)]
StreamingTV [('No', 0), ('No internet service', 1), ('Yes', 2)]
StreamingMovies [('No', 0), ('No internet service', 1), ('Yes', 2)]
Contract [('Month-to-month', 0), ('One year', 1), ('Two year', 2)]
PaymentMethod [('Bank transfer (automatic)', 0), ('Credit card (automatic)', 1), ('Electronic check', 2), ('Mailed check', 3)]
| customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | ... | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 313 | 2799-ARNLO | 0 | 1 | 1 | 0 | 5 | 1 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 69.35 | 341.6 | 0 |
| 911 | 7508-SMHXL | 0 | 1 | 0 | 0 | 15 | 1 | 2 | 1 | 0 | ... | 0 | 0 | 2 | 0 | 0 | 0 | 1 | 89.00 | 1288.3 | 0 |
| 5332 | 4565-EVZMJ | 0 | 0 | 0 | 0 | 47 | 1 | 0 | 0 | 2 | ... | 0 | 2 | 0 | 0 | 1 | 1 | 3 | 58.90 | 2813.05 | 0 |
| 4062 | 1915-IOFGU | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 70.50 | 70.5 | 1 |
| 2629 | 5028-GZLDO | 1 | 0 | 0 | 0 | 2 | 1 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 70.70 | 140.7 | 1 |
5 rows Ć 21 columns
| customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | ... | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1281 | 9531-NSBMR | 0 | 0 | 0 | 0 | 36 | 1 | 0 | 2 | 1 | ... | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 19.25 | 679.8 | 0 |
| 3159 | 5150-ITWWB | 1 | 1 | 0 | 0 | 3 | 1 | 2 | 1 | 0 | ... | 0 | 0 | 2 | 2 | 0 | 1 | 2 | 94.85 | 335.75 | 0 |
| 7024 | 7398-LXGYX | 1 | 0 | 1 | 0 | 44 | 1 | 2 | 1 | 2 | ... | 2 | 0 | 0 | 0 | 0 | 1 | 1 | 84.80 | 3626.35 | 0 |
| 3110 | 6569-KTMDU | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 2 | 1 | ... | 1 | 1 | 1 | 1 | 0 | 0 | 3 | 19.10 | 19.1 | 0 |
| 1067 | 7030-NJVDP | 1 | 0 | 1 | 0 | 24 | 1 | 0 | 1 | 2 | ... | 0 | 2 | 0 | 2 | 0 | 1 | 2 | 89.25 | 2210.2 | 0 |
5 rows Ć 21 columns
InĀ [5]:
# Set features and target (x and y axis) for training data
x_train = train_df.drop(['Churn', 'customerID', 'TotalCharges', 'MonthlyCharges'], axis=1)
y_train = train_df['Churn']
# Set features and target (x and y axis) for testing data
x_test = test_df.drop(['Churn', 'customerID', 'TotalCharges', 'MonthlyCharges'], axis=1)
y_test = test_df['Churn']
InĀ [6]:
# Logistic Regression Hyperparameters
lr_model = LogisticRegression(max_iter=1000)
lr_parameters = {
'C': [0.01, 0.1, 1, 10],
'penalty': ['l2'],
'solver': ['liblinear', 'lbfgs', 'saga'],
'class_weight': [None, 'balanced']
}
lr_grid = GridSearchCV(lr_model, param_grid=lr_parameters, scoring='accuracy')
lr_grid.fit(x_train, y_train)
print("Best Logistic Regression Parameters:", lr_grid.best_params_)
Best Logistic Regression Parameters: {'C': 0.1, 'class_weight': None, 'penalty': 'l2', 'solver': 'liblinear'}
InĀ [7]:
# SVM Hyperparameters
# https://www.geeksforgeeks.org/machine-learning/svm-hyperparameter-tuning-using-gridsearchcv-ml/
"""
#commented out due to the length of time taken to execute
svm_model = SVC(probability=True)
svm_parameters = [
{'kernel': ['linear'], 'C': [0.01, 0.1, 1, 10]},
{'kernel': ['rbf', 'poly'], 'C': [0.01, 0.1, 1, 10],
'gamma': [1, 0.1, 0.01, 0.001]}
]
svm_grid = GridSearchCV(svm_model, param_grid=svm_parameters, scoring='accuracy', n_jobs=-1)
svm_grid.fit(x_train, y_train)
print("Best SVM Params:", svm_grid.best_params_)
"""
svm_model = SVC(probability=True, kernel='linear', C=10)
svm_model.fit(x_train, y_train)
Out[7]:
SVC(C=10, kernel='linear', probability=True)In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
SVC(C=10, kernel='linear', probability=True)
InĀ [8]:
# LR predict test data using best parameters identified
best_lr = lr_grid.best_estimator_
y_lr_pred = best_lr.predict(x_test)
print("Logistic Regression Model Accuracy Score:", accuracy_score(y_test, y_lr_pred))
print(classification_report(y_test, y_lr_pred))
# SVM predict test data using best parameters identified
y_svm_pred = svm_model.predict(x_test)
print("SVM Model Accuracy Score:", accuracy_score(y_test, y_svm_pred))
print(classification_report(y_test, y_svm_pred))
Logistic Regression Model Accuracy Score: 0.8012304779933743
precision recall f1-score support
0 0.83 0.92 0.87 1552
1 0.68 0.47 0.56 561
accuracy 0.80 2113
macro avg 0.76 0.69 0.71 2113
weighted avg 0.79 0.80 0.79 2113
SVM Model Accuracy Score: 0.79649787032655
precision recall f1-score support
0 0.82 0.92 0.87 1552
1 0.67 0.46 0.54 561
accuracy 0.80 2113
macro avg 0.75 0.69 0.71 2113
weighted avg 0.78 0.80 0.78 2113
InĀ [9]:
# https://www.geeksforgeeks.org/machine-learning/confusion-matrix-machine-learning/
# confusion matrix for LR model
cm = confusion_matrix(y_test, y_lr_pred, labels=[0, 1])
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=['0 - No', '1 - Yes'])
disp.plot(cmap=plt.cm.Blues)
plt.title('Confusion Matrix for LR where Customer Churn is Predicted', fontsize=15, pad=20)
plt.xlabel('Predicted values')
plt.ylabel('Actual values')
plt.show()
# confusion matrix SVM model
cm = confusion_matrix(y_test, y_svm_pred, labels=[0, 1])
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=['0 - No', '1 - Yes'])
disp.plot(cmap=plt.cm.Blues)
plt.title('Confusion Matrix for SVM where Customer Churn is Predicted', fontsize=15, pad=20)
plt.xlabel('Predicted values')
plt.ylabel('Actual values')
plt.show()
InĀ [10]:
# plot auc roc curve
# https://www.geeksforgeeks.org/machine-learning/auc-roc-curve/
y_lr_pred = best_lr.predict_proba(x_test)[:, 1]
y_svm_pred = svm_model.predict_proba(x_test)[:, 1]
test_df = pd.DataFrame(
{'True': y_test, 'LR': y_lr_pred, 'SVM': y_svm_pred})
plt.figure(figsize=(7, 5))
for model in ['LR', 'SVM']:
fpr, tpr, _ = roc_curve(test_df['True'], test_df[model])
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, label=f'{model} (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], 'r--', label='Random Guess')
plt.xlabel('False Positive')
plt.ylabel('True Positive')
plt.title('ROC Curve Graph for LR and SVM Model')
plt.legend()
plt.show()