📌 페널티 로지스틱 회귀분석(Penalized Logistic Regression Analysis)
📌 Lasso 회귀분석을 이용한 페널티 로지스틱 회귀분석
환자의 당뇨병 여부와 관련된 임상 데이터
- diabetes (결과변수) : 당뇨병 여부
> library(mlbench)
> data("PimaIndiansDiabetes2")
> str(PimaIndiansDiabetes2)
'data.frame': 768 obs. of 9 variables:
$ pregnant: num 6 1 8 1 0 5 3 10 2 8 ...
$ glucose : num 148 85 183 89 137 116 78 115 197 125 ...
$ pressure: num 72 66 64 66 40 74 50 NA 70 96 ...
$ triceps : num 35 29 NA 23 35 NA 32 NA 45 NA ...
$ insulin : num NA NA NA 94 168 NA 88 NA 543 NA ...
$ mass : num 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 NA ...
$ pedigree: num 0.627 0.351 0.672 0.167 2.288 ...
$ age : num 50 31 32 21 33 30 26 29 53 54 ...
$ diabetes: Factor w/ 2 levels "neg","pos": 2 1 2 1 2 1 2 1 2 2 ...
> PimaIndiansDiabetes3 <- na.omit(PimaIndiansDiabetes2)
> library(caret)
> set.seed(123)
> PimaIndiansDiabetes3 <- na.omit(PimaIndiansDiabetes2)
> train <- createDataPartition(y=PimaIndiansDiabetes3$diabetes,
+ p=0.7, list=FALSE)
> # 훈련 데이터
> diabetes.train <- PimaIndiansDiabetes3[train, ]
> # 테스트 데이터
> diabetes.test <- PimaIndiansDiabetes3[-train, ]
훈련 데이터와 테스트 데이터가 적절히 분할되었는지 확인하기 위해 당뇨병 환자의 비율 계산한다.
> table(diabetes.train$diabetes) # 당뇨병 환자 91명
neg pos
184 91
> prop.table(table(diabetes.train$diabetes)) # 당뇨병 환자 33%
neg pos
0.6690909 0.3309091
> table(diabetes.test$diabetes) # 당뇨병 환자 39명
neg pos
78 39
> prop.table(table(diabetes.test$diabetes)) # 33%
neg pos
0.6666667 0.3333333
훈련 데이터와 테스트 데이터가 적절히 분할되었다.
cv.glmnet
은 포뮬러 형식을 지원하지 않는다.행렬
형식벡터
형식> library(glmnet)
> set.seed(123)
> x <- model.matrix(diabetes ~ ., data=diabetes.train)[, -1]
> head(x)
pregnant glucose pressure triceps insulin mass pedigree age
7 3 78 50 32 88 31.0 0.248 26
9 2 197 70 45 543 30.5 0.158 53
14 1 189 60 23 846 30.1 0.398 59
17 0 118 84 47 230 45.8 0.551 31
20 1 115 70 30 96 34.6 0.529 32
21 3 126 88 41 235 39.3 0.704 2
> y <- ifelse(diabetes.train$diabetes=='pos', 1, 0)
> head(y)
[1] 1 1 1 1 1 0
> diabetes.cv <- cv.glmnet(x=x, y=y, family='binomial', alpha=1)
> diabetes.cv$lambda.min
[1] 0.01087884
> diabetes.cv$lambda.1se
[1] 0.05805712
> coef(diabetes.cv, diabetes.cv$lambda.min) # 1개의 예측변수 제거
9 x 1 sparse Matrix of class "dgCMatrix"
s1
(Intercept) -9.23133474
pregnant 0.01006916
glucose 0.03446189
pressure .
triceps 0.02678380
insulin 0.00147175
mass 0.04309304
pedigree 1.14386810
age 0.03039017
> coef(diabetes.cv, diabetes.cv$lambda.1se) # 2개의 예측변수 제거
9 x 1 sparse Matrix of class "dgCMatrix"
s1
(Intercept) -5.6548823980
pregnant .
glucose 0.0276723502
pressure .
triceps 0.0137868665
insulin 0.0001604243
mass 0.0120567459
pedigree 0.3234881001
age 0.0137023589
> # 예측 정확도 평가
> diabetes.gnet1 <- glmnet(x=x, y=y, family='binomial',
+ alpha=1, lambda=diabetes.cv$lambda.min)
> # 예측변수 행렬
> diabetes.test.x <- model.matrix(diabetes ~ ., data=diabetes.test)[, -1]
> # 예측 확률
> diabetes.pred1 <- predict(diabetes.gnet1,
+ newx=diabetes.test.x,
+ type='response')
> # 예측 확률 0.5를 기준으로 당뇨병 여부 판정
> diabetes.pred1 <- ifelse(diabetes.pred1 > 0.5,
+ 'pos', 'neg')
> # 혼동행렬
> table(diabetes.test$diabetes, diabetes.pred1,
+ dnn=c('Actual', 'Predicted'))
Predicted
Actual neg pos
neg 69 9
pos 20 19
대각선 : 실제 판정 결과와 예측 모델의 예측 결과가 일치하는 개수
> mean(diabetes.test$diabetes==diabetes.pred1) # 75.2%
[1] 0.7521368
간명도를 높인 모델 lambda.1se를 평가한다.
> # 예측 정확도 평가
> diabetes.gnet2 <- glmnet(x=x, y=y, family='binomial',
+ alpha=1, lambda=diabetes.cv$lambda.1se)
> # 예측변수 행렬
> diabetes.test.x <- model.matrix(diabetes ~ ., data=diabetes.test)[, -1]
> # 예측 확률
> diabetes.pred2 <- predict(diabetes.gnet2,
+ newx=diabetes.test.x,
+ type='response')
> # 예측 확률 0.5를 기준으로 당뇨병 여부 판정
> diabetes.pred2 <- ifelse(diabetes.pred2 > 0.5,
+ 'pos', 'neg')
# 혼동행렬
> table(diabetes.test$diabetes, diabetes.pred2,
+ dnn=c('Actual', 'Predicted'))
Predicted
Actual neg pos
neg 71 7
pos 22 17
> # 예측 정확도
> mean(diabetes.test$diabetes==diabetes.pred2) # 75.2%
[1] 0.7521368
lambda.mse와 lambda.1se와 같은 예측 정확도가 나타났다.
📌 이항 로지스틱 회귀분석
일반적인 이항 로지스틱 회귀분석을 수행해 페널티 회귀분석과의 비교해보자.
> diabetes.logit <- glm(diabetes ~ ., data=diabetes.train,
+ family=binomial(link='logit'))
> diabetes.logit.pred <- predict(diabetes.logit,
+ newdata=diabetes.test,
+ type='response')
> # 예측 확률 0.5를 기준으로 당뇨병 여부 판정
> diabetes.logit.pred <- ifelse(diabetes.logit.pred > 0.5,
+ 'pos', 'neg')
> table(diabetes.test$diabetes, diabetes.logit.pred,
+ dnn=c('Actual', 'Predicted'))
Predicted
Actual neg pos
neg 66 12
pos 20 19
> mean(diabetes.test$diabetes==diabetes.logit.pred) # 72.6%
[1] 0.7264957
📝 결론