데이터분석응용 in R_로지스틱 회귀 pt2 실습260415
# 이전에 데이터부터 싹 깔끔히 날려주고
# 새로운 시작!
rm(list=ls())
# install.packages("caret")
# install.packages("glmnet")
# 혹시 안깔아뒀다면 스근하게 깔아주고~~
# 라이브러리 호출!
library(tidyverse)
library(caret) # 데이터 분할용
library(glmnet) # 로지스틱 회귀
# 여기에서 ID, ZIP Code 열을 제거한 후
# Personal.Loan 을 제외한 열들로 Personal.Loan을 예측해보겠다.
# 1. 데이터 로드 및 기본 전처리
bank_df <- read.csv("./datasets/UniversalBank.csv")

# ID, ZIP Code 제거
bank_df <- bank_df %>% select(-ID, -'ZIP.Code')
# 참고로 ID, ZIP Code는
# Personal.Loan 예측에 필요없을것으로 보이니 먼저 제거하려는 것이다.
# 열이름의 공백을 언더스코어로 변경
(names(bank_df) <- gsub("\\.", "_", names(bank_df)))
# .은 있는지도 없는지도 모르겠으니 일괄적으로 미리 변경해주는것이다.
# 괄호도 쳐서 결과값 출력도!
[1] "Age"
[2] "Experience"
[3] "Income"
[4] "Family"
[5] "CCAvg"
[6] "Education"
[7] "Mortgage"
[8] "Personal_Loan"
[9] "Securities_Account"
[10] "CD_Account"
[11] "Online"
[12] "CreditCard"
# bank_df의 구조 확인
str(bank_df)
'data.frame': 5000 obs. of 12 variables:
$ Age : int 25 45 39 35 35 37 53 50 35 34 ...
$ Experience : int 1 19 15 9 8 13 27 24 10 9 ...
$ Income : int 49 34 11 100 45 29 72 22 81 180 ...
$ Family : int 4 3 1 1 4 4 2 1 3 1 ...
$ CCAvg : num 1.6 1.5 1 2.7 1 0.4 1.5 0.3 0.6 8.9 ...
$ Education : int 1 1 1 2 2 2 2 3 2 3 ...
$ Mortgage : int 0 0 0 0 0 155 0 0 104 0 ...
$ Personal_Loan : int 0 0 0 0 0 0 0 0 0 1 ...
$ Securities_Account: int 1 1 0 0 0 0 0 0 0 0 ...
$ CD_Account : int 0 0 0 0 0 0 0 0 0 0 ...
$ Online : int 0 0 0 0 0 1 1 0 1 0 ...
$ CreditCard : int 0 0 0 0 1 0 0 1 0 0 ...
# 특정 열을 범주형으로 변환하기
# Education에서 숫자들이 의미하는게 고졸, 대졸, 석박졸..(?)임을 인지한다면,
# 이것들은 범주형으로 취급되야한다고 생각할 수 있겠다.
# 현재 int로 되어 있으니 바꿔보자!
# 우선 Education이 가지는 값 종류부터 체크
table(bank_df$Education)
1 2 3
2096 1403 1501
# 예상대로 고졸, 대졸, 석박졸로 분류된게 맞다.
# 라벨을 붙여서 범주형으로 변환하자.
bank_df <- bank_df %>%
mutate(Education = factor(Education,
levels = c(1, 2, 3),
labels = c("Undergrad", "Graduate", "Advanced/Professional")))
# 잘 되었는지 확인!!
str(bank_df)
'data.frame': 5000 obs. of 12 variables:
$ Age : int 25 45 39 35 35 37 53 50 35 34 ...
$ Experience : int 1 19 15 9 8 13 27 24 10 9 ...
$ Income : int 49 34 11 100 45 29 72 22 81 180 ...
$ Family : int 4 3 1 1 4 4 2 1 3 1 ...
$ CCAvg : num 1.6 1.5 1 2.7 1 0.4 1.5 0.3 0.6 8.9 ...
$ Education : Factor w/ 3 levels "Undergrad","Graduate",..: 1 1 1 2 2 2 2 3 2 3 ...
$ Mortgage : int 0 0 0 0 0 155 0 0 104 0 ...
$ Personal_Loan : int 0 0 0 0 0 0 0 0 0 1 ...
$ Securities_Account: int 1 1 0 0 0 0 0 0 0 0 ...
$ CD_Account : int 0 0 0 0 0 0 0 0 0 0 ...
$ Online : int 0 0 0 0 0 1 1 0 1 0 ...
$ CreditCard : int 0 0 0 0 1 0 0 1 0 0 ...
# 아참. 훈련돌리기전에 필수적인 사항.
# Personal_Loan도 범주형으로 변환해야한다.
# 로지스틱 회귀를 돌리기 위한 전제조건이기 때문이다!
# 우선 값의 종류를 한번 보자.
table(bank_df$Personal_Loan)
0 1
4520 480
# 이항분류하기 적합!
# 범주화한 후 구조 재출력해보자
bank_df$Personal_Loan <- factor(bank_df$Personal_Loan)
str(bank_df)
'data.frame': 5000 obs. of 12 variables:
$ Age : int 25 45 39 35 35 37 53 50 35 34 ...
$ Experience : int 1 19 15 9 8 13 27 24 10 9 ...
$ Income : int 49 34 11 100 45 29 72 22 81 180 ...
$ Family : int 4 3 1 1 4 4 2 1 3 1 ...
$ CCAvg : num 1.6 1.5 1 2.7 1 0.4 1.5 0.3 0.6 8.9 ...
$ Education : Factor w/ 3 levels "Undergrad","Graduate",..: 1 1 1 2 2 2 2 3 2 3 ...
$ Mortgage : int 0 0 0 0 0 155 0 0 104 0 ...
$ Personal_Loan : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 2 ...
$ Securities_Account: int 1 1 0 0 0 0 0 0 0 0 ...
$ CD_Account : int 0 0 0 0 0 0 0 0 0 0 ...
$ Online : int 0 0 0 0 0 1 1 0 1 0 ...
$ CreditCard : int 0 0 0 0 1 0 0 1 0 0 ...
# 기본 세팅 끝
# 종속변수(y)와 독립변수(X) 분리해보자.
y <- bank_df$Personal_Loan
X <- bank_df %>% select(-Personal_Loan)
# 훈련/검증 데이터 분할하자. (훈련:검증 = 60:40)
set.seed(1)
# set.seed(1)는 실험할적마다 결과 안바뀌게 할려고
train_index <- createDataPartition(y, p = 0.6, list = FALSE)
# createDataPartition 함수는 단순히 데이터를 나누는게 아니라
# y의 클래스 비율을 유지하면서 데이터를 나눠줌
# list = false를 하면 나중에 index를 벡터 형태로 반환해줌
# 뽑은걸 슬쩍 봐보자.
head(train_index)
Resample1
[1,] 1
[2,] 3
[3,] 4
[4,] 5
[5,] 6
[6,] 7
# 잘 나온것 같으니,,
# 6:4로 분할한 index 차례로 df에 넣어주자.
# train, test, 그리고 그들의 y값에도 넣어줘야 6:4 완료다!
train_df <- bank_df[train_index, ]
valid_df <- bank_df[-train_index, ]
train_y <- bank_df$Personal_Loan[train_index]
valid_y <- bank_df$Personal_Loan[-train_index]
# 로지스틱 회귀 모델 적합해보자
logit_reg <- glm(Personal_Loan ~ .,
data = train_df,
family = binomial(link = "logit"))
# family = binomial(link = "logit")는
# 종속변수가 이항분포를 가지며
# 로짓변환을 이용한다는 뜻.
# 즉, 로지스틱 회귀
# 결과 출력해보자
summary(logit_reg)
Call:
glm(formula = Personal_Loan ~ ., family = binomial(link = "logit"),
data = train_df)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -1.307e+01 2.314e+00 -5.650 1.61e-08 ***
Age -1.021e-02 8.477e-02 -0.120 0.904150
Experience 2.014e-02 8.448e-02 0.238 0.811601
Income 6.068e-02 3.901e-03 15.554 < 2e-16 ***
Family 6.713e-01 9.958e-02 6.741 1.57e-11 ***
CCAvg 1.569e-01 5.868e-02 2.673 0.007517 **
EducationGraduate 3.906e+00 3.414e-01 11.443 < 2e-16 ***
EducationAdvanced/Professional 3.968e+00 3.392e-01 11.700 < 2e-16 ***
Mortgage 9.171e-04 7.688e-04 1.193 0.232914
Securities_Account -1.326e+00 4.161e-01 -3.188 0.001432 **
CD_Account 3.872e+00 4.295e-01 9.015 < 2e-16 ***
Online -6.895e-01 2.098e-01 -3.287 0.001012 **
CreditCard -8.785e-01 2.608e-01 -3.368 0.000757 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1897.2 on 2999 degrees of freedom
Residual deviance: 720.4 on 2987 degrees of freedom
AIC: 746.4
Number of Fisher Scoring iterations: 8
# Income, Family, CCAvg,,,CreditCard 등 유의미한 변수가 많다~~~
# 회귀 계수들만 따로 추출해보기. 절편 제외
coef(logit_reg)[-1]
Age Experience
-0.0102081878 0.0201372747
Income Family
0.0606773777 0.6712530553
CCAvg EducationGraduate
0.1568570150 3.9064261503
EducationAdvanced/Professional Mortgage
3.9681371558 0.0009170549
Securities_Account CD_Account
-1.3264514304 3.8720628033
Online CreditCard
-0.6895199295 -0.8785267112
# 만들어진 모델로 확률값 추출해보자
predictions <- predict(logit_reg, newdata = valid_df, type = "response")
# 검증 데이터를 모델에 넣어 예측 확률을 계산
# type = "response"는 예측값을 확률로 반환하라는 뜻
head(predictions, 20)
2 8 10 13 17 19
3.845908e-05 3.493688e-04 9.762645e-01 1.674083e-01 9.020834e-01 9.943503e-01
21 23 24 27 28 29
4.677389e-03 1.116869e-04 3.441551e-05 1.915253e-01 1.671022e-02 1.213129e-03
34 35 36 37 40 43
2.364308e-03 1.779875e-02 2.368770e-03 4.574626e-03 1.179604e-01 7.687487e-01
49 50
4.436226e-02 4.056307e-05
# 이제 컷오프로 분류하기!
predictions_binary <- ifelse(predictions >0.5, 1, 0)
# 이제 실제답과 예측한 값 비교하기!
data.frame(valid_y, predictions_binary)
valid_y predictions_binary
2 0 0
8 0 0
10 1 1
13 0 0
17 1 1
19 1 1
21 0 0
23 0 0
24 0 0
27 0 0
28 0 0
29 0 0
34 0 0
35 0 0
36 0 0
37 0 0
40 0 0
43 1 1
49 0 0
50 0 0
52 0 0
54 1 1
55 0 0
57 0 0
58 1 1
61 0 0
66 0 0
71 0 0
74 0 0
77 0 0
82 0 0
84 0 0
85 0 0
87 0 0
89 0 0
90 0 0
100 0 0
# 등등 밑 부분 생략
# 제일 좋은 상황은 p_b가 v_y와 완전히 일치하는 것
# 성능평가
# 훈련데이터 성능평가
logit_reg_pred_train <- predict(logit_reg, newdata = train_df, type = "response")
logit_reg_pred_train_class <- ifelse(logit_reg_pred_train > 0.5, 1, 0)
confusionMatrix(factor(logit_reg_pred_train_class),
factor(train_y), positive = "1")
# 입력값이 반드시 범주형이여야함을 유의하자!!!!
# 우리는 1을 찾는데 관심이 있으므로, 1을 positve으로 설정한것.
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 2678 92
1 34 196
Accuracy : 0.958
95% CI : (0.9502, 0.9649)
No Information Rate : 0.904
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.7341
Mcnemar's Test P-Value : 3.815e-07
Sensitivity : 0.68056
Specificity : 0.98746
Pos Pred Value : 0.85217
Neg Pred Value : 0.96679
Prevalence : 0.09600
Detection Rate : 0.06533
Detection Prevalence : 0.07667
Balanced Accuracy : 0.83401
'Positive' Class : 1
# 검증 데이터 성능 평가
logit_reg_pred_valid <- predict(logit_reg, newdata = valid_df, type = "response")
logit_reg_pred_valid_class <- ifelse(logit_reg_pred_valid > 0.5, 1, 0)
confusionMatrix(factor(logit_reg_pred_valid_class),
factor(valid_y), positive = "1")
Confusion Matrix and Statistics
Reference
Prediction 0 1
0 1787 58
1 21 134
Accuracy : 0.9605
95% CI : (0.951, 0.9686)
No Information Rate : 0.904
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.751
Mcnemar's Test P-Value : 5.115e-05
Sensitivity : 0.6979
Specificity : 0.9884
Pos Pred Value : 0.8645
Neg Pred Value : 0.9686
Prevalence : 0.0960
Detection Rate : 0.0670
Detection Prevalence : 0.0775
Balanced Accuracy : 0.8432
'Positive' Class : 1
# train 성능 > valid 성능인 경우 과적합의 가능성이 있다
# train 성능과 valid 성능이 비슷하다면 일반화 성공했다고 볼 수 있다.
# 성능의 지표라 볼 수 있는 Accuracy가 0.958 vs 0.9605 라서
# 후자다. 굿!
# confusion Matrix에 쓰인 성능 지표들,,
# 이해하기 쉽게 예시와 곁들였다.
# 1. accuracy : (TP + TN) / (TP + TN + FP + FN)
# 2. sensitivity (recall) : TP / (TP + FN)
# 민감도/재현율
# P인것들 중 실제 P인 비율이니, 1-sensitivity는 잠재고객
# 3. specificity : TN / (TN + FP)
# 특이도
# N인것들 중 실제 N 비율이니, 대출 안할 고객을 맞힌 비율.
# 4. Precision : TP / (TP + FP)
# Pos Pred Value(정밀도)
# 예상 잠재(전체 P) 중 실제 고객 비율(T인 P)
# 5. Balanced Accuracy : (Sensitivity + Specificity) / 2
# 불균형 데이터에서 가장 믿을만한 지표
# 민감도와 특이도의 평균치
# 모델의 전반적인 균형 감각
# 추가적인 Insight
# 1. 검증 데이터를 보아하니,,
# Reference
# Prediction 0 1
# 0 1787 58
# 1 21 134
# 확실하지 않으면
# 안할거라고 예측하는 경향이 있다.
# 2. 수익 극대화가 목적이면
# 컷오프 임계값을 조정해보자.