지난번 WDBC(Wisconsin Diagnostic Breast Cancer) dataset 소개 및 분석 목적과 방향 설정 포스팅에 이어서, 이번 포스팅은 두번째 순서로 'WDBC 데이터셋에 대한 탐색적 데이터 분석과 전처리'에 대해서 알아보겠습니다.
|
지난번 첫번째 포스팅에서 데이터 셋 가져와서 DataFrame으로 만들었을 때 사용했던 R코드는 아래와 같습니다.
rm(list=ls()) options(scipen=30) # data loading: WDBC (Wisconsin Diagnostic Breast Cancer) library(data.table) library(curl) url <- c("https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data") wdbc <- data.frame(fread(url)) str(wdbc) # column names colnames(wdbc) <- c("id", "diagnosis", "radius_mean", "texture_mean", "perimeter_mean", "area_mean", "smoothness_mean", "compactness_mean", "concavity_mean", "concave.points_mean", "symmetry_mean", "fractal_dimension_mean", "radius_se", "texture_se", "perimeter_se", "area_se", "smoothness_se", "compactness_se", "concavity_se", "concave.points_se", "symmetry_se", "fractal_dimension_se", "radius_worst", "texture_worst", "perimeter_worst", "area_worst", "smoothness_worst", "compactness_worst", "concavity_worst", "concave.points_worst", "symmetry_worst", "fractal_dimension_worst") str(wdbc) head(wdbc, 2)
|
이렇게 불러온 WDBC DataFrame에 대해서
(1) 결측값 확인 및 처리
(2) 중복 데이터 확인 및 처리
(3) 목표변수 범주/계급 구성 분포 확인 및 처리
(4) 설명변수 간 다중공선성 확인 및 처리
(5) 표준화, 척도 변환
의 순서로 탐색적 데이터 분석(EDA: Exploratory Data Analysis) 및 데이터 전처리(Data Preprocessing)를 해보겠습니다.
(1) 결측값 (Missing Value) 확인 및 처리 |
is.na() 함수를 사용해서 결측값 여부를 확인 결과 다행히도 결측값이 없네요. 데이터셋 설명된 사이트에 보면 처음에는 결측값이 있었다고 하는데요, 지금 사용하는 데이터셋은 결측값을 다 제거한 자료네요.
혹시 결측값이 있다면 http://rfriend.tistory.com/34 포스팅을 참고하여 분석 목적에 맞게 결측값을 처리하시기 바랍니다.
> attach(wdbc) The following objects are masked from wdbc (pos = 3): area_mean, area_se, area_worst, compactness_mean, compactness_se, compactness_worst, concave.points_mean, concave.points_se, concave.points_worst, concavity_mean, concavity_se, concavity_worst, diagnosis, fractal_dimension_mean, fractal_dimension_se, fractal_dimension_worst, id, perimeter_mean, perimeter_se, perimeter_worst, radius_mean, radius_se, radius_worst, smoothness_mean, smoothness_se, smoothness_worst, symmetry_mean, symmetry_se, symmetry_worst, texture_mean, texture_se, texture_worst > > # check missing value > colSums(is.na(wdbc)) # no missing value, good id diagnosis radius_mean texture_mean 0 0 0 0 perimeter_mean area_mean smoothness_mean compactness_mean 0 0 0 0 concavity_mean concave.points_mean symmetry_mean fractal_dimension_mean 0 0 0 0 radius_se texture_se perimeter_se area_se 0 0 0 0 smoothness_se compactness_se concavity_se concave.points_se 0 0 0 0 symmetry_se fractal_dimension_se radius_worst texture_worst 0 0 0 0 perimeter_worst area_worst smoothness_worst compactness_worst 0 0 0 0 concavity_worst concave.points_worst symmetry_worst fractal_dimension_worst 0 0 0 0 > sum(is.na(wdbc)) [1] 0 |
(2) 중복 데이터 (Duplicated Data) 확인 및 처리 |
다음으로 duplicated() 함수를 사용해서 중복 데이터가 있는지 확인해보았는데요, 다행히도 중복 데이터는 없네요. ^^
혹시 중복 데이터가 있다면 유일한 값을 선별하는 방법은 http://rfriend.tistory.com/165 포스팅을 참고하시기 바랍니다.
> # check duplicated data > sum(duplicated(wdbc)) # no duplicated data, good~! [1] 0
|
(3) 목표변수 범주/계급 구성 분포(class distribution) 확인 및 처리 |
세번째로 목표변수(반응변수, 종속변수) diagnosis 의 범주(category, class)의 분포를 살펴보겠습니다.
table() 함수로 각 계급별 빈도 수 집계, margin.table() 함수로 총 계 계산, prop.table() 함수로 비율을 구하였습니다.
다행히도 양성(Benign)과 악성(Malignant) 비율이 62.7% vs. 37.2% 로서, 어느 한쪽으로 심하게 불균형이 아니고 양쪽으로 적정하게 배분이 되어 있네요. (데이터를 공개한 교수님들이 참 친절하십니다!)
> # check Y: diagnosis > # The diagnosis of breast tissues (M = malignant, B = benign) > table(diagnosis); cat("total :", margin.table(table(diagnosis))) diagnosis B M 357 212 total : 569 > prop.table(table(diagnosis)) # Benign 62.7% vs. Malignant 37.2% diagnosis B M 0.6274165 0.3725835
|
만약, 우리가 관심있는 악성(M)의 비율이 1% 미만인 심하게 불균형한 자료(imbalanced dataset)인 경우에는 관측치 빈도수가 클 경우 majority undersampling (정보 손실의 단점 있음)을 하거나, 아니면 SMOTE(Synthetic Minority Over-Sampling TEchnique) 알고리즘을 사용하여 minority oversampling 을 하거나, 혹은 모델 알고리즘에서 가중치를 목표변수 계급의 구성비를 기준으로 조정하는 방법을 사용하면 됩니다.
(4) 설명변수 간 다중공선성(Multicollinearity) 확인 및 처리 |
네번째로, 설명변수 간 강한 상관관계가 있는 다중공선성(Multicolleniarity)이 존재하는지를
- 산점도 (Scatter plot)
- 상관계수 (Correlation Coefficient)
- 분산팽창지수 (VIF: Variance Inflation Factor)
를 사용하여 확인해보겠습니다. 회귀모형에서는 설명변수간 독립성(independence)을 가정하므로, 독립성 가정 만족 여부를 만족하지 않을 경우 조치하기 위함입니다.
PerformanceAnalytics 패키지의 chart.Correlation() 함수를 사용하면 한꺼번에 편리하게 각 변수의 히스토그램, 변수들 간의 산점도와 상관계수를 그려볼 수 있습니다.
코딩하기에 편하도록 설명변수만 따로 떼어서 X 라는 데이터프레임을 만들었습니다. 그리고 설명변수가 30개씩이나 되어 한꺼번에 그래프를 그릴 경우 그래프 결과나 너무나 작고 조밀하게 나와서 보기에 불편하므로, 편의상 'mean' 측정 10개 변수, 'se(standard error' 측정 10개 변수, 'worst' 측정 10개 변수의 3개 그룹으로 나누어서 그래프를 그려보았습니다. (변수 간 상관성이 높게 나온 결과가 3개 그룹간 유사합니다).
분석 결과 매우 높은 상관관계(상관계수 0.9 이상)를 보이는 설명변수들이 여러개 존재하므로, 다중공선성을 강하게 의심할 수 있습니다.
> # split Y, X > Y <- ifelse(wdbc$diagnosis == 'M', 1, 0) > X <- wdbc[,c(3:32)] > names(X) [1] "radius_mean" "texture_mean" "perimeter_mean" [4] "area_mean" "smoothness_mean" "compactness_mean" [7] "concavity_mean" "concave.points_mean" "symmetry_mean" [10] "fractal_dimension_mean" "radius_se" "texture_se" [13] "perimeter_se" "area_se" "smoothness_se" [16] "compactness_se" "concavity_se" "concave.points_se" [19] "symmetry_se" "fractal_dimension_se" "radius_worst" [22] "texture_worst" "perimeter_worst" "area_worst" [25] "smoothness_worst" "compactness_worst" "concavity_worst" [28] "concave.points_worst" "symmetry_worst" "fractal_dimension_worst"
> # distribution and correlation check among input variables of WDBC > install.packages("PerformanceAnalytics") > library(PerformanceAnalytics) > chart.Correlation(X[,c(1:10)], histogram=TRUE, col="grey10", pch=1) # MEAN > chart.Correlation(X[,c(11:20)], histogram=TRUE, col="grey10", pch=1) # SE > chart.Correlation(X[,c(21:30)], histogram=TRUE, col="grey10", pch=1) # WORST |
GGally 패키지의 ggcorr() 함수를 사용하여 30개의 모든 설명변수간 상관계수를 구하고, -1~1의 상관계수에 따라 색을 달리하는 히트맵(heatmap)을 그려보았습니다. 역시나 여러개의 설명변수들이 서로 상관계수 0.9이상의 매우 강한 상관관계를 가지고 있음을 확인할 수 있습니다.
> # heatmap plot of correlation coefficients of WDBC > install.packages("GGally") > library(GGally) > ggcorr(X, name="corr", label=T)
|
설명변수들 간에 강한 상관성을 가지는 다중공선성(Multicolleniarity)가 존재하면 추정한 회귀계수의 분산이 매우 커지게 되어 추정한 회귀계수를 신뢰하기 힘들게 됩니다. 다시 말하면, 다중공선성이 있는 변수들을 사용해서 회귀계수를 추정하면, 원래 유의미하게 나와야 할 회귀계수가 검정을 해보면 유의미하지 않게 나올 수도 있으며, 반대로 유의미하지 않은 설명변수가 회귀계수 검정 결과 유의미하게 나오는 경우도 유발할 수 있습니다.
게다가 설명변수들간 강한 상관관계가 존재할 경우 해석하는데도 문제가 생길 수 있습니다. 회귀모형의 경우 다른 설명변수를 고정(fix, control)한 상태에서 해당 설명변수가 한 단위 증가할 때 회귀계수 만큼 종속변수(목표변수)가 변화한다고 해석을 하는데요, 만약 다중공선성이 존재한다면 '다른 설명변수를 고정한다'는 설명변수의 독립성 가정이 안맞기 때문에 해석이 곤란해집니다. 따라서 다중공선성이 의심되면 처리를 해주는게 필요합니다.
일반적으로 k개의 설명변수별 분산팽창지수(VIF: Variance Inflation Factor)를 구했을 때 가장 큰 VIF 값이 5 이상이면 다중공선성이 있다고 보며, VIF 값이 10 이상이며 다중공선성이 매우 심각하다고 평가합니다.
다중공선성이 확인되면 이를 해결하기 위한 방법으로
(1) 상관계수가 가장 높은 변수를 제거(remove the highly correlated Xj variable, VIF 10 이상인 설명변수들 중에서 가장 큰 VIF 변수 제거 -> 나머지 모든 변수에 대해 VIF 계산 -> VIF 10 이상인 설명변수들 중에서 가장 큰 VIF 변수 제거 -> 나머지 변수들 VIF 계산 -> 제거 .... 의 과정을 반복함),
(2) 주성분분석(PCA), 요인분석(factor analysis), VAE(Variable Auto Encoder) 등의 알고리즘을 이용한 차원 축소 (dimension reduction),
(3) 모수 추정 시 최소자승법(Least Squares Method) 대신에 Regularization (penalty)를 부여하는 Ridge regression, LASSO, PLS(Partial Least Squares Regression) 등을 사용하는 방법이 있습니다.
지난번 포스팅에서 분석 방향 설정 부분에서 의사 선생님들이 진단하는데 사용하는 모델인 만큼 '모델 해석력(interpretability)'이 매우 중요할 것으로 예상이 된다고 했으므로, 이번 포스팅에서는 (1) 분산팽창지수(VIF) 10 이상인 설명변수를 제거하는 방법을 사용하겠습니다.
R의 fmsb 패키지의 VIF() 함수를 사용해서 모든 설명변수의 분산팽창지수를 구한 후에 가장 큰 값이 10 이상이 경우 순차적으로 제거하겠습니다. 먼저, 예시로 첫번째와 두번째 설명변수인 radius_mean, texture_mean에 대한 분산팽창지수를 계산해본 것인데요, radius_mean 변수의 VIF가 3,806으로서 미친듯이 높음을 알 수 있습니다. (나머지 29의 설명변수와 매우 강한 선형상관관계를 가지고 있다는 뜻)
> # multicolleniarity check > install.packages("fmsb") > library(fmsb) > VIF(lm(radius_mean ~ .,data=X)) [1] 3806.115 > VIF(lm(texture_mean ~ .,data=X)) [1] 11.88405
|
문제는 설명변수가 30개씩이나 되고, 30개 변수의 VIF를 모두 구해서 제일 큰 VIF 값의 설명변수를 제거하고, 다시 29개의 설명변수의 VIF를 모두 구해서 제일 큰 VIF 값의 설명변수를 제거하고.... 이런 단순 반복작업을 VIF 값이 10 이상인 설명변수가 없을 때 까지 반복해야만 한다는 점입니다.
이처럼 반복작업인 경우 사용자 정의 함수(User Defined Function)을 짜놓고 사용하면 편리하고 시간도 줄일 수 있어서 좋습니다.
[분산팽창지수를 구하고 VIF 10 이상인 변수 중 가장 큰 값을 순차적으로 제거하는 R 사용자 정의함수]
# Multi-collinearity check and remove the highly correlated variables step by step # UDF of stepwise VIF function with preallocated vectors # code source: https://beckmw.wordpress.com/2013/02/05/collinearity-and-stepwise-vif-selection/ vif_func <- function(in_frame,thresh=10, trace=F,...){
require(fmsb)
if(class(in_frame) != 'data.frame') in_frame<-data.frame(in_frame)
#get initial vif value for all comparisons of variables vif_init <- vector('list', length = ncol(in_frame)) names(vif_init) <- names(in_frame) var_names <- names(in_frame)
for(val in var_names){ regressors <- var_names[-which(var_names == val)] form <- paste(regressors, collapse = '+') form_in <- formula(paste(val,' ~ .')) vif_init[[val]] <- VIF(lm(form_in,data=in_frame,...)) } vif_max<-max(unlist(vif_init))
if(vif_max < thresh){ if(trace==T){ #print output of each iteration prmatrix(vif_init,collab=c('var','vif'),rowlab=rep('', times = nrow(vif_init) ),quote=F) cat('\n') cat(paste('All variables have VIF < ', thresh,', max VIF ',round(vif_max,2), sep=''),'\n\n') } return(names(in_frame)) } else{
in_dat<-in_frame
#backwards selection of explanatory variables, stops when all VIF values are below 'thresh' while(vif_max >= thresh){
vif_vals <- vector('list', length = ncol(in_dat)) names(vif_vals) <- names(in_dat) var_names <- names(in_dat)
for(val in var_names){ regressors <- var_names[-which(var_names == val)] form <- paste(regressors, collapse = '+') form_in <- formula(paste(val,' ~ .')) vif_add <- VIF(lm(form_in,data=in_dat,...)) vif_vals[[val]] <- vif_add }
max_row <- which.max(vif_vals) #max_row <- which( as.vector(vif_vals) == max(as.vector(vif_vals)) )
vif_max<-vif_vals[max_row]
if(vif_max<thresh) break
if(trace==T){ #print output of each iteration vif_vals <- do.call('rbind', vif_vals) vif_vals prmatrix(vif_vals,collab='vif',rowlab=row.names(vif_vals),quote=F) cat('\n') cat('removed: ', names(vif_max),unlist(vif_max),'\n\n') flush.console() } in_dat<-in_dat[,!names(in_dat) %in% names(vif_max)] } return(names(in_dat)) } } |
== 참고로 Python으로 다중공선성 처리하는 사용자 정의 함수는 아래 참고하세요. ==
# Remove multicollinarity recursively using Python from statsmodels.stats.outliers_influence import variance_inflation_factor def X_filter_multicollinearity(X, thresh=5.0): from datetime import datetime start_tm = datetime.now()
variables = list(range(X.shape[1])) dropped = True while dropped: dropped = False vif = [variance_inflation_factor(X.iloc[:, variables].values, ix) for ix in range(X.iloc[:, variables].shape[1])]
maxloc = vif.index(max(vif))
if max(vif) > thresh: print('==> [Dropped variable] : ' + X.iloc[:, variables].columns[maxloc]) del variables[maxloc]
if len(variables) > 1: dropped = True print('[Remaining variables] :') print(X.columns[variables]) print('[Elapsed time] :', str(datetime.now() - start_tm))
return variables # run the UDF X_remained_idx = X_filter_multicollinearity(X) print('X index after filtering multicollinearity:', X_remained_idx)
|
위의 사용자정의함수 vif_func() 를 먼저 실행시키고, 다음으로 아래처럼 설명변수 X DataFrame과 VIF 기준(threshold)을 10으로 설정하고, 순차적인 제거(remove) 결과를 프린트하도록 해서 vif_func(X, thresh=10, trace=T) 함수를 실행시키면 아래와 같습니다.
> # run vif_function > X_independent <- vif_func(X, thresh=10, trace=T) vif radius_mean 3806.115296 texture_mean 11.884048 perimeter_mean 3786.400419 area_mean 347.878657 smoothness_mean 8.194282 compactness_mean 50.505168 concavity_mean 70.767720 concave.points_mean 60.041733 symmetry_mean 4.220656 fractal_dimension_mean 15.756977 radius_se 75.462027 texture_se 4.205423 perimeter_se 70.359695 area_se 41.163091 smoothness_se 4.027923 compactness_se 15.366324 concavity_se 15.694833 concave.points_se 11.520796 symmetry_se 5.175426 fractal_dimension_se 9.717987 radius_worst 799.105946 texture_worst 18.569966 perimeter_worst 405.023336 area_worst 337.221924 smoothness_worst 10.923061 compactness_worst 36.982755 concavity_worst 31.970723 concave.points_worst 36.763714 symmetry_worst 9.520570 fractal_dimension_worst 18.861533 removed: radius_mean 3806.115 vif texture_mean 11.882933 perimeter_mean 541.712931 area_mean 317.093211 smoothness_mean 7.990641 compactness_mean 38.106611 concavity_mean 65.978202 concave.points_mean 60.025840 symmetry_mean 4.203501 fractal_dimension_mean 15.677673 radius_se 75.101495 texture_se 4.185513 perimeter_se 67.720819 area_se 41.089343 smoothness_se 4.017499 compactness_se 15.341790 concavity_se 15.234133 concave.points_se 11.399633 symmetry_se 5.175369 fractal_dimension_se 9.699518 radius_worst 616.350861 texture_worst 18.539292 perimeter_worst 375.408537 area_worst 304.471896 smoothness_worst 10.727206 compactness_worst 36.053404 concavity_worst 31.968539 concave.points_worst 36.763168 symmetry_worst 9.511243 fractal_dimension_worst 18.841897 removed: radius_worst 616.3509 vif texture_mean 11.759131 perimeter_mean 325.641312 area_mean 237.012095 smoothness_mean 7.988003 compactness_mean 36.681620 concavity_mean 64.836935 concave.points_mean 60.019062 symmetry_mean 4.123603 fractal_dimension_mean 15.670406 radius_se 38.637579 texture_se 4.132025 perimeter_se 59.062677 area_se 33.911923 smoothness_se 4.010296 compactness_se 15.304014 concavity_se 15.002055 concave.points_se 11.218541 symmetry_se 5.156085 fractal_dimension_se 9.542616 texture_worst 18.191599 perimeter_worst 308.052048 area_worst 168.343121 smoothness_worst 10.679641 compactness_worst 35.779767 concavity_worst 31.942417 concave.points_worst 35.761242 symmetry_worst 9.312564 fractal_dimension_worst 18.445566 removed: perimeter_mean 325.6413 vif texture_mean 11.714252 area_mean 34.491349 smoothness_mean 7.964156 compactness_mean 31.979571 concavity_mean 64.655174 concave.points_mean 59.967015 symmetry_mean 4.123603 fractal_dimension_mean 14.921612 radius_se 36.056151 texture_se 4.092556 perimeter_se 42.980382 area_se 32.570748 smoothness_se 3.914161 compactness_se 15.283194 concavity_se 14.769198 concave.points_se 10.464462 symmetry_se 5.128175 fractal_dimension_se 9.542575 texture_worst 18.112512 perimeter_worst 123.257811 area_worst 72.764912 smoothness_worst 10.648133 compactness_worst 34.263137 concavity_worst 31.681663 concave.points_worst 35.231031 symmetry_worst 9.268771 fractal_dimension_worst 18.287262 removed: perimeter_worst 123.2578 vif texture_mean 11.679833 area_mean 28.534200 smoothness_mean 7.909212 compactness_mean 28.746302 concavity_mean 64.654796 concave.points_mean 59.816820 symmetry_mean 4.071436 fractal_dimension_mean 12.724264 radius_se 36.045576 texture_se 4.040107 perimeter_se 31.225949 area_se 20.995394 smoothness_se 3.894739 compactness_se 15.199363 concavity_se 14.766025 concave.points_se 10.344938 symmetry_se 5.007681 fractal_dimension_se 9.302515 texture_worst 18.004692 area_worst 23.311066 smoothness_worst 10.619439 compactness_worst 34.253186 concavity_worst 31.669493 concave.points_worst 34.141124 symmetry_worst 9.077526 fractal_dimension_worst 18.285365 removed: concavity_mean 64.6548 vif texture_mean 11.679763 area_mean 28.512892 smoothness_mean 7.543056 compactness_mean 26.110203 concave.points_mean 28.499831 symmetry_mean 4.064239 fractal_dimension_mean 12.668596 radius_se 35.617518 texture_se 4.034866 perimeter_se 31.178650 area_se 19.985188 smoothness_se 3.872144 compactness_se 14.858964 concavity_se 11.587995 concave.points_se 10.013827 symmetry_se 5.005381 fractal_dimension_se 9.248401 texture_worst 18.003124 area_worst 23.026283 smoothness_worst 10.598388 compactness_worst 33.972256 concavity_worst 21.188494 concave.points_worst 32.115706 symmetry_worst 9.068073 fractal_dimension_worst 18.090939 removed: radius_se 35.61752 vif texture_mean 11.465264 area_mean 25.427695 smoothness_mean 7.482881 compactness_mean 26.043226 concave.points_mean 27.801240 symmetry_mean 4.047744 fractal_dimension_mean 12.232750 texture_se 4.011486 perimeter_se 16.007813 area_se 16.951023 smoothness_se 3.861783 compactness_se 14.577396 concavity_se 11.499061 concave.points_se 9.999462 symmetry_se 5.003384 fractal_dimension_se 8.800984 texture_worst 17.724662 area_worst 20.617761 smoothness_worst 10.595373 compactness_worst 33.960639 concavity_worst 21.056095 concave.points_worst 32.088040 symmetry_worst 9.065905 fractal_dimension_worst 18.084650 removed: compactness_worst 33.96064 vif texture_mean 11.448852 area_mean 25.389376 smoothness_mean 7.479515 compactness_mean 19.208231 concave.points_mean 25.697888 symmetry_mean 3.982308 fractal_dimension_mean 10.961595 texture_se 3.998307 perimeter_se 15.960835 area_se 16.935889 smoothness_se 3.859669 compactness_se 9.974677 concavity_se 10.850219 concave.points_se 9.805676 symmetry_se 4.941233 fractal_dimension_se 7.983689 texture_worst 17.701635 area_worst 20.613295 smoothness_worst 10.586935 concavity_worst 18.432076 concave.points_worst 30.596655 symmetry_worst 8.754474 fractal_dimension_worst 13.187594 removed: concave.points_worst 30.59666 vif texture_mean 11.278555 area_mean 25.387830 smoothness_mean 7.162886 compactness_mean 19.175385 concave.points_mean 19.091402 symmetry_mean 3.918815 fractal_dimension_mean 10.902634 texture_se 3.937299 perimeter_se 15.808730 area_se 16.917891 smoothness_se 3.637606 compactness_se 9.956527 concavity_se 9.775933 concave.points_se 5.347299 symmetry_se 4.900803 fractal_dimension_se 7.965699 texture_worst 17.427935 area_worst 20.406468 smoothness_worst 9.741783 concavity_worst 16.192763 symmetry_worst 8.435382 fractal_dimension_worst 13.047801 removed: area_mean 25.38783 vif texture_mean 11.179202 smoothness_mean 7.162712 compactness_mean 18.843208 concave.points_mean 15.619381 symmetry_mean 3.895936 fractal_dimension_mean 9.707446 texture_se 3.937174 perimeter_se 15.619268 area_se 16.655447 smoothness_se 3.632008 compactness_se 9.936443 concavity_se 9.705569 concave.points_se 5.250584 symmetry_se 4.872228 fractal_dimension_se 7.946733 texture_worst 17.236427 area_worst 10.626847 smoothness_worst 9.608528 concavity_worst 16.109962 symmetry_worst 8.409532 fractal_dimension_worst 13.023306 removed: compactness_mean 18.84321 vif texture_mean 11.134313 smoothness_mean 6.970849 concave.points_mean 11.753066 symmetry_mean 3.829642 fractal_dimension_mean 7.907186 texture_se 3.890957 perimeter_se 15.333308 area_se 16.345495 smoothness_se 3.552541 compactness_se 6.363339 concavity_se 9.367267 concave.points_se 5.245367 symmetry_se 4.871870 fractal_dimension_se 7.584276 texture_worst 17.232376 area_worst 10.602010 smoothness_worst 9.606389 concavity_worst 15.700019 symmetry_worst 8.401090 fractal_dimension_worst 13.023120 removed: texture_worst 17.23238 vif texture_mean 1.715846 smoothness_mean 6.795612 concave.points_mean 11.715292 symmetry_mean 3.654734 fractal_dimension_mean 7.890069 texture_se 2.033874 perimeter_se 15.281161 area_se 16.333806 smoothness_se 3.384881 compactness_se 6.337432 concavity_se 9.364521 concave.points_se 5.235966 symmetry_se 4.312472 fractal_dimension_se 7.575192 area_worst 10.540176 smoothness_worst 8.644833 concavity_worst 15.699140 symmetry_worst 7.294569 fractal_dimension_worst 13.021119 removed: area_se 16.33381 vif texture_mean 1.709993 smoothness_mean 6.701262 concave.points_mean 11.653729 symmetry_mean 3.651771 fractal_dimension_mean 7.750052 texture_se 2.009042 perimeter_se 4.317808 smoothness_se 3.338723 compactness_se 6.317986 concavity_se 8.849322 concave.points_se 4.645375 symmetry_se 4.312339 fractal_dimension_se 7.575158 area_worst 8.677078 smoothness_worst 8.642994 concavity_worst 15.510661 symmetry_worst 7.265658 fractal_dimension_worst 12.938791 removed: concavity_worst 15.51066 > X_independent [1] "texture_mean" "smoothness_mean" "concave.points_mean" [4] "symmetry_mean" "fractal_dimension_mean" "texture_se" [7] "perimeter_se" "smoothness_se" "compactness_se" [10] "concavity_se" "concave.points_se" "symmetry_se" [13] "fractal_dimension_se" "area_worst" "smoothness_worst" [16] "symmetry_worst" "fractal_dimension_worst"
|
이렇게 VIF 10 이상인 설명변수를 순차적으로 제거해서 처음에 30개의 설명변수가 -> 17개의 설명변수로 줄어들었습니다.
남은 17개 설명변수만을 가진 X_2 데이터프레임을 만들고, 상관계수 히트맵을 다시 그려보았습니다. 일부 변수가 상관계수 0.8인 경우가 있기는 합니다만, 0.9이상의 매우 강한 상관관계를 가진 설명변수는 없네요.
> # explanatory/independant variables after VIF test > X_2 <- X[, X_independent] > str(X_2) 'data.frame': 569 obs. of 17 variables: $ texture_mean : num 10.4 17.8 21.2 20.4 14.3 ... $ smoothness_mean : num 0.1184 0.0847 0.1096 0.1425 0.1003 ... $ concave.points_mean : num 0.1471 0.0702 0.1279 0.1052 0.1043 ... $ symmetry_mean : num 0.242 0.181 0.207 0.26 0.181 ... $ fractal_dimension_mean : num 0.0787 0.0567 0.06 0.0974 0.0588 ... $ texture_se : num 0.905 0.734 0.787 1.156 0.781 ... $ perimeter_se : num 8.59 3.4 4.58 3.44 5.44 ... $ smoothness_se : num 0.0064 0.00522 0.00615 0.00911 0.01149 ... $ compactness_se : num 0.049 0.0131 0.0401 0.0746 0.0246 ... $ concavity_se : num 0.0537 0.0186 0.0383 0.0566 0.0569 ... $ concave.points_se : num 0.0159 0.0134 0.0206 0.0187 0.0188 ... $ symmetry_se : num 0.03 0.0139 0.0225 0.0596 0.0176 ... $ fractal_dimension_se : num 0.00619 0.00353 0.00457 0.00921 0.00511 ... $ area_worst : num 2019 1956 1709 568 1575 ... $ smoothness_worst : num 0.162 0.124 0.144 0.21 0.137 ... $ symmetry_worst : num 0.46 0.275 0.361 0.664 0.236 ... $ fractal_dimension_worst: num 0.1189 0.089 0.0876 0.173 0.0768 ... > > # correlation heatmap plot again > ggcorr(X_2, name="corr", label=T)
|
(5) 설명변수 표준화, 척도 변환 (standardization, rescal) |
지난번 첫번째 포스팅에서 데이터셋 설명할 때 설명변수들간의 측정 척도(scale)이 서로 다르다고 했는데요, 나중에 그래프 그려서 비교하기에 유용하도록 척도를 평균이 '0', 표준편차가 '1'인 새로운 척도로 표준화(standardization) 하도록 하겠습니다.
scale() 함수를 이용하면 간단하게 표준화를 할 수 있습니다. summary() 함수로 확인해보니 평균이 '0'으로 중심이 바뀌었으며, max 값이 들쭉 날쭉 한걸로 봐서는 변수별로 outlier들이 꽤 있는 것처럼 보이네요. 로지스틱 회귀모형을 적합할 계획이므로 별도로 이상치(outlier) 처리는 다루지 않겠습니다.
> # Standardization > X_3 <- scale(X_2) > summary(X_3) texture_mean smoothness_mean concave.points_mean symmetry_mean fractal_dimension_mean Min. :-2.2273 Min. :-3.10935 Min. :-1.2607 Min. :-2.74171 Min. :-1.8183 1st Qu.:-0.7253 1st Qu.:-0.71034 1st Qu.:-0.7373 1st Qu.:-0.70262 1st Qu.:-0.7220 Median :-0.1045 Median :-0.03486 Median :-0.3974 Median :-0.07156 Median :-0.1781 Mean : 0.0000 Mean : 0.00000 Mean : 0.0000 Mean : 0.00000 Mean : 0.0000 3rd Qu.: 0.5837 3rd Qu.: 0.63564 3rd Qu.: 0.6464 3rd Qu.: 0.53031 3rd Qu.: 0.4706 Max. : 4.6478 Max. : 4.76672 Max. : 3.9245 Max. : 4.48081 Max. : 4.9066 texture_se perimeter_se smoothness_se compactness_se concavity_se Min. :-1.5529 Min. :-1.0431 Min. :-1.7745 Min. :-1.2970 Min. :-1.0566 1st Qu.:-0.6942 1st Qu.:-0.6232 1st Qu.:-0.6235 1st Qu.:-0.6923 1st Qu.:-0.5567 Median :-0.1973 Median :-0.2864 Median :-0.2201 Median :-0.2808 Median :-0.1989 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 3rd Qu.: 0.4661 3rd Qu.: 0.2428 3rd Qu.: 0.3680 3rd Qu.: 0.3893 3rd Qu.: 0.3365 Max. : 6.6494 Max. : 9.4537 Max. : 8.0229 Max. : 6.1381 Max. :12.0621 concave.points_se symmetry_se fractal_dimension_se area_worst smoothness_worst Min. :-1.9118 Min. :-1.5315 Min. :-1.0960 Min. :-1.2213 Min. :-2.6803 1st Qu.:-0.6739 1st Qu.:-0.6511 1st Qu.:-0.5846 1st Qu.:-0.6416 1st Qu.:-0.6906 Median :-0.1404 Median :-0.2192 Median :-0.2297 Median :-0.3409 Median :-0.0468 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 Mean : 0.0000 3rd Qu.: 0.4722 3rd Qu.: 0.3554 3rd Qu.: 0.2884 3rd Qu.: 0.3573 3rd Qu.: 0.5970 Max. : 6.6438 Max. : 7.0657 Max. : 9.8429 Max. : 5.9250 Max. : 3.9519 symmetry_worst fractal_dimension_worst Min. :-2.1591 Min. :-1.6004 1st Qu.:-0.6413 1st Qu.:-0.6913 Median :-0.1273 Median :-0.2163 Mean : 0.0000 Mean : 0.0000 3rd Qu.: 0.4497 3rd Qu.: 0.4504 Max. : 6.0407 Max. : 6.8408
|
이상으로 탐색적 데이터 분석 및 데이터 전처리 포스팅을 마치겠습니다.
다음번 포스팅에서는 세번째로 '1차 변수 선택 및 목표변수와 설명변수 간 관계 분석'에 대해서 다루어보겠습니다.
이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾸욱 눌러주세요. ^^
'R 분석과 프로그래밍 > R 통계분석' 카테고리의 다른 글
[R] 로지스틱 회귀분석을 통한 유방암 예측(분류) (4/4): 로지스틱 회귀모형 적합 및 모델평가, 해석 (6) | 2018.12.18 |
---|---|
[R] 로지스틱 회귀분석을 통한 유방암 예측(분류) (3/4): 1차 변수 선택 및 목표변수와 설명변수 간 관계 분석 (8) | 2018.12.17 |
[R] 로지스틱 회귀분석을 통한 유방암 예측(분류) (2/4): 탐색적 데이터 분석 및 전처리 (32) | 2018.12.16 |
[R] 로지스틱 회귀분석을 통한 유방암 예측(분류) (1/4): WDBC Data 소개 (2) | 2018.12.16 |
[R] 카이제곱 적합도 검정(Chi-squared goodness of fit test)으로 특정 분포인지 확인하기 (2) | 2018.06.19 |
R 범주형 자료분석 (3) 동질성 검정(test of homogeneity) : chisq.test() (2) | 2015.11.24 |
댓글을 달아 주세요
너무나 잘 보고 있습니다.
항상 감사드립니다.
오차가 보여서 댓글을 답니다.
독립성(independace) -> 독립성(independence)
안녕하세요 이부일님,
퇴근 후에 오타 수정해놓겠습니다.
꼼꼼히 봐주시고 댓글 남겨주셔서 감사합니다. ^^
R을 공부하면서 너무나 많은 도움을 주셨고,
지금도 주시고 있습니다.
정말 정말 정말 감사드려요^^
응원해주셔서 힘이 납니다. 댓글 고맙습니다. :-)
안녕하세요 위의 코드를 다른 데이터로 따라가는 도중에
아래의 vif_func 을 실행하는 위치에서
에러와 함께 추가 실행이 안되는데,
에러 코드를 이해를 못하겠습니다.
해결 방법을 찾을 수 있을까요?
> # run vif_function
> X_indepedent <- vif_func(X, thresh =10, trace =TRUE)
Error in if (vif_max < thresh) { : missing value where TRUE/FALSE needed
In addition: Warning messages:
1: In summary.lm(X) : essentially perfect fit: summary may be unreliable
2: In summary.lm(X) : essentially perfect fit: summary may be unreliable
3: In summary.lm(X) : essentially perfect fit: summary may be unreliable
안녕하세요.
에러 메시지를 보니 X 변수들 중에서 perfect multicollinearity (다른 변수들로 완벽하게 설명/예측가능한 변수)가 있는 변수가 있나봅니다.
for loop 으로 vif filter하는 코드를 더 단순화시킬 수 있는데요, ... 제가 퇴근 후에 파이썬으로 짠 좀더 간단한 코드 올래드릴께요.
제 data를 통해서 진행시에
> VIF(lm(radius_mean ~ .,data=X))
[1] 5.974472
> VIF(lm(texture_mean ~ .,data=X))
[1] 6.296069
로 VIF Thresh 가 10 미만으로 진행이 안되는 것일까요.....??
Error in if (vif_max < thresh) { : missing value where TRUE/FALSE needed
에서 몇일째 진행이 안되고 있네요....
data를 좀더 수정해볼까도 생각되지만,
에러를 이해하고 바꾸는게 맞는것 같습니다.
안녕하세요 윤수한님,
혹시 제가 에러를 재현할 수 있도록 샘플 데이터와 코드를 올려주실 수 있을까요? 제가 퇴근 후에 살펴볼께요.
https://gist.githubusercontent.com/fawda123/4717702/raw/4878688f9539db4304033f1a8bc26dfd0e1e9e28/vif_fun.r
왜인지 모르겠지만, 링크의 원본 함수로 해결했습니다.
다만 VIF 10 이상값이 잔존합니다.
while 함수 쪽에서 또 뭔가 다른 듯합니다.
뭐가 다른지 찾아보고 있는데,
vif_init <- NULL 이 다르고....
항상 도움을 주셔서 감사합니다.
회사 파일이라 안되네요. ㅠ_ㅠ
네, 해결 되었다니 다행이네요. 나중에 시간되면 좀더 간결한 코드로 수정할까하는데요, 시간 내기가 힘드네요.
추가로 VIF 값이 NaN 이 나오는 경우는 무엇인가요??
추측컨데 perfect collinearity (vif 분모가 0) 인 경우 같습니다. 혹은 칼럼이 1개 남은 경우이거나요.
링크의 함수와
예제의 함수로
예제의 dataset 내용을 적용시 동일한 결과를 확인하였습니다.
원리는 같지만 호환성의 문제(?) 같은 느낌입니다.
일단 링크의 함수로 적용하여 진행하겠습니다.
저도 잘 모르고 머리 부딛혀 가며 공부하는 터라
항상 감사하게 게시글을 찾아보고 있습니다.
다시한번 감사합니다.
와 정말 이 블로그 대단한 정보들이 담겨있네요... 많이 배우고 갑니다. 아직은 이해하지 못하는 내용이 더 많네요 ㅠㅠ 강해져서 다시돌아오겠습니다 좋은글 감사합니다!
댓글 감사합니다. 덕분에 기분이 좋네요. :-)
항상 너무 잘보고 있습니다... 진짜 복 많이 받으세요...
댓글 감사합니다. :-)
안녕하세요 r한지 얼마 안되는 학생인데요.
혹시 vif할때 함수를
Y<-ifelse(D$Y=='M',1,0)
X<-D[,c(2:31)]
VIF_TEST<-X
V.V<-fmsb::VIF(lm(VIF_TEST[,1]~.,data = VIF_TEST[,-1]))
V.V<-c()
for (i in 1:dim(VIF_TEST)[2]){
V.V[i]<-fmsb::VIF(lm(VIF_TEST[,i]~.,data = VIF_TEST[,-i]))
}
VIF_TEST<-X
Criteria<-10
V.V<-rep(Criteria,times=dim(VIF_TEST)[2])
while (max(V.V)>=Criteria) {
for (i in 1:dim(VIF_TEST)[2]) {
V.V[i]<-fmsb::VIF(lm(VIF_TEST[,i]~.,data = VIF_TEST[,-i]))
}
Var_num<-Var_num+1
VIF_TEST<-VIF_TEST[,-which(V.V==max(V.V))]
V.V<-V.V[-which(V.V==max(V.V))]
}
이런식으로는 할 수 없을까요?
이렇게 돌리니까 17개가 안나오고 16개만 나오네요 자꾸..ㅠ
안녕하세요.
코드를 봐서는 문제 없어보입니다.
X <- D[,c(2:31)] 에서 칼럼 2~31까지 사용하는게 맞는지요?
(본문 포스팅에서는 X <- wdbc[, c(3:32)] )
비밀 댓글 말고 오픈 모드로 코드 공유해주시면 다른 분들도 참고할 수 있어서 좋을거 같습니다. :-)
저렇게 코드돌리고 나서 본문에서 X_independt처럼 제거된 숫자들 볼려고 하면 어떤 함수 하면 되는거에요?
질문의 뜻을 잘 이해를 못하겠습니다. output image 예시를 남겨주시면 이해하는데 도움이 되겠습니다.
포스팅의 중간에 Python으로 다중공선성 변수 처리하는 사용자 정의함수 추가하였습니다. (프로세스 참고용)
VIF는 설명변수들의 관계를 확인하는거니 VIF를 확인할 data frame의 경우 종속변수를 제외한 설명변수들만 확인 하는것이 맞나요???
그리고 현재
> X_independent <- vif_func(x, thresh=10, trace = T)
Error in if (vif_max < thresh) { : missing value where TRUE/FALSE needed
In addition: There were 22 warnings (use warnings() to see them)
R로 구동시 이런 오류가 나는데 이유가 무엇일까요...
네, 설명변수들만 확인하는게 맞습니다.
에러메시질 보니 결측값이 포함되어있어서 에러가 닌거 깉습니다. 결측값 확인 후 결측값 처리 후 다시 해보실래요.
네, 설명변수들만 확인하는게 맞습니다.
에러메시질 보니 결측값이 포함되어있어서 에러가 닌거 깉습니다. 결측값 확인 후 결측값 처리 후 다시 해보실래요.
네!! 항상 많은 정보가 도움이 됩니다. 정말 감사합니다.
그런데
> sum(is.na(x))
[1] 0
> X_independent <- vif_func(x, thresh=10, trace = T)
Error in if (vif_max < thresh) { : missing value where TRUE/FALSE needed
In addition: There were 22 warnings (use warnings() to see them)
이렇게 결측값이 없는것으로 확인을 했는데 같은 오류가 납니다...ㅠㅠ
콘솔창에서 warnings()
를 실행시켜서 22개 warning 메시지를 확인해봐야 알겠네요.
안녕하세요. 포스팅을 따라하다가 질문이 있어서요. 혹시 설명변수간 다중공선성을 확인하려고 하는데 설명변수가 다 범주형이면(교육수준: 초, 중, 고, 대학, 결혼 유,무 등) 이걸 다 as.numeric으로 바꾸어서 chart.Correlation으로 해야하는 걸까요? 아니면 범주형 변수일때는 다른 방법으로 다중공선성을 확인해야하나요?
아니면, 그냥 vif 함수로 해서 GVIF값이 4이하? 5이하? 이면 다중공선성이 없다고 판단해도 될까요?
> vif(x1)
GVIF Df GVIF^(1/(2*Df))
gencat 1.125447 1 1.060871
agecat 4.664634 8 1.101035
educat 3.200886 4 1.156535
mcat 1.444456 1 1.201855
incomecat 2.299039 9 1.047336
pubincat 1.347201 5 1.030251
workcat 1.408433 1 1.186774
항상 좋은 포스팅에 많은 도움 받고 있습니다.
감사합니다.
안녕하세요.
범주형 데이터의 경우 다중공선성은 독립성 검정하는 방법으로 확인하면 됩니다.
R의 통계 카테고리의 포스팅 중에서 범주형 자료 독립성 검정하는 포스팅 참고하시면 되겠네요.
- 순서형 자료: spearman rank correlation coefficient
- 명목형 자료: chi-squared test
범주형 자료와 연속형 자료간의 독립성에 대해서는 범주가 2개 집단이면 t-test, 2개 이상 집단이면 ANOVA 이용하면 됩니다.
답변 매우 감사드립니다!
한가지만 더 여쭤보자면.. 카이제곱 테스트는 설명변수와 종속변수(결혼유무와 종속변수, 종교종류와 종속변수) 이렇게 하나씩 검증을 하고, 나중에 표를 합쳐서 그리는 건지요..?
아니면, 설명변수가 여러개일때 한꺼번에 종속변수와 카이제곱 테스트를 하는 r 코드가 따로있는지 궁금합니다.
다중공선성은 설명변수들끼리만을 대상으로 하며, 종속변수와는 관련이 없습니다.
지금 데이터셋이 어떤 형태인지 잘 몰라서 답변드리기 좀 애매한데요, 연속형과 범주형 변수가 여러개있다면 범주형변수를 가변수(dummy variable)로 변환 후에 연속형&가변수로 변환한 범주형 설명변수들을 대상으로 다중공선성을 vif 분석하고 처리하면 될거 같습니다.
답변 정말 감사드립니다. 제가 계속 헛발질만 하고 있네요 ㅠㅠ
저는 현재 설명변수가 모두 factor(범주형)인 변수입니다.
(성별)gencat chr "0" "1" "1" "0" ...
(보장유형)pubincat Factor w/ 6 levels "company","public officer",..: 1
(연령) agecat Factor w/ 9 levels "20-29","30-39",..: 7 5 5 5 1 6
(결혼여부)mcat Factor w/ 2 levels "marry","unmarry": 2 1 1 1 2 1 1
(교육)educat Factor w/ 5 levels "unedu","ele",..: 1 5 5 2 5 5 4
(고용) workcat Factor w/ 2 levels "work","nowork": 2 1 1 1 2 1 2 1
그래서 이 변수끼리 다중공선성을 확인해야할 것 같은 상황입니다. 말씀해주신것처럼 범주형변수를 가변수..로 변환하는 것은 0또는 1로 하는 것인가요? 그럼 교육수준처럼 5개의 범주로 되어있는건 어떻게 처리해야할까요..?
https://rfriend.tistory.com/138 을 보고 하려고 하는데, 저처럼 범주형 설명 변수가 많을때, 카이제곱은 어떻게 해야할지 모르겠네요..
범주가 k개 이묜 k-1개의 가변수(1, 0) 를 만들어주면 됩니다.
교육수준처럼 순서형인 경우 edu (초1/중2/고3/대4) 처럼 1개 숫자형 변수로 처리하는 것도 해볼수 있겠네요.