1 Introduction

The purpose of this project is to apply data mining techniques to the German Credit dataset in order to explore patterns and build predictive models. The focus is on decision tree classification, evaluating its performance, and interpreting the results in the context of credit risk assessment.

The analysis follows a structured process: importing and preprocessing the dataset, conducting exploratory data analysis, building classification models (Decision Tree and Random Forest), and evaluating their predictive accuracy.

This work is part of the Datamanz project, which emphasizes the application of statistical learning methods and reproducible research practices using R.

2 Import Libraries and Dataset

This code block ensures that all the necessary packages for data analysis and visualization are installed and loaded into the R environment. Conditional installation is applied so that only missing packages are installed. The libraries included provide tools for clustering, advanced graphics, statistical analysis, machine learning, and data manipulation.

if (!require('C50')) install.packages('C50')
## Cargando paquete requerido: C50
## Warning: package 'C50' was built under R version 4.4.3
library(C50)
if (!require('gridExtra')) install.packages('gridExtra')
## Cargando paquete requerido: gridExtra
library(gridExtra)
if (!require('grid')) install.packages('grid')
## Cargando paquete requerido: grid
library(grid)
if (!require('ggpubr')) install.packages('ggpubr')
## Cargando paquete requerido: ggpubr
## Cargando paquete requerido: ggplot2
library(ggpubr)
if (!require('cluster')) install.packages('cluster')
## Cargando paquete requerido: cluster
library(cluster)
if (!require('Stat2Data')) install.packages('Stat2Data')
## Cargando paquete requerido: Stat2Data
library(Stat2Data)
if (!require('dplyr')) install.packages('dplyr')
## Cargando paquete requerido: dplyr
## 
## Adjuntando el paquete: 'dplyr'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(dplyr)
if (!require('ggplot2')) install.packages("ggplot2")
library(ggplot2)
if (!require('factoextra')) install.packages("factoextra")
## Cargando paquete requerido: factoextra
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(factoextra)
if (!require('NbClust')) install.packages("NbClust")
## Cargando paquete requerido: NbClust
library(NbClust)
if (!require('dbscan')) install.packages('dbscan')
## Cargando paquete requerido: dbscan
## 
## Adjuntando el paquete: 'dbscan'
## The following object is masked from 'package:stats':
## 
##     as.dendrogram
library(dbscan)
if (!require('tidyr')) install.packages('tidyr')
## Cargando paquete requerido: tidyr
library(tidyr)
if (!require('factoextra')) install.packages('factoextra')
library(factoextra)
if (!require('corrplot')) install.packages('corrplot')
## Cargando paquete requerido: corrplot
## corrplot 0.95 loaded
library(corrplot)
if (!require('psych')) install.packages('psych')
## Cargando paquete requerido: psych
## 
## Adjuntando el paquete: 'psych'
## The following objects are masked from 'package:ggplot2':
## 
##     %+%, alpha
library(psych)
if (!require('DescTools')) install.packages('DescTools')
## Cargando paquete requerido: DescTools
## Warning: package 'DescTools' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'DescTools'
## The following objects are masked from 'package:psych':
## 
##     AUC, ICC, SD
library(DescTools)
if (!require('rpart')) install.packages('rpart')
## Cargando paquete requerido: rpart
library(rpart)
if (!require('rpart.plot')) install.packages('rpart.plot')
## Cargando paquete requerido: rpart.plot
## Warning: package 'rpart.plot' was built under R version 4.4.3
library(rpart.plot)
if (!require('DiagrammeR')) install.packages('DiagrammeR')
## Cargando paquete requerido: DiagrammeR
## Warning: package 'DiagrammeR' was built under R version 4.4.3
library(DiagrammeR)
if (!require('randomForest')) install.packages('randomForest')
## Cargando paquete requerido: randomForest
## Warning: package 'randomForest' was built under R version 4.4.3
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
## 
## Adjuntando el paquete: 'randomForest'
## The following object is masked from 'package:psych':
## 
##     outlier
## The following object is masked from 'package:dplyr':
## 
##     combine
## The following object is masked from 'package:ggplot2':
## 
##     margin
## The following object is masked from 'package:gridExtra':
## 
##     combine
library(randomForest)
if (!require('caret')) install.packages('caret')
## Cargando paquete requerido: caret
## Warning: package 'caret' was built under R version 4.4.3
## Cargando paquete requerido: lattice
## 
## Adjuntando el paquete: 'caret'
## The following objects are masked from 'package:DescTools':
## 
##     MAE, RMSE
library(caret)
if (!require('e1071')) install.packages('e1071')
## Cargando paquete requerido: e1071
## Warning: package 'e1071' was built under R version 4.4.3
library(e1071)
if (!require('pROC')) install.packages('pROC')
## Cargando paquete requerido: pROC
## Warning: package 'pROC' was built under R version 4.4.3
## Type 'citation("pROC")' for a citation.
## 
## Adjuntando el paquete: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
library(pROC)

Next, the German Credit dataset is loaded from a CSV file and stored in the object dfcredit. This data frame will be used throughout the project for preprocessing, exploratory analysis, and predictive modeling.

dfcredit <- read.csv("C:/Users/Manuel/Desktop/credit.csv")

3 Preprocessing and Exploratory Analysis

The head(dfcredit) command displays the first rows of the data frame dfcredit. This provides a quick overview of the loaded dataset, helping to understand its structure and contents.

head(dfcredit)
##   checking_balance months_loan_duration credit_history   purpose amount
## 1           < 0 DM                    6       critical  radio/tv   1169
## 2       1 - 200 DM                   48         repaid  radio/tv   5951
## 3          unknown                   12       critical education   2096
## 4           < 0 DM                   42         repaid furniture   7882
## 5           < 0 DM                   24        delayed car (new)   4870
## 6          unknown                   36         repaid education   9055
##   savings_balance employment_length installment_rate personal_status
## 1         unknown           > 7 yrs                4     single male
## 2        < 100 DM         1 - 4 yrs                2          female
## 3        < 100 DM         4 - 7 yrs                2     single male
## 4        < 100 DM         4 - 7 yrs                2     single male
## 5        < 100 DM         1 - 4 yrs                3     single male
## 6         unknown         1 - 4 yrs                2     single male
##   other_debtors residence_history                 property age installment_plan
## 1          none                 4              real estate  67             none
## 2          none                 2              real estate  22             none
## 3          none                 3              real estate  49             none
## 4     guarantor                 4 building society savings  45             none
## 5          none                 4             unknown/none  53             none
## 6          none                 4             unknown/none  35             none
##    housing existing_credits default dependents telephone foreign_worker
## 1      own                2       1          1       yes            yes
## 2      own                1       2          1      none            yes
## 3      own                1       1          2      none            yes
## 4 for free                1       1          2      none            yes
## 5 for free                2       2          2      none            yes
## 6 for free                1       1          2       yes            yes
##                  job
## 1   skilled employee
## 2   skilled employee
## 3 unskilled resident
## 4   skilled employee
## 5   skilled employee
## 6 unskilled resident

The str(dfcredit) command shows the internal structure of the data frame dfcredit. It provides details such as the type of object, the number of observations and variables, data types, and sample values for each column.

str(dfcredit)
## 'data.frame':    1000 obs. of  21 variables:
##  $ checking_balance    : chr  "< 0 DM" "1 - 200 DM" "unknown" "< 0 DM" ...
##  $ months_loan_duration: int  6 48 12 42 24 36 24 36 12 30 ...
##  $ credit_history      : chr  "critical" "repaid" "critical" "repaid" ...
##  $ purpose             : chr  "radio/tv" "radio/tv" "education" "furniture" ...
##  $ amount              : int  1169 5951 2096 7882 4870 9055 2835 6948 3059 5234 ...
##  $ savings_balance     : chr  "unknown" "< 100 DM" "< 100 DM" "< 100 DM" ...
##  $ employment_length   : chr  "> 7 yrs" "1 - 4 yrs" "4 - 7 yrs" "4 - 7 yrs" ...
##  $ installment_rate    : int  4 2 2 2 3 2 3 2 2 4 ...
##  $ personal_status     : chr  "single male" "female" "single male" "single male" ...
##  $ other_debtors       : chr  "none" "none" "none" "guarantor" ...
##  $ residence_history   : int  4 2 3 4 4 4 4 2 4 2 ...
##  $ property            : chr  "real estate" "real estate" "real estate" "building society savings" ...
##  $ age                 : int  67 22 49 45 53 35 53 35 61 28 ...
##  $ installment_plan    : chr  "none" "none" "none" "none" ...
##  $ housing             : chr  "own" "own" "own" "for free" ...
##  $ existing_credits    : int  2 1 1 1 2 1 1 1 1 2 ...
##  $ default             : int  1 2 1 1 2 1 1 1 1 2 ...
##  $ dependents          : int  1 1 2 2 2 2 1 1 1 1 ...
##  $ telephone           : chr  "yes" "none" "none" "none" ...
##  $ foreign_worker      : chr  "yes" "yes" "yes" "yes" ...
##  $ job                 : chr  "skilled employee" "skilled employee" "unskilled resident" "skilled employee" ...

Next, we provide a short description of the variables:

The summary(dfcredit) command generates a statistical summary of the dataset, including minimum, maximum, median, quartiles, and frequency counts for categorical variables. This helps identify unusual values and provides insight into the distribution of the data.

summary(dfcredit)
##  checking_balance   months_loan_duration credit_history       purpose         
##  Length:1000        Min.   : 4.0         Length:1000        Length:1000       
##  Class :character   1st Qu.:12.0         Class :character   Class :character  
##  Mode  :character   Median :18.0         Mode  :character   Mode  :character  
##                     Mean   :20.9                                              
##                     3rd Qu.:24.0                                              
##                     Max.   :72.0                                              
##      amount      savings_balance    employment_length  installment_rate
##  Min.   :  250   Length:1000        Length:1000        Min.   :1.000   
##  1st Qu.: 1366   Class :character   Class :character   1st Qu.:2.000   
##  Median : 2320   Mode  :character   Mode  :character   Median :3.000   
##  Mean   : 3271                                         Mean   :2.973   
##  3rd Qu.: 3972                                         3rd Qu.:4.000   
##  Max.   :18424                                         Max.   :4.000   
##  personal_status    other_debtors      residence_history   property        
##  Length:1000        Length:1000        Min.   :1.000     Length:1000       
##  Class :character   Class :character   1st Qu.:2.000     Class :character  
##  Mode  :character   Mode  :character   Median :3.000     Mode  :character  
##                                        Mean   :2.845                       
##                                        3rd Qu.:4.000                       
##                                        Max.   :4.000                       
##       age        installment_plan     housing          existing_credits
##  Min.   :19.00   Length:1000        Length:1000        Min.   :1.000   
##  1st Qu.:27.00   Class :character   Class :character   1st Qu.:1.000   
##  Median :33.00   Mode  :character   Mode  :character   Median :1.000   
##  Mean   :35.55                                         Mean   :1.407   
##  3rd Qu.:42.00                                         3rd Qu.:2.000   
##  Max.   :75.00                                         Max.   :4.000   
##     default      dependents     telephone         foreign_worker    
##  Min.   :1.0   Min.   :1.000   Length:1000        Length:1000       
##  1st Qu.:1.0   1st Qu.:1.000   Class :character   Class :character  
##  Median :1.0   Median :1.000   Mode  :character   Mode  :character  
##  Mean   :1.3   Mean   :1.155                                        
##  3rd Qu.:2.0   3rd Qu.:1.000                                        
##  Max.   :2.0   Max.   :2.000                                        
##      job           
##  Length:1000       
##  Class :character  
##  Mode  :character  
##                    
##                    
## 

The following commands provide further insights:

lapply(dfcredit, unique): Applies the unique function to each column of the data frame, returning a list of unique values for every variable.

sapply(dfcredit, function(x) length(unique(x))): Calculates the number of unique values in each column, returning a vector of counts.

sapply(dfcredit, class): Returns the data type (class) of each column, providing an overview of how variables are stored.

lapply(dfcredit, unique)
## $checking_balance
## [1] "< 0 DM"     "1 - 200 DM" "unknown"    "> 200 DM"  
## 
## $months_loan_duration
##  [1]  6 48 12 42 24 36 30 15  9 10  7 60 18 45 11 27  8 54 20 14 33 21 16  4 47
## [26] 13 22 39 28  5 26 72 40
## 
## $credit_history
## [1] "critical"               "repaid"                 "delayed"               
## [4] "fully repaid"           "fully repaid this bank"
## 
## $purpose
##  [1] "radio/tv"            "education"           "furniture"          
##  [4] "car (new)"           "car (used)"          "business"           
##  [7] "domestic appliances" "repairs"             "others"             
## [10] "retraining"         
## 
## $amount
##   [1]  1169  5951  2096  7882  4870  9055  2835  6948  3059  5234  1295  4308
##  [13]  1567  1199  1403  1282  2424  8072 12579  3430  2134  2647  2241  1804
##  [25]  2069  1374   426   409  2415  6836  1913  4020  5866  1264  1474  4746
##  [37]  6110  2100  1225   458  2333  1158  6204  6187  6143  1393  2299  1352
##  [49]  7228  2073  5965  1262  3378  2225   783  6468  9566  1961  6229  1391
##  [61]  1537  1953 14421  3181  5190  2171  1007  1819  2394  8133   730  1164
##  [73]  5954  1977  1526  3965  4771  9436  3832  5943  1213  1568  1755  2315
##  [85]  1412 12612  2249  1108   618  1409   797  3617  1318 15945  2012  2622
##  [97]  2337  7057  1469  2323   932  1919  2445 11938  6458  6078  7721  1410
## [109]  1449   392  6260  7855  1680  3578  7174  2132  4281  2366  1835  3868
## [121]  1768   781  1924  2121   701   639  1860  3499  8487  6887  2708  1984
## [133] 10144  1240  8613   766  2728  1881   709  4795  3416  2462  2288  3566
## [145]   860   682  5371  1582  1346  5848  7758  6967  1288   339  3512  1898
## [157]  2872  1055  7308   909  2978  1131  1577  3972  1935   950   763  2064
## [169]  1414  3414  7485  2577   338  1963   571  9572  4455  1647  3777   884
## [181]  1360  5129  1175   674  3244  4591  3844  3915  2108  3031  1501  1382
## [193]   951  2760  4297   936  1168  5117   902  1495 10623  1424  6568  1413
## [205]  3074  3835  5293  1908  3342  3104  3913  3021  1364   625  1200   707
## [217]  4657  2613 10961  7865  1478  3149  4210  2507  2141   866  1544  1823
## [229] 14555  2767  1291  2522   915  1595  4605  1185  3447  1258   717  1204
## [241]  1925   433   666  2251  2150  4151  2030  7418  2684  2149  3812  1154
## [253]  1657  1603  5302  2748  1231   802  6304  1533  8978   999  2662  1402
## [265] 12169  3060 11998  2697  2404  4611  1901  3368  1574  1445  1520  3878
## [277] 10722  4788  7582  1092  1024  1076  9398  6419  4796  7629  9960  4675
## [289]  1287  2515  2745   672  3804  1344  1038 10127  1543  4811   727  1237
## [301]   276  5381  5511  3749   685  1494  2746   708  4351  3643  4249  1938
## [313]  2910  2659  1028  3398  5801  1525  4473  1068  6615  1864  7408 11590
## [325]  4110  3384  2101  1275  4169  1521  5743  3599  3213  4439  3949  1459
## [337]   882  3758  1743  1136  1236   959  3229  6199  1246  2331  4463   776
## [349]  2406  1239  3399  2247  1766  2473  1542  3850  3650  3446  3001  3079
## [361]  6070  2146 13756 14782  7685  2320   846 14318   362  2212 12976  1283
## [373]  1330  4272  2238  1126  7374  2326  1820   983  3249  1957 11760  2578
## [385]  2348  1223  1516  1473  1887  8648  2899  2039  2197  1053  3235   939
## [397]  1967  7253  2292  1597  1381  5842  2579  8471  2782  1042  3186  2028
## [409]   958  1591  2762  2779  2743  1149  1313  1190  3448 11328  1872  2058
## [421]  2136  1484   660  3394   609  1884  1620  2629   719  5096  1244  1842
## [433]  2576  1512 11054   518  2759  2670  4817  2679  3905  3386   343  4594
## [445]  3620  1721  3017   754  1950  2924  1659  7238  2764  4679  3092   448
## [457]   654  1238  1245  3114  2569  5152  1037  3573  1201  3622   960  1163
## [469]  1209  3077  3757  1418  3518  1934  8318   368  2122  2996  9034  1585
## [481]  1301  1323  3123  5493  1216  1207  1309  2360  6850  8588   759  4686
## [493]  2687   585  2255  1361  7127  1203   700  5507  3190  7119  3488  1113
## [505]  7966  1532  1503  2302   662  2273  2631  1311  3105  2319  3612  7763
## [517]  3049  1534  2032  6350  2864  1255  1333  2022  1552   626  8858   996
## [529]  1750  6999  1995  1331  2278  5003  3552  1928  2964  1546   683 12389
## [541]  4712  1553  1372  3979  6758  3234  5433   806  1082  2788  2930  1927
## [553]  2820   937  1056  3124  1388  2384  2133  2799  1289  1217  2246   385
## [565]  1965  1572  2718  1358   931  1442  4241  2775  3863  2329   918  1837
## [577]  3349  2828  4526  2671  2051  1300   741  3357  3632  1808 12204  9157
## [589]  3676  3441   640  3652  1530  3914  1858  2600  1979  2116  1437  4042
## [601]  3660  1444  1980  1355  1376 15653  1493  4370   750  1308  4623  1851
## [613]  1880  7980  4583  1386   947   684  7476  1922  2303  8086  2346  3973
## [625]   888 10222  4221  6361  1297   900  1050  1047  6314  3496  3609  4843
## [637]  4139  5742 10366  2080  2580  4530  5150  5595  1453  1538  2279  5103
## [649]  9857  6527  1347  2862  2753  3651   975  2896  4716  2284  1103   926
## [661]  1800  1905  1123  6331  1377  2503  2528  5324  6560  2969  1206  2118
## [673]   629  1198  2476  1138 14027  7596  1505  3148  6148  1337  1228   790
## [685]  2570   250  1316  1882  6416  6403  1987   760  2603  3380  3990 11560
## [697]  4380  6761  4280  2325  1048  3160  2483 14179  1797  2511  1274  5248
## [709]  3029   428   976   841  5771  1555  1285  1299  1271   691  5045  2124
## [721]  2214 12680  2463  1155  3108  2901  1655  2812  8065  3275  2223  1480
## [733]  1371  3535  3509  5711  3872  4933  1940   836  1941  2675  2751  6224
## [745]  5998  1188  6313  1221  2892  3062  2301  7511  1549  1795  7472  9271
## [757]   590   930  9283  1778   907   484  9629  3051  3931  7432  1338  1554
## [769] 15857  1345  1101  3016  2712   731  3780  1602  3966  4165  8335  6681
## [781]  2375 11816  5084  2327   886   601  2957  2611  5179  2993  1943  1559
## [793]  3422  3976  1249  2235  1471 10875   894  3343  3959  3577  5804  2169
## [805]  2439  2210  2221  2389  3331  7409   652  7678  1343   874  3590  1322
## [817]  3595  1422  6742  7814  9277  2181  1098  4057   795  2825 15672  6614
## [829]  7824  2442  1829  5800  8947  2606  1592  2186  4153  2625  3485 10477
## [841]  1278  1107  3763  3711  3594  3195  4454  4736  2991  2142  3161 18424
## [853]  2848 14896  2359  3345  1817 12749  1366  2002  6872   697  1049 10297
## [865]  1867  1747  1670  1224   522  1498   745  2063  6288  6842  3527   929
## [877]  1455  1845  8358  2859  3621  2145  4113 10974  1893  3656  4006  3069
## [889]  1740  2353  3556  2397   454  1715  2520  3568  7166  3939  1514  7393
## [901]  1193  7297  2831   753  2427  2538  8386  4844  2923  8229  1433  6289
## [913]  6579  3565  1569  1936  2390  1736  3857   804  4576
## 
## $savings_balance
## [1] "unknown"       "< 100 DM"      "501 - 1000 DM" "> 1000 DM"    
## [5] "101 - 500 DM" 
## 
## $employment_length
## [1] "> 7 yrs"    "1 - 4 yrs"  "4 - 7 yrs"  "unemployed" "0 - 1 yrs" 
## 
## $installment_rate
## [1] 4 2 3 1
## 
## $personal_status
## [1] "single male"   "female"        "divorced male" "married male" 
## 
## $other_debtors
## [1] "none"         "guarantor"    "co-applicant"
## 
## $residence_history
## [1] 4 2 3 1
## 
## $property
## [1] "real estate"              "building society savings"
## [3] "unknown/none"             "other"                   
## 
## $age
##  [1] 67 22 49 45 53 35 61 28 25 24 60 32 44 31 48 26 36 39 42 34 63 27 30 57 33
## [26] 37 58 23 29 52 50 46 51 41 40 66 47 56 54 20 21 38 70 65 74 68 43 55 64 75
## [51] 19 62 59
## 
## $installment_plan
## [1] "none"   "bank"   "stores"
## 
## $housing
## [1] "own"      "for free" "rent"    
## 
## $existing_credits
## [1] 2 1 3 4
## 
## $default
## [1] 1 2
## 
## $dependents
## [1] 1 2
## 
## $telephone
## [1] "yes"  "none"
## 
## $foreign_worker
## [1] "yes" "no" 
## 
## $job
## [1] "skilled employee"        "unskilled resident"     
## [3] "mangement self-employed" "unemployed non-resident"
sapply(dfcredit, function(x) length(unique(x)))
##     checking_balance months_loan_duration       credit_history 
##                    4                   33                    5 
##              purpose               amount      savings_balance 
##                   10                  921                    5 
##    employment_length     installment_rate      personal_status 
##                    5                    4                    4 
##        other_debtors    residence_history             property 
##                    3                    4                    4 
##                  age     installment_plan              housing 
##                   53                    3                    3 
##     existing_credits              default           dependents 
##                    4                    2                    2 
##            telephone       foreign_worker                  job 
##                    2                    2                    4
sapply(dfcredit, class)
##     checking_balance months_loan_duration       credit_history 
##          "character"            "integer"          "character" 
##              purpose               amount      savings_balance 
##          "character"            "integer"          "character" 
##    employment_length     installment_rate      personal_status 
##          "character"            "integer"          "character" 
##        other_debtors    residence_history             property 
##          "character"            "integer"          "character" 
##                  age     installment_plan              housing 
##            "integer"          "character"          "character" 
##     existing_credits              default           dependents 
##            "integer"            "integer"            "integer" 
##            telephone       foreign_worker                  job 
##          "character"          "character"          "character"

As observed, the results from these functions are not very informative in their current form, since many columns are not yet properly categorized. Therefore, it is both logical and practical to transform the categorical variables into factors. This facilitates working with categorical data, optimizes statistical analysis, and allows the use of specialized visualization tools.

Reference Nº1

The following transformation converts all character-type columns in dfcredit into factors:

dfcredit <- dfcredit %>%
  mutate(across(where(is.character), factor))
str(dfcredit)
## 'data.frame':    1000 obs. of  21 variables:
##  $ checking_balance    : Factor w/ 4 levels "< 0 DM","> 200 DM",..: 1 3 4 1 1 4 4 3 4 3 ...
##  $ months_loan_duration: int  6 48 12 42 24 36 24 36 12 30 ...
##  $ credit_history      : Factor w/ 5 levels "critical","delayed",..: 1 5 1 5 2 5 5 5 5 1 ...
##  $ purpose             : Factor w/ 10 levels "business","car (new)",..: 8 8 5 6 2 5 6 3 8 2 ...
##  $ amount              : int  1169 5951 2096 7882 4870 9055 2835 6948 3059 5234 ...
##  $ savings_balance     : Factor w/ 5 levels "< 100 DM","> 1000 DM",..: 5 1 1 1 1 5 4 1 2 1 ...
##  $ employment_length   : Factor w/ 5 levels "> 7 yrs","0 - 1 yrs",..: 1 3 4 4 3 3 1 3 4 5 ...
##  $ installment_rate    : int  4 2 2 2 3 2 3 2 2 4 ...
##  $ personal_status     : Factor w/ 4 levels "divorced male",..: 4 2 4 4 4 4 4 4 1 3 ...
##  $ other_debtors       : Factor w/ 3 levels "co-applicant",..: 3 3 3 2 3 3 3 3 3 3 ...
##  $ residence_history   : int  4 2 3 4 4 4 4 2 4 2 ...
##  $ property            : Factor w/ 4 levels "building society savings",..: 3 3 3 1 4 4 1 2 3 2 ...
##  $ age                 : int  67 22 49 45 53 35 53 35 61 28 ...
##  $ installment_plan    : Factor w/ 3 levels "bank","none",..: 2 2 2 2 2 2 2 2 2 2 ...
##  $ housing             : Factor w/ 3 levels "for free","own",..: 2 2 2 1 1 1 2 3 2 2 ...
##  $ existing_credits    : int  2 1 1 1 2 1 1 1 1 2 ...
##  $ default             : int  1 2 1 1 2 1 1 1 1 2 ...
##  $ dependents          : int  1 1 2 2 2 2 1 1 1 1 ...
##  $ telephone           : Factor w/ 2 levels "none","yes": 2 1 1 1 1 2 1 2 1 1 ...
##  $ foreign_worker      : Factor w/ 2 levels "no","yes": 2 2 2 2 2 2 2 2 2 2 ...
##  $ job                 : Factor w/ 4 levels "mangement self-employed",..: 2 2 4 2 2 4 2 1 4 1 ...

This transformation ensures that categorical variables are correctly encoded, preparing the dataset for subsequent modeling and analysis.

summary(dfcredit)
##    checking_balance months_loan_duration                credit_history
##  < 0 DM    :274     Min.   : 4.0         critical              :293   
##  > 200 DM  : 63     1st Qu.:12.0         delayed               : 88   
##  1 - 200 DM:269     Median :18.0         fully repaid          : 40   
##  unknown   :394     Mean   :20.9         fully repaid this bank: 49   
##                     3rd Qu.:24.0         repaid                :530   
##                     Max.   :72.0                                      
##                                                                       
##        purpose        amount           savings_balance  employment_length
##  radio/tv  :280   Min.   :  250   < 100 DM     :603    > 7 yrs   :253    
##  car (new) :234   1st Qu.: 1366   > 1000 DM    : 48    0 - 1 yrs :172    
##  furniture :181   Median : 2320   101 - 500 DM :103    1 - 4 yrs :339    
##  car (used):103   Mean   : 3271   501 - 1000 DM: 63    4 - 7 yrs :174    
##  business  : 97   3rd Qu.: 3972   unknown      :183    unemployed: 62    
##  education : 50   Max.   :18424                                          
##  (Other)   : 55                                                          
##  installment_rate      personal_status      other_debtors residence_history
##  Min.   :1.000    divorced male: 50    co-applicant: 41   Min.   :1.000    
##  1st Qu.:2.000    female       :310    guarantor   : 52   1st Qu.:2.000    
##  Median :3.000    married male : 92    none        :907   Median :3.000    
##  Mean   :2.973    single male  :548                       Mean   :2.845    
##  3rd Qu.:4.000                                            3rd Qu.:4.000    
##  Max.   :4.000                                            Max.   :4.000    
##                                                                            
##                      property        age        installment_plan     housing   
##  building society savings:232   Min.   :19.00   bank  :139       for free:108  
##  other                   :332   1st Qu.:27.00   none  :814       own     :713  
##  real estate             :282   Median :33.00   stores: 47       rent    :179  
##  unknown/none            :154   Mean   :35.55                                  
##                                 3rd Qu.:42.00                                  
##                                 Max.   :75.00                                  
##                                                                                
##  existing_credits    default      dependents    telephone  foreign_worker
##  Min.   :1.000    Min.   :1.0   Min.   :1.000   none:596   no : 37       
##  1st Qu.:1.000    1st Qu.:1.0   1st Qu.:1.000   yes :404   yes:963       
##  Median :1.000    Median :1.0   Median :1.000                            
##  Mean   :1.407    Mean   :1.3   Mean   :1.155                            
##  3rd Qu.:2.000    3rd Qu.:2.0   3rd Qu.:1.000                            
##  Max.   :4.000    Max.   :2.0   Max.   :2.000                            
##                                                                          
##                       job     
##  mangement self-employed:148  
##  skilled employee       :630  
##  unemployed non-resident: 22  
##  unskilled resident     :200  
##                               
##                               
## 

Low balances in accounts (checking_balance and savings_balance)
Most customers have less than 100 DM in savings (603 cases) or unknown balances (183).
For current accounts, 274 customers are negative (< 0 DM), while 394 have missing or unknown values.

Moderate loan amounts
The average loan amount is 3,271 DM, with a median of 2,320 and values ranging from 250 to 18,424.
While small to moderate loans dominate, there are large loans exceeding 10,000 DM.

Loan purposes focused on consumer goods
The most common purposes are radio/TV (280), car (new) (234), and furniture (181), showing high demand for basic consumption.
Less frequent categories include business (97) and education (50), indicating lower use for productive activities.

Credit history with frequent problems
293 customers have a critical credit history, representing a significant proportion of credit risk.
Only 40 customers have fully repaid loans, suggesting general repayment difficulties.

Target variable (default) and prediction of non-compliance
The variable default takes values 1 and 2, corresponding to two classes: compliant (1) and non-compliant (2).
The analysis is oriented toward predicting the probability of default to support credit decisions.

Average customer age
The average age is 35.55 years, with a median of 33 and a wide range from 19 to 75 years.
Most customers fall into the young adult group, with 75% under 42 years old.

Foreign workers and lack of registered telephone
The vast majority are foreign workers (yes: 963 vs. no: 37), reflecting a highly mobile clientele.
Additionally, 596 customers do not have a registered telephone, making direct contact difficult and increasing operational risks.

To consolidate these initial observations, it is essential to confirm that the dataset has a solid structure and is valuable for analysis. A key step in ensuring data quality is verifying the absence of erroneous or inconsistent values that could bias results.

The logical first step is to check for empty values, missing values, or entries marked as NA (Not Available), as these represent gaps in information that could affect calculations and subsequent models. Detecting and properly handling these values is critical to ensuring analytical precision and avoiding errors in interpretation or prediction.

missing_values <- is.na(dfcredit) | dfcredit == "" | dfcredit == "NA"
missing_values_count <- colSums(missing_values)
print(missing_values_count)
##     checking_balance months_loan_duration       credit_history 
##                    0                    0                    0 
##              purpose               amount      savings_balance 
##                    0                    0                    0 
##    employment_length     installment_rate      personal_status 
##                    0                    0                    0 
##        other_debtors    residence_history             property 
##                    0                    0                    0 
##                  age     installment_plan              housing 
##                    0                    0                    0 
##     existing_credits              default           dependents 
##                    0                    0                    0 
##            telephone       foreign_worker                  job 
##                    0                    0                    0

This code combines multiple conditions to detect missing values. It evaluates whether data are NA, empty cells (““), or explicitly marked as”NA” in text format. Then, colSums counts how many missing values exist in each column of the data frame, providing a clear summary of the dataset’s state.

Based on this analysis, we can determine whether missing values exist that require treatment, either through imputation, removal, or another method. This step ensures that the dataset meets the quality standards required for reliable analysis and accurate results.

As seen from the execution, the dataset does not contain missing values. This means there are no NA records, empty cells, or explicit “NA” text. This is highly positive, as it guarantees that the dataset is complete and does not require further preprocessing to handle missing values.

The absence of missing values allows us to proceed with the analysis without implementing imputation techniques, row/column removal, or other strategies for handling incomplete data. This not only simplifies the workflow but also ensures that the results are not biased due to missing information.

Having a clean dataset with no missing values provides a strong foundation for statistical analysis, exploratory research, and predictive modeling, maximizing both the reliability and usefulness of the results.

Next, we proceed to analyze outliers in the dataset, focusing exclusively on numeric variables. Outliers are data points that fall significantly above or below the expected range and can strongly influence analytical results. Therefore, it is critical to identify and evaluate them carefully to decide how to handle them in the context of this study.

Recall that outliers may represent data errors, exceptional cases, or unusual behaviors, and their treatment depends on the study’s objective. In this case, the Interquartile Range (IQR) method will be used to detect extreme values in numeric columns, while categorical variables remain unaffected.

df_numeric <- dfcredit[, sapply(dfcredit, is.numeric)]
Q1 <- apply(df_numeric, 2, function(x) quantile(x, 0.25, na.rm = TRUE))
Q3 <- apply(df_numeric, 2, function(x) quantile(x, 0.75, na.rm = TRUE))
IQR <- Q3 - Q1

lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
outliers <- (df_numeric < lower_bound | df_numeric > upper_bound)

outliers_count <- colSums(outliers, na.rm = TRUE)
boxplot(df_numeric, main = "Boxplot de las columnas numéricas", las = 2)

The analysis reveals the presence of outliers in the variable amount, representing loan amounts. Some loans are significantly larger or smaller than the majority, which could influence statistical analyses and predictive models by biasing measures of central tendency, dispersion, or model predictions.

It is therefore essential to analyze these extreme values in detail. We will explore the distribution of amount, evaluate the magnitude of outliers, and determine whether they correspond to valid data (e.g., exceptional loans) or represent errors. This will guide decisions on whether to adjust, remove, or retain them depending on the study’s context.

summary(dfcredit$amount)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     250    1366    2320    3271    3972   18424
boxplot(dfcredit$amount, main = "Boxplot de 'amount'")

lower_bound_amount <- Q1["amount"] - 1.5 * IQR["amount"]
upper_bound_amount <- Q3["amount"] + 1.5 * IQR["amount"]

outliers_amount <- dfcredit[dfcredit$amount < lower_bound_amount | dfcredit$amount > upper_bound_amount, ]
print(outliers_amount)
##     checking_balance months_loan_duration         credit_history    purpose
## 6            unknown                   36                 repaid  education
## 18            < 0 DM                   30           fully repaid   business
## 19        1 - 200 DM                   24                 repaid car (used)
## 58           unknown                   36               critical   radio/tv
## 64        1 - 200 DM                   48           fully repaid   business
## 71           unknown                   36                 repaid car (used)
## 79           unknown                   54           fully repaid car (used)
## 88        1 - 200 DM                   36                 repaid  education
## 96        1 - 200 DM                   54           fully repaid   business
## 106       1 - 200 DM                   24               critical     others
## 131       1 - 200 DM                   48                 repaid  car (new)
## 135          unknown                   60                 repaid   radio/tv
## 137          unknown                   27                delayed car (used)
## 181          unknown                   36                delayed   business
## 206           < 0 DM                   30               critical car (used)
## 227       1 - 200 DM                   48                 repaid   radio/tv
## 237       1 - 200 DM                    6                 repaid  car (new)
## 269           < 0 DM                   14                 repaid  car (new)
## 273       1 - 200 DM                   48 fully repaid this bank  car (new)
## 275           < 0 DM                   30                 repaid    repairs
## 286           < 0 DM                   47                 repaid  car (new)
## 292       1 - 200 DM                   36                 repaid car (used)
## 296       1 - 200 DM                   48                 repaid  furniture
## 305          unknown                   48               critical  car (new)
## 334          unknown                   48               critical car (used)
## 374          unknown                   60               critical  car (new)
## 375       1 - 200 DM                   60 fully repaid this bank     others
## 379       1 - 200 DM                   36                 repaid  car (new)
## 382       1 - 200 DM                   18                 repaid car (used)
## 396       1 - 200 DM                   39                delayed  education
## 403          unknown                   24                delayed   business
## 418           < 0 DM                   18                delayed  education
## 432       1 - 200 DM                   24                 repaid     others
## 451          unknown                   36               critical car (used)
## 492       1 - 200 DM                   27           fully repaid   business
## 497       1 - 200 DM                   36                 repaid  furniture
## 510          unknown                   39                 repaid car (used)
## 526       1 - 200 DM                   26                 repaid car (used)
## 550          unknown                   48               critical car (used)
## 564       1 - 200 DM                   36                 repaid  car (new)
## 616       1 - 200 DM                   48           fully repaid   business
## 617       1 - 200 DM                   60                delayed   radio/tv
## 638          unknown                   60                delayed   radio/tv
## 646          unknown                   36                delayed   business
## 654       1 - 200 DM                   36                delayed  car (new)
## 658          unknown                   48                 repaid   radio/tv
## 673          unknown                   60                 repaid  car (new)
## 685       1 - 200 DM                   36                delayed   business
## 715       1 - 200 DM                   60                 repaid  car (new)
## 737       1 - 200 DM                   24                 repaid car (used)
## 745           < 0 DM                   39               critical  furniture
## 764          unknown                   21               critical  car (new)
## 772           < 0 DM                   36               critical  education
## 806           < 0 DM                   36                 repaid  car (new)
## 809       1 - 200 DM                   42 fully repaid this bank car (used)
## 813           < 0 DM                   36               critical car (used)
## 819           < 0 DM                   36                 repaid     others
## 829           < 0 DM                   36                 repaid car (used)
## 833           < 0 DM                   45           fully repaid   business
## 855          unknown                   36                delayed  car (new)
## 882          unknown                   24                 repaid car (used)
## 888       1 - 200 DM                   48                 repaid   business
## 896          unknown                   36                delayed car (used)
## 903          unknown                   36               critical car (used)
## 916       1 - 200 DM                   48           fully repaid     others
## 918           < 0 DM                    6                 repaid  car (new)
## 922          unknown                   48                delayed   radio/tv
## 928           < 0 DM                   48                 repaid car (used)
## 946       1 - 200 DM                   48           fully repaid  car (new)
## 954          unknown                   36                 repaid  furniture
## 981       1 - 200 DM                   30               critical  furniture
## 984           < 0 DM                   36                 repaid car (used)
##     amount savings_balance employment_length installment_rate personal_status
## 6     9055         unknown         1 - 4 yrs                2     single male
## 18    8072         unknown         0 - 1 yrs                2     single male
## 19   12579        < 100 DM           > 7 yrs                4          female
## 58    9566        < 100 DM         1 - 4 yrs                2          female
## 64   14421        < 100 DM         1 - 4 yrs                2     single male
## 71    8133        < 100 DM         1 - 4 yrs                1          female
## 79    9436         unknown         1 - 4 yrs                2     single male
## 88   12612    101 - 500 DM         1 - 4 yrs                1     single male
## 96   15945        < 100 DM         0 - 1 yrs                3     single male
## 106  11938        < 100 DM         1 - 4 yrs                2     single male
## 131   8487         unknown         4 - 7 yrs                1          female
## 135  10144    101 - 500 DM         4 - 7 yrs                2          female
## 137   8613       > 1000 DM         1 - 4 yrs                2     single male
## 181   9572        < 100 DM         0 - 1 yrs                1   divorced male
## 206  10623        < 100 DM           > 7 yrs                3     single male
## 227  10961       > 1000 DM         4 - 7 yrs                1     single male
## 237  14555         unknown        unemployed                1     single male
## 269   8978        < 100 DM           > 7 yrs                1   divorced male
## 273  12169         unknown        unemployed                4     single male
## 275  11998        < 100 DM         0 - 1 yrs                1   divorced male
## 286  10722        < 100 DM         0 - 1 yrs                1          female
## 292   9398        < 100 DM         0 - 1 yrs                1    married male
## 296   9960        < 100 DM         0 - 1 yrs                1          female
## 305  10127   501 - 1000 DM         1 - 4 yrs                2     single male
## 334  11590    101 - 500 DM         1 - 4 yrs                2          female
## 374  13756         unknown           > 7 yrs                2     single male
## 375  14782    101 - 500 DM           > 7 yrs                3          female
## 379  14318        < 100 DM           > 7 yrs                4     single male
## 382  12976        < 100 DM        unemployed                3          female
## 396  11760    101 - 500 DM         4 - 7 yrs                2     single male
## 403   8648        < 100 DM         0 - 1 yrs                2     single male
## 418   8471         unknown         1 - 4 yrs                1          female
## 432  11328        < 100 DM         1 - 4 yrs                2     single male
## 451  11054         unknown         1 - 4 yrs                4     single male
## 492   8318        < 100 DM           > 7 yrs                2          female
## 497   9034    101 - 500 DM         0 - 1 yrs                4     single male
## 510   8588    101 - 500 DM           > 7 yrs                4     single male
## 526   7966        < 100 DM         0 - 1 yrs                2     single male
## 550   8858         unknown         4 - 7 yrs                2     single male
## 564  12389         unknown         1 - 4 yrs                1     single male
## 616  12204         unknown         1 - 4 yrs                2     single male
## 617   9157         unknown         1 - 4 yrs                2     single male
## 638  15653        < 100 DM         4 - 7 yrs                2     single male
## 646   7980         unknown         0 - 1 yrs                4     single male
## 654   8086    101 - 500 DM           > 7 yrs                2     single male
## 658  10222         unknown         4 - 7 yrs                4     single male
## 673  10366        < 100 DM           > 7 yrs                2     single male
## 685   9857    101 - 500 DM         4 - 7 yrs                1     single male
## 715  14027        < 100 DM         4 - 7 yrs                4     single male
## 737  11560        < 100 DM         1 - 4 yrs                1          female
## 745  14179         unknown         4 - 7 yrs                4     single male
## 764  12680         unknown           > 7 yrs                4     single male
## 772   8065        < 100 DM         1 - 4 yrs                3          female
## 806   9271        < 100 DM         4 - 7 yrs                2     single male
## 809   9283        < 100 DM        unemployed                1     single male
## 813   9629        < 100 DM         4 - 7 yrs                4     single male
## 819  15857        < 100 DM        unemployed                2   divorced male
## 829   8335         unknown           > 7 yrs                3     single male
## 833  11816        < 100 DM           > 7 yrs                2     single male
## 855  10875        < 100 DM           > 7 yrs                2     single male
## 882   9277         unknown         1 - 4 yrs                2   divorced male
## 888  15672        < 100 DM         1 - 4 yrs                2     single male
## 896   8947         unknown         4 - 7 yrs                3     single male
## 903  10477         unknown           > 7 yrs                2     single male
## 916  18424        < 100 DM         1 - 4 yrs                1          female
## 918  14896        < 100 DM           > 7 yrs                1     single male
## 922  12749   501 - 1000 DM         4 - 7 yrs                4     single male
## 928  10297        < 100 DM         4 - 7 yrs                4     single male
## 946   8358   501 - 1000 DM         0 - 1 yrs                1          female
## 954  10974        < 100 DM        unemployed                4          female
## 981   8386        < 100 DM         4 - 7 yrs                2     single male
## 984   8229        < 100 DM         1 - 4 yrs                2     single male
##     other_debtors residence_history                 property age
## 6            none                 4             unknown/none  35
## 18           none                 3                    other  25
## 19           none                 2             unknown/none  44
## 58           none                 2                    other  31
## 64           none                 2                    other  25
## 71           none                 2 building society savings  30
## 79           none                 2 building society savings  39
## 88           none                 4             unknown/none  47
## 96           none                 4             unknown/none  58
## 106  co-applicant                 3                    other  39
## 131          none                 2                    other  24
## 135          none                 4              real estate  21
## 137          none                 2                    other  27
## 181          none                 1                    other  28
## 206          none                 4             unknown/none  38
## 227  co-applicant                 2             unknown/none  27
## 237          none                 2 building society savings  23
## 269          none                 4 building society savings  45
## 273  co-applicant                 4             unknown/none  36
## 275          none                 1             unknown/none  34
## 286          none                 1              real estate  35
## 292          none                 4                    other  28
## 296          none                 2                    other  26
## 305          none                 2             unknown/none  44
## 334          none                 4                    other  24
## 374          none                 4             unknown/none  63
## 375          none                 4             unknown/none  60
## 379          none                 2             unknown/none  57
## 382          none                 4             unknown/none  38
## 396          none                 3             unknown/none  32
## 403          none                 2                    other  27
## 418          none                 2                    other  23
## 432  co-applicant                 3                    other  29
## 451          none                 2                    other  30
## 492          none                 4             unknown/none  42
## 497  co-applicant                 1             unknown/none  29
## 510          none                 2                    other  45
## 526          none                 3                    other  30
## 550          none                 1             unknown/none  35
## 564          none                 4             unknown/none  37
## 616          none                 2                    other  48
## 617          none                 2             unknown/none  27
## 638          none                 4                    other  21
## 646          none                 4                    other  27
## 654          none                 4                    other  42
## 658          none                 3                    other  37
## 673          none                 4 building society savings  42
## 685          none                 3 building society savings  31
## 715          none                 2             unknown/none  27
## 737          none                 4                    other  23
## 745          none                 4 building society savings  30
## 764          none                 4             unknown/none  30
## 772          none                 2             unknown/none  25
## 806          none                 1                    other  24
## 809          none                 2             unknown/none  55
## 813          none                 4                    other  24
## 819  co-applicant                 3                    other  43
## 829          none                 4             unknown/none  47
## 833          none                 4                    other  29
## 855          none                 2                    other  45
## 882          none                 4             unknown/none  48
## 888          none                 2                    other  23
## 896          none                 2                    other  31
## 903          none                 4             unknown/none  42
## 916          none                 2 building society savings  32
## 918          none                 4             unknown/none  68
## 922          none                 1                    other  37
## 928          none                 4             unknown/none  39
## 946          none                 1                    other  30
## 954          none                 2                    other  26
## 981          none                 2 building society savings  49
## 984          none                 2 building society savings  26
##     installment_plan  housing existing_credits default dependents telephone
## 6               none for free                1       1          2       yes
## 18              bank      own                3       1          1      none
## 19              none for free                1       2          1       yes
## 58            stores      own                2       1          1      none
## 64              none      own                1       2          1       yes
## 71              bank      own                1       1          1      none
## 79              none      own                1       1          2      none
## 88              none for free                1       2          2       yes
## 96              none     rent                1       2          1       yes
## 106             none      own                2       2          2       yes
## 131             none      own                1       1          1      none
## 135             none      own                1       1          1       yes
## 137             none      own                2       1          1      none
## 181             none      own                2       2          1      none
## 206             none for free                3       1          2       yes
## 227             bank      own                2       2          1       yes
## 237             none      own                1       2          1       yes
## 269             none      own                1       2          1       yes
## 273             none for free                1       1          1       yes
## 275             none      own                1       2          1       yes
## 286             none      own                1       1          1       yes
## 292             none     rent                1       2          1       yes
## 296             none      own                1       2          1       yes
## 305             bank for free                1       2          1      none
## 334             bank     rent                2       2          1      none
## 374             bank for free                1       1          1       yes
## 375             bank for free                2       2          1       yes
## 379             none for free                1       2          1       yes
## 382             none for free                1       2          1       yes
## 396             none     rent                1       1          1       yes
## 403             bank      own                2       2          1       yes
## 418             none     rent                2       1          1       yes
## 432             bank      own                2       2          1       yes
## 451             none      own                1       1          1       yes
## 492             none for free                2       2          1       yes
## 497             none     rent                1       2          1       yes
## 510             none      own                1       1          1       yes
## 526             none      own                2       1          1      none
## 550             none for free                2       1          1       yes
## 564             none for free                1       2          1       yes
## 616             bank      own                1       1          1       yes
## 617             none for free                1       1          1      none
## 638             none      own                2       1          1       yes
## 646             none     rent                2       2          1       yes
## 654             none      own                4       2          1       yes
## 658           stores      own                1       1          1       yes
## 673             none      own                1       1          1       yes
## 685             none      own                2       1          2       yes
## 715             none      own                1       2          1       yes
## 737             none     rent                2       2          1      none
## 745             none      own                2       1          1       yes
## 764             none for free                1       2          1       yes
## 772             none      own                2       2          1       yes
## 806             none      own                1       2          1       yes
## 809             bank for free                1       1          1       yes
## 813             none      own                2       2          1       yes
## 819             none      own                1       1          1      none
## 829             none for free                1       2          1      none
## 833             none     rent                2       2          1      none
## 855             none      own                2       1          2       yes
## 882             none for free                1       1          1       yes
## 888             none      own                1       2          1       yes
## 896           stores      own                1       1          2       yes
## 903             none for free                2       1          1      none
## 916             bank      own                1       2          1       yes
## 918             bank      own                1       2          1       yes
## 922             none      own                1       1          1       yes
## 928           stores for free                3       2          2       yes
## 946             none      own                2       1          1      none
## 954             none      own                2       2          1       yes
## 981             none      own                1       2          1      none
## 984             none      own                1       2          2      none
##     foreign_worker                     job
## 6              yes      unskilled resident
## 18             yes        skilled employee
## 19             yes mangement self-employed
## 58             yes        skilled employee
## 64             yes        skilled employee
## 71             yes        skilled employee
## 79             yes      unskilled resident
## 88             yes        skilled employee
## 96             yes        skilled employee
## 106            yes mangement self-employed
## 131            yes        skilled employee
## 135            yes        skilled employee
## 137            yes        skilled employee
## 181            yes        skilled employee
## 206            yes mangement self-employed
## 227            yes        skilled employee
## 237            yes unemployed non-resident
## 269             no mangement self-employed
## 273            yes mangement self-employed
## 275            yes      unskilled resident
## 286            yes      unskilled resident
## 292            yes mangement self-employed
## 296            yes        skilled employee
## 305            yes        skilled employee
## 334            yes      unskilled resident
## 374            yes mangement self-employed
## 375            yes mangement self-employed
## 379            yes mangement self-employed
## 382            yes mangement self-employed
## 396            yes        skilled employee
## 403            yes        skilled employee
## 418            yes        skilled employee
## 432            yes mangement self-employed
## 451            yes mangement self-employed
## 492            yes mangement self-employed
## 497            yes mangement self-employed
## 510            yes mangement self-employed
## 526            yes        skilled employee
## 550            yes        skilled employee
## 564            yes        skilled employee
## 616            yes mangement self-employed
## 617            yes mangement self-employed
## 638            yes        skilled employee
## 646            yes        skilled employee
## 654            yes mangement self-employed
## 658            yes        skilled employee
## 673            yes mangement self-employed
## 685            yes      unskilled resident
## 715            yes mangement self-employed
## 737            yes mangement self-employed
## 745            yes mangement self-employed
## 764            yes mangement self-employed
## 772            yes mangement self-employed
## 806            yes        skilled employee
## 809            yes mangement self-employed
## 813            yes        skilled employee
## 819            yes mangement self-employed
## 829            yes        skilled employee
## 833            yes        skilled employee
## 855            yes        skilled employee
## 882            yes        skilled employee
## 888            yes        skilled employee
## 896            yes mangement self-employed
## 903            yes        skilled employee
## 916             no mangement self-employed
## 918            yes mangement self-employed
## 922            yes mangement self-employed
## 928            yes        skilled employee
## 946            yes        skilled employee
## 954            yes mangement self-employed
## 981            yes        skilled employee
## 984            yes        skilled employee

In other datasets, a high number of outliers in amount could indicate inconsistencies, errors, or exceptional cases. However, in this specific context, the amount variable reflects loan amounts that can legitimately vary depending on multiple factors, such as bank policy, customer solvency, and loan purpose (e.g., car purchase, business, education).

Such variability does not necessarily imply incorrect or anomalous values. On the contrary, differences in loan amounts may simply reflect the diversity of banking decisions and customer needs. Without explicit information on maximum allowable amounts or internal bank rules, it is not possible to determine whether extreme values are true outliers or valid cases.

Therefore, all values in amount, including large loans, should be considered plausible. Large amounts may correspond to corporate clients or high-value projects. It would be inappropriate to automatically treat them as erroneous outliers without additional context.

Finally, we analyze the frequency distribution of categorical variables to better understand their composition and detect potential imbalances.

For categorical variables, frequency counts highlight dominant categories and reveal whether certain categories are underrepresented or overly dominant. This is especially relevant for the target variable default, where class imbalance could affect predictive performance.

For numeric variables, frequencies can be explored through binning to observe distribution ranges, concentration of values, and potential extremes.

This step provides a global view of the dataset and informs decisions on cleaning, transformation, or segmentation, ensuring efficient processing and accurate predictive results.

categorical_vars_names <- names(dfcredit)[sapply(dfcredit, is.factor)]
par(mfrow = c(3, 3), mar = c(5, 5, 3, 2)) 

for (var in categorical_vars_names) {
  freq <- table(dfcredit[[var]])
  barplot(freq, 
          main = paste("Frecuencia de", var), 
          col = "blue", 
          las = 1,        
          cex.names = 0.7,  
          horiz = TRUE     
  )
}

par(mfrow = c(1, 1))

Before moving to numerical correlation analysis, we begin by exploring the relationships among variables visually. This approach allows us to intuitively identify patterns, trends, and potential associations among numeric variables that may not be immediately obvious.

df_numeric <- dfcredit[, sapply(dfcredit, is.numeric)]
correlation_matrix <- cor(df_numeric, use = "complete.obs")
print(correlation_matrix)
##                      months_loan_duration      amount installment_rate
## months_loan_duration           1.00000000  0.62498420       0.07474882
## amount                         0.62498420  1.00000000      -0.27131570
## installment_rate               0.07474882 -0.27131570       1.00000000
## residence_history              0.03406720  0.02892632       0.04930237
## age                           -0.03613637  0.03271642       0.05826568
## existing_credits              -0.01128360  0.02079455       0.02166874
## default                        0.21492667  0.15473864       0.07240394
## dependents                    -0.02383448  0.01714215      -0.07120694
##                      residence_history         age existing_credits
## months_loan_duration       0.034067202 -0.03613637      -0.01128360
## amount                     0.028926323  0.03271642       0.02079455
## installment_rate           0.049302371  0.05826568       0.02166874
## residence_history          1.000000000  0.26641918       0.08962523
## age                        0.266419184  1.00000000       0.14925358
## existing_credits           0.089625233  0.14925358       1.00000000
## default                    0.002967159 -0.09112741      -0.04573249
## dependents                 0.042643426  0.11820083       0.10966670
##                           default   dependents
## months_loan_duration  0.214926665 -0.023834475
## amount                0.154738641  0.017142154
## installment_rate      0.072403937 -0.071206943
## residence_history     0.002967159  0.042643426
## age                  -0.091127409  0.118200833
## existing_credits     -0.045732489  0.109666700
## default               1.000000000 -0.003014853
## dependents           -0.003014853  1.000000000
corrplot(correlation_matrix, method = "circle", type = "upper", order = "hclust", 
         tl.col = "black", tl.srt = 45, addrect = 2)

We also compute an alternative correlation plot with numerical values for confirmation:

correlation_matrix <- cor(df_numeric, use = "complete.obs")
corrplot(correlation_matrix, 
         method = "number",  
         type = "upper",     
         order = "hclust",   
         tl.col = "black",   
         tl.srt = 45,        
         addrect = 2,        
         col = colorRampPalette(c("blue", "white", "red"))(200))  

We now focus on analyzing the default variable, which indicates whether loans are repaid or not. This analysis is crucial for identifying factors associated with non-compliance, assessing credit risk, and understanding how variables such as loan size or credit history influence customer behavior.

freq_default <- table(df_numeric$default)
cumple <- freq_default[1]  
incumple <- freq_default[2]  
cat("Cantidad de Cumple:", cumple, "\n")
## Cantidad de Cumple: 700
cat("Cantidad de Incumple:", incumple, "\n")
## Cantidad de Incumple: 300
barplot(freq_default, 
        main = "Frecuencia de incumplimiento (default)", 
        col = c("green", "red"), 
        names.arg = c("Cumple", "Incumple"), 
        las = 1)

We begin by examining loan compliance across different age groups.

df_numeric$age_group <- cut(
  df_numeric$age,
  breaks = c(18, 25, 35, 45, 55, 65, Inf),
  labels = c("18-25", "26-35", "36-45", "46-55", "56-65", "65+"),
  right = FALSE
)

df_table <- as.data.frame(table(df_numeric$age_group, df_numeric$default))
colnames(df_table) <- c("AgeGroup", "Default", "Frequency")

ggplot(df_table, aes(x = AgeGroup, y = Frequency, fill = Default)) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +  
  labs(title = "Distribución de Default según Edad", x = "Grupo de Edad", y = "Frecuencia") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Based on the graph and data:

The pattern suggests that age influences both access to and behavior regarding credit, with younger and middle-aged groups facing more challenges in meeting obligations.

ggplot(df_numeric, aes(x = amount, y = factor(default, levels = c(1, 2), labels = c("Cumple", "Incumple")), color = factor(default))) +
  geom_point() +
  labs(title = "Relación entre cantidad del Préstamo y Default", 
       x = "Cantidad del Préstamo", 
       y = "Default (Cumple / Incumple)") +
  scale_color_manual(values = c("lightgreen", "lightcoral")) +
  theme_minimal()

Relationship between loan amount and compliance status (default):

The pattern suggests that age influences both access to and behavior regarding credit, with younger and middle-aged groups facing more challenges in meeting obligations.

ggplot(df_numeric, aes(x = factor(installment_rate), fill = factor(default, levels = c(1, 2), labels = c("Cumple", "Incumple")))) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +
  labs(title = "Distribución de Default según Tasa de Cuota", 
       x = "Tasa de Cuota del Préstamo", 
       y = "Proporción") +
  theme_minimal()

Relationship between loan amount and compliance status (default):

ggplot(df_numeric, aes(x = factor(residence_history), fill = factor(default, levels = c(1, 2), labels = c("Cumple", "Incumple")))) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +
  labs(title = "Distribución de Default según Historial de Residencia", 
       x = "Historial de Residencia", 
       y = "Proporción") +
  theme_minimal()

Distribution of compliance status (Default) according to residence history:

Compliant: The proportion of customers who comply with their payments is consistent and predominant across all residence history levels (1 to 4), with values close to or above 75%.

Default: Although defaults are lower in proportion, they remain evenly distributed across all residence history levels, without significant change as residence duration increases.

General trend: There does not appear to be a strong relationship between residence history and the probability of default, since proportions are fairly similar across all categories.

This suggests that residence history may not be a decisive factor in credit risk within this dataset.

ggplot(df_numeric, aes(x = factor(existing_credits), fill = factor(default, levels = c(1, 2), labels = c("Cumple", "Incumple")))) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +
  labs(title = "Distribución de Default según Créditos Existentes", 
       x = "Número de Créditos Existentes", 
       y = "Proporción") +
  theme_minimal()

Distribution of compliance (Default) according to the number of existing credits:

Compliant: Customers who comply with their loans predominate across all categories of existing credits, maintaining a proportion close to 75%.

Default: The proportion of defaults is consistent and slightly higher as the number of existing credits increases, especially in the category with 4 existing credits.

General trend: Although the overall percentage of defaults is moderate, a higher number of existing credits seems to be associated with a gradual increase in the proportion of defaults, which may indicate higher credit risk for customers with multiple financial commitments.

This analysis suggests that the number of existing credits could be a relevant factor in credit risk evaluation.

ggplot(dfcredit, aes(x = purpose, fill = factor(default, levels = c(1, 2), labels = c("Cumple", "Incumple")))) +
  geom_bar(position = "fill") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +
  labs(title = "Distribución de Default según Purpose", 
       x = "Purpose", 
       y = "Proporción") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Distribution of compliance (Default) according to loan purpose:

Compliant: Most loan purposes show a high proportion of compliance. Categories such as “radio/TV”, “education”, and “repairs” stand out with above-average compliance rates.

Default: Loan purposes related to “domestic appliances” and “business” show a higher proportion of defaults compared to other purposes. This could suggest that these loans carry greater risk.

General trend: The most common purposes such as “car (new)” and “radio/TV” appear relatively safe in terms of compliance, while less common or higher-risk purposes show a higher incidence of defaults.

This indicates that loan purpose is a relevant factor for predicting default risk.

3.1 Bias, Xenophobia, and Discrimination

The following section of the exploratory analysis is carried out with the main objective of avoiding potential biases in the interpretation of data and the evaluation of credit risks. By including variables such as marital status, foreign worker status, and other demographic or financial factors, we can identify relevant patterns that may influence loan approval or default. This approach provides a more complete and realistic view of customer behavior, ensuring that credit-related decisions are based on objective data rather than assumptions.

In this way, we aim to prevent prejudices or subjective interpretations from affecting the conclusions of the analysis. This not only ensures a fairer and more equitable approach to risk evaluation, but also contributes to optimizing credit policies and strengthening customer trust in financial institutions. Carefully considering these variables allows us to obtain more precise insights and develop strategies that better fit customer needs and behaviors, minimizing risks and maximizing the effectiveness of the analysis.

df_personal_status <- as.data.frame(table(dfcredit$personal_status, dfcredit$default))
colnames(df_personal_status) <- c("PersonalStatus", "Default", "Frequency")
df_personal_status$Default <- factor(df_personal_status$Default, levels = c(1, 2), labels = c("Cumple", "Incumple"))

ggplot(df_personal_status, aes(x = PersonalStatus, y = Frequency, fill = Default)) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +
  labs(title = "Distribución de Default por Estado Civil", x = "Estado Civil", y = "Frecuencia") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

df_foreign_worker <- as.data.frame(table(dfcredit$foreign_worker, dfcredit$default))
colnames(df_foreign_worker) <- c("ForeignWorker", "Default", "Frequency")
df_foreign_worker$Default <- factor(df_foreign_worker$Default, levels = c(1, 2), labels = c("Cumple", "Incumple"))

ggplot(df_foreign_worker, aes(x = ForeignWorker, y = Frequency, fill = Default)) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +
  labs(title = "Distribución de Default por Trabajador Extranjero", x = "Trabajador Extranjero", y = "Frecuencia") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Distribution by Foreign Worker:

  • Compliant: Most foreign workers comply with their credit obligations, but they also represent a large group of defaulters in absolute terms.
  • Default: While defaults among local workers are scarce, among foreign workers there is a significant proportion of defaults.
  • Trend: Foreign worker status is associated with a relatively higher risk of default compared to local workers.

Distribution by Marital Status:

  • Single Male: Show a higher proportion of defaults compared to other marital status groups.
  • Female: Women display higher compliance rates and lower default incidence compared to single males.
  • Married Male: Perform well in terms of compliance, representing a low-risk group.
df_job <- as.data.frame(table(dfcredit$job, dfcredit$default))
colnames(df_job) <- c("Job", "Default", "Frequency")
df_job$Default <- factor(df_job$Default, levels = c(1, 2), labels = c("Cumple", "Incumple"))

ggplot(df_job, aes(x = Job, y = Frequency, fill = Default)) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_fill_manual(values = c("lightgreen", "lightcoral")) +
  labs(title = "Distribución de Default por Tipo de Trabajo", x = "Tipo de Trabajo", y = "Frecuencia") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Analysis of Distribution by Job Type

  • Skilled Employee: Represents the largest proportion of clients in both compliance and default. Although compliant clients predominate, the absolute volume of defaulters in this group is also considerable due to its size.
  • Unskilled Resident: Proportionally, shows a more concerning balance between compliance and defaults compared to skilled employees, indicating higher relative risk.
  • Management / Self-Employed: Although a smaller group, the proportion of compliant clients is notably higher, indicating a low-risk profile.
  • Unemployed Non-Resident: This group has the lowest absolute frequency, but the relative default rate is high, representing a higher-risk profile.

4 Statistical Significance Tests

Risk groups to be used in order to confirm whether our exploratory analysis is correct. Summary below:

tabla_purpose_default <- table(dfcredit$purpose, dfcredit$default)
tabla_personal_status_default <- table(dfcredit$personal_status, dfcredit$default)
tabla_job_default <- table(dfcredit$job, dfcredit$default)

phi_purpose_default <- Phi(tabla_purpose_default)
cramer_v_purpose_default <- CramerV(tabla_purpose_default)
cat("Phi para Purpose vs Default:", phi_purpose_default, "\n")
## Phi para Purpose vs Default: 0.1826375
cat("Cramér V para Purpose vs Default:", cramer_v_purpose_default, "\n")
## Cramér V para Purpose vs Default: 0.1826375
phi_personal_status_default <- Phi(tabla_personal_status_default)
cramer_v_personal_status_default <- CramerV(tabla_personal_status_default)
cat("Phi para Personal Status vs Default:", phi_personal_status_default, "\n")
## Phi para Personal Status vs Default: 0.09800619
cat("Cramér V para Personal Status vs Default:", cramer_v_personal_status_default, "\n")
## Cramér V para Personal Status vs Default: 0.09800619
phi_job_default <- Phi(tabla_job_default)
cramer_v_job_default <- CramerV(tabla_job_default)
cat("Phi para Job vs Default:", phi_job_default, "\n")
## Phi para Job vs Default: 0.04341838
cat("Cramér V para Job vs Default:", cramer_v_job_default, "\n")
## Cramér V para Job vs Default: 0.04341838
chisq.test(tabla_purpose_default)
## Warning in chisq.test(tabla_purpose_default): Chi-squared approximation may be
## incorrect
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_purpose_default
## X-squared = 33.356, df = 9, p-value = 0.0001157
chisq.test(tabla_personal_status_default)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_personal_status_default
## X-squared = 9.6052, df = 3, p-value = 0.02224
chisq.test(tabla_job_default)
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_job_default
## X-squared = 1.8852, df = 3, p-value = 0.5966

The results of the Phi, Cramér’s V, and Chi-square tests provide a clear view of the association between categorical variables and the target variable default, which represents loan non-compliance. These metrics are essential for evaluating the strength and significance of the relationships among variables, allowing us to identify important patterns that may influence credit behavior.

For the variable Purpose (loan purpose), both the Phi coefficient and Cramér’s V yield a value of 0.1826, indicating a weak but statistically significant association with default. In addition, the Chi-square test reports X² = 33.356 with a p-value of 0.0001157, confirming that there is a statistically significant relationship between loan purpose and default. This suggests that certain purposes may be associated with higher default risk, which could be relevant for adjusting credit policies depending on the loan’s objective.

For the variable Personal Status, the Phi and Cramér’s V values are lower, 0.0980, indicating a very weak association with default. However, the Chi-square test with X² = 9.6052 and a p-value of 0.02224 shows that the relationship is still statistically significant. This may reflect small differences in default rates depending on personal status, although the overall impact appears limited.

Finally, for the variable Job (occupation), both Phi and Cramér’s V yield extremely low values, 0.0434, indicating virtually no association with default. The Chi-square test, with X² = 1.8852 and a p-value of 0.5966, confirms that there is no statistically significant relationship between occupation and default. This implies that job type does not appear to be a determining factor in loan repayment behavior.

We graph the results below.

data <- data.frame(
  Variable = c("Purpose", "Personal Status", "Job"),
  Cramer_V = c(0.1826, 0.0980, 0.0434),
  P_Value = c(0.0001, 0.0222, 0.5966)
)

data$Significance <- ifelse(data$P_Value < 0.05, "Significant", "Not Significant")

ggplot(data, aes(x = Variable, y = Cramer_V, fill = Variable)) +
  geom_bar(stat = "identity", color = "black") +
  geom_hline(yintercept = 0.1, linetype = "dashed", color = "red", size = 1) +
  labs(
    title = "Cramér V Analysis",
    x = "Variables",
    y = "Cramér V"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("skyblue", "lightgreen", "coral"))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

ggplot(data, aes(x = Variable, y = P_Value, fill = Significance)) +
  geom_bar(stat = "identity", color = "black") +
  geom_hline(yintercept = 0.05, linetype = "dashed", color = "red", size = 1) +
  labs(
    title = "Chi-Square Test P-Values",
    x = "Variables",
    y = "P-Value"
  ) +
  theme_minimal() +
  scale_fill_manual(values = c("green", "orange"))

Purpose (Loan Purpose):

Personal Status:

Job (Employment Type):

Overall Conclusion:
Purpose is the most relevant variable, showing statistical significance in Phi, Cramér’s V, and Chi-square. Personal Status has a minor impact, while Job shows no relevance.


The initial analysis aimed to visually explore the relationships between categorical variables and loan compliance or default. Through bar plots and frequency tables, I identified patterns in variables such as loan purpose (purpose), marital status (personal_status), and employment type (job).

For example, loans aimed at household appliances and business had higher proportions of defaults, while purposes such as education or repairs showed higher compliance rates. Regarding personal_status, single males stood out with higher default rates, and among job categories, unskilled residents showed relatively higher risk.

To validate these observations and rule out coincidences, I conducted statistical tests such as Phi, Cramér’s V, and Chi-square. The results confirmed my initial findings. The variable purpose showed a weak but statistically significant association (Cramér’s V: 0.1826, p-value < 0.001), supporting its relevance as a risk factor. In contrast, personal_status showed a weak but significant association (Cramér’s V: 0.0980, p-value = 0.0222), and job showed no significant relationship (Cramér’s V: 0.0434, p-value = 0.5966).

Descriptive analysis and statistical metrics converge, confirming that loan purpose is a key variable in default risk, while marital status and job have minor or negligible impact.

5 Building the First Decision Tree

You may choose to use all variables or, with justification, exclude some from the model.

Justification for excluding variables

dfcredit$default <- ifelse(dfcredit$default > 1, "yes", "no")
dfcredit$default <- as.factor(dfcredit$default)
dfcredit <- subset(dfcredit, select = -job)

We could build a decision tree directly using the entire dataset, but as demonstrated in practical examples, class notes, and prior research, it is considered good practice to split the dataset and train the model beforehand. This approach ensures that the model not only performs well on the data it was built with, but is also capable of generalizing its performance to unseen data. Training the model before evaluating it is an essential step in any supervised learning process, as it guarantees that conclusions and predictions are not biased by the training data.

Splitting the dataset into two subsets—one for training and one for testing—allows us to build the model with part of the data and then evaluate its performance with data that was not used in its construction. This is key to validating the model’s ability to correctly predict outcomes in real-world situations. If we were to fit the model directly using the entire dataset, we might obtain an artificially high accuracy, but we would not know how the model behaves with new data. This phenomenon, known as overfitting, occurs when a model learns the details and noise of the training data too well, losing its ability to generalize to unseen cases.

The following code implements this approach by splitting the dfcredit dataset into two subsets: one with 90% of the data for training and the other with the remaining 10% for testing. This division ensures that a representative portion of the dataset is used to train the model, while the rest is reserved for evaluating its performance. We also verify that the proportions of the target variable classes (default) are similar in both subsets, ensuring that the sample is representative and does not introduce bias into training or evaluation.

Training the model on a subset of the data also allows us to tune hyperparameters and experiment with different configurations before validating the final model. This provides additional control over the process and increases confidence in the obtained results. In summary, prior training is a fundamental step to develop a robust, reliable, and useful model capable of facing real-world scenarios with precision and effectiveness.

Reference Nº2

set.seed(100)  

sample <- sample(1000,900)
str(sample)
##  int [1:900] 714 503 358 624 985 718 919 470 966 516 ...
train <- dfcredit[sample,]
test <- dfcredit[-sample,]
prop.table(table(train$default))
## 
##        no       yes 
## 0.7033333 0.2966667
prop.table(table(test$default))
## 
##   no  yes 
## 0.67 0.33
train$default <- as.factor(train$default)
model <- C5.0(default ~ ., data = train)
summary(model)
## 
## Call:
## C5.0.formula(formula = default ~ ., data = train)
## 
## 
## C5.0 [Release 2.07 GPL Edition]      Fri Aug 22 11:44:18 2025
## -------------------------------
## 
## Class specified by attribute `outcome'
## 
## Read 900 cases (20 attributes) from undefined.data
## 
## Decision tree:
## 
## checking_balance = unknown: no (354/40)
## checking_balance in {< 0 DM,> 200 DM,1 - 200 DM}:
## :...credit_history in {fully repaid,fully repaid this bank}:
##     :...savings_balance in {< 100 DM,101 - 500 DM}: yes (55/12)
##     :   savings_balance in {> 1000 DM,501 - 1000 DM,unknown}:
##     :   :...dependents <= 1: no (9/1)
##     :       dependents > 1: yes (4/1)
##     credit_history in {critical,delayed,repaid}:
##     :...months_loan_duration > 27:
##         :...savings_balance = > 1000 DM: no (2/1)
##         :   savings_balance = 501 - 1000 DM: yes (1)
##         :   savings_balance = 101 - 500 DM:
##         :   :...credit_history in {critical,delayed}: no (7/2)
##         :   :   credit_history = repaid: yes (6)
##         :   savings_balance = unknown:
##         :   :...checking_balance in {> 200 DM,1 - 200 DM}: no (11/1)
##         :   :   checking_balance = < 0 DM:
##         :   :   :...credit_history = critical: no (1)
##         :   :       credit_history in {delayed,repaid}: yes (4)
##         :   savings_balance = < 100 DM:
##         :   :...dependents <= 1:
##         :       :...months_loan_duration > 47: yes (18/1)
##         :       :   months_loan_duration <= 47:
##         :       :   :...purpose in {business,car (used),domestic appliances,
##         :       :       :           education,retraining}: yes (8/1)
##         :       :       purpose in {others,repairs}: no (3/1)
##         :       :       purpose = car (new):
##         :       :       :...property in {building society savings,
##         :       :       :   :            real estate}: no (2)
##         :       :       :   property in {other,unknown/none}: yes (8)
##         :       :       purpose = furniture:
##         :       :       :...employment_length in {0 - 1 yrs,
##         :       :       :   :                     4 - 7 yrs}: yes (3)
##         :       :       :   employment_length in {> 7 yrs,1 - 4 yrs,
##         :       :       :                         unemployed}: no (3)
##         :       :       purpose = radio/tv:
##         :       :       :...months_loan_duration <= 36: no (8/1)
##         :       :           months_loan_duration > 36: yes (4)
##         :       dependents > 1:
##         :       :...checking_balance = > 200 DM: yes (0)
##         :           checking_balance = 1 - 200 DM: no (1)
##         :           checking_balance = < 0 DM:
##         :           :...residence_history <= 2: yes (5/1)
##         :               residence_history > 2:
##         :               :...months_loan_duration <= 42: no (5)
##         :                   months_loan_duration > 42: yes (3/1)
##         months_loan_duration <= 27:
##         :...other_debtors = guarantor:
##             :...housing in {for free,own}: no (26)
##             :   housing = rent: yes (3/1)
##             other_debtors in {co-applicant,none}:
##             :...months_loan_duration <= 11: no (82/14)
##                 months_loan_duration > 11:
##                 :...amount <= 1381:
##                     :...savings_balance = > 1000 DM: no (5)
##                     :   savings_balance in {< 100 DM,101 - 500 DM,
##                     :   :                   501 - 1000 DM,unknown}:
##                     :   :...installment_plan in {bank,stores}: yes (11/1)
##                     :       installment_plan = none:
##                     :       :...checking_balance = > 200 DM:
##                     :           :...credit_history = critical: yes (2)
##                     :           :   credit_history in {delayed,repaid}: no (4)
##                     :           checking_balance = < 0 DM:
##                     :           :...property in {other,
##                     :           :   :            unknown/none}: yes (13)
##                     :           :   property in {building society savings,
##                     :           :   :            real estate}:
##                     :           :   :...installment_rate <= 3: no (3)
##                     :           :       installment_rate > 3: yes (20/7)
##                     :           checking_balance = 1 - 200 DM:
##                     :           :...credit_history in {critical,
##                     :               :                  delayed}: no (3)
##                     :               credit_history = repaid:
##                     :               :...dependents > 1: yes (2)
##                     :                   dependents <= 1:
##                     :                   :...residence_history > 3: no (5)
##                     :                       residence_history <= 3: [S1]
##                     amount > 1381:
##                     :...installment_plan = stores:
##                         :...amount <= 2171: yes (2)
##                         :   amount > 2171: no (6)
##                         installment_plan = bank:
##                         :...age > 26: no (18)
##                         :   age <= 26:
##                         :   :...purpose in {business,car (new),car (used),
##                         :       :           domestic appliances,education,
##                         :       :           furniture,others,repairs,
##                         :       :           retraining}: yes (2)
##                         :       purpose = radio/tv: no (3/1)
##                         installment_plan = none:
##                         :...savings_balance in {> 1000 DM,101 - 500 DM,
##                             :                   unknown}: no (46/8)
##                             savings_balance = 501 - 1000 DM:
##                             :...months_loan_duration <= 21: no (4)
##                             :   months_loan_duration > 21: yes (2)
##                             savings_balance = < 100 DM:
##                             :...other_debtors = co-applicant: [S2]
##                                 other_debtors = none:
##                                 :...credit_history = critical: no (26/5)
##                                     credit_history = delayed: [S3]
##                                     credit_history = repaid:
##                                     :...existing_credits > 1: yes (5/1)
##                                         existing_credits <= 1:
##                                         :...amount > 7174: yes (4)
##                                             amount <= 7174: [S4]
## 
## SubTree [S1]
## 
## residence_history > 1: yes (3)
## residence_history <= 1:
## :...amount <= 1209: no (2)
##     amount > 1209: yes (2)
## 
## SubTree [S2]
## 
## purpose in {business,car (new),car (used),domestic appliances,education,others,
## :           radio/tv,repairs,retraining}: yes (5)
## purpose = furniture:
## :...property in {building society savings,real estate,unknown/none}: no (3)
##     property = other: yes (1)
## 
## SubTree [S3]
## 
## checking_balance = < 0 DM: yes (2)
## checking_balance in {> 200 DM,1 - 200 DM}: no (5/1)
## 
## SubTree [S4]
## 
## property = unknown/none: no (6/1)
## property = building society savings:
## :...dependents > 1: no (2)
## :   dependents <= 1:
## :   :...residence_history <= 3: yes (6)
## :       residence_history > 3: no (3/1)
## property = other:
## :...personal_status in {female,married male}: no (15)
## :   personal_status in {divorced male,single male}:
## :   :...telephone = yes: yes (2)
## :       telephone = none:
## :       :...amount <= 2522: yes (2)
## :           amount > 2522: no (3)
## property = real estate:
## :...dependents > 1: yes (3)
##     dependents <= 1:
##     :...telephone = yes: yes (2)
##         telephone = none:
##         :...purpose in {business,car (new),car (used),domestic appliances,
##             :           education,furniture,others,repairs,
##             :           retraining}: no (7)
##             purpose = radio/tv: yes (4/1)
## 
## 
## Evaluation on training data (900 cases):
## 
##      Decision Tree   
##    ----------------  
##    Size      Errors  
## 
##      68  106(11.8%)   <<
## 
## 
##     (a)   (b)    <-classified as
##    ----  ----
##     605    28    (a): class no
##      78   189    (b): class yes
## 
## 
##  Attribute usage:
## 
##  100.00% checking_balance
##   60.67% credit_history
##   53.11% months_loan_duration
##   44.89% savings_balance
##   41.67% other_debtors
##   29.33% amount
##   28.78% installment_plan
##   13.89% dependents
##   11.67% property
##    7.11% purpose
##    7.11% existing_credits
##    3.78% residence_history
##    3.22% housing
##    2.56% installment_rate
##    2.56% age
##    2.44% personal_status
##    2.22% telephone
##    0.67% employment_length
## 
## 
## Time: 0.0 secs

Visualizing the entire decision tree can result in an excessively large and complex structure, which is not useful when it contains too many branches or nodes, making the interpretation of important rules difficult. Pruning the tree helps simplify its structure by removing irrelevant or redundant branches, making the model more visually manageable and easier to interpret.

In addition, pruning combats overfitting, a problem where the tree fits too closely to the training data, capturing noise and irrelevant patterns that reduce its ability to generalize. By pruning, the model’s performance on new data is improved, leading to more reliable predictions and reducing the risk of unnecessary complexity.

Reference Nº3

model_rpart <- rpart(default ~ ., data = train, control = rpart.control(cp = 0.01))
plot(model_rpart, uniform = TRUE, main = "Árbol Podado")
text(model_rpart, use.n = TRUE, cex = 0.8)

Tree Structure

Tree Size and Accuracy

Class Balance

The model is more accurate at classifying the No class (compliant customers), which may be due to this class being overrepresented in the training set.
The Yes class (defaulters) has a higher error rate, suggesting that balancing the classes could improve sensitivity towards defaulters.

Variable Importance

Model Strengths

Tree Size:
Although the tree has a reasonable size, it could still benefit from pruning to simplify its structure and improve generalization capacity.

Conclusions

The use of a fixed seed (set.seed) ensured that results are reproducible, which is crucial for validating and comparing different model configurations. This guarantees that changes in the tree are due to the model setup or the data, and not to randomness in sampling.

The decision tree stands out for its interpretability and ability to identify important patterns in the dataset. However, class imbalance negatively affects performance, especially in predicting defaulters.

6 With the obtained tree, provide a brief explanation of the rules derived and any points of interest. One element to consider, for example, is how many observations fall under each rule.

Reference Nº4

grViz("
digraph tree {
  graph [layout = dot]
  
  # Caso 1: Simple
  node1 [label = 'Checking Balance = Unknown', shape = box]
  node2 [label = 'Class = No (354 cases, 40 errors)', shape = oval]
  node1 -> node2
  
  # Caso 2: Intermedio
  node3 [label = 'Checking Balance < 0 DM', shape = box]
  node4 [label = 'Credit History = Fully Repaid', shape = box]
  node5 [label = 'Savings Balance < 100 DM', shape = box]
  node6 [label = 'Class = Yes (55 cases, 12 errors)', shape = oval]
  
  node3 -> node4
  node4 -> node5
  node5 -> node6
  
  # Caso 3: Complejo
  node7 [label = 'Months Loan Duration > 27', shape = box]
  node8 [label = 'Checking Balance < 0 DM', shape = box]
  node9 [label = 'Purpose = Car (New)', shape = box]
  node10 [label = 'Property = Other or Unknown', shape = box]
  node11 [label = 'Class = Yes (8 cases, 1 error)', shape = oval]
  
  node7 -> node8
  node8 -> node9
  node9 -> node10
  node10 -> node11
}
")

Explanation: At the root node of the decision tree, if the customer’s current account balance (checking_balance) is unknown, the customer is classified directly as compliant (No). This means the model does not need to evaluate any other variable to reach this conclusion. This case is simple because the decision is made at the first node without exploring additional splits.

Observations:
- This node includes 354 cases in the training data.
- The model makes 40 errors in this node, meaning some customers classified as No were actually defaulters (Yes).

Interpretation: Customers with unknown balances likely represent a group where the model assumes lower credit risk. This outcome may reflect a tendency in the data where this category is associated with a history of compliance. However, the errors suggest that not all customers in this group comply, indicating the rule could be improved by incorporating additional variables.

Explanation: This rule requires evaluating three variables to classify the customer as a defaulter (Yes):
- checking_balance less than 0 DM, indicating a negative account balance.
- credit_history = fully repaid, suggesting previous loans were repaid.
- savings_balance less than 100 DM, indicating low savings.

Observations:
- This node includes 55 cases, with 12 errors.
- This means that most customers with these characteristics are correctly classified as defaulters.

Interpretation: A negative balance combined with low savings appears to be a signal of higher credit risk, despite having a history of fully repaid loans. This indicates that the model considers the current financial situation more relevant than past loan history when predicting default.

Explanation: This rule combines multiple variables and conditions:
- checking_balance less than 0 DM, indicating a negative balance.
- months_loan_duration > 27, reflecting a longer loan term.
- purpose = car (new), meaning the loan is requested for a new car.
- property = other or unknown/none, indicating no clear property as collateral.

Observations:
- This node includes 8 cases, with 1 error.
- The small number of cases suggests this rule applies to a very specific customer segment.

Interpretation: This case combines several risk signals: negative balance, long loan duration, specific purpose (new car), and lack of known collateral. The model uses these features to identify customers with a higher probability of default. However, the small sample size may indicate this is an uncommon pattern in the dataset and could reflect local overfitting.

7 Once you have a valid model, proceed to perform a goodness-of-fit analysis on the test set and the confusion matrix. Do you consider this model sufficiently good to be used? Justify your answer by considering all possible types of error.

Reference Nº5

predicted_model <- predict(model, test)
conf_matrix <- table(test$default, predicted_model, dnn = c("Real", "Predicción"))
print(conf_matrix)
##      Predicción
## Real  no yes
##   no  52  15
##   yes 18  15
accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
sensitivity <- conf_matrix["yes", "yes"] / sum(conf_matrix["yes", ])
specificity <- conf_matrix["no", "no"] / sum(conf_matrix["no", ])
precision <- conf_matrix["yes", "yes"] / sum(conf_matrix[, "yes"])

cat("Precisión Global:", round(accuracy * 100, 2), "%\n")
## Precisión Global: 67 %
cat("Sensibilidad:", round(sensitivity * 100, 2), "%\n")
## Sensibilidad: 45.45 %
cat("Especificidad:", round(specificity * 100, 2), "%\n")
## Especificidad: 77.61 %
cat("Precisión (Valor Predictivo Positivo):", round(precision * 100, 2), "%\n")
## Precisión (Valor Predictivo Positivo): 50 %
accuracy <- (52 + 15) / (52 + 15 + 15 + 18) * 100
sensitivity <- 15 / (15 + 18) * 100
specificity <- 52 / (52 + 15) * 100
precision <- 15 / (15 + 15) * 100

metrics <- data.frame(
  Metric = c("Precisión Global", "Sensibilidad", "Especificidad", "Precisión"),
  Value = c(accuracy, sensitivity, specificity, precision)
)

ggplot(metrics, aes(x = Metric, y = Value, fill = Metric)) +
  geom_bar(stat = "identity", color = "black") +
  ylim(0, 100) +
  labs(title = "Métricas del Modelo", y = "Porcentaje", x = "") +
  theme_minimal()

The confusion matrix and generated metrics reflect the model’s performance on the test set. The matrix indicates that, of the actual compliant customers (No), 52 were correctly classified, while 15 were incorrectly classified as defaulters. On the other hand, of the actual defaulters (Yes), only 15 were correctly classified, while 18 were incorrectly classified as compliant.

Regarding the metrics, the overall accuracy of the model is 67%, meaning the model correctly classifies 67 out of 100 cases. Although this figure may seem reasonable, the breakdown of metrics highlights important areas for improvement. The model’s sensitivity is low, at 45.45%, indicating it correctly identifies fewer than half of actual defaulters. This could be critical in a credit system, as such errors may lead to significant financial losses. By contrast, specificity is 77.61%, showing the model is more effective at identifying compliant customers, reducing restrictions on reliable clients. Positive predictive value is moderate, at 50%, meaning that only half of the customers classified as defaulters actually are, generating a considerable number of false alarms.

The error analysis highlights that false negatives (18 cases) represent the greatest challenge, since risky customers are classified as reliable. This can cause a significant financial impact. False positives (15 cases), on the other hand, have a smaller operational impact but may harm customer experience.

8 Using a similar approach to the previous points and considering the same variables, enrich the exercise by fitting complementary decision tree models. Is the new approach better than the original? Justify your answer.

We will now fit a Random Forest model, as described in the following reference.

Reference Nº6

rf_model <- randomForest(default ~ ., data = train, ntree = 500, mtry = 3, importance = TRUE)
print(rf_model)
## 
## Call:
##  randomForest(formula = default ~ ., data = train, ntree = 500,      mtry = 3, importance = TRUE) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 3
## 
##         OOB estimate of  error rate: 23.56%
## Confusion matrix:
##      no yes class.error
## no  587  46  0.07266983
## yes 166 101  0.62172285
rf_predictions <- predict(rf_model, test)
conf_matrix_rf <- table(test$default, rf_predictions, dnn = c("Real", "Predicción"))
print(conf_matrix_rf)
##      Predicción
## Real  no yes
##   no  63   4
##   yes 20  13
accuracy_rf <- sum(diag(conf_matrix_rf)) / sum(conf_matrix_rf) * 100
sensitivity_rf <- conf_matrix_rf["yes", "yes"] / sum(conf_matrix_rf["yes", ]) * 100
specificity_rf <- conf_matrix_rf["no", "no"] / sum(conf_matrix_rf["no", ]) * 100
precision_rf <- conf_matrix_rf["yes", "yes"] / sum(conf_matrix_rf[, "yes"]) * 100

cat("Precisión Global:", round(accuracy_rf, 2), "%\n")
## Precisión Global: 76 %
cat("Sensibilidad:", round(sensitivity_rf, 2), "%\n")
## Sensibilidad: 39.39 %
cat("Especificidad:", round(specificity_rf, 2), "%\n")
## Especificidad: 94.03 %
cat("Precisión (Valor Predictivo Positivo):", round(precision_rf, 2), "%\n")
## Precisión (Valor Predictivo Positivo): 76.47 %
varImpPlot(rf_model, main = "Variable Importance in Random Forest")

The Random Forest model trained with 500 trees and 3 variables per split shows moderate performance, as reflected in the confusion matrix and the metrics obtained. In the matrix, the model correctly classifies 61 cases as compliant (No) and 13 as defaulters (Yes). However, it makes 20 false negative errors (defaulters classified as compliant) and 6 false positive errors (compliant customers classified as defaulters).

The overall accuracy of the model is 74%, which indicates acceptable but improvable performance. Specificity, at 91.04%, is high, showing that the model is very effective at identifying compliant customers. However, sensitivity, at 39.39%, is low, reflecting difficulties in identifying defaulters. The positive predictive value, at 68.42%, indicates that a significant proportion of the customers predicted as defaulters are correctly identified, although there is still room for improvement.

The variable importance analysis shows that checking_balance and months_loan_duration are the most influential factors, followed by credit_history and amount. Variables such as telephone and foreign_worker have little relevance in the model.

Although the model improves in overall accuracy and specificity compared to the original decision tree, its low sensitivity and high number of false negatives make it insufficient for real-world applications without further adjustments.

When comparing the original decision tree with the Random Forest (RF) model, we see that RF significantly improves overall accuracy and specificity but faces similar challenges with sensitivity. While the decision tree achieved an overall accuracy of 67%, RF increased it to 74%, showing better general performance. In addition, RF is much more effective at identifying compliant customers (No), with specificity of 91.04% compared to 77.61% for the decision tree. This means that RF misclassifies fewer compliant customers as defaulters, reducing the operational impact of false positives.

However, sensitivity remains low in both models. The decision tree correctly identifies 45.45% of actual defaulters, while RF only reaches 39.39%. This indicates that both models struggle to detect high-risk customers, which could result in financial losses due to false negatives (18 in the decision tree vs. 20 in RF).

Positive predictive value also improves slightly in RF, rising from 50% in the decision tree to 68.42%. This suggests that RF is more reliable when predicting defaulters, although there is still room for improvement in both models.

We will now build a more accurate Random Forest model using the caret package, which provides greater flexibility and advanced tools to optimize the model and evaluate its performance. This approach differs from the basic model in several important ways. First, it employs 5-fold cross-validation during training, meaning the data is split into multiple parts to evaluate the model more robustly and ensure it performs well across different subsets. In addition, it automatically tunes hyperparameters, testing different configurations and selecting the best one to improve model performance.

This approach then generates predictions on the test set and evaluates performance using a confusion matrix that includes key metrics such as accuracy, sensitivity, and specificity. It also analyzes variable importance, showing which features are most relevant for the model. Finally, it calculates prediction probabilities and generates a ROC curve with the area under the curve (AUC), providing both a visual and numerical measure of how well the model balances sensitivity and specificity.

Reference Nº7

train_control <- trainControl(method = "cv",  
                              number = 5,    
                              verboseIter = TRUE)

set.seed(100)
rf_model_caret <- train(default ~ ., 
                        data = train, 
                        method = "rf", 
                        trControl = train_control,
                        tuneLength = 5) 
## + Fold1: mtry= 2 
## - Fold1: mtry= 2 
## + Fold1: mtry=12 
## - Fold1: mtry=12 
## + Fold1: mtry=23 
## - Fold1: mtry=23 
## + Fold1: mtry=34 
## - Fold1: mtry=34 
## + Fold1: mtry=45 
## - Fold1: mtry=45 
## + Fold2: mtry= 2 
## - Fold2: mtry= 2 
## + Fold2: mtry=12 
## - Fold2: mtry=12 
## + Fold2: mtry=23 
## - Fold2: mtry=23 
## + Fold2: mtry=34 
## - Fold2: mtry=34 
## + Fold2: mtry=45 
## - Fold2: mtry=45 
## + Fold3: mtry= 2 
## - Fold3: mtry= 2 
## + Fold3: mtry=12 
## - Fold3: mtry=12 
## + Fold3: mtry=23 
## - Fold3: mtry=23 
## + Fold3: mtry=34 
## - Fold3: mtry=34 
## + Fold3: mtry=45 
## - Fold3: mtry=45 
## + Fold4: mtry= 2 
## - Fold4: mtry= 2 
## + Fold4: mtry=12 
## - Fold4: mtry=12 
## + Fold4: mtry=23 
## - Fold4: mtry=23 
## + Fold4: mtry=34 
## - Fold4: mtry=34 
## + Fold4: mtry=45 
## - Fold4: mtry=45 
## + Fold5: mtry= 2 
## - Fold5: mtry= 2 
## + Fold5: mtry=12 
## - Fold5: mtry=12 
## + Fold5: mtry=23 
## - Fold5: mtry=23 
## + Fold5: mtry=34 
## - Fold5: mtry=34 
## + Fold5: mtry=45 
## - Fold5: mtry=45 
## Aggregating results
## Selecting tuning parameters
## Fitting mtry = 34 on full training set
print(rf_model_caret)
## Random Forest 
## 
## 900 samples
##  19 predictor
##   2 classes: 'no', 'yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold) 
## Summary of sample sizes: 720, 720, 720, 721, 719 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##    2    0.7222436  0.1084040
##   12    0.7489166  0.3276304
##   23    0.7522314  0.3573191
##   34    0.7644538  0.3979491
##   45    0.7622006  0.3887855
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 34.
rf_predictions <- predict(rf_model_caret, newdata = test)
conf_matrix_rf <- confusionMatrix(rf_predictions, test$default)
print(conf_matrix_rf)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction no yes
##        no  58  20
##        yes  9  13
##                                           
##                Accuracy : 0.71            
##                  95% CI : (0.6107, 0.7964)
##     No Information Rate : 0.67            
##     P-Value [Acc > NIR] : 0.23006         
##                                           
##                   Kappa : 0.2836          
##                                           
##  Mcnemar's Test P-Value : 0.06332         
##                                           
##             Sensitivity : 0.8657          
##             Specificity : 0.3939          
##          Pos Pred Value : 0.7436          
##          Neg Pred Value : 0.5909          
##              Prevalence : 0.6700          
##          Detection Rate : 0.5800          
##    Detection Prevalence : 0.7800          
##       Balanced Accuracy : 0.6298          
##                                           
##        'Positive' Class : no              
## 
varImp_rf <- varImp(rf_model_caret)
plot(varImp_rf, main = "Variable Importance in Random Forest")

rf_probs <- predict(rf_model_caret, newdata = test, type = "prob")[, 2]
roc_curve <- roc(test$default, rf_probs)
## Setting levels: control = no, case = yes
## Setting direction: controls < cases
plot(roc_curve, main = "Curve ROC for Random Forest")

auc(roc_curve)
## Area under the curve: 0.7614

The model was trained and optimized using 5-fold cross-validation. During training, five different values of mtry (number of variables used per split) were tested, and the model selected the optimal value, mtry = 34, based on the highest average accuracy obtained across the folds.

In terms of results, the model achieved an overall accuracy of 71%, meaning it correctly classified 71% of the cases. However, performance across classes is unbalanced. Sensitivity, which measures how well the model correctly identifies compliant customers (No), is high at 86.57%. On the other hand, specificity, which measures the ability to correctly identify defaulters (Yes), is low at 39.39%. This means the model tends to misclassify many defaulters as compliant customers. The Positive Predictive Value (PPV) is 74.36%, indicating that the majority of predictions classified as compliant are correct.

The variable importance analysis shows that the most relevant features are checking_balance, months_loan_duration, and amount. These variables play a key role in the model’s decisions. The ROC curve shows a decent AUC, suggesting a reasonable balance between sensitivity and specificity at different thresholds, although there is still room for improvement.

This model improved compared to the decision tree and offers greater robustness thanks to cross-validation. However, the low specificity limits its ability to correctly identify defaulters, which is critical in credit risk analysis.


Compared to the Random Forest trained directly with the randomForest package, this caret-based model shows some notable differences. The overall accuracy of the previous model was 74%, while this caret model achieves 71%. Although the general accuracy is slightly lower, the cross-validation process makes this model more reliable and less prone to overfitting.

The sensitivity of the caret model is significantly higher (86.57% compared to 39.39% in the previous RF). This indicates that this model is much better at identifying compliant customers (No). However, its specificity is much lower (39.39% compared to 91.04% in the previous RF), meaning that this model misclassifies many defaulters as compliant customers.

In comparison with the decision tree, this model performs better in overall accuracy (71% vs. 67%) and in sensitivity (86.57% vs. 45.45%). However, it shows similar issues in terms of low specificity, which was also a problem for the decision tree.

In general, although the caret model significantly improves sensitivity, it still struggles to identify defaulters, which remains a critical issue in this context.

9 What alternatives remain after not being fully convinced with our analyses?

Reference Nº8

preProcValues <- preProcess(train[, -ncol(train)], method = c("center", "scale"))
train_scaled <- predict(preProcValues, train[, -ncol(train)])
test_scaled <- predict(preProcValues, test[, -ncol(test)])

train_scaled$default <- train$default
test_scaled$default <- test$default

set.seed(123)
svm_model <- svm(default ~ ., data = train_scaled, kernel = "radial", cost = 1, gamma = 0.1, probability = TRUE)
svm_predictions <- predict(svm_model, test_scaled, probability = TRUE)
conf_matrix_svm <- confusionMatrix(svm_predictions, test_scaled$default)
print(conf_matrix_svm)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction no yes
##        no  60  19
##        yes  7  14
##                                           
##                Accuracy : 0.74            
##                  95% CI : (0.6427, 0.8226)
##     No Information Rate : 0.67            
##     P-Value [Acc > NIR] : 0.08146         
##                                           
##                   Kappa : 0.3523          
##                                           
##  Mcnemar's Test P-Value : 0.03098         
##                                           
##             Sensitivity : 0.8955          
##             Specificity : 0.4242          
##          Pos Pred Value : 0.7595          
##          Neg Pred Value : 0.6667          
##              Prevalence : 0.6700          
##          Detection Rate : 0.6000          
##    Detection Prevalence : 0.7900          
##       Balanced Accuracy : 0.6599          
##                                           
##        'Positive' Class : no              
## 
svm_probabilities <- attr(svm_predictions, "probabilities")[, "yes"]
roc_curve_svm <- roc(test_scaled$default, svm_probabilities)
## Setting levels: control = no, case = yes
## Setting direction: controls < cases
plot(roc_curve_svm, main = "Curva ROC para SVM")

auc(roc_curve_svm)
## Area under the curve: 0.777

The confusion matrix and generated metrics show that the SVM model achieved 74% overall accuracy, slightly higher than the previous models. Sensitivity is high at 89.55%, meaning it correctly identifies most compliant customers (No). However, specificity is low at 42.42%, which indicates difficulties in correctly classifying defaulters (Yes). The Positive Predictive Value (PPV) is 75.95%, showing that most customers classified as compliant are indeed correct.

The ROC curve demonstrates reasonable performance with an AUC consistent with a useful, though not perfect, model. This shows that the SVM provides an acceptable balance between sensitivity and specificity.

SVM offers balanced and slightly improved performance compared to previous models, particularly in overall accuracy and sensitivity. However, its low specificity still limits its ability to correctly identify defaulters, which could be critical in a credit risk system.

This model represents a significant improvement in some aspects, but it would be more effective if class weights were adjusted or balancing techniques were applied to improve specificity. Additionally, tuning parameters such as cost and gamma could further refine the model.

10 Summary of Main Findings Across All Models

The analysis compares the performance of three machine learning models – Decision Tree, Random Forest, and Support Vector Machine (SVM) – applied to a binary classification dataset. Each model was evaluated in terms of key metrics such as overall accuracy, sensitivity, specificity, and overall robustness. Below is a detailed summary with specific results and conclusions.

Decision Trees are known for their simplicity and interpretability, making them useful as a baseline model for classification tasks. However, this analysis highlights their limitations:

The overall accuracy is acceptable, but the low sensitivity reveals a poor ability to correctly identify defaulters (positive class). This means that more than half of positive cases are misclassified, which is critical in sensitive applications like fraud detection or credit risk assessment.

While specificity is relatively high (80%), indicating good ability to identify compliant customers, the model lacks robustness with noisy or imbalanced data. In short, Decision Trees are useful for quick interpretation but are not the best for maximizing predictive performance.

The first Random Forest model showed significant improvements over the Decision Tree:

Random Forest, by combining multiple decision trees and averaging results, is less prone to overfitting and more robust to variability in data. In this analysis, its overall accuracy improved to 74%, a considerable gain over the Decision Tree.

Specificity reached an excellent 91.04%, showing that this model is highly reliable for classifying compliant customers. However, sensitivity remains moderate (59.09%), meaning it still fails to detect a significant portion of defaulters. This imbalance between sensitivity and specificity can be problematic in contexts where false negatives are costly.

The Random Forest tuned with the caret package and hyperparameter optimization achieved a better balance between sensitivity and overall accuracy:

This model displayed very high sensitivity (86.57%), making it the best at detecting defaulters. However, its specificity is low, misclassifying many compliant customers as defaulters. This can be problematic when false positives have significant implications, such as denying loans to reliable clients.

Cross-validation and hyperparameter tuning made this model more robust and generalizable, positioning it as a strong option in contexts where sensitivity is the priority.

The SVM achieved balanced performance:

SVM excels in handling high-dimensional problems and non-linear decision boundaries. It is less interpretable than tree-based models and requires parameter tuning, but it can provide strong results. In this analysis, SVM achieved high accuracy and sensitivity, outperforming the Decision Tree and comparable to Random Forest. However, its low specificity remains a limitation.

11 Final Conclusions

Overall, the Random Forest tuned with caret appears the most promising, especially when sensitivity (detecting defaulters) is the priority. However, improvements should focus on reducing false positives to achieve a more robust balance for real-world credit risk applications.

12 Final Conclusion

After evaluating all models, I consider Random Forest the most suitable for this analysis due to its balance between overall accuracy and specificity. With an error rate of 23.44%, this model is reliable for identifying defaulters in a credit risk system. While SVM has high sensitivity (89.55%), its low specificity (42.42%) poses a higher risk of misclassifying defaulters. The Decision Tree, although interpretable, had an error rate of 33%, making it less accurate for this task.

In conclusion, I would select the Random Forest tuned with caret, optimized through cross-validation, to maximize effectiveness in credit risk detection.

13 References