Post List

2018년 5월 14일 월요일

adj_outlier 함수를 이용한 극단치 데이터 (outlier) 보정





금융 데이터를 다루다보면 심심치 않게 만나는 녀석이
극단치, 즉 outlier 입니다.




실제 예를 한번 살펴볼까요?


devtools::install_github("hyunyulhenry/HenryQuant")
library(HenryQuant)
library(magrittr)
library(tibble)

value = read.csv("~/KOR_value_list.csv", row.names = 1)


HenryQuant 패키지의 get_KOR_fs2 함수를 이용해 다운받은
밸류 리스트들을 불러옵니다.




시총 상위중에도 벌써부터
한눈에 봐도 극단치인 데이터가 존재하는 군요

훗! 회계조작좀 할줄 아는 녀석인가!




PER = value$PER
hist(PER)
boxplot(PER)
summary(PER)


밸류 항목 중 PER만 때와서
분석하도록 해보겠습니다.




히스토그램과 박스그림을 보면
PER의 아웃라이어들이 상당히 많이 존재함이 보입니다.


Min.
1st Qu.
Median
Mean
3rd Qu.
Max.
NA's
-7,230
-5.096
11.151
18.977
26.981
6,390
134


요약통계값을 보면 그 이유를 알 수 있습니다.
PER 평균이 18인 반면에

최소값은 -7,230, 최대값은 무려 6,390 이나 기록하고 있습니다.



이정도는 되야 성장주지......



극단값을 보정하는 방법은 크게 2가지가 있습니다.
첫번째, 경계값 너머의 값을 경계값으로 보정하는
윈저라이징(Winsorizing) 방법

두번째, 경계값 너머의 값을 모두 제거해버리는
트림(Trim) 혹은 트렁케이션(Truncation) 방법


또한 경계값을 정하는 방법도 두가지고 있습니다.
A. 상하위 n%로 정하는 방법 (보통 ±1%)
B. 관측값 기준 z-score로 정하는 방법 (보통 ±3)


예를 들어 상하위 1% 윈저라이징 방법을 택한다면,
상위 0~1% 값의 데이터는 상위 1% 값으로,
하위 99~100% 값의 데이터는 하위 99% 값으로
강제 변환 해줍니다.

Z-Score 기준 트림 방법을 택한다면,
Z-Score가 -3 보다 낮은 값과 3 보다 큰 값은
강제로 삭제해 줍니다.


HenryQuant 내 adj_outlier 함수는
Percentile 기준 윈저라이징 및 트림 방법으로
아웃라이어 데이터를 처리합니다.


adj_outlier(Input, Target = 0.01, Winsorizing = TRUE) 



Input
보정하고자 하는 데이터입니다. 해당 데이터에서는 PERinput 값입니다.
Target
원하는 경계값 입니다. 디폴트 값으로 1%가 부여되어 있습니다.
Winsorizing
윈저라이징과 트림 방법을 선택하는 항목입니다. TRUE의 경우 윈저라이징이, FALSE의 경우 트림 방법이 사용되며, 디폴트로는 윈저라이징 방법을 사용합니다.




PER.winsorizing = adj_outlier(PER, 0.01, TRUE)
PER.trim = adj_outlier(PER, 0.01, FALSE)


윈저라이징과 트림 방법 모두를 적용해 봅니다.



par(mfrow = c(3,1))
hist(PER, main = "NORMAL")
hist(PER.winsorizing, main = "WINSORIZING")
hist(PER.trim, main = "TRIM")





원 데이터에서 x축 최소값이 -5000 이었던 반면,

Winsorizing -400 으로 줄어들어
아웃라이어 값이 많이 보정된 것이 보입니다.

Trim 의 경우 더욱 보정되어 -200~-400에
아웃라이어 값이 분포합니다.

(다만 아까운 데이터가 날아간다는 단점도 존재합니다.)



max(PER, na.rm = TRUE)
max(PER.winsorizing, na.rm = TRUE)
max(PER.trim, na.rm = TRUE)

min(PER, na.rm = TRUE)
min(PER.winsorizing, na.rm = TRUE)
min(PER.trim, na.rm = TRUE)

max(scale(PER), na.rm = TRUE)
max(scale(PER.winsorizing), na.rm = TRUE)
max(scale(PER.trim), na.rm = TRUE)

min(scale(PER), na.rm = TRUE)
min(scale(PER.winsorizing), na.rm = TRUE)
min(scale(PER.trim), na.rm = TRUE)



최대값
최소값
Normal
Winsorizing
Trim
Normal
Winsorizing
Trim
6390
618
583
-7230
-460
-389
Z스코어 최대값
Z스코어 최소값
Normal
Winsorizing
Trim
Normal
Winsorizing
Trim
20.11
5.53
7.46
-22.89
-4.41
-5.35



극단치를 보정해주는 결과와의 비교입니다.
Z-Score가 훨씬 안정적인 모습으로 보입니다.


PER.winsorizing.2 = adj_outlier(PER, 0.05, TRUE)
min(PER.winsorizing, na.rm = TRUE)
min(PER.winsorizing.2, na.rm = TRUE)


이번에는 보정값을 더욱 크게하여
상하위 5% 기준 윈저라이징 방법을 택합니다.


1%
5%
최소값
-460.321
-80.388



PER 최소값이 훨씬 낮아진 것이 보입니다.


하지만 팩터 분석에 있어 가장 쉬우면서도 안정적으로 만드는 방법은
팩터 익스포저의 랭킹을 구한 후,
Z-Score를 구하는 것입니다.





랭킹을 구한 후 Z-Score를 구하면
거의 유니폼한 분포를 얻을 수 있습니다.

이러한 장점으로 팩터간 결합을 하는 멀티팩터에서
가장 많이 사용되고는 합니다.
(AQR 에서도 해당 방법을 사용합니다.)

댓글 2개:

  1. 랭킹을 다시 z-score로 변환하는 이유는 뭘까요?

    답글삭제
    답글
    1. 하나의 데이터만 가지고 평가할때는 굳이 필요가 없지만, 여러 데이터들의 상대강도를 합칠때 단순히 랭킹을 합치는거 보다 정확하게 계산됩니다. AQR의 QMJ 논문 보시면 자세하게 나와있습니다.

      삭제