Το επιλεγμένο dataset είναι ένα συνθετικό σύνολο δεδομένων που περιλαμβάνει προσωπικά, οικονομικά και πιστωτικά χαρακτηριστικά αιτούντων δανείου. Σκοπός του είναι η ανάλυση και πρόβλεψη της έγκρισης ή απόρριψης δανείων, καθώς και η αξιολόγηση της πιστοληπτικής ικανότητας των αιτούντων, βοηθώντας στη λήψη τεκμηριωμένων αποφάσεων από χρηματοπιστωτικούς οργανισμούς.
Πηγή δεδομένων:https://www.kaggle.com/datasets/taweilo/loan-approval-classification-data
Η συγκεκριμένη βάση δεδομένων συγκεντρώνει μεταβλητές που σχετίζονται με:
✅ Δημογραφικά χαρακτηριστικά (π.χ. ηλικία, φύλο, επίπεδο εκπαίδευσης)
✅ Οικονομικά στοιχεία (π.χ. ετήσιο εισόδημα, εμπειρία εργασίας, κατοχή κατοικίας)
✅ Στοιχεία δανείου (π.χ. ποσό δανείου, σκοπός, επιτόκιο, ποσοστό δανείου ως προς το εισόδημα)
✅ Πιστωτικό ιστορικό (π.χ. διάρκεια πιστωτικού ιστορικού, πιστωτική βαθμολογία, προηγούμενες αθετήσεις πληρωμών)
✅ Κατάσταση αίτησης δανείου (εγκεκριμένο ή απορριφθέν)
Το dataset περιλαμβάνει 14 μεταβλητές.
Εξαρτημένη Μεταβλητή: loan_status
🔹Τύπος: Δυαδική (0 = απόρριψη, 1 = έγκριση)
🔹Είναι η μεταβλητή που θέλουμε να προβλέψουμε (αν το δάνειο θα εγκριθεί ή όχι).
Ανεξάρτητες Μεταβλητές: Οι υπόλοιπες 13 μεταβλητές είναι ανεξάρτητες, δηλαδή τα χαρακτηριστικά βάσει των οποίων προβλέπεται η loan_status.
Αναλυτικά παρουσιάζονται στον παρακάτω πίνακα.
| Variable | Description | Variable_Type | Variable_Range |
|---|---|---|---|
| person_age | Ηλικία ατόμου | Αριθμητική(Float) | 18 – 100+ |
| person_gender | Φύλο | Κατηγορική | Male, Female |
| person_education | Επίπεδο εκπαίδευσης | Κατηγορική | High School, College, Graduate |
| person_income | Ετήσιο εισόδημα | Αριθμητική(Float) | 10,000 – 200,000+ |
| person_emp_exp | Έτη επαγγελματικής εμπειρίας | Αριθμητική(Float) | 0 – 50 |
| person_home_ownership | Κατοχή κατοικίας (ενοίκιο, ιδιόκτητο, υποθήκη) | Κατηγορική | Rent, Own, Mortgage |
| loan_amnt | Ποσό αιτούμενου δανείου | Αριθμητική(Float) | 1,000 – 50,000+ |
| loan_intent | Σκοπός δανείου | Κατηγορική | Personal, Education, Home Improvement, Medical, etc. |
| loan_int_rate | Επιτόκιο δανείου | Αριθμητική(Float) | 5.0 – 30.0% |
| loan_percent_income | Ποσοστό δανείου σε σχέση με το εισόδημα | Αριθμητική(Float) | 0.01 – 1.0 |
| cb_person_cred_hist_length | Διάρκεια πιστωτικού ιστορικού (έτη) | Αριθμητική(Float) | 0 – 30+ |
| credit_score | Πιστωτική βαθμολογία | Αριθμητική(Integer) | 300 – 850 |
| previous_loan_defaults_on_file | Ένδειξη προηγούμενων αθετήσεων πληρωμών | Κατηγορική | Yes, No |
| loan_status | Κατάσταση αίτησης | Δυαδική (Integer) | 1 = Εγκεκριμένο, 0 = Απορριφθέν |
Εισάγω τα δεδομένα του dataset για επεξεργασία.
library(readr)
loan <- read.csv("loan_data.csv")
Χρησιμοποιώ τη μέθοδο Classification and Regression Trees (CART).
Πριν φτιάξω τα μοντέλα, διαχωρίζω το dataset:
Το training set είναι το 65% της βάσης και το υπόλοιπο 35% είναι για το testing set.
Πριν διαχωρίσω το dataset ορίζω το ίδιο seed για να εξασφαλιστεί ότι όλα τα sets θα είναι ίδια.
library(caTools)
set.seed(3000)
Και στην συνέχεια δημιουργώ τα δύο καινούργια datasets.
split <- sample.split(loan$loan_status,SplitRatio=0.65)
Ονομάζω τα sets ως train και test.
loanTrain = subset(loan, split==TRUE)
loanTest = subset(loan, split==FALSE)
Και καταγράφω το πόσες είναι οι καταχωρήσεις σε κάθε set.
nrow(loanTrain)
## [1] 29250
nrow(loanTest)
## [1] 15750
Δημιουργώ το μοντέλο CART με όνομα LoanTree:
library(rpart)
library(rpart.plot)
LoanTree <- rpart(loan_status ~ person_age + person_gender + person_education + person_income + person_emp_exp + person_home_ownership + loan_amnt + loan_intent + loan_int_rate + loan_percent_income + cb_person_cred_hist_length + credit_score + previous_loan_defaults_on_file, data = loanTrain, method="class", minbucket=25)
summary(LoanTree)
## Call:
## rpart(formula = loan_status ~ person_age + person_gender + person_education +
## person_income + person_emp_exp + person_home_ownership +
## loan_amnt + loan_intent + loan_int_rate + loan_percent_income +
## cb_person_cred_hist_length + credit_score + previous_loan_defaults_on_file,
## data = loanTrain, method = "class", minbucket = 25)
## n= 29250
##
## CP nsplit rel error xerror xstd
## 1 0.16769231 0 1.0000000 1.0000000 0.010938835
## 2 0.04953846 3 0.4749231 0.4749231 0.008084177
## 3 0.01215385 4 0.4253846 0.4258462 0.007701621
## 4 0.01038462 5 0.4132308 0.4224615 0.007674139
## 5 0.01030769 7 0.3924615 0.4060000 0.007538310
## 6 0.01000000 8 0.3821538 0.3926154 0.007425119
##
## Variable importance
## previous_loan_defaults_on_file loan_percent_income
## 36 21
## loan_int_rate person_income
## 18 10
## person_home_ownership credit_score
## 9 5
## loan_intent loan_amnt
## 1 1
##
## Node number 1: 29250 observations, complexity param=0.1676923
## predicted class=0 expected loss=0.2222222 P(node) =1
## class counts: 22750 6500
## probabilities: 0.778 0.222
## left son=2 (14822 obs) right son=3 (14428 obs)
## Primary splits:
## previous_loan_defaults_on_file splits as RL, improve=2967.7790, (0 missing)
## loan_percent_income < 0.245 to the left, improve=1837.8310, (0 missing)
## loan_int_rate < 14.355 to the left, improve=1214.9140, (0 missing)
## person_income < 42613.5 to the right, improve= 741.7176, (0 missing)
## person_home_ownership splits as LRLR, improve= 665.1626, (0 missing)
## Surrogate splits:
## loan_percent_income < 0.235 to the left, agree=0.581, adj=0.150, (0 split)
## loan_int_rate < 12.995 to the left, agree=0.578, adj=0.144, (0 split)
## credit_score < 632.5 to the left, agree=0.574, adj=0.136, (0 split)
## person_home_ownership splits as LRLR, agree=0.569, adj=0.125, (0 split)
## person_income < 54523 to the right, agree=0.566, adj=0.120, (0 split)
##
## Node number 2: 14822 observations
## predicted class=0 expected loss=0 P(node) =0.506735
## class counts: 14822 0
## probabilities: 1.000 0.000
##
## Node number 3: 14428 observations, complexity param=0.1676923
## predicted class=0 expected loss=0.4505129 P(node) =0.493265
## class counts: 7928 6500
## probabilities: 0.549 0.451
## left son=6 (11474 obs) right son=7 (2954 obs)
## Primary splits:
## loan_percent_income < 0.245 to the left, improve=1301.0010, (0 missing)
## loan_int_rate < 13.995 to the left, improve= 914.4131, (0 missing)
## person_home_ownership splits as LRLR, improve= 699.7532, (0 missing)
## person_income < 48189.5 to the right, improve= 634.4270, (0 missing)
## credit_score < 618.5 to the right, improve= 192.9823, (0 missing)
## Surrogate splits:
## loan_amnt < 18119.5 to the left, agree=0.801, adj=0.03, (0 split)
## person_income < 10800.5 to the right, agree=0.795, adj=0.00, (0 split)
##
## Node number 6: 11474 observations, complexity param=0.1676923
## predicted class=0 expected loss=0.342775 P(node) =0.3922735
## class counts: 7541 3933
## probabilities: 0.657 0.343
## left son=12 (8731 obs) right son=13 (2743 obs)
## Primary splits:
## loan_int_rate < 13.985 to the left, improve=1051.9270, (0 missing)
## person_income < 42619 to the right, improve= 366.0019, (0 missing)
## loan_intent splits as RLRRLL, improve= 201.1309, (0 missing)
## person_home_ownership splits as LRLR, improve= 198.4327, (0 missing)
## credit_score < 611.5 to the right, improve= 123.2972, (0 missing)
## Surrogate splits:
## credit_score < 458.5 to the right, agree=0.761, adj=0.001, (0 split)
##
## Node number 7: 2954 observations, complexity param=0.01215385
## predicted class=1 expected loss=0.1310088 P(node) =0.1009915
## class counts: 387 2567
## probabilities: 0.131 0.869
## left son=14 (639 obs) right son=15 (2315 obs)
## Primary splits:
## person_home_ownership splits as LRLR, improve=302.65990, (0 missing)
## person_income < 40379.5 to the right, improve= 17.59191, (0 missing)
## credit_score < 622.5 to the right, improve= 12.76783, (0 missing)
## loan_intent splits as RLLRLL, improve= 12.12985, (0 missing)
## loan_amnt < 10525 to the right, improve= 11.85497, (0 missing)
## Surrogate splits:
## person_income < 113456.5 to the right, agree=0.788, adj=0.020, (0 split)
## loan_int_rate < 5.555 to the left, agree=0.785, adj=0.008, (0 split)
## credit_score < 738 to the right, agree=0.784, adj=0.002, (0 split)
##
## Node number 12: 8731 observations, complexity param=0.04953846
## predicted class=0 expected loss=0.2227694 P(node) =0.2984957
## class counts: 6786 1945
## probabilities: 0.777 0.223
## left son=24 (8315 obs) right son=25 (416 obs)
## Primary splits:
## person_income < 24783.5 to the right, improve=385.46760, (0 missing)
## loan_int_rate < 10.775 to the left, improve= 95.53720, (0 missing)
## loan_amnt < 5059 to the right, improve= 78.33077, (0 missing)
## person_home_ownership splits as LRLR, improve= 75.84301, (0 missing)
## credit_score < 598.5 to the right, improve= 72.38624, (0 missing)
## Surrogate splits:
## loan_amnt < 700 to the right, agree=0.953, adj=0.005, (0 split)
##
## Node number 13: 2743 observations, complexity param=0.01038462
## predicted class=1 expected loss=0.2752461 P(node) =0.09377778
## class counts: 755 1988
## probabilities: 0.275 0.725
## left son=26 (1204 obs) right son=27 (1539 obs)
## Primary splits:
## loan_intent splits as RLRRLL, improve=114.43900, (0 missing)
## person_home_ownership splits as LLLR, improve= 54.82771, (0 missing)
## person_income < 72676.5 to the right, improve= 36.40910, (0 missing)
## loan_int_rate < 15.305 to the left, improve= 35.05550, (0 missing)
## credit_score < 647.5 to the right, improve= 23.55018, (0 missing)
## Surrogate splits:
## person_home_ownership splits as RLLR, agree=0.570, adj=0.021, (0 split)
## person_age < 22.5 to the left, agree=0.569, adj=0.018, (0 split)
## person_income < 19990.5 to the left, agree=0.569, adj=0.017, (0 split)
## loan_amnt < 2013 to the left, agree=0.565, adj=0.008, (0 split)
## loan_int_rate < 13.995 to the left, agree=0.563, adj=0.005, (0 split)
##
## Node number 14: 639 observations, complexity param=0.01030769
## predicted class=0 expected loss=0.4381847 P(node) =0.02184615
## class counts: 359 280
## probabilities: 0.562 0.438
## left son=28 (572 obs) right son=29 (67 obs)
## Primary splits:
## person_income < 25255 to the right, improve=47.24946, (0 missing)
## loan_int_rate < 14.365 to the left, improve=35.57887, (0 missing)
## loan_amnt < 6850 to the right, improve=33.50133, (0 missing)
## loan_intent splits as RLRRLL, improve=33.21556, (0 missing)
## credit_score < 623.5 to the right, improve=14.53987, (0 missing)
## Surrogate splits:
## loan_amnt < 7062.5 to the right, agree=0.975, adj=0.761, (0 split)
## loan_percent_income < 0.575 to the left, agree=0.898, adj=0.030, (0 split)
##
## Node number 15: 2315 observations
## predicted class=1 expected loss=0.01209503 P(node) =0.0791453
## class counts: 28 2287
## probabilities: 0.012 0.988
##
## Node number 24: 8315 observations
## predicted class=0 expected loss=0.189537 P(node) =0.2842735
## class counts: 6739 1576
## probabilities: 0.810 0.190
##
## Node number 25: 416 observations
## predicted class=1 expected loss=0.1129808 P(node) =0.01422222
## class counts: 47 369
## probabilities: 0.113 0.887
##
## Node number 26: 1204 observations, complexity param=0.01038462
## predicted class=1 expected loss=0.4385382 P(node) =0.04116239
## class counts: 528 676
## probabilities: 0.439 0.561
## left son=52 (445 obs) right son=53 (759 obs)
## Primary splits:
## person_home_ownership splits as LLLR, improve=64.14073, (0 missing)
## person_income < 54878.5 to the right, improve=35.01313, (0 missing)
## credit_score < 647.5 to the right, improve=19.15007, (0 missing)
## loan_int_rate < 15.455 to the left, improve=17.79294, (0 missing)
## loan_amnt < 7775 to the right, improve=12.00847, (0 missing)
## Surrogate splits:
## person_income < 89748.5 to the right, agree=0.694, adj=0.173, (0 split)
## loan_amnt < 13100 to the right, agree=0.656, adj=0.070, (0 split)
## loan_int_rate < 19.415 to the right, agree=0.633, adj=0.007, (0 split)
## credit_score < 733.5 to the right, agree=0.631, adj=0.002, (0 split)
##
## Node number 27: 1539 observations
## predicted class=1 expected loss=0.1474984 P(node) =0.05261538
## class counts: 227 1312
## probabilities: 0.147 0.853
##
## Node number 28: 572 observations
## predicted class=0 expected loss=0.3723776 P(node) =0.01955556
## class counts: 359 213
## probabilities: 0.628 0.372
##
## Node number 29: 67 observations
## predicted class=1 expected loss=0 P(node) =0.002290598
## class counts: 0 67
## probabilities: 0.000 1.000
##
## Node number 52: 445 observations
## predicted class=0 expected loss=0.3483146 P(node) =0.01521368
## class counts: 290 155
## probabilities: 0.652 0.348
##
## Node number 53: 759 observations
## predicted class=1 expected loss=0.3135705 P(node) =0.02594872
## class counts: 238 521
## probabilities: 0.314 0.686
prp(LoanTree)
Ανάλυση του δέντρου CART
1)Κόμβος ρίζας: previous_loan_defaults_on_file = Yes / No
Εάν ο χρήστης έχει ιστορικό προηγούμενων αθετήσεων, απορρίπτεται αμέσως (έξοδος 0).
Αν όχι, προχωράει σε περαιτέρω έλεγχο.
Είναι σημαντική η επιλογή ως ρίζα, καθώς το πιστωτικό ιστορικό είναι κρίσιμο.
2)Δεύτερο split: loan_percent_income < 0.25
Δηλαδή, το δάνειο είναι μικρότερο από το 25% του εισοδήματος ⇒ θετικό σημάδι (χαμηλός δανειακός κίνδυνος).
3)Ανάλυση υποκατηγοριών
➤ Όταν loan_percent_income < 0.25:
A. Αν loan_int_rate < 14:
Αν income >= 25,000, τότε δεν εγκρίνεται (0).
Αν income < 25,000, τότε εγκρίνεται (1).
Β. Αν όμως loan_int_rate >= 14 τότε ελέγχεται το loan_intent και home_ownership.
Συγκεκριμένα, αν loan_intent δεν ανήκει σε καμία από τις κατηγορίες EDU, PER, VEN τότε εγκρίνεται (1).
Αν το loan_intent ανήκει σε μία από τις κατηγορίες EDU, PER, VEN και το home_ownership σε καμία από τις κατηγορίες MOR, OTH, OWN τότε εγκρίνεται (1).
Αν όμως το loan_intent ανήκει σε μία από τις κατηγορίες EDU, PER, VEN και το home_ownership σε μία από τις κατηγορίες MOR, OTH, OWN τότε δεν εγκρίνεται (0).
➤ Όταν loan_percent_income >= 0.25:
Αν person_home_ownership ∈ {MOR, OWN}:
Εάν person_income >= 25,000, τότε δεν εγκρίνεται (0).
Αλλιώς, εγκρίνεται (1).
📌 Συμπερασματικά,το δέντρο εστιάζει σε ισχυρούς δείκτες ρίσκου όπως:
➤προηγούμενες αθετήσεις πληρωμών,
➤αναλογία δανείου/εισοδήματος,
➤εισόδημα και επιτόκιο.
PredictCART <- predict (LoanTree, newdata=loanTest, type="class")
table(loanTest$loan_status, PredictCART)
## PredictCART
## 0 1
## 0 11942 308
## 1 1120 2380
| Variables | Model_R_squared |
|---|---|
| Accuracy (Ακρίβεια) | 90.9% |
| Ευαισθησία (Sensitivity / True Positive Rate) | 68% |
| Ειδικότητα (Specificity / True Negative Rate) | 97.5% |
| Ακρίβεια του Baseline Model | 77.8% |
Συμπεράσματα που προκύπτουν από τον παραπάνω πίνακα:
Από 77.8% (baseline) → σε 90.9%.
Δηλαδή, έχει μάθει να κάνει σωστές προβλέψεις και στις δύο κλάσεις, όχι μόνο στην πλειοψηφική (0).
Το μοντέλο σχεδόν πάντα αναγνωρίζει σωστά τους μη αξιόπιστους δανειολήπτες (0).
Δηλαδή, σπάνια εγκρίνει κάποιον που δεν θα ξεπληρώσει → καλό για τράπεζες.
Δεν αναγνωρίζει όλους τους καλούς πελάτες (1).
Υπάρχει ποσοστό πελατών που θα ξεπλήρωναν, αλλά το μοντέλο τους απορρίπτει.
➤ Αυτό σημαίνει χαμένη ευκαιρία για έγκριση δανείων σε καλούς πελάτες. Ωστόσο, προστατεύει την τράπεζα από επισφαλείς πελάτες.
library(ROCR)
PredictROC <- predict(LoanTree, newdata = loanTest)
pred <- prediction(PredictROC[,2], loanTest$loan_status)
perf <- performance(pred, "tpr", "fpr")
plot(perf)
as.numeric(performance(pred, "auc")@y.values)
## [1] 0.9418172
Συμπεράσματα για τα παραπάνω αποτελέσματα:
Η καμπύλη ROC δείχνει πόσο καλά διαχωρίζει το μοντέλο τις δύο κατηγορίες σε διάφορα κατώφλια.
Η True Positive Rate (TPR) αυξάνεται πολύ γρήγορα χωρίς μεγάλο κόστος σε False Positive Rate (FPR).
Το AUC = 0.94 είναι πολύ υψηλό (το ιδανικό είναι 1.0). Αυτό σημαίνει:
Το μοντέλο έχει πολύ καλή διακριτική ικανότητα μεταξύ αυτών που παίρνουν και αυτών που δεν παίρνουν το δάνειο.
library(caret)
## Loading required package: ggplot2
## Loading required package: lattice
library(e1071)
numFolds <- trainControl (method = "cv", number= 7)
cpGrid <- expand.grid (.cp = seq(0.01,0.7,0.01))
LoanTreeCV <- rpart (loan_status ~ person_age + person_gender + person_education + person_income + person_emp_exp + person_home_ownership + loan_amnt + loan_intent + loan_int_rate + loan_percent_income + cb_person_cred_hist_length + credit_score + previous_loan_defaults_on_file, data = loanTrain, method="class", cp=0.06)
PredictCV <- predict (LoanTreeCV, newdata = loanTest, type = "class")
table (loanTest$loan_status, PredictCV)
## PredictCV
## 0 1
## 0 11662 588
## 1 1143 2357
| Variables | Model_R_squared |
|---|---|
| Accuracy (Ακρίβεια) | 89.05% |
| Ευαισθησία (Sensitivity / True Positive Rate) | 67.34% |
| Ειδικότητα (Specificity / True Negative Rate) | 95.2% |
| Ακρίβεια του Baseline Model | 77.78% |
Συμπεράσματα:
Το νέο μοντέλο είναι πιο απλό και γενικευμένο με cp=0.06 και ενθαρρύνει κλάδεμα του δέντρου (pruning).
Αν και έχει ελαφρώς μικρότερη ακρίβεια τώρα, είναι πιο σταθερό και αξιόπιστο.
Η αλλαγή στην ακρίβεια από το αρχικό είναι πολύ μικρή (μόνο ~1%), άρα:
Δεν είναι πρόβλημα. Αντιθέτως, είναι σημάδι σταθερότητας.
library(ROCR)
PredictROCV <- predict(LoanTreeCV, newdata = loanTest)
predV <- prediction(PredictROCV[,2], loanTest$loan_status)
perfV <- performance(predV, "tpr", "fpr")
plot(perfV)
as.numeric(performance(predV, "auc")@y.values)
## [1] 0.9234109
Σχόλια για τα παραπάνω αποτελέσματα:
Η σημασία του AUC = 0.923:
Η AUC (Area Under Curve) κυμαίνεται από 0.5 (τυχαίο μοντέλο) έως 1.0 (τέλειο μοντέλο).
Με AUC = 0.923 σημαίνει ότι το μοντέλο έχει πολύ ισχυρή ικανότητα διαχωρισμού μεταξύ των δύο κατηγοριών (δηλαδή αν θα πάρει κάποιος το δάνειο ή όχι).
Η ROC καμπύλη ανεβαίνει γρήγορα προς τα πάνω, υποδηλώνοντας υψηλό TPR με χαμηλό FPR, το οποίο αποτελεί πολύ θετική ένδειξη.
Λογιστική Παλινδρόμηση:
Προσφέρει σαφείς συντελεστές (coefficients) για κάθε μεταβλητή.
Οι p-values δείχνουν τη στατιστική σημαντικότητα κάθε παράγοντα.
CART (Δέντρο Αποφάσεων):
Δίνει οπτικά διαδρομές απόφασης και μπορεί να είναι πιο κατανοητό σε μη ειδικούς.
Δεν παρέχει p-values αλλά μόνο σχετική σημασία μεταβλητών και δομή κανόνων.
Συμπερασματικά:
Λογιστική Παλινδρόμηση είναι ιδανική για κατανόηση επιρροών και προσφέρει ισχυρή στατιστική τεκμηρίωση.
CART είναι καλύτερο για πρακτικές αποφάσεις και διαχειρίζεται περισσότερη πολυπλοκότητα και αλληλεπιδράσεις.