네이버 지식인에서 아래와 같은 이메일이 왔다.
R을 이용한 결측치 예측
제가 가지고 있는 데이터셋은 연속형 변수인 x1,x2,x3과 y로 이루어져 있는데, y 변수에 결측치들이 군데군데 존재합니다. 이 결측치를 제거하지 않는 방법을 찾아보니 회귀 모형으로 예측을 하는 것이 가능하다고 하는데, 회귀모형을 어떤 식으로 만들면 위에서 말한대로 결측치를 예측할 수 있는 걸까요? train set과 test set을 나눠서 회귀 예측 모형을 만들 수 있나요?
결측치 전치 |
하긴 했는데, 회귀모형 진단을 해보니, R squared 값이 0.3866 이 나왔다. 회귀모형으로 예측은 가능 한데, 설명력이 떨어져 결측치를 전치 해도 별로 좋아 보이지는 않는다.
회귀방식에 따른 결측치 전치
방법에 대한 질문이 있었기 때문에 첫번째 단계로 회귀모형으로 예측한 다음에 전치를 하겠다.
library(rio)
library(dplyr)
library(DMwR2)
library(readxl)
library(httr)
# 파일을 불러오는 path를 url 변수로 정의
url = "https://drive.google.com/u/0/uc?id=1c0jxY0MjTPzkr44o92pagO4hkfmIizAp&export=download"
GET(url, write_disk(tf <- tempfile(fileext = ".xlsx")))
reg_na <- read_excel(tf, 1L)
# 데이터 프레임으로 "NA"
df <- reg_na %>%
mutate(y = ifelse(y == "NA",NA, y))
# na 값으로 바뀐것 확인
apply(df, 2, function(x){ sum(is.na(x))})
## X1 X2 X3 X4 y
## 0 0 0 0 1263
# 첫번째 방식
# 일반 선형 회귀 모형 진단
fit <- lm(y~ X1+X2+X3+X4, data=
df %>% filter(!is.na(y)))
summary(fit)
##
## Call:
## lm(formula = y ~ X1 + X2 + X3 + X4, data = df %>% filter(!is.na(y)))
##
## Residuals:
## Min 1Q Median 3Q Max
## -74.250 -18.862 -3.653 15.340 174.275
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -15.73934 0.88658 -17.75 <2e-16 ***
## X1 11.96302 0.15758 75.92 <2e-16 ***
## X2 0.40609 0.02113 19.21 <2e-16 ***
## X3 -0.25483 0.01119 -22.77 <2e-16 ***
## X4 -0.22542 0.02125 -10.61 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 26.83 on 19995 degrees of freedom
## Multiple R-squared: 0.3866, Adjusted R-squared: 0.3864
## F-statistic: 3150 on 4 and 19995 DF, p-value: < 2.2e-16
위의 데이터는 선형성을 가지기가 어렵다. 나중에 Train Set과 test Set을 나누어 Machine learning 하여도, 좋은 예측 결과를 갖기 어렵다.
결측치 예측은 아래와 같이 독립변수 4개를 선택 하여 x 변수를 만든 다음에 predict 함수를 이용하여 y_hat 변수로 예측 하여, 그값을 cbind로 붙이면 예측 값이 나온다.
# 예측 모형
x <- df %>% select(X1, X2, X3, X4)
y_hat <- predict(fit, newdata = x)
df_result <- cbind(reg_na, y_hat)
head(df_result)
## X1 X2 X3 X4 y y_hat
## 1 4 74.38888 54.67878 52.96255 4.0999999999999996 36.44852
## 2 3 95.48690 62.23835 59.03245 11.4 29.75846
## 3 6 100.86128 50.88198 78.38262 32 66.36202
## 4 4 76.44456 51.24567 59.35667 92.83 36.71681
## 5 6 31.80306 31.08071 15.84518 35 57.46150
## 6 6 95.47081 56.30415 71.88264 74 64.25652
이것을 앞서 na 결측치로 만든 데이터 값을 전치 하면 결측치 예측에 의해 전치가 끝난 것이다. # 데이터 전치
df_result <- df_result %>%
mutate(y = ifelse(is.na(y),y_hat, y))
# na 값으로 바뀐것 확인
apply(df_result, 2, function(x){ sum(is.na(x))})
## X1 X2 X3 X4 y y_hat
## 0 0 0 0 0 0
y에 결측치가 존재 하지 않는다. 이미 예측 값에 위해 전치가 된것이다.
KNN 전치
K-최근접 이웃(K-Nearest Neighbor, KNN)은 지도학습 알고리즘 중의 하나인데, 아래 녹색의 데이터가 정해지면, 최근접 데이터의 값을 찾은 다음에 클래스를 정하던가 아니면 최근점 이웃값을 거리를 구한 다음 평균을 구하는 것이다.
df$y <- as.double(df$y)
# 두번째 방식 knn 전치 방법
knn_result <- knnImputation(df, k = 10, meth = "weighAvg",
distData = NULL )
이것은 아주 깔끔 하게 결측치 값이 전치 된다.
Train Set과 Test Set을 나누어 예측
회귀 분석의 머신러닝에 의한 방법으로 할 수 있는 caret 패키지를 이용하여 할 수 있다. 우선 아래의 4개의 패키지를 로딩 후 각 변수 간의 상관관계 분석 후 상관관계가 높은 변수는 전부 소거 하고, 실제 분석에 들어 간다.
# 머신러닝에 의한 방법
library(corrplot)
library(caret)
library(tidyr)
library(e1071)
# knn_result 값을 df1 으로 한다.
df1 <- knn_result
# 회귀모형이므로, 별도로 스케일링 절차 없이 데이터 셋을 나눈다.
# 상관관계를 시각적으로 확인 하기 위해서 corrplot을 그리기 위해
# 종속변수 y 제거
cor_df1 <- df1 %>% select(-y)
# 상관관계 분석
correlations <- cor(cor_df1)
correlations
## X1 X2 X3 X4
## X1 1.0000000 -0.1419228 -0.3530644 -0.2929688
## X2 -0.1419228 1.0000000 0.8159770 0.9402982
## X3 -0.3530644 0.8159770 1.0000000 0.8482415
## X4 -0.2929688 0.9402982 0.8482415 1.0000000
# 상관관계 구조를 확인 한다.
corrplot(correlations, order ="hclust")
# 상관관계가 높은 것을 데이터를 걸러낸다.
highCorr <- findCorrelation(correlations, cutoff = .75)
cor_df1 <- cor_df1[, -highCorr]
head(cor_df1)
## X1 X2
## 1 4 74.38888
## 2 3 95.48690
## 3 6 100.86128
## 4 4 76.44456
## 5 6 31.80306
## 6 6 95.47081
데이터를 걸러내는 이유는 변수의 상관관계가 높으면 다중 공선선의 오류가 발생 하기 때문이다. 아래는 데이터 셋을 8:2로 5겹 교차 검증을 통해 리샘플의 성능 추정 값을 구할 것이다. 하지만, 머신러닝을 돌리더라도, RMSE 값과, R-squared 값의 변화는 거의 없을 것이다.
y = df1$y
cor_df1 <- cbind(cor_df1, y )
# 데이터 셋을 나눈다
set.seed(1001)
inx <- createDataPartition(cor_df1$X2, p = 0.8, list = F)
train <- cor_df1[inx, ]
test <- cor_df1[-inx, ]
# 데이터 갯수가 많으므로 k-fold 교차 검증 5회 실시
ctrl <- trainControl(method = "cv", number = 5)
# train set 만들기
train_set <- train %>% select(-y)
# 모델을 돌린다.
set.seed(1001)
lmFit <- train(x = train_set, y = train$y,
method = "lm", trControl =ctrl)
lmFit
## Linear Regression
##
## 17012 samples
## 2 predictor
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 13609, 13610, 13611, 13609, 13609
## Resampling results:
##
## RMSE Rsquared MAE
## 27.24755 0.3647485 21.17054
##
위의 질문 사항에 대한 답변을 정하였지만, 예측 점수는 매우 낮으므로, 위의 데이터가지고, 예측하는 것은 의사 결정에 문제가 생길 수도 있다.
이번 건은 방법에 대한 것만 설명 되었다고 보면 된다.
댓글 없음:
댓글 쓰기