Biblioteka
Zaladowanie danych.
library(googlesheets4)
gs4_deauth()
kredyty <- read_sheet("1ZxsqdQ8NlfZ5etCP9LR1vXNWkIcS6mgcnbc_8L-x-pA")
## v Reading from "kredyty_auto_Scoring2025s".
## v Range 'Training'.
head(kredyty)
## # A tibble: 6 x 13
## LP data_akceptacji grupa_ryzyka kod_partnera typ_umowy scoring_FICO
## <dbl> <dttm> <dbl> <dbl> <chr> <dbl>
## 1 1 2005-04-17 00:00:00 2 2 R 702
## 2 2 2005-04-17 00:00:00 2 2 N 719
## 3 3 2005-04-17 00:00:00 1 3 N 802
## 4 4 2005-04-17 00:00:00 1 1 R 723
## 5 5 2005-04-17 00:00:00 3 1 U 690
## 6 6 2005-04-21 00:00:00 1 3 N 681
## # i 7 more variables: okres_kredytu <dbl>, kwota_kredytu <dbl>,
## # oproc_refin <dbl>, oproc_konkur <dbl>, koszt_pieniadza <dbl>,
## # oproc_propon <dbl>, akceptacja_klienta <dbl>
Pierwszym krokiem jest sprawdzenie statystyk opisowych zmiennyc. w danych “kredyty” jest kilka pozycji, z ktorych nie trzeba opisywac takie jak LP, data_akceptacji, grupa_ryzyka, kod_partnera, typ_umowy, koszt_pieniadza, wiec je wykluczamy. Zmienne: grupa ryzyka, kod partnera oraz typ umowy zostaną przedstawione w tabeli.
stat_op<-psych::describe(kredyty %>% select(-c(LP,data_akceptacji,grupa_ryzyka,kod_partnera,typ_umowy, koszt_pieniadza)))
stat_op
## vars n mean sd median trimmed mad
## scoring_FICO 1 40000 720.73 46.58 717.00 719.12 51.89
## okres_kredytu 2 40000 57.19 11.39 60.00 57.98 8.90
## kwota_kredytu 3 40000 24211.65 11211.39 23000.00 23463.43 11079.79
## oproc_refin 4 40000 0.02 0.04 0.00 0.02 0.00
## oproc_konkur 5 40000 0.05 0.01 0.05 0.05 0.01
## oproc_propon 6 40000 0.07 0.02 0.06 0.06 0.02
## akceptacja_klienta 7 40000 0.26 0.44 0.00 0.20 0.00
## min max range skew kurtosis se
## scoring_FICO 601.00 8.51e+02 250.00 0.27 -0.71 0.23
## okres_kredytu 36.00 7.20e+01 36.00 -0.60 -0.53 0.06
## kwota_kredytu 4526.62 1.00e+05 95473.38 0.87 1.83 56.06
## oproc_refin 0.00 2.40e-01 0.24 1.63 2.06 0.00
## oproc_konkur 0.03 6.00e-02 0.03 -0.09 0.45 0.00
## oproc_propon 0.03 1.10e-01 0.09 0.64 -0.57 0.00
## akceptacja_klienta 0.00 1.00e+00 1.00 1.10 -0.79 0.00
Wykresy zmiennych.
#Histogram scoring_FICO
ggplot(kredyty, aes(x = scoring_FICO)) +
geom_histogram(fill = "steelblue", color = "white", bins = 30) +
theme_minimal() +
labs(title = "Rozkład scoringu FICO", x = "Scoring FICO", y = "Liczba klientów")
# Histogram kwota_kredytu
ggplot(kredyty, aes(x = kwota_kredytu)) +
geom_histogram(fill = "darkorange", color = "white", bins = 30) +
theme_minimal() +
labs(title = "Rozkład kwoty kredytu", x = "Kwota kredytu", y = "Liczba klientów")
# Wykres słupkowy typ_umowy
ggplot(kredyty, aes(x = typ_umowy)) +
geom_bar(fill = "darkgreen") +
theme_minimal() +
labs(title = "Liczba przypadków według typu umowy", x = "Typ umowy", y = "Liczba") +
theme(axis.text = element_text(size = 12), plot.title = element_text(size = 14, face = "bold"))
# Boxplot kwota kredytu vs akceptacja
ggplot(kredyty, aes(x = factor(akceptacja_klienta), y = kwota_kredytu, fill = factor(akceptacja_klienta))) +
geom_boxplot() +
scale_fill_manual(values = c("#F8766D", "#00BA38")) +
theme_minimal() +
labs(title = "Kwota kredytu a akceptacja klienta", x = "Akceptacja (0 = NIE, 1 = TAK)", y = "Kwota kredytu")
# Średni scoring FICO vs akceptacja
kredyty %>%
group_by(akceptacja_klienta) %>%
summarise(sredni_scor = mean(scoring_FICO, na.rm = TRUE)) %>%
ggplot(aes(x = factor(akceptacja_klienta), y = sredni_scor, fill = factor(akceptacja_klienta))) +
geom_col() +
scale_fill_manual(values = c("#619CFF", "#F564E3")) +
theme_minimal() +
labs(title = "Średni scoring FICO wg akceptacji", x = "Akceptacja", y = "Średni scoring")
# Udział akceptacji wg grupy ryzyka
ggplot(kredyty, aes(x = factor(grupa_ryzyka), fill = factor(akceptacja_klienta))) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent) +
scale_fill_manual(values = c("gray70", "darkblue")) +
theme_minimal() +
labs(title = "Procent akceptacji w grupach ryzyka", x = "Grupa ryzyka", y = "Udział (%)")
# Scatterplot scoring vs kwota
ggplot(kredyty, aes(x = scoring_FICO, y = kwota_kredytu)) +
geom_density_2d_filled(alpha = 0.7) +
theme_minimal() +
labs(title = "Gęstość 2D: scoring FICO vs kwota kredytu",
x = "Scoring FICO", y = "Kwota kredytu", fill = "Gęstość")
Sprawdzamy za pomoca funkcji table, ile wystepuje grup ryzyka i ile jest akcetpacji w danej grupie ryzka i taki same pivoty robimy dla innych kombinacji.
table(kredyty$grupa_ryzyka)
##
## 1 2 3 4
## 17448 8265 8977 5310
table(kredyty$kod_partnera)
##
## 1 2 3
## 14810 6243 18947
table(kredyty$typ_umowy)
##
## N R U
## 14323 11298 14379
table(kredyty$akceptacja_klienta)
##
## 0 1
## 29633 10367
table(kredyty$akceptacja_klienta, kredyty$grupa_ryzyka)
##
## 1 2 3 4
## 0 11357 6530 7432 4314
## 1 6091 1735 1545 996
table(kredyty$akceptacja_klienta, kredyty$kod_partnera)
##
## 1 2 3
## 0 10040 5701 13892
## 1 4770 542 5055
table(kredyty$akceptacja_klienta, kredyty$typ_umowy)
##
## N R U
## 0 12625 6692 10316
## 1 1698 4606 4063
aggregate(scoring_FICO ~ grupa_ryzyka, data = kredyty, FUN = mean)
## grupa_ryzyka scoring_FICO
## 1 1 759.5769
## 2 2 710.1155
## 3 3 693.6748
## 4 4 655.3380
aggregate(kwota_kredytu ~ grupa_ryzyka, data = kredyty, FUN = mean)
## grupa_ryzyka kwota_kredytu
## 1 1 24387.22
## 2 2 25140.98
## 3 3 24941.85
## 4 4 20953.79
Opis wyników: najwiecej klientow znajduje sie w 1 grupie ryzyka. Najczesciej wystepujacy kod partnera to 2. Wnioski o finansowanie na nowe i uzywane samochodu sa skladane podobnie czesto. Okolo 25% klienktow zaakceptowalo oferte. Partner 2 ma bardzo niski współczynnik akceptacji: tylko 542 osoby zaakceptowane spośród 6 243. Im wyższa grupa ryzyka tym nizszy scroring FIC0. Im wyższa grupa ryzyka tym niższa kwota kredytu.
Dodanie zmiennych pochodnych
kredyty <- kredyty %>%
mutate(
koszt_marzy = oproc_propon - koszt_pieniadza,
kwota_na_miesiac = kwota_kredytu / okres_kredytu,
relacja_oproc_konkur = oproc_propon - oproc_konkur
)
Zmienne grupa ryzyka, kod partera, typ_umowy oraz akceptacja_klienta sa zmiennymi skategoryzowanymi. Powinny byc zapisane jako facctor (oprocz akceptacja klienta) Zmienne scoring FICO, okres kredytu, kwota kredytu, oproc_refin, oproc_konkur, kost_pieniasza oraz oproc_proponowane to zmienne, ktĂłre wymagaja skubelkowania, zanim przejdziemy do obliczenia WOE i IV. Na razie dane zbinuje dzielac je na 10, a na podstawie WOE okresle czy takie zbinowanie jest sensowne Zapisanie zmiennych skateryzowanych jako faktor. Oprocentowanie konkur tez zmienilam na faktor, bo byky z nim problemy.
kredyty <- kredyty %>%
mutate(
grupa_ryzyka = factor(grupa_ryzyka),
kod_partnera = factor(kod_partnera),
typ_umowy = factor(typ_umowy),
)
kredyty <- kredyty %>%
select(-data_akceptacji, -LP)
Niektore zmienne zbinuje recznie.
kredyty$koszt_pieniadza <- cut(kredyty$koszt_pieniadza,
breaks = seq(0.011, 0.021010, length.out =11),
include.lowest = TRUE,
right = FALSE)
kredyty$oproc_konkur <- cut(kredyty$oproc_konkur,
breaks = c(0.02, 0.04, 0.045, 0.05, 0.055, 0.07),
include.lowest = TRUE,
right = FALSE)
bins <- 10
kredyty$oproc_propon<- cut(kredyty$oproc_propon,
breaks = seq(min(kredyty$oproc_propon),
max(kredyty$oproc_propon),
length.out = bins + 1),
include.lowest = TRUE,
right = FALSE)
Obliczenie IV i WOE.
IV <- create_infotables(data=kredyty, y="akceptacja_klienta", bins=10, parallel=FALSE)
IV_Value = data.frame(IV$Summary)
print(IV_Value)
## Variable IV
## 6 kwota_kredytu 0.966148739
## 12 kwota_na_miesiac 0.916174986
## 3 typ_umowy 0.403734127
## 7 oproc_refin 0.284525554
## 13 relacja_oproc_konkur 0.233783214
## 2 kod_partnera 0.220400987
## 11 koszt_marzy 0.204111988
## 10 oproc_propon 0.187659568
## 1 grupa_ryzyka 0.175409351
## 8 oproc_konkur 0.173245076
## 4 scoring_FICO 0.093247876
## 5 okres_kredytu 0.070220937
## 9 koszt_pieniadza 0.008085915
#Wyprintowanie wartosci WOE - podsumowanie
print(IV$Tables, row.names = FALSE)
## $grupa_ryzyka
## grupa_ryzyka N Percent WOE IV
## 1 17448 0.436200 0.4272389 0.08727731
## 2 8265 0.206625 -0.2751386 0.10186089
## 3 8977 0.224425 -0.5205102 0.15483367
## 4 5310 0.132750 -0.4156126 0.17540935
##
## $kod_partnera
## kod_partnera N Percent WOE IV
## 1 14810 0.370250 0.30603011 0.03712218
## 2 6243 0.156075 -1.30286995 0.21966154
## 3 18947 0.473675 0.03932564 0.22040099
##
## $typ_umowy
## typ_umowy N Percent WOE IV
## N 14323 0.358075 -0.9559670 0.2507084
## R 11298 0.282450 0.6767079 0.3985455
## U 14379 0.359475 0.1184864 0.4037341
##
## $scoring_FICO
## scoring_FICO N Percent WOE IV
## [601,659] 3892 0.097300 -0.1978831 0.003625452
## [660,676] 3903 0.097575 -0.3021104 0.011868833
## [677,690] 4068 0.101700 -0.3064896 0.020701165
## [691,703] 3869 0.096725 -0.3683311 0.032630863
## [704,716] 4131 0.103275 -0.1123181 0.033898104
## [717,729] 3809 0.095225 -0.2446837 0.039256761
## [730,745] 4209 0.105225 0.4941902 0.067770815
## [746,763] 3945 0.098625 0.4057750 0.085499635
## [764,786] 4148 0.103700 0.2389825 0.091752344
## [787,851] 4026 0.100650 0.1201937 0.093247876
##
## $okres_kredytu
## okres_kredytu N Percent WOE IV
## [36,36] 6109 0.152725 0.48111512 0.03913377
## [48,48] 5590 0.139750 0.21146724 0.04569277
## [60,60] 19376 0.484400 -0.22557601 0.06897765
## [66,66] 988 0.024700 -0.22344522 0.07014330
## [72,72] 7937 0.198425 -0.01982804 0.07022094
##
## $kwota_kredytu
## kwota_kredytu N Percent WOE IV
## [4526.62,10837.33] 3999 0.099975 1.6154806 0.3183702
## [10848.08,14521] 4000 0.100000 1.0772626 0.4572758
## [14523,17114] 4000 0.100000 0.3891941 0.4737605
## [17116,19999.99] 3744 0.093600 0.3000710 0.4827722
## [20000,22999.99] 4110 0.102750 -0.1694886 0.4856016
## [23000,25488] 4146 0.103650 -0.6266782 0.5199981
## [25489,29999.88] 3898 0.097450 -0.4260002 0.5358210
## [29999.99,33728.1] 4102 0.102550 -1.2373985 0.6462079
## [33733,39897] 3999 0.099975 -1.4872749 0.7901769
## [39900,1e+05] 4002 0.100050 -1.6973823 0.9661487
##
## $oproc_refin
## oproc_refin N Percent WOE IV
## [0,0.06] 31871 0.796775 -0.2973024 0.06527248
## [0.06,0.09] 4095 0.102375 0.6498807 0.11449319
## [0.09,0.24] 4034 0.100850 1.1823316 0.28452555
##
## $oproc_konkur
## oproc_konkur N Percent WOE IV
## [0.02,0.04) 2007 0.050175 0.01508932 1.146567e-05
## [0.04,0.045) 8049 0.201225 -0.93381905 1.353520e-01
## [0.045,0.05) 10976 0.274400 0.05539797 1.362053e-01
## [0.05,0.055) 14173 0.354325 0.20333262 1.515533e-01
## [0.055,0.07] 4795 0.119875 0.40706984 1.732451e-01
##
## $koszt_pieniadza
## koszt_pieniadza N Percent WOE IV
## <NA> 1 0.000025 0.00000000 0.0000000000
## [0.011,0.012) 12486 0.312150 0.03185066 0.0003190856
## [0.012,0.013) 3358 0.083950 -0.15526107 0.0022660753
## [0.013,0.014) 5121 0.128025 -0.07905035 0.0030507515
## [0.014,0.015) 2454 0.061350 -0.01922309 0.0030733169
## [0.015,0.016) 4906 0.122650 0.12224069 0.0049592201
## [0.016,0.017) 4507 0.112675 0.10143682 0.0061465688
## [0.017,0.018) 2357 0.058925 0.02443832 0.0061819672
## [0.018,0.019) 4809 0.120225 -0.12785030 0.0080859146
## [0.02,0.021] 1 0.000025 0.00000000 0.0080859146
##
## $oproc_propon
## oproc_propon N Percent WOE IV
## [0.0259,0.0347) 395 0.009875 0.98441436 0.01135978
## [0.0347,0.0434) 2372 0.059300 0.05683582 0.01155394
## [0.0434,0.0522) 11697 0.292425 0.30784397 0.04123271
## [0.0522,0.0609) 3979 0.099475 0.64521466 0.08834119
## [0.0609,0.0697) 6024 0.150600 -0.19260466 0.09366457
## [0.0697,0.0785) 4591 0.114775 -0.12746858 0.09547154
## [0.0785,0.0872) 4728 0.118200 -0.71655228 0.14542216
## [0.0872,0.096) 1864 0.046600 -0.41080257 0.15248817
## [0.096,0.105) 895 0.022375 -0.35005892 0.15499331
## [0.105,0.113] 3455 0.086375 -0.67359768 0.18765957
##
## $koszt_marzy
## koszt_marzy N Percent WOE IV
## [0.01,0.03] 3995 0.099875 -0.04135484 0.0001691003
## [0.03,0.03] 4003 0.100075 0.03422692 0.0002872991
## [0.03,0.04] 4000 0.100000 0.58188199 0.0384196092
## [0.04,0.04] 3978 0.099450 0.68423754 0.0916967242
## [0.04,0.05] 4022 0.100550 0.30215072 0.1015163800
## [0.05,0.05] 3997 0.099925 -0.03936479 0.1016697487
## [0.05,0.06] 4004 0.100100 -0.22252209 0.1063558641
## [0.06,0.07] 3963 0.099075 -0.79002559 0.1561544436
## [0.07,0.09] 4036 0.100900 -0.34099623 0.1669005364
## [0.09,0.1] 4002 0.100050 -0.66739058 0.2041119883
##
## $kwota_na_miesiac
## kwota_na_miesiac N Percent WOE IV
## [69.44,208.33] 3987 0.099675 1.54413359 0.2900567
## [208.33,263.89] 4008 0.100200 0.92134014 0.3903739
## [263.89,312.5] 3925 0.098125 0.65645961 0.4385594
## [312.5,354.17] 4077 0.101925 0.18055349 0.4420233
## [354.17,409.4] 4002 0.100050 0.02815652 0.4421032
## [409.44,445.82] 3998 0.099950 -0.67203409 0.4797453
## [445.83,500] 3414 0.085350 -0.65275737 0.5102428
## [500,575.76] 4584 0.114600 -1.08178478 0.6091312
## [575.76,666.67] 3410 0.085250 -1.60940262 0.7476017
## [666.67,2777.78] 4595 0.114875 -1.50558367 0.9161750
##
## $relacja_oproc_konkur
## relacja_oproc_konkur N Percent WOE IV
## [-0.02,0] 3356 0.083900 0.61565666 0.03600958
## [0,0] 2531 0.063275 0.39656543 0.04685442
## [0,0.01] 13990 0.349750 0.34475796 0.09170548
## [0.01,0.02] 4090 0.102250 -0.01793815 0.09173824
## [0.02,0.03] 4013 0.100325 -0.41025951 0.10691273
## [0.03,0.03] 3931 0.098275 -0.73646225 0.15052598
## [0.04,0.05] 4076 0.101900 -0.70840261 0.19271554
## [0.05,0.08] 4013 0.100325 -0.70392063 0.23378321
Zapisanie wynikow do tabeli
grupa_ryzyka = data.frame(IV$Tables$grupa_ryzyka)
kod_partnera = data.frame(IV$Tables$kod_partnera)
typ_umowy = data.frame(IV$Tables$typ_umowy)
scoring_FICO = data.frame(IV$Tables$scoring_FICO)
okres_kredytu = data.frame(IV$Tables$okres_kredytu)
kwota_kredytu = data.frame(IV$Tables$kwota_kredytu)
oproc_refin = data.frame(IV$Tables$oproc_refin)
oproc_konkur = data.frame(IV$Tables$oproc_konkur)
koszt_pieniadza = data.frame(IV$Tables$koszt_pieniadza)
oproc_propon = data.frame(IV$Tables$oproc_propon)
Rysowanie wykresow wartosci WOE
plot_infotables(IV, "grupa_ryzyka", same_scale = FALSE)
plot_infotables(IV, "kod_partnera", same_scale = FALSE)
plot_infotables(IV, "typ_umowy", same_scale = FALSE)
plot_infotables(IV, "scoring_FICO", same_scale = FALSE)
plot_infotables(IV, "okres_kredytu", same_scale = FALSE)
plot_infotables(IV, "kwota_kredytu", same_scale = FALSE)
plot_infotables(IV, "oproc_refin", same_scale = FALSE)
plot_infotables(IV, "oproc_konkur", same_scale = FALSE)
plot_infotables(IV, "koszt_pieniadza", same_scale = FALSE)
plot_infotables(IV, "oproc_propon", same_scale = FALSE)
Czesc 2
library(dplyr)
library(scorecard)
library(kableExtra)
## Warning: pakiet 'kableExtra' został zbudowany w wersji R 4.1.3
##
## Dołączanie pakietu: 'kableExtra'
## Następujący obiekt został zakryty z 'package:dplyr':
##
## group_rows
library(pROC)
## Warning: pakiet 'pROC' został zbudowany w wersji R 4.1.3
## Type 'citation("pROC")' for a citation.
##
## Dołączanie pakietu: 'pROC'
## Następujące obiekty zostały zakryte z 'package:stats':
##
## cov, smooth, var
library(bigstatsr)
## Warning: pakiet 'bigstatsr' został zbudowany w wersji R 4.1.3
##
## Dołączanie pakietu: 'bigstatsr'
## Następujący obiekt został zakryty z 'package:psych':
##
## AUC
library(dplyr)
library(scorecard)
library(kableExtra)
library(pROC)
library(bigstatsr)
library(readxl)
knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE
)
Definicja ręcznych podziałów dla zmiennych kategorycznych
breaks_list = list(
grupa_ryzyka = as.character(sort(unique(kredyty$grupa_ryzyka))),
kod_partnera = as.character(sort(unique(kredyty$kod_partnera))),
typ_umowy = as.character(sort(unique(kredyty$typ_umowy)))
)
Fragment kodu tworzy listę breaks_list, która zawiera unikalne, posortowane wartości dla trzech zmiennych kategorycznych: grupa_ryzyka, kod_partnera oraz typ_umowy. Każda z tych zmiennych jest konwertowana na typ tekstowy, aby mogła być wykorzystana do ręcznego binningowania w funkcji woebin(). Zapewni to, że zmienne zostaną zaklasyfikowane dokładnie według istniejących kategorii, bez automatycznego łączenia grup.
Tworzenie binów WOE dla zmiennych
bins <- woebin(
kredyty,
y = "akceptacja_klienta",
var_skip = c("LP", "data_akceptacji"),
breaks_list = breaks_list
)
## v Binning on 40000 rows and 14 columns in 00:00:03
Utworzono biny WOE dla zmiennych z danych kredyty, przyjmując akceptacja_klienta jako zmienną docelową. Zmiennym LP i data_akceptacji pomija binning, a dla zmiennych kategorycznych używa ręcznie zdefiniowanych przedziałów z breaks_list.
Przekształcenie danych na wartości WOE
kredyty_woe <- woebin_ply(kredyty, bins, to = "woe")
## v Woe transformating on 40000 rows and 13 columns in 00:00:11
Ten kod przekształca dane kredyty na wersję binarną odpowiadającą przypisanym przedziałom WOE, zgodnie z wcześniej stworzonymi binami. Utworzy to nową ramkę danych na podstawie której będzie ustawiany model.
Budowa modelu regresji logistycznej
model <- glm(
akceptacja_klienta ~ .,
family = binomial(),
data = kredyty_woe %>% select(-any_of(c("LP", "data_akceptacji")))
)
Na podstawie zbioru treningowego opracowano model regresji logistycznej, wykorzystując zmienne przekształcone do formatu WOE (Weight of Evidence). Taka transformacja pozwala uwzględnić relacje nieliniowe między zmiennymi a zmienną celu, a także ogranicza wpływ wartości odstających. Model prognozuje prawdopodobieństwo zaakceptowania klienta (akceptacja_klienta = 1) w oparciu o cechy kredytu, klienta i oferty.
Zmienne objaśniające zostały wcześniej zbinowane na podstawie optymalnych lub zdefiniowanych przedziałów, co umożliwiło ich jednoznaczne zakodowanie. Wśród predyktorów znalazły się zarówno zmienne liczbowe, jak i kategoryczne, m.in.:
grupa_ryzyka — gdzie wyższe grupy (np. 3 i 4) istotnie obniżają szanse akceptacji,
kod_partnera_bin2 — o silnym negatywnym wpływie (Estimate = -1.88, p < 0.001),
typ_umowy_binR i typ_umowy_binU — które zwiększają prawdopodobieństwo akceptacji (Estimate ≈ 1.1, p < 0.001),
a także scoring_FICO, kwota_kredytu, oproc_refin, kwota_na_miesiac i inne.
Zdecydowana większość zmiennych osiągnęła istotność statystyczną (p < 0.05), co wskazuje na ich wysoką wartość predykcyjną. Wyjątki (np. część przedziałów koszt_pieniadza czy relacja_oproc_konkur) wykazały mniejszą istotność, co może sugerować ich ograniczoną przydatność w końcowej wersji modelu.
Oceniając jakość modelu:
AUC wynosi 0.84, co oznacza wysoką zdolność modelu do rozróżniania pomiędzy klientami zaakceptowanymi a odrzuconymi,
Wskaźnik Gini = 0.68, również potwierdza dobrą separację klas,
Redukcja deviance z 32051 do 23282 wskazuje na dobrą poprawę dopasowania względem modelu zerowego,
AIC = 23360, co może być wykorzystane jako punkt odniesienia przy porównywaniu alternatywnych modeli.
Predykcja prawdopodobieństw na zbiorze testowym
#pred_probs <- predict(model, newdata = test_data, type = "response")
Krzywa ROC i AUC
# Oblicz predykcje na całym zbiorze
pred_probs <- predict(model, newdata = kredyty_woe, type = "response")
# Krzywa ROC i AUC na całym zbiorze
library(pROC)
kredyty_woe$akceptacja_klienta <- as.numeric(as.character(kredyty_woe$akceptacja_klienta))
roc_obj <- roc(kredyty_woe$akceptacja_klienta, pred_probs)
auc_val <- auc(roc_obj)
Wykres ROC
plot(roc_obj,
col = "blue",
lwd = 2,
main = paste("Krzywa ROC - AUC =", round(auc_val, 3)))
Wartość Gini
gini_val <- 2 * auc_val - 1
gini_val
## [1] 0.6654948
Wskaźnik Gini na poziomie 0.68 świadczy o bardzo dobrej zdolności modelu do rozróżniania pomiędzy klientami zaakceptowanymi a odrzuconymi, co oznacza, że model efektywnie klasyfikuje ryzyko i może być skutecznie wykorzystywany w procesie decyzyjnym.
Wyznaczenie optymalnego punktu odcięcia
optimal_cutoff <- coords(roc_obj, "best", ret = "threshold")
Predykcja klasy na podstawie progu dla KAŻDEGO rekordu w zbiorze testowym
pred_class <- ifelse(pred_probs >= optimal_cutoff, 1, 0)
Stworzenie karty scoringowej
# Karta scoringowa (bez jawnego ustawiania points0, zachowuje sumę 108) ---
card <- scorecard(bins, model)
# Funkcja do zebrania pełnej tabeli scoringowej ---
scoring_ranges_df <- function(card) {
dplyr::bind_rows(card, .id = "zmienna")
}
#Stworzenie i wyświetlenie tabeli scoringowej ---
tabela_scoringowa <- scoring_ranges_df(card)
#View(tabela_scoringowa)
# Lub wyświetlić w RMarkdown
tabela_scoringowa %>%
kbl(caption = "Pełna tabela scoringowa") %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
| zmienna | variable | bin | woe | points | count | count_distr | neg | pos | posprob | bin_iv | total_iv | breaks | is_special_values |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| basepoints | basepoints | NA | NA | 464 | NA | NA | NA | NA | NA | NA | NA | NA | NA |
| grupa_ryzyka | grupa_ryzyka | 1 | 0.4272389 | -28 | 17448 | 0.436200 | 11357 | 6091 | 0.3490945 | 0.0872773 | 0.1754094 | 1 | FALSE |
| grupa_ryzyka | grupa_ryzyka | 2 | -0.2751386 | 18 | 8265 | 0.206625 | 6530 | 1735 | 0.2099214 | 0.0145836 | 0.1754094 | 2 | FALSE |
| grupa_ryzyka | grupa_ryzyka | 3 | -0.5205102 | 34 | 8977 | 0.224425 | 7432 | 1545 | 0.1721065 | 0.0529728 | 0.1754094 | 3 | FALSE |
| grupa_ryzyka | grupa_ryzyka | 4 | -0.4156126 | 27 | 5310 | 0.132750 | 4314 | 996 | 0.1875706 | 0.0205757 | 0.1754094 | 4 | FALSE |
| kod_partnera | kod_partnera | 1 | 0.3060301 | -26 | 14810 | 0.370250 | 10040 | 4770 | 0.3220797 | 0.0371222 | 0.2204010 | 1 | FALSE |
| kod_partnera | kod_partnera | 2 | -1.3028700 | 112 | 6243 | 0.156075 | 5701 | 542 | 0.0868172 | 0.1825394 | 0.2204010 | 2 | FALSE |
| kod_partnera | kod_partnera | 3 | 0.0393256 | -3 | 18947 | 0.473675 | 13892 | 5055 | 0.2667969 | 0.0007394 | 0.2204010 | 3 | FALSE |
| typ_umowy | typ_umowy | N | -0.9559670 | 18 | 14323 | 0.358075 | 12625 | 1698 | 0.1185506 | 0.2507084 | 0.4037341 | N | FALSE |
| typ_umowy | typ_umowy | R | 0.6767079 | -13 | 11298 | 0.282450 | 6692 | 4606 | 0.4076828 | 0.1478371 | 0.4037341 | R | FALSE |
| typ_umowy | typ_umowy | U | 0.1184864 | -2 | 14379 | 0.359475 | 10316 | 4063 | 0.2825649 | 0.0051887 | 0.4037341 | U | FALSE |
| scoring_FICO | scoring_FICO | [-Inf,700) | -0.3144567 | -8 | 14390 | 0.359750 | 11462 | 2928 | 0.2034746 | 0.0328179 | 0.0922810 | 700 | FALSE |
| scoring_FICO | scoring_FICO | [700,730) | -0.1597909 | -4 | 9282 | 0.232050 | 7150 | 2132 | 0.2296919 | 0.0056938 | 0.0922810 | 730 | FALSE |
| scoring_FICO | scoring_FICO | [730,770) | 0.4393000 | 11 | 9402 | 0.235050 | 6094 | 3308 | 0.3518400 | 0.0498343 | 0.0922810 | 770 | FALSE |
| scoring_FICO | scoring_FICO | [770, Inf) | 0.1481777 | 4 | 6926 | 0.173150 | 4927 | 1999 | 0.2886226 | 0.0039350 | 0.0922810 | Inf | FALSE |
| okres_kredytu | okres_kredytu | [-Inf,48) | 0.4811151 | 0 | 6109 | 0.152725 | 3901 | 2208 | 0.3614339 | 0.0391338 | 0.0702208 | 48 | FALSE |
| okres_kredytu | okres_kredytu | [48,60) | 0.2114672 | 0 | 5590 | 0.139750 | 3903 | 1687 | 0.3017889 | 0.0065590 | 0.0702208 | 60 | FALSE |
| okres_kredytu | okres_kredytu | [60,72) | -0.2254726 | 0 | 20364 | 0.509100 | 15919 | 4445 | 0.2182774 | 0.0244504 | 0.0702208 | 72 | FALSE |
| okres_kredytu | okres_kredytu | [72, Inf) | -0.0198280 | 0 | 7937 | 0.198425 | 5910 | 2027 | 0.2553862 | 0.0000776 | 0.0702208 | Inf | FALSE |
| kwota_kredytu | kwota_kredytu | [-Inf,10000) | 1.6729663 | -56 | 3179 | 0.079475 | 1110 | 2069 | 0.6508336 | 0.2712168 | 0.9361042 | 10000 | FALSE |
| kwota_kredytu | kwota_kredytu | [10000,14000) | 1.1687825 | -39 | 4156 | 0.103900 | 1955 | 2201 | 0.5295958 | 0.1710333 | 0.9361042 | 14000 | FALSE |
| kwota_kredytu | kwota_kredytu | [14000,20000) | 0.3929395 | -13 | 8408 | 0.210200 | 5538 | 2870 | 0.3413416 | 0.0353464 | 0.9361042 | 20000 | FALSE |
| kwota_kredytu | kwota_kredytu | [20000,30000) | -0.4129267 | 14 | 12363 | 0.309075 | 10039 | 2324 | 0.1879803 | 0.0473234 | 0.9361042 | 30000 | FALSE |
| kwota_kredytu | kwota_kredytu | [30000, Inf) | -1.4488486 | 48 | 11894 | 0.297350 | 10991 | 903 | 0.0759206 | 0.4111843 | 0.9361042 | Inf | FALSE |
| oproc_refin | oproc_refin | [-Inf,0.065) | -0.2973024 | 10 | 31871 | 0.796775 | 25297 | 6574 | 0.2062690 | 0.0652725 | 0.2837004 | 0.065 | FALSE |
| oproc_refin | oproc_refin | [0.065,0.1) | 0.7360215 | -24 | 5480 | 0.137000 | 3167 | 2313 | 0.4220803 | 0.0855535 | 0.2837004 | 0.1 | FALSE |
| oproc_refin | oproc_refin | [0.1, Inf) | 1.2861543 | -43 | 2649 | 0.066225 | 1169 | 1480 | 0.5587014 | 0.1328744 | 0.2837004 | Inf | FALSE |
| oproc_konkur | oproc_konkur | [0.02,0.04) | 0.0150893 | -1 | 2007 | 0.050175 | 1481 | 526 | 0.2620827 | 0.0000115 | 0.1697603 | 0.04 | FALSE |
| oproc_konkur | oproc_konkur | [0.04,0.045) | -0.9338191 | 35 | 8049 | 0.201225 | 7076 | 973 | 0.1208846 | 0.1353406 | 0.1697603 | 0.045 | FALSE |
| oproc_konkur | oproc_konkur | [0.045,0.05)%,%[0.05,0.055) | 0.1399191 | -5 | 25149 | 0.628725 | 17933 | 7216 | 0.2869299 | 0.0127165 | 0.1697603 | 0.055 | FALSE |
| oproc_konkur | oproc_konkur | [0.055,0.07] | 0.4070698 | -15 | 4795 | 0.119875 | 3143 | 1652 | 0.3445255 | 0.0216918 | 0.1697603 | [0.055,0.07] | FALSE |
| koszt_pieniadza | koszt_pieniadza | [0.011,0.012)%,%missing | 0.0317416 | -1 | 12487 | 0.312175 | 9174 | 3313 | 0.2653159 | 0.0003169 | 0.0075512 | 0.012%,%missing | FALSE |
| koszt_pieniadza | koszt_pieniadza | [0.012,0.013) | -0.1552611 | 3 | 3358 | 0.083950 | 2584 | 774 | 0.2304943 | 0.0019470 | 0.0075512 | 0.013 | FALSE |
| koszt_pieniadza | koszt_pieniadza | [0.013,0.014)%,%[0.014,0.015) | -0.0594714 | 1 | 7575 | 0.189375 | 5697 | 1878 | 0.2479208 | 0.0006601 | 0.0075512 | 0.015 | FALSE |
| koszt_pieniadza | koszt_pieniadza | [0.015,0.016)%,%[0.016,0.017)%,%[0.017,0.018) | 0.0949847 | -2 | 11770 | 0.294250 | 8500 | 3270 | 0.2778250 | 0.0027148 | 0.0075512 | 0.018 | FALSE |
| koszt_pieniadza | koszt_pieniadza | [0.018,0.019)%,%[0.02,0.021] | -0.1281222 | 2 | 4810 | 0.120250 | 3678 | 1132 | 0.2353430 | 0.0019123 | 0.0075512 | 0.019%,%[0.02,0.021] | FALSE |
| oproc_propon | oproc_propon | [0.0259,0.0347)%,%[0.0347,0.0434)%,%[0.0434,0.0522) | 0.2887002 | -8 | 14464 | 0.361600 | 9860 | 4604 | 0.3183075 | 0.0321509 | 0.1737405 | 0.0522 | FALSE |
| oproc_propon | oproc_propon | [0.0522,0.0609) | 0.6452147 | -19 | 3979 | 0.099475 | 2387 | 1592 | 0.4001005 | 0.0471085 | 0.1737405 | 0.0609 | FALSE |
| oproc_propon | oproc_propon | [0.0609,0.0697)%,%[0.0697,0.0785) | -0.1641510 | 5 | 10615 | 0.265375 | 8185 | 2430 | 0.2289213 | 0.0068639 | 0.1737405 | 0.0785 | FALSE |
| oproc_propon | oproc_propon | [0.0785,0.0872)%,%[0.0872,0.096)%,%[0.096,0.105)%,%[0.105,0.113] | -0.6145916 | 18 | 10942 | 0.273550 | 9201 | 1741 | 0.1591117 | 0.0876172 | 0.1737405 | 0.105%,%[0.105,0.113] | FALSE |
| koszt_marzy | koszt_marzy | [-Inf,0.032) | -0.0043320 | 0 | 7730 | 0.193250 | 5733 | 1997 | 0.2583441 | 0.0000036 | 0.2226622 | 0.032 | FALSE |
| koszt_marzy | koszt_marzy | [0.032,0.044) | 0.6472858 | 9 | 9703 | 0.242575 | 5816 | 3887 | 0.4005978 | 0.1156519 | 0.2226622 | 0.044 | FALSE |
| koszt_marzy | koszt_marzy | [0.044,0.052) | -0.0977319 | -1 | 5468 | 0.136700 | 4151 | 1317 | 0.2408559 | 0.0012747 | 0.2226622 | 0.052 | FALSE |
| koszt_marzy | koszt_marzy | [0.052,0.058) | 0.2098067 | 3 | 2571 | 0.064275 | 1796 | 775 | 0.3014391 | 0.0029684 | 0.2226622 | 0.058 | FALSE |
| koszt_marzy | koszt_marzy | [0.058, Inf) | -0.5742860 | -8 | 14528 | 0.363200 | 12137 | 2391 | 0.1645787 | 0.1027636 | 0.2226622 | Inf | FALSE |
| kwota_na_miesiac | kwota_na_miesiac | [-Inf,240) | 1.3686085 | -54 | 6101 | 0.152525 | 2569 | 3532 | 0.5789215 | 0.3476300 | 0.8573478 | 240 | FALSE |
| kwota_na_miesiac | kwota_na_miesiac | [240,400) | 0.3861469 | -15 | 13266 | 0.331650 | 8758 | 4508 | 0.3398161 | 0.0537874 | 0.8573478 | 400 | FALSE |
| kwota_na_miesiac | kwota_na_miesiac | [400,500) | -0.6080042 | 24 | 8044 | 0.201100 | 6757 | 1287 | 0.1599950 | 0.0631588 | 0.8573478 | 500 | FALSE |
| kwota_na_miesiac | kwota_na_miesiac | [500, Inf) | -1.3571172 | 54 | 12589 | 0.314725 | 11549 | 1040 | 0.0826118 | 0.3927716 | 0.8573478 | Inf | FALSE |
| relacja_oproc_konkur | relacja_oproc_konkur | [-Inf,0.012) | 0.3985631 | -21 | 19877 | 0.496925 | 13067 | 6810 | 0.3426070 | 0.0860621 | 0.2217037 | 0.012 | FALSE |
| relacja_oproc_konkur | relacja_oproc_konkur | [0.012,0.018) | -0.0179382 | 1 | 4090 | 0.102250 | 3044 | 1046 | 0.2557457 | 0.0000328 | 0.2217037 | 0.018 | FALSE |
| relacja_oproc_konkur | relacja_oproc_konkur | [0.018, Inf) | -0.6333760 | 34 | 16033 | 0.400825 | 13522 | 2511 | 0.1566145 | 0.1356088 | 0.2217037 | Inf | FALSE |
# Funkcja scoringowa ---
score_zespol <- function(df){
scorecard_ply(df %>% select(-LP), card)$score
}
W karcie scoringowej wartość bazowa (basepoints) została ustalona na poziomie 217 punktów i stanowi punkt wyjścia do obliczania końcowego wyniku scoringowego dla każdego klienta. Do tej wartości dodawane lub odejmowane są punkty przypisane do konkretnych przedziałów zmiennych (binów), co pozwala uzyskać ostateczny wynik punktacji indywidualnej. Taka konstrukcja ułatwia interpretację oraz porównywanie klientów pod względem ryzyka.
rmarkdown::render(“projekt_scoring_1.Rmd”, output_format = “html_document”)
#funkcja scoringowa
# --- 1. Wczytaj dane z Google Sheet ---
gs4_deauth()
dane <- read_sheet("https://docs.google.com/spreadsheets/d/1ZxsqdQ8NlfZ5etCP9LR1vXNWkIcS6mgcnbc_8L-x-pA")
# --- 2. Dodaj zmienne pochodne ---
dane <- dane %>%
mutate(
koszt_marzy = oproc_propon - koszt_pieniadza,
kwota_na_miesiac = kwota_kredytu / okres_kredytu,
relacja_oproc_konkur = oproc_propon - oproc_konkur
)
# --- 3. Funkcja scoringowa zgodna z kartą scoringową ---
oblicz_score <- function(df) {
464 +
ifelse(df$grupa_ryzyka == 1, -28,
ifelse(df$grupa_ryzyka == 2, 18,
ifelse(df$grupa_ryzyka == 3, 34, 27))) +
ifelse(df$kod_partnera == 1, -26,
ifelse(df$kod_partnera == 2, 112, -3)) +
ifelse(df$typ_umowy == "N", 18,
ifelse(df$typ_umowy == "R", -13, -2)) +
ifelse(df$scoring_FICO < 700, -8,
ifelse(df$scoring_FICO < 730, -4,
ifelse(df$scoring_FICO < 770, 11, 4))) +
ifelse(df$okres_kredytu < 48, 0,
ifelse(df$okres_kredytu < 60, 0,
ifelse(df$okres_kredytu < 72, 0, 0))) +
ifelse(df$kwota_kredytu < 10000, -56,
ifelse(df$kwota_kredytu < 14000, -39,
ifelse(df$kwota_kredytu < 20000, -13,
ifelse(df$kwota_kredytu < 30000, 14, 48)))) +
ifelse(df$oproc_refin < 0.065, 10,
ifelse(df$oproc_refin < 0.1, -24, -43)) +
ifelse(df$oproc_konkur < 0.04, -1,
ifelse(df$oproc_konkur < 0.045, 35,
ifelse(df$oproc_konkur < 0.055, -5, -15))) +
ifelse(df$koszt_pieniadza < 0.012, -1,
ifelse(df$koszt_pieniadza < 0.013, 3,
ifelse(df$koszt_pieniadza < 0.015, 1,
ifelse(df$koszt_pieniadza < 0.018, -2, 2)))) +
ifelse(df$oproc_propon < 0.0522, -8,
ifelse(df$oproc_propon < 0.0609, -19,
ifelse(df$oproc_propon < 0.0785, 5, 18))) +
ifelse(df$koszt_marzy < 0.032, 0,
ifelse(df$koszt_marzy < 0.044, 9,
ifelse(df$koszt_marzy < 0.052, -1,
ifelse(df$koszt_marzy < 0.058, 3, -8)))) +
ifelse(df$kwota_na_miesiac < 240, -54,
ifelse(df$kwota_na_miesiac < 400, -15,
ifelse(df$kwota_na_miesiac < 500, 24, 54))) +
ifelse(df$relacja_oproc_konkur < 0.012, -21,
ifelse(df$relacja_oproc_konkur < 0.018, 1, 34))
}
# --- 4. Oblicz scoring ---
dane$score <- oblicz_score(dane)
# --- 5. AUC i Gini ---
roc_obj <- roc(dane$akceptacja_klienta, -dane$score)
auc_val <- auc(roc_obj)
gini_val <- 2 * auc_val - 1
print(paste("AUC =", round(auc_val, 4)))
## [1] "AUC = 0.8328"
print(paste("Gini =", round(gini_val, 4)))
## [1] "Gini = 0.6656"
```