In [11]:
## Data Loading
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, roc_auc_score, roc_curve
data_path = 'credit_card_default.csv'
# Load the dataset into a pandas DataFrame and set the index to the "ID" column
ccd = pd.read_csv(data_path, index_col="ID")
# Understand the data
ccd.describe()
Out[11]:
| LIMIT_BAL | SEX | EDUCATION | MARRIAGE | AGE | PAY_1 | PAY_2 | PAY_3 | PAY_4 | PAY_5 | ... | BILL_AMT4 | BILL_AMT5 | BILL_AMT6 | PAY_AMT1 | PAY_AMT2 | PAY_AMT3 | PAY_AMT4 | PAY_AMT5 | PAY_AMT6 | default payment next month | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | ... | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 | 3.000000e+04 | 30000.00000 | 30000.000000 | 30000.000000 | 30000.000000 | 30000.000000 |
| mean | 167484.322667 | 1.603733 | 1.853133 | 1.553667 | 35.485500 | -0.016700 | -0.133767 | -0.166200 | -0.220667 | -0.266200 | ... | 43262.948967 | 40311.400967 | 38871.760400 | 5663.580500 | 5.921163e+03 | 5225.68150 | 4826.076867 | 4799.387633 | 5215.502567 | 0.221200 |
| std | 129747.661567 | 0.489129 | 0.790349 | 0.518326 | 9.217904 | 1.123802 | 1.197186 | 1.196868 | 1.169139 | 1.133187 | ... | 64332.856134 | 60797.155770 | 59554.107537 | 16563.280354 | 2.304087e+04 | 17606.96147 | 15666.159744 | 15278.305679 | 17777.465775 | 0.415062 |
| min | 10000.000000 | 1.000000 | 0.000000 | 1.000000 | 21.000000 | -2.000000 | -2.000000 | -2.000000 | -2.000000 | -2.000000 | ... | -170000.000000 | -81334.000000 | -339603.000000 | 0.000000 | 0.000000e+00 | 0.00000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
| 25% | 50000.000000 | 1.000000 | 1.000000 | 1.000000 | 28.000000 | -1.000000 | -1.000000 | -1.000000 | -1.000000 | -1.000000 | ... | 2326.750000 | 1763.000000 | 1256.000000 | 1000.000000 | 8.330000e+02 | 390.00000 | 296.000000 | 252.500000 | 117.750000 | 0.000000 |
| 50% | 140000.000000 | 2.000000 | 2.000000 | 2.000000 | 34.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 19052.000000 | 18104.500000 | 17071.000000 | 2100.000000 | 2.009000e+03 | 1800.00000 | 1500.000000 | 1500.000000 | 1500.000000 | 0.000000 |
| 75% | 240000.000000 | 2.000000 | 2.000000 | 2.000000 | 41.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | ... | 54506.000000 | 50190.500000 | 49198.250000 | 5006.000000 | 5.000000e+03 | 4505.00000 | 4013.250000 | 4031.500000 | 4000.000000 | 0.000000 |
| max | 1000000.000000 | 2.000000 | 6.000000 | 3.000000 | 79.000000 | 8.000000 | 8.000000 | 8.000000 | 8.000000 | 8.000000 | ... | 891586.000000 | 927171.000000 | 961664.000000 | 873552.000000 | 1.684259e+06 | 896040.00000 | 621000.000000 | 426529.000000 | 528666.000000 | 1.000000 |
8 rows × 24 columns
In [2]:
#Clean PAY_ features
pay_features = ['PAY_' + str(i) for i in range(1,7)]
# Loop through each 'pay_' feature
for x in pay_features:
# Transform the -1 and -2 values to 0, assuming these represent non-defaulted payments
ccd.loc[ccd[x] <= 0, x] = 0
# Rename the 'default payment next month' column to 'default' for brevity
ccd.rename(columns={'default payment next month':'default'}, inplace=True)
categorical_features = ['SEX', 'EDUCATION', 'MARRIAGE']
numerical_features = ['LIMIT_BAL', 'AGE', 'PAY_1', 'PAY_2', 'PAY_3', 'PAY_4', \
'PAY_5', 'PAY_6', 'BILL_AMT1', 'BILL_AMT2', 'BILL_AMT3', 'BILL_AMT4', \
'BILL_AMT5', 'BILL_AMT6', 'PAY_AMT1', 'PAY_AMT2', 'PAY_AMT3', 'PAY_AMT4',\
'PAY_AMT5', 'PAY_AMT6']
target_feature = ['default']
In [3]:
#standardize data
scaler = StandardScaler()
ccd[numerical_features] = scaler.fit_transform(ccd[numerical_features])
ccd = pd.get_dummies(ccd, columns=categorical_features, drop_first=True)
In [4]:
#splitting data inot train and test
X = ccd.drop(columns=['default'])
y = ccd['default']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
In [5]:
#model buidling
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
rf_train_pred = rf_model.predict(X_train)
rf_test_pred = rf_model.predict(X_test)
rf_test_proba = rf_model.predict_proba(X_test)[:, 1]
dt_model = DecisionTreeClassifier(random_state=42)
dt_model.fit(X_train, y_train)
dt_train_pred = dt_model.predict(X_train)
dt_test_pred = dt_model.predict(X_test)
dt_test_proba = dt_model.predict_proba(X_test)[:, 1]
lr_model = LogisticRegression(max_iter=1000)
lr_model.fit(X_train, y_train)
lr_train_pred = lr_model.predict(X_train)
lr_test_pred = lr_model.predict(X_test)
lr_test_proba = lr_model.predict_proba(X_test)[:, 1]
In [6]:
#model performance comparison
models = ['Random Forest', 'Decision Tree', 'Logistic Regression']
train_accuracies = [
accuracy_score(y_train, rf_train_pred),
accuracy_score(y_train, dt_train_pred),
accuracy_score(y_train, lr_train_pred)
]
test_accuracies = [
accuracy_score(y_test, rf_test_pred),
accuracy_score(y_test, dt_test_pred),
accuracy_score(y_test, lr_test_pred)
]
auroc_scores = [
roc_auc_score(y_test, rf_test_proba),
roc_auc_score(y_test, dt_test_proba),
roc_auc_score(y_test, lr_test_proba)
]
results_df = pd.DataFrame({
'Model': models,
'Training Accuracy': [f"{x:.4f}" for x in train_accuracies],
'Testing Accuracy': [f"{x:.4f}" for x in test_accuracies],
'AUROC Score': [f"{x:.4f}" for x in auroc_scores]
})
# Print the table
print("Model Performance Comparison:")
print(results_df)
Model Performance Comparison:
Model Training Accuracy Testing Accuracy AUROC Score
0 Random Forest 0.9994 0.8150 0.7620
1 Decision Tree 0.9995 0.7268 0.6145
2 Logistic Regression 0.8195 0.8162 0.7630
In [12]:
#print decision tree
plt.figure(figsize=(12, 8))
plot_tree(dt_model, max_depth=2, class_names=['No Default', 'Default'],filled=True)
plt.show()
In [15]:
# Calculate ROC curves
rf_fpr, rf_tpr, _ = roc_curve(y_test, rf_test_proba)
dt_fpr, dt_tpr, _ = roc_curve(y_test, dt_test_proba)
lr_fpr, lr_tpr, _ = roc_curve(y_test, lr_test_proba)
# ax3 = plt.plot(1, 1, 1)
plt.plot(rf_fpr, rf_tpr, label=f'Random Forest (AUC = {auroc_scores[0]:.3f})', plt.plot(dt_fpr, dt_tpr, label=f'Decision Tree (AUC = {auroc_scores[1]:.3f})', color='blue')
color='orange')
plt.plot(lr_fpr, lr_tpr, label=f'Logistic Regression (AUC = {auroc_scores[2]:.3f})', color='green')
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve Comparison')
plt.legend()
plt.grid(True)
In [16]:
# Create probability distribution plots
fig, axes = plt.subplots(1, 3, figsize=(18, 5), sharey=True)
# Random Forest
sns.histplot(rf_test_proba, bins=20, kde=True, ax=axes[0], color='blue')
axes[0].set_title('Random Forest Probability Distribution')
axes[0].set_xlabel('Predicted Probability')
axes[0].set_ylabel('Frequency')
# Decision Tree
sns.histplot(dt_test_proba, bins=20, kde=True, ax=axes[1], color='orange')
axes[1].set_title('Decision Tree Probability Distribution')
axes[1].set_xlabel('Predicted Probability')
# Logistic Regression
sns.histplot(lr_test_proba, bins=20, kde=True, ax=axes[2], color='green')
axes[2].set_title('Logistic Regression Probability Distribution')
axes[2].set_xlabel('Predicted Probability')
plt.tight_layout()
plt.show()
In [ ]: