01 결측값 처리와 이상값 검색
-01 결측값
-02 이상값
01 결측값 처리와 이상값 검색
분석에 들어가기 전 데이터 전처리가 필요하다.
데이터 전처리는 데이터의 품질을 높이고 분석의 정확도를 위해 필요한 데이터 정제 작업으로, 데이터 분석에 있어 전처리가 전체 과정의 80 ~ 90%를 차지한다 할 수 있을 정도로 시간이 많이 소요되는 구간이다.
데이터 전처리에 있어 결측값 및 이상값을 처리하여 데이터의 품질을 높일 필요가 있다.
(어떤 분석들은 결측값이 존재하는 경우 분석이 어려운 경우도 있다.)
-01 결측값
1) 결측값의 처리 및 개요
입력이 누락된 값 (무응답)이며, NA(Not Available), NaN, -1, NULL 등으로 표현한다.
ex) 설문조사시 소득 기재를 거부한 경우, 직업을 공란으로 두었거나 '.', '-' 등으로 기재한 경우
결측값을 포함한 데이터의 연산 및 학습 불가 문제가 존재하기에 분석 전에 처리 혹은 대치가 필요하다.
결측값이 많을수록 데이터 전처리 비용이 증가하며 데이터의 왜곡이 발생할 가능성이 높아진다. (품질의 저하 발생)
+) 프로젝트 진행 시 '.', '-' 등으로 기재된 경우 데이터를 불러왔을 때 문자로 불러와지는 경우가 있기 때문에 결측치로 처리하는 과정이 선행되어야 한다.
결측치를 모두 확인하였다면 각 컬럼별 결측치 수/비율이 필요하다.
만약 특정 컬럼의 80 ~ 90% 이상이 결측치인 경우, 노이즈가 매우 심하기 때문에 아무리 좋은 컬럼이라 할지라도 컬럼 자체의 제거를 고려하여야 한다.
2) 결측값의 대치 방법
(1) 단순대치법 : 결측값을 그런듯한 값으로 대체하는 통계적 기법이다.
① 완전분석법
불완전한 자료는 모두 삭제하고, 완전 관측된 자료만 사용하여 분석하므로 데이터 손실이 가장 크다.
만약 모든 항목에 대해, 혹은 대부분 무응답한 경우는 삭제하는 것이 좋으나 하나의 열만 NA인 경우 삭제를 진행하는 것은 위험한 방법이다.
② 평균대치법
평균으로 대치하는 방식이며 두 가지 종류가 존재한다.
평균대치법을 이용한 결측치 대치는 다른 방법에 비해 비교적 쉽다는 장점이 있으나,
오차(평균과의 차이 : y - yhat)가 심해져 데이터의 왜곡이 발생하는 단점이 있다. (표준오차의 과소추정 문제)
> 비조건부 평균대치법 (단순 평균 대치법) (데이터의 평균값으로 결측값을 대치)
> 조건부 평균대치법 (회귀분석 결과값으로 결측값을 대치)
평균에 조건을 넣는 방식으로 전체 평균을 넣는 방식이 아니라 20대이며 남성이고, 직업이 학생인 경우에 한정된 평균으로 대치하는 방식이다.
즉, 결측치가 확인된 행과 동일한 값을 가진 행들의 평균으로 결측치를 대치한다.
비조건부 평균대치법보다 조건부 평균대치법이 더 자주 사용된다.
③ 단순확률대치법 (★)
평균 대치법으로 결측값 대치 시, 표준오차의 과소추정 문제가 발생하므로 이를 보완하기 위해 고안된 방법이다.
표준오차의 과소추정 문제란, 원래 데이터가 가지고 있었을 본래 값이 평균과 대치되며 기질적 특성이 무시되는 것을 말한다.
따라서 평균으로 대치하는 것보다 확률적으로 발생 가능한 값으로 대치하는 방식이라 할 수 있다.
> 핫덱 (Hot-Deck)
무응답을 현재 진행 중인 연구에서 비슷한 성향을 가진 응답자의 자료로 대체한다.
가지고 있는 자료 중 나머지 데이터가 비슷한 것을 참조한다.
표본조사에서 흔히 사용한다.
> 콜드덱 (Cold-Deck)
핫덱과 비슷하지만 대체할 자료를 현재 진행 중인 연구에서 얻는 것이 아니라 외부 출처 또는 이전의 비슷한 연구에서 가져오는 방법이다.
> Nearest Neighbor (★)
결측값을 가진 관측치와 가장 유사한 다른 관측치를 찾아서 그 값을 대체하는 방식이다.
"가장 가까운 이웃 k명에서 물어보아라." -> KNN
KNN imputer라고 하며, k의 수를 지정할 수 있다.
따라서 KNN을 이해해야 KNN imputer를 사용할 수 있다.
+) KNN (K-Nearest Neighbors)
분류분석을 수행하기 위한 알고리즘이지만 현재는 회귀분석도 가능하다.
예측할 값과 가장 가까운 k개의 이웃의 정답을 평균 또는 다수결에 의해 결합하여 최종 예측을 수행하는 방식이다.
거리기반 모델이기에 이상치, 변수 선택, 스케일에 매우 민감하다.
게으른 학습으로서 처음부터 거리 계산을 하지 않고 학습할 데이터가 들어옴과 동시에 거리를 계산한다.
따라서 학습 속도가 빠른 장점이 있다.
* k-means와 다르다! → 군집분석에 해당되며 데이터의 거리를 모두 계산한 후 군집을 나누기 때문에 학습 속도가 느리다.
또한 k의 결정이 매우 중요하다.
통상 5 ~ 7 정도를 사용하나 동률인 경우 랜덤하게 결정할 때가 있다.
따라서 클래스가 이진 데이터인 경우 k를 짝수로 지정하는 것을 피해야 하며, 클래스가 3의 배수인 경우 3배수를 피하는 것이 좋다.
(n배수, 짝수 지양)
이상치에 민감하기 때문에 KNN 전에 이상치 처리를 적절히 수행해야 하며, 거리 계산을 정확하게 수행해야 한다.
(데이터가 수치형이기 때문에 유클리디안 거리 사용, 데이터가 명목형이기 때문에 자카드 거리 사용...)
스케일도 맞추어야 하며 변수의 선택도 적절해야 한다.
후자의 경우 실제 관계에 있어 매우 유사한 경향을 가지고 있으나, 관계없는 변수들을 포함함으로서 거리가 극단적으로 멀어질 수 있다.
=> k의 수, 이상치 처리, 정확한 거리 계산, 스케일, 변수 선택이 완전히 통제되어야 knn이 수행 가능하다.
[ 예제 - KNN 분류 시각화 ]
df <- read.csv("knn_ex.csv")
head(df)
> head(df)
x1 x2 class
1 2.685479 3.143323 A
2 1.717651 1.305570 A
3 2.181564 1.860606 A
4 2.316431 1.933339 A
5 2.202134 2.317975 A
6 1.946938 1.857874 A
# 예측 포인트 생성
new_point <- data.frame(x1=4.5,x2=4.2)
# 가장 가까운 이웃 확인 (k=3)
dists <- sqrt((df$x1 - new_point$x1)^2 + (df$x2 - new_point$x2)^2)
> dists
[1] 2.0997744 4.0148715 3.2936166 3.1473358 2.9702196
[6] 3.4646332 3.9358303 4.2646084 2.1433326 3.4562813
[11] 3.6007833 0.4242641 3.4061932 3.2833482 3.8991625
[16] 2.2941877 2.6723765 2.1828384 2.4834531 2.8628337
[21] 3.2767613 3.1663911 0.5000000 0.4472136 0.7280110
[26] 1.0049876 1.4317821
nn3 <- order(dists)[1:3] # new_point로부터 가장 가까운 3개의 이웃
> order(dists)[1:3]
[1] 12 24 23
df[nn3, ] # 가장 가까운 3개의 이웃에 대한 정답 확인
# A 1개, B 2개이므로 최종 예측 결과는 B가 됨
> df[nn3, ]
x1 x2 class
12 4.2 3.9 A
24 4.7 3.8 B
23 4.8 4.6 B
# KNN 예측 (k=3)
library(class)
knn(train, # 학습데이터 (x)
test, # 새로운 데이터(에측할 데이터, 평가할 데이터가 아니다.) (x)
cl, # 학습데이터 (y)
k) # 이웃의 수
knn(train = df[,-3],
test = new_point,
cl = df$class,
k =3) # new_point에 대한 최종 예측결과 : 다수결에 의한 B 리턴
> knn(train = df[,-3],
+ test = new_point,
+ cl = df$class,
+ k =3)
[1] B
Levels: A B
# 시각화
plot(df$x1, df$x2,
col = ifelse(df$class == "A", "#378ADD", "#E24B4A"),
pch = 19,
cex = 1.4,
xlab = "설명변수 X1",
ylab = "설명변수 X2",
main = paste0("KNN 분류 (K=", k, ")"),
xlim = c(0, 9),
ylim = c(0, 9)
)
# 예측 포인트
points(new_point$x1, new_point$x2,
pch = 19, cex = 1.8, col = "#EF9F27")
points(new_point$x1, new_point$x2,
pch = 1, cex = 1.8, col = "#633806", lwd = 2)
text(new_point$x1 - 0.8, new_point$x2 + 0.1,
labels = "새 데이터", col = "#633806", font = 2, cex = 0.9)
# 범례
legend("topleft",
legend = c("클래스 A", "클래스 B", "예측 포인트"),
col = c("#378ADD", "#E24B4A", "#EF9F27"),
pch = 19,
pt.cex = 1.3,
bty = "n"
)
# 이웃까지 연결선
neighbors <- df[nn3,]
for (i in 1:nrow(neighbors)) {
lines(c(new_point$x1, neighbors$x1[i]),
c(new_point$x2, neighbors$x2[i]),
col = "#BA7517", lty = 2, lwd = 1.5)
}

[ 예제 - 결측치 대치 ]
df <- read.csv('knn_imputer.csv')
head(df)
> head(df)
job age_group gender region favorite_color pet blood_type sns_platform birth_month income purchase
1 사무직 40대 남 서울 파랑 있음 B 인스타 7 500.1681 0
2 노무직 40대 여 서울 초록 있음 AB 틱톡 6 328.4018 1
3 서비스직 30대 여 광역시 노랑 없음 B 없음 8 309.2808 1
4 전문직 30대 남 경기 파랑 없음 A 인스타 6 654.7130 1
5 전문직 40대 여 기타 초록 없음 O 없음 9 528.0263 1
6 사무직 30대 남 기타 파랑 있음 O 인스타 3 431.6229 1
# 각 컬럼별 결측치 수 확인
colSums(is.na(df))
> colSums(is.na(df))
job age_group gender region favorite_color pet blood_type
0 0 0 0 0 0 0
sns_platform birth_month income purchase
0 0 50 0
▲ 소득(income)이 중요한 변수라고 예상되었으나, 다수의 결측치가 income 열에서만 확인되었다.
colSums(is.na(df)) / nrow(df)
> colSums(is.na(df)) / nrow(df)
job age_group gender region favorite_color pet blood_type
0.0 0.0 0.0 0.0 0.0 0.0 0.0
sns_platform birth_month income purchase
0.0 0.0 0.1 0.0
▲ 소득 열의 10%가 결측치임을 비율로 확인할 수 있다. 열 삭제를 하기에는 차량 구매에 주요한 영향을 줄 것으로 예상되는 변수이며, 결측치의 비율이 높지 않음으로 삭제가 아닌 대치로 진행한다.
# 결측치 대치
대치 결과별 비교를 위하여 원본데이터는 손상시키지 않은 채 각 대치별로 복사본을 만든다.
1. 전체 평균으로 대치
# 데이터프레임 복사
df_imp1 <- df
# 전체 평균으로 대치 진행
df_imp1$income[is.na(df_imp1$income)] <- mean(df$income, na.rm = T)
# 대치 후 NA 수 확인
sum(is.na(df_imp1$income))
> sum(is.na(df_imp1$income))
[1] 0
2. 조건별 평균으로 대치
# 데이터프레임 복사
df_imp2 <- df
# 소득에 영향을 주는 다른 변수는 직업 (job)으로 예상된다.
# 따라서 직업별 소득의 평균을 확인한다.
library(plyr)
ddply(df_imp2, .(job), summarise, imcome_mean = mean(income, na.rm = T))
> ddply(df_imp2, .(job), summarise, imcome_mean = mean(income, na.rm = T))
job imcome_mean
1 노무직 280.2613
2 사무직 440.8829
3 서비스직 314.1158
4 전문직 644.9901
# 또한 연령대 (age_group) 역시 소득에 영향을 줄 것으로 예상된다.
ddply(df_imp2, .(job, age_group), summarise, imcome_mean = mean(income, na.rm = T))
> ddply(df_imp2, .(job, age_group), summarise, imcome_mean = mean(income, na.rm = T))
job age_group imcome_mean
1 노무직 20대 207.6865
2 노무직 30대 277.8721
3 노무직 40대 318.8736
4 노무직 50대 310.4427
5 사무직 20대 361.6156
6 사무직 30대 449.6758
7 사무직 40대 491.0000
8 사무직 50대 443.8579
9 서비스직 20대 260.6926
10 서비스직 30대 320.5172
11 서비스직 40대 364.1360
12 서비스직 50대 332.2203
13 전문직 20대 578.2989
14 전문직 30대 650.3500
15 전문직 40대 702.7377
16 전문직 50대 658.6118
▲ 동일 직종 내에서도 연령대별 소득 평균에 차이가 일부 있는 것으로 보인다.
따라서 직업 + 연령을 함께 고려한 평균으로 대치한다.
# 결측치 치환
df_imp2 <- ddply(df_imp2, .(job, age_group), mutate, # 전체를 축소시키지 않음
income = ifelse(is.na(income), mean(income, na.rm = T), income))
sum(is.na(df_imp2$income))
> sum(is.na(df_imp2$imcome))
[1] 0
3. knn imputer 대치
# 데이터프레임 복사
df_imp3 <- df
# 패키지 설치 및 라이브러리 불러오기
install.packages('VIM')
library(VIM)
* kNN과 knn이 다르니 주의가 필요하다. : knn은 분류, kNN은 대치

kNN(data, # 결측치 대치에 필요한 데이터
variable, # 결측치 대치할 컬럼
dist_var, # 결측치 대치에 필요한 컬럼 선택 (기본 : 전체컬럼)
k =5, # k의 수 (기본 : 5)
imp_var = T) # indicator
# 3-1) 모든 컬럼 사용
X <- df[,-ncol(df)]
df_imp3 <- kNN(X, variable = 'income', dist_var = colnames(X), k = 5, imp_var = F)
df_imp3$purchase <- df$purchase
▲ 기존 코드에서 수정되었다.
> kNN(df, variable = 'income', dist_var = colnames(df), k = 5, imp_var = F)
job age_group gender region favorite_color pet blood_type sns_platform birth_month income purchase
1 사무직 40대 남 서울 파랑 있음 B 인스타 7 500.1681 0
2 노무직 40대 여 서울 초록 있음 AB 틱톡 6 328.4018 1
3 서비스직 30대 여 광역시 노랑 없음 B 없음 8 309.2808 1
4 전문직 30대 남 경기 파랑 없음 A 인스타 6 654.7130 1
5 전문직 40대 여 기타 초록 없음 O 없음 9 528.0263 1
6 사무직 30대 남 기타 파랑 있음 O 인스타 3 431.6229 1
7 서비스직 40대 남 기타 빨강 없음 A 인스타 6 319.9890 0
8 전문직 40대 여 광역시 빨강 없음 O 유튜브 9 766.6483 1
9 서비스직 50대 여 광역시 빨강 없음 B 틱톡 8 347.4079 0
10 서비스직 40대 남 기타 노랑 있음 B 없음 3 305.3457 0
sum(is.na(df_imp3$income))
> sum(is.na(df_imp3$income))
[1] 0
# 3-2) 필요 컬럼 사용
df_imp4 <- kNN(df, variable = 'income',
dist_var = c('job', 'age_group', 'region'), k = 5, imp_var = F)
sum(is.na(df_imp4$income))
> sum(is.na(df_imp4$income))
[1] 0
# 4. 성능 비교
library(randomForest)
# random forest는 함수만으로 분류/회귀 모두 분석 가능하다.
# 현재는 분류과제이기 때문에 분석 전 y를 factor로 처리하지 않으면 분류가 아닌 회귀로 풀어진다.
# 의사결정나무와 다르게 random forest 는 반드시 factor 여부를 확인해야 한다.
> rf2 <- randomForest(purchase ~ ., data = df_imp2, ntree = 100)
경고메시지(들):
randomForest.default(m, y, ...)에서:
The response has five or fewer unique values. Are you sure you want to do regression?
▲ factor로 선언하지 않고 코드를 입력해보니 회귀분석으로 진행하는 것이 맞는지 확인하라는 메시지가 출력된다.
# randomForest 학습을 위한 종속변수의 factor 타입 변경(필수)
df_imp1$purchase <- factor(df_imp1$purchase)
df_imp2$purchase <- factor(df_imp2$purchase)
df_imp3$purchase <- factor(df_imp3$purchase)
df_imp4$purchase <- factor(df_imp4$purchase)
# 학습
set.seed(0)
rf1 <- randomForest(purchase ~ ., data = df_imp1, ntree = 100)
set.seed(0)
rf2 <- randomForest(purchase ~ ., data = df_imp2, ntree = 100)
set.seed(0)
rf3 <- randomForest(purchase ~ ., data = df_imp3, ntree = 100)
set.seed(0)
rf4 <- randomForest(purchase ~ ., data = df_imp4, ntree = 100)
# accuracy
sum(df_imp1$purchase == rf1$predicted) / nrow(df) * 100
sum(df_imp2$purchase == rf2$predicted) / nrow(df) * 100
sum(df_imp3$purchase == rf3$predicted) / nrow(df) * 100
sum(df_imp4$purchase == rf4$predicted) / nrow(df) * 100
> sum(df_imp1$purchase == rf1$predicted) / nrow(df) * 100
[1] 67.6
> sum(df_imp2$purchase == rf2$predicted) / nrow(df) * 100
[1] 68.2
> sum(df_imp3$purchase == rf3$predicted) / nrow(df) * 100
[1] 71.6
> sum(df_imp4$purchase == rf4$predicted) / nrow(df) * 100
[1] 70.8
# OOB accuracy 비교
data.frame(
방법 = c("전체평균 대치", "그룹평균 대치", "모든변수 KNN", "관련변수 KNN"),
Accuracy = round(c(1 - rf1$err.rate[100, "OOB"],
1 - rf2$err.rate[100, "OOB"],
1 - rf3$err.rate[100, "OOB"],
1 - rf4$err.rate[100, "OOB"]), 4)
)
방법 Accuracy
1 전체평균 대치 0.676
2 그룹평균 대치 0.682
3 모든변수 KNN 0.716
4 관련변수 KNN 0.708
# ** 반복 평가 점수 확인
n_iter <- 30
result <- data.frame(iter = 1:n_iter, imp1 = NA, imp2 = NA, imp3 = NA, imp4 = NA)
for (i in 1:n_iter) {
set.seed(i)
rf1 <- randomForest(purchase ~ ., data = df_imp1, ntree = 100)
rf2 <- randomForest(purchase ~ ., data = df_imp2, ntree = 100)
rf3 <- randomForest(purchase ~ ., data = df_imp3, ntree = 100)
rf4 <- randomForest(purchase ~ ., data = df_imp4, ntree = 100)
result$imp1[i] <- 1 - rf1$err.rate[100, 'OOB']
result$imp2[i] <- 1 - rf2$err.rate[100, 'OOB']
result$imp3[i] <- 1 - rf3$err.rate[100, 'OOB']
result$imp4[i] <- 1 - rf4$err.rate[100, 'OOB']
}
result
> result
iter imp1 imp2 imp3 imp4
1 1 0.678 0.706 0.736 0.680
2 2 0.704 0.686 0.726 0.692
3 3 0.698 0.690 0.724 0.692
4 4 0.696 0.686 0.718 0.704
5 5 0.676 0.692 0.714 0.698
6 6 0.690 0.696 0.732 0.702
7 7 0.696 0.686 0.714 0.680
8 8 0.670 0.680 0.744 0.706
9 9 0.708 0.678 0.720 0.708
10 10 0.690 0.696 0.726 0.690
11 11 0.694 0.702 0.700 0.700
12 12 0.674 0.686 0.726 0.686
13 13 0.690 0.714 0.728 0.690
14 14 0.694 0.680 0.714 0.684
15 15 0.676 0.688 0.708 0.702
16 16 0.696 0.694 0.702 0.702
17 17 0.698 0.686 0.720 0.698
18 18 0.702 0.680 0.728 0.698
19 19 0.690 0.690 0.724 0.690
20 20 0.692 0.686 0.720 0.688
21 21 0.696 0.700 0.718 0.692
22 22 0.678 0.696 0.730 0.704
23 23 0.690 0.692 0.728 0.680
24 24 0.660 0.692 0.724 0.684
25 25 0.690 0.690 0.718 0.698
26 26 0.684 0.686 0.712 0.700
27 27 0.678 0.672 0.720 0.692
28 28 0.678 0.690 0.732 0.674
29 29 0.680 0.684 0.728 0.696
30 30 0.694 0.682 0.714 0.696
colMeans(result[,-1])
> colMeans(result[,-1])
imp1 imp2 imp3 imp4
0.6880000 0.6895333 0.7216000 0.6935333
▲ 각 모델을 30번씩 반복했을 때의 평균 정확도를 확인할 수 있다.
(2) 다중대치법
단순대치법을 m번 대치를 통해 m개의 가상적인 완전한 자료를 만들어 분석하는 방법이다.
대치 → 분석 → 결합의 3단계로 구성된다.
-02 이상값
1) 이상값 개념 및 특징
관측된 데이터의 범위에서 많이 벗어난 아주 작은 값이나 아주 큰 값을 의미한다.
데이터를 수집하는 과정에서 입력 실수가 발생했거나 설문조사의 경우 고의적 응답 회피용으로 발생한다.
결측값처럼 제거하는 경우도 있으나 보다 주의가 필요하다.
(위험 분석, 이탈요인 등에 속할 수도 있기 때문)
발생원인은 다음과 같다. :
(1) 입력오류 : 라벨링 과정에서 잘못 기입
(2) 측정오류 : 측정도구의 잘못된 선택으로 인한 단위 오기입
(3) 실험오류 : 실험 조건이 동일하지 않은 경우 (다이어트 약 테스트중인 참가자가 독감에 걸림)
(4) 고의적 이상값 : 응답 회피용
(5) 표본추출에러 : 일반 학생 키 조사하는데 농구선수 센터가 포함된 경우
또한 이상치는 여러 이상치에 민감한 모델에 있어 추정 오류를 발생시킨다.
(전통회귀모델, 거리기반모델 (KNN), SVM 등)
또한 이상치의 탐색은 다음을 통해 확인할 수 있다. :
(1) 산업적 기준 정의
(2) 개별 데이터 관찰 : 정상치로부터 벗어난 패턴 확인
(3) 통계적 검출 : ESD, 사분위수 범위 등
(4) 시각화 : 산점도, 상자그림, 히스토그램, 막대그래프, 파이차트
(5) 머신러닝 기법 : k-means, iForest 기법 등
> k-means의 경우 샘플이 유독 작은 군집이 확인되는 경우 이상치일 수 있다.
2) 이상값 탐지 (통계적 검출)
(1) ESD (Extreme Studentized Deviation)
평균으로부터 3sigma 떨어진 값을 이상치로 정의한다.
μ - 3σ < 정상치 < μ + 3σ
μ : 평균, σ : 표준편차
(2) 사분위수 범위
사분위수를 사용하여 이상값을 탐색하는 방법으로 상자그림에서의 이상치 정의와 같다.
Q1 - 1.5 * IQR < 정상치 < Q3 + 1.5 * IQR
Q1 : 제1사분위수, Q3 : 제3사분위수, IQR : Q3 - Q1
3) 이상값 탐지 (머신러닝 기법)
(1) isolation Forest (iForest)
tree 학습 과정에서 가장 먼저 분류되는 데이터들을 이상치로 특정하는 기법이다.
install.packages('isotree')
library(isotree)
X <- iris[, -5]
iso_model <- isolation.forest(X, ntrees = 100, seed = 123)
iso_depth <- predict(iso_model, X, type = 'avg_depth') # 짧을수록 이상치에 가까움
iso_score <- predict(iso_model, X, type = 'score') # 클수록 이상치에 가까움
sort(iso_depth)
> sort(iso_depth)
132 119 14 118 16 42 15 61 110 107 123
5.755103 5.831748 6.087183 6.119971 6.581630 6.971384 7.153146 7.163360 7.220549 7.456350 7.553950
23 106 33 136 99 19 58 34 101 63 94
7.708793 7.778493 7.847689 8.011419 8.043697 8.263593 8.315940 8.401546 8.423723 8.434711 8.489862
108 45 9 131 17 44 60 109 24 38 115
8.607711 8.635761 8.714588 8.730537 8.884181 8.908217 8.944400 9.080695 9.130993 9.140648 9.158090
25 149 6 120 137 114 69 51 88 126 39
9.225017 9.245771 9.257923 9.319818 9.320622 9.406972 9.430785 9.443200 9.453798 9.482153 9.496323
145 130 82 13 135 81 43 122 54 80 10
9.502194 9.507092 9.548426 9.559193 9.677999 9.734369 9.749267 9.815828 9.836781 9.872097 9.880696
86 7 21 36 32 37 46 65 103 125 91
9.899186 9.915364 9.931244 10.135605 10.145227 10.149348 10.165285 10.207068 10.225173 10.231264 10.265585
68 70 144 26 142 141 57 121 85 147 105
10.344355 10.358991 10.362136 10.377029 10.431872 10.474278 10.542583 10.546070 10.603537 10.608983 10.652203
22 133 27 47 41 140 71 4 20 77 30
10.657607 10.708054 10.724124 10.743344 10.745831 10.753283 10.770727 10.793575 10.827072 10.844706 10.857850
90 112 146 111 116 3 83 53 11 73 129
10.862291 10.868236 10.870238 10.895636 10.922753 10.938600 10.945975 10.975414 10.990721 11.048203 11.068500
113 96 72 93 2 74 66 31 12 102 143
11.082573 11.093216 11.097955 11.104840 11.108961 11.131748 11.156648 11.158261 11.205226 11.237253 11.237253
18 49 48 59 104 89 62 75 76 150 138
11.241134 11.242892 11.256144 11.287973 11.358886 11.394918 11.404607 11.439130 11.460630 11.468863 11.502065
67 84 148 78 52 95 124 29 117 28 5
11.512332 11.513794 11.530433 11.535874 11.554436 11.567582 11.585295 11.588649 11.592704 11.608626 11.629505
87 35 127 56 139 50 98 1 134 128 55
11.642793 11.659887 11.688706 11.733458 11.752142 11.761638 11.806019 11.852233 11.902382 11.942306 11.986618
100 92 40 97 8 79 64
12.049641 12.074308 12.080402 12.111116 12.125095 12.266180 12.327174
# 사분위수에 의해 이상치로 확인된 행 번호는 16, 33, 34, 61번
# 그러나 iForest를 통해 이상치로 확인된 행 번호는 143, 119, 14, 118번
▶ 변수 하나에서의 이상치를 탐지하기 위해서라면 사분위수를,
여러 변수를 동시에 고려한 이상치를 탐지하기 위해서는 머신러닝 기법을 사용하는 것이 좋다.
(2) LOF (Local Outlier Factor)
DBSCAN 처럼 데이터들의 밀도를 기반으로 밀집해있지 않은 데이터를 이상치로 검출하는 방식이다.
- minPts가 작게 설정되어 있는 경우, 매우 국소적인 밀도를 비교하게 되기에 민감하게 반응한다. → 이상치 검출 ↑
- minPts가 크게 설정되어 있는 경우, 보다 넓은 범위의 밀도를 비교하게 되기에 둔감하게 반응한다. → 이상치 검출 ↓
lof = 이웃의 평균 밀도 / 지점의 밀도
- 나와 이웃의 밀도가 비슷한 경우 → 1에 가까워진다. (정상치에 가까워진다)
- 나와 이웃의 밀도가 다름 → 1보다 커진다. (이상치)
install.packages('dbscan')
library(dbscan)
lof_score <- lof(scale(X), minPts = 5)
X$lof_score <- lof_score
X[order(X$lof_score, decreasing = T), ][1:5, ] # 클수록 이상치
> X[order(X$lof_score, decreasing = T), ][1:5, ]
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
462 27.420 26.27 186.90 2501.0 0.10840 0.1988 0.36350 0.16890 0.2061 0.05623 2.5470 1.3060 18.650 542.20
213 28.110 18.47 188.50 2499.0 0.11420 0.1516 0.32010 0.15950 0.1648 0.05525 2.8730 1.4760 21.980 525.60
214 17.420 25.56 114.50 948.0 0.10060 0.1146 0.16820 0.06597 0.1308 0.05866 0.5296 1.6670 3.767 58.53
153 9.731 15.34 63.78 300.2 0.10720 0.1599 0.41080 0.07857 0.2548 0.09296 0.8245 2.6640 4.073 49.85
117 8.950 15.76 58.74 245.2 0.09462 0.1243 0.09263 0.02308 0.1305 0.07163 0.3132 0.9789 3.280 16.94
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
462 0.00765 0.05374 0.08055 0.02598 0.01697 0.004558 36.040 31.37 251.20 4254.0 0.1357 0.4256 0.6833
213 0.01345 0.02772 0.06389 0.01407 0.04783 0.004476 28.110 18.47 188.50 2499.0 0.1142 0.1516 0.3201
214 0.03113 0.08555 0.14380 0.03927 0.02175 0.012560 18.070 28.07 120.40 1021.0 0.1243 0.1793 0.2803
153 0.01097 0.09586 0.39600 0.05279 0.03546 0.029840 11.020 19.49 71.04 380.5 0.1292 0.2772 0.8216
117 0.01835 0.06760 0.09263 0.02308 0.02384 0.005601 9.414 17.07 63.34 270.0 0.1179 0.1879 0.1544
concave_points_worst symmetry_worst fractal_dimension_worst lof_score
462 0.26250 0.2641 0.07427 2.710884
213 0.15950 0.1648 0.05525 2.698033
214 0.10990 0.1603 0.06818 2.253350
153 0.15710 0.3108 0.12590 1.952300
117 0.03846 0.1652 0.07722 1.859377
4) 이상값 처리
(1) 삭제
이상값을 제거한다.
데이터가 왜곡되거나 데이터 손실로 인해 분석 정확보다 낮아질 수 있으므로 주의해야 한다.
가장 마지막에 고려해야하는 방법이다.
(2) 대체
평균이나 중앙값, 혹은 상, 하한값 등으로 대체한다.
대체적으로는 이상치를 제외한 정상치들의 상한 / 하한값으로 대치를 많이 한다.
(3) 변환
로그, 지수, 제곱근 변환 등을 통해 값의 극단적인 분포를 조절한다.
(극단적으로 한 쪽의 긴 꼬리를 가지고 있는 경우 한 쪽에 치우치지 않게 분포를 조절)
(4) 무시
이상치에 덜 민감한 분석 방법을 사용한다. (tree 기반 모델)
[ 통계기법을 이용한 이상치 탐색 ]
# 1. ESD
# 평균 - 3*표준편차 < 정상치 < 평균 + 3*표준편차
vmean <- mean(iris$Sepal.Width)
vsd <- sd(iris$Sepal.Width)
# 하한이상치
v1 <- sum(iris$Sepal.Width < vmean - 3*vsd)
> sum(iris$Sepal.Width < vmean - 3*vsd)
[1] 0
# 상한이상치
v2 <- sum(iris$Sepal.Width > vmean + 3*vsd)
> sum(iris$Sepal.Width > vmean + 3*vsd)
[1] 1
print(v1 + v2)
# 2. 사분위수 범위
# Q1 - 1.5*IQR < 정상치 < Q3 + 1.5*IQR
boxplot(iris[,-5])
IQR(iris$Sepal.Width)
> IQR(iris$Sepal.Width)
[1] 0.5
quantile(iris$Sepal.Width)
> quantile(iris$Sepal.Width)
0% 25% 50% 75% 100%
2.0 2.8 3.0 3.3 4.4
Q3 <- quantile(iris$Sepal.Width)[4]
Q1 <- quantile(iris$Sepal.Width)[2]
sum(iris$Sepal.Width > Q3 + 1.5*IQR(iris$Sepal.Width))
sum(iris$Sepal.Width < Q1 - 1.5*IQR(iris$Sepal.Width))
또는
v1 <- sum(iris$Sepal.Width < q1 - 1.5 * (q3 - q1))
v2 <- sum(iris$Sepal.Width > q3 + 1.5 * (q3 - q1))
# 이상치 찾기
iris[iris$Sepal.Width < q1 - 1.5 * (q3 - q1) | iris$Sepal.Width > q3 + 1.5 * (q3 - q1), 3]
+)
예측 대상 변수(Y)가 없는 상태에서 데이터를 탐색하던 중,
소수의 이상치와 다수의 정상치가 관측되었다고 가정하자.
이때 이상치가 위험군으로 판단된다면:
- 이상치는 Y(위험군)
- 정상치는 N(정상군)
으로 라벨링할 수 있다.
이후에는 어떤 조건과 패턴에서 데이터가 위험군(이상치)에 해당하는지, 또는 정상군에 해당하는지를 학습하는 문제가 된다.
즉, 이는 이상치 탐지 문제에서 출발하지만, 라벨이 정의된 이후에는 위험군과 정상군을 구분하는 이진 분류(binary classification) 문제로 전환된다.
따라서 이상치의 탐지와 정의 과정 자체가 이후 예측·분류 모델의 성능과 방향을 결정하는 매우 중요한 단계가 된다.
'아이티윌_데이터 분석 55기 > 강의내용 필기_통계 및 분석' 카테고리의 다른 글
| #17 17일차_로지스틱 회귀, 시계열 분석, 연관 분석 (0) | 2026.05.08 |
|---|---|
| #15 15일차_차원축소 : 다차원 척도법(MDS), 주성분 분석(PCA) (0) | 2026.05.06 |
| #14 14일차_회귀분석, 변수 선택 및 제거 (0) | 2026.04.30 |
| #13 13일차_군집분석 및 평가 ·해석, 변수 스케일링, 회귀분석 (0) | 2026.04.29 |
| #12 12일차_교차검증, 분류모형, 군집분석 (0) | 2026.04.28 |