-
[Data Science] abalone 데이터 회귀 분석 - 전복 나이 예측Data Science/Data Science in R 2018. 12. 18. 07:39
목표 : abalone 데이터를 통해 전복 나이를 예측하는 회귀 분석을 해보자.
* 데이터 설명 : http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.names
* 데이터 다운로드 : http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data
1. 함수 작성 및 환경 설정
rmse <- function(yi, yhat_i){
sqrt(mean((yi - yhat_i)^2))
}
binomial_deviance <- function(y_obs, yhat){
epsilon = 0.0001
yhat = ifelse(yhat < epsilon, epsilon, yhat)
yhat = ifelse(yhat > 1-epsilon, 1-epsilon, yhat)
a = ifelse(y_obs==0, 0, y_obs * log(y_obs/yhat))
b = ifelse(y_obs==1, 0, (1-y_obs) * log((1-y_obs)/(1-yhat)))
return(2*sum(a + b))
}
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...){
usr <- par("usr"); on.exit(par(usr))
par(usr = c(0, 1, 0, 1))
r <- abs(cor(x, y))
txt <- format(c(r, 0.123456789), digits = digits)[1]
txt <- paste0(prefix, txt)
if(missing(cex.cor)) cex.cor <- 0.8/strwidth(txt)
text(0.5, 0.5, txt, cex = cex.cor * r)
}
library(tidyverse)
library(gridExtra)
library(MASS)
library(glmnet)
library(randomForest)
library(gbm)
library(rpart)
library(boot)
library(data.table)
library(ROCR)
library(ggplot2)
library(dplyr)
library(gridExtra)
2. 데이터 다운로드 및 Read
URL <- "http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data"
download.file(URL, destfile = "data.csv")
data <- tbl_df(read.table(file.choose(), strip.white = TRUE,
sep=",", header = FALSE))
names(data) <- c('Sex','Length','Diameter','Height','Whole',
'Shucked','Viscera','Shell','Rings')
> glimpse(data)
Observations: 4,177
Variables: 9
$ Sex <fct> M, M, F, M, I, I, F, F, M, F, F, M, M, F, F, M, I, F, M, M, M, I, F, F, ...
$ Length <dbl> 0.455, 0.350, 0.530, 0.440, 0.330, 0.425, 0.530, 0.545, 0.475, 0.550, 0....
$ Diameter <dbl> 0.365, 0.265, 0.420, 0.365, 0.255, 0.300, 0.415, 0.425, 0.370, 0.440, 0....
$ Height <dbl> 0.095, 0.090, 0.135, 0.125, 0.080, 0.095, 0.150, 0.125, 0.125, 0.150, 0....
$ Whole <dbl> 0.5140, 0.2255, 0.6770, 0.5160, 0.2050, 0.3515, 0.7775, 0.7680, 0.5095, ...
$ Shucked <dbl> 0.2245, 0.0995, 0.2565, 0.2155, 0.0895, 0.1410, 0.2370, 0.2940, 0.2165, ...
$ Viscera <dbl> 0.1010, 0.0485, 0.1415, 0.1140, 0.0395, 0.0775, 0.1415, 0.1495, 0.1125, ...
$ Shell <dbl> 0.150, 0.070, 0.210, 0.155, 0.055, 0.120, 0.330, 0.260, 0.165, 0.320, 0....
$ Rings <int> 15, 7, 9, 10, 7, 8, 20, 16, 9, 19, 14, 10, 11, 10, 10, 12, 7, 10, 7, 9, ...
결측치는 없다.
3. 시각화
set.seed(1810)
pairs(data %>% sample_n(min(1000, nrow(data))),
lower.panel=function(x,y){ points(x,y); abline(0, 1, col='red')},
upper.panel = panel.cor)
1) 반응변수 RIngs에 대해 설명변수 Shell이 상관관계가 가장 높음을 알 수 있다.
2) Whole과 Shucked, Whole과 Shell의 상관관계가 가장 높음을 알 수 있다.
조금 더 자세히 살펴보면 다음과 같다.
p1 <- data %>% ggplot(aes(Rings)) + geom_bar()
p2 <- data %>% ggplot(aes(factor(Rings), Shell)) + geom_boxplot()
p3 <- data %>% ggplot(aes(factor(Rings), Height)) + geom_boxplot()
p4 <- data %>% ggplot(aes(Shell, Whole)) + geom_point(alpha=.1) + geom_smooth()
grid.arrange(p1, p2, p3, p4, ncol=2)
1) 전복이 5~15살에 많이 분포되어 있음을 알 수 있다.
2) 나이가 많을수록 말린 후의 무게가 많이 나감을 알 수 있다.
3) 무게와 나이가 양의 상관관계를 보임을 알 수 있다.
4) 당연한거겠지만 전체 무게와 말린 후의 무게가 강한 양의 상관관계를 보인다.
4. 훈련, 검증, 테스트세트 데이터 구분
set.seed(1810)
n <- nrow(data)
idx <- 1:n
training_idx <- sample(idx, n * .60)
idx <- setdiff(idx, training_idx)
validate_idx <- sample(idx, n * .20)
test_idx <- setdiff(idx, validate_idx)
training <- data[training_idx,]
validation <- data[validate_idx,]
test <- data[test_idx,]
5. 선형 회귀 모형 적합
lm 모형을 적합하면 다음과 같다.
> data_lm_full <- lm(Rings ~ ., data=training)
> summary(data_lm_full)
Call:
lm(formula = Rings ~ ., data = training)
Residuals:
Min 1Q Median 3Q Max
-8.3557 -1.3089 -0.3421 0.8809 14.2300
Coefficients:
...생략...
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.166 on 2496 degrees of freedom
Multiple R-squared: 0.5411, Adjusted R-squared: 0.5394
F-statistic: 327 on 9 and 2496 DF, p-value: < 2.2e-16
> predict(data_lm_full, newdata = data[1:5,])
1 2 3 4 5
8.904290 7.728173 10.936506 9.627705 6.644735
SexM과 Length를 제외한 모든 변수가 유의함을 보인다.
이차상호작용 모형을 적합하면 다음과 같다.
> data_lm_full2 = lm(Rings ~ .^2, data = training)
> summary(data_lm_full2)
Call:
lm(formula = Rings ~ .^2, data = training)
Residuals:
Min 1Q Median 3Q Max
-10.6899 -1.2168 -0.2542 0.8566 13.8243
...생략...
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.086 on 2461 degrees of freedom
Multiple R-squared: 0.5805, Adjusted R-squared: 0.573
F-statistic: 77.4 on 44 and 2461 DF, p-value: < 2.2e-16
> length(coef(data_lm_full2))
[1] 45
이차상호작용의 모수는 45개이다.
stepwise 변수 선택 모형을 적합하면 다음과 같다.
> data_step = stepAIC(data_lm_full, scope = list(upper = ~ .^2, lower = ~1))
> summary(data_step)
Call:
lm(formula = Rings ~ Sex + Length + Diameter + Height + Whole +
Shucked + Viscera + Shell + Sex:Shucked + Shucked:Viscera +
Sex:Diameter + Sex:Shell + Height:Shell + Height:Shucked +
Length:Viscera + Length:Whole + Length:Diameter + Diameter:Viscera +
Whole:Viscera + Shucked:Shell + Diameter:Shell, data = training)
Residuals:
Min 1Q Median 3Q Max
-10.6159 -1.2343 -0.2731 0.8435 14.2758
Coefficients:
...생략...
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.084 on 2480 degrees of freedom
Multiple R-squared: 0.578, Adjusted R-squared: 0.5737
F-statistic: 135.9 on 25 and 2480 DF, p-value: < 2.2e-16
> length(coef(data_step))
[1] 26
stepwise 변수 선택 모형의 모수는 26개이다.
세 가지의 모형 평가를 실시해보면 다음과 같다.
> y_obs <- validation$Rings
> yhat_lm <- predict(data_lm_full, newdata=validation)
> yhat_lm_2 <- predict(data_lm_full2, newdata=validation)
> yhat_step <- predict(data_step, newdata=validation)
> rmse(y_obs, yhat_lm)
[1] 2.169579
> rmse(y_obs, yhat_lm_2)
[1] 2.133406
> rmse(y_obs, yhat_step)
[1] 2.136988
이차상호작용 모형을 적용했을 때, RMSE값이 가장 낮은 것으로 보았을 때 예측력이 가장 좋음을 알 수 있다.
6. 라쏘 모형 적합
xx <- model.matrix(Rings ~ .^2-1, data)
x <- xx[training_idx, ]
y <- training$Rings
glimpse(x)
data_cvfit <- cv.glmnet(x, y)
plot(data_cvfit)
각 lambda 값에서 선택된 변수의 개수는 26개, 13개이다.
모형 평가는 다음과 같다.
> y_obs <- validation$Rings
> yhat_glmnet <- predict(data_cvfit, s="lambda.min", newx=xx[validate_idx,])
> yhat_glmnet <- yhat_glmnet[,1] # change to a vector from [n*1] matrix
> rmse(y_obs, yhat_glmnet)
[1] 2.118328
이차상호작용 모형보다 glmnet 모형의 RMSE값이 더 작은 것으로 보았을 때, 예측력이 더 좋음을 알 수 있다.
7. 나무 모형 적합
> data_tr <- rpart(Rings ~ ., data = training)
> data_tr
n= 2506
node), split, n, deviance, yval
* denotes terminal node
1) root 2506 25519.5100 9.882682
2) Shell< 0.15375 765 3131.1950 7.308497
4) Diameter< 0.2225 121 180.3306 4.925620 *
5) Diameter>=0.2225 644 2134.7250 7.756211
10) Sex=I 415 872.8048 7.178313 *
11) Sex=F,M 229 872.1572 8.803493 *
3) Shell>=0.15375 1741 15091.6700 11.013790
6) Shell< 0.4095 1470 10021.9900 10.602720
12) Shell< 0.24925 594 2772.3520 9.796296 *
13) Shell>=0.24925 876 6601.4100 11.149540
26) Shucked>=0.42975 574 2172.0350 10.449480 *
27) Shucked< 0.42975 302 3613.3810 12.480130
54) Whole< 0.98875 231 2246.2940 11.891770 *
55) Whole>=0.98875 71 1026.9580 14.394370 *
7) Shell>=0.4095 271 3473.9260 13.243540
14) Shucked>=0.58525 194 2037.9850 12.448450
28) Shell< 0.568 160 1150.9750 11.862500 *
29) Shell>=0.568 34 573.5588 15.205880 *
15) Shucked< 0.58525 77 1004.3120 15.246750 *
> opar <- par(mfrow = c(1,1), xpd = NA)
> plot(data_tr)
> text(data_tr, use.n = TRUE)
> par(opar)
모형 평가는 다음과 같다.
> yhat_tr <- predict(data_tr, validation)
> rmse(y_obs, yhat_tr)
[1] 2.358271
다른 모형들에 비해 높은 RMSE값을 보이며, 그에 따라 다른 모형에 비해 좋지 않은 모형임을 알 수 있다.
8. 랜덤 포레스트 모형 적합
set.seed(1810)
data_rf <- randomForest(Rings ~ ., training)
data_rf
opar <- par(mfrow=c(1,2))
plot(data_rf)
varImpPlot(data_rf)
par(opar)
위에서도 알 수 있다시피, Shell변수와의 상관관계가 높은 것을 다시 한번 알 수 있다.
모형 평가는 다음과 같다.
> yhat_rf <- predict(data_rf, newdata=validation)
> rmse(y_obs, yhat_rf)
[1] 2.103759
현재까지 적합된 모형들과 비교했을 때 가장 좋은 예측력을 보인다.
9. 부스팅 모형 적합
set.seed(1810)
data_gbm <- gbm(Rings ~ ., data=training,
n.trees=40000, cv.folds=3, verbose = TRUE)
> (best_iter = gbm.perf(data_gbm, method="cv"))
[1] 659
최적의 트리 개수는 659개이다.
모형 평가는 다음과 같다.
> yhat_gbm <- predict(data_gbm, n.trees=best_iter, newdata=validation)
> rmse(y_obs, yhat_gbm)
[1] 2.178855
RF모형 보다 떨어지는 예측력을 보인다.
10. 최종 모형 선택과 테스트세트 오차 계산
> data.frame(lm = rmse(y_obs, yhat_lm_2),
+ glmnet = rmse(y_obs, yhat_glmnet),
+ rf = rmse(y_obs, yhat_rf),
+ gbm = rmse(y_obs, yhat_gbm)) %>%
+ reshape2::melt(value.name = 'rmse', variable.name = 'method')
No id variables; using all as measure variables
method rmse
1 lm 2.133406
2 glmnet 2.118328
3 rf 2.103759
4 gbm 2.178855
> rmse(test$Rings, predict(data_rf, newdata = test))
[1] 2.225618
RF 모형을 적합할 때 가장 낮은 RMSE 값을 나타낸다. 즉, 가장 좋은 예측 모형이다.
따라서, RF모형을 통해 테스트세트 데이터를 적합시키면 RMSE=2.225를 얻을 수 있다.
예측오차의 분포를 병렬상자그림으로 오차를 비교해보면 다음과 같다.
boxplot(list(lm = y_obs-yhat_step,
glmnet = y_obs-yhat_glmnet,
rf = y_obs-yhat_rf,
gbm = y_obs-yhat_gbm), ylab="Error in Validation Set")
abline(h=0, lty=2, col='blue')
모든 모형들이 다 비슷한 수준의 예측오차를 보인다. 또한 모형들간의 예측값들끼리 산점도 행렬을 그려보면 다음과 같다.
pairs(data.frame(y_obs=y_obs,
yhat_lm=yhat_step,
yhat_glmnet=c(yhat_glmnet),
yhat_rf=yhat_rf,
yhat_gbm=yhat_gbm),
lower.panel=function(x,y){ points(x,y); abline(0, 1, col='red')},
upper.panel = panel.cor)
glmnet 모형과 rf모형의 예측력이 가장 높음을 알 수 있으며, glmnet과 lm모형이 높은 상관관계를 보임을 알 수 있다.
* 전체 소스코드 : https://github.com/SeongkukCHO/PCOY/blob/master/data-science/abalone/abalone.R
* 참고문헌 : 실리콘밸리 데이터 과학자가 알려주는 따라하며 배우는 데이터 과학(저자 : 권재명)
'Data Science > Data Science in R' 카테고리의 다른 글
[ISL] 5장 - Resampling Methods(CV) (0) 2019.10.24 [ISL] 4장 - 분류(이론) (0) 2019.10.23 [Data Science] Winequality(red wine) 데이터 회귀 분석 - 와인 품질 예측 (0) 2018.12.18 [Data Science] Winequality(white wine) 데이터 회귀 분석 - 와인 품질 예측 (0) 2018.12.18 [Data Science] Boston 데이터 회귀 분석 - 부동산 가격 예측 문제 (0) 2018.12.17