결측치(Missing values, Nulls) 처리에 대해서 (Imputation): SimpleImputer, IterativeImputer, MICE ..

All We Need is Data, itself !·2022년 1월 10일
2

Machine-Learning

목록 보기
4/4

이번에 카카오 테크 인턴 서합을 하면서 사전과제를 받았다.
지금 진행중인데.. 결측치가 굉~장히 많았다.
칼럼이나 갯수를 보아하니 의도적으로 정해진 비율만큼 제거하신 모양인(,,)

데이터 갯수가 워낙 많아서 그냥 결측치를 떨궈도 70퍼센트정도는 살아있다고 생각하고 결측치를 dropna() 시키고 간단하게 했는데,
글쎄 test set 에도 결측치가 있었다. 🙄

지금껏 봐왔던 대회 데이터랑 상당히 느낌이 달라서 더 당황했던 것 같다.

그래서 결측치 처리를 어떻게 할까 하다가 전에 진행해봤던 Mice Imputation이 생각나서 한번 시도해보자고 생각했다.

그래서 imputation 기능 총정리를 한번 해보고자 한다.! 'ㅅ'

지금 학습중인데 코랩 너 왜그래 세션 닫지마 제발 8ㅁ8
이건 여담인데 구글 진짜 똑똑한 것 같다 코랩+를 사면 노트북을 닫아도 돌아간다면서...갖고싶다

Imputation?

https://en.wikipedia.org/wiki/Imputation_(statistics)

이번엔 얼른 저장된 애들 평균내러 가야하기때문에 위키로 대체 ㅠ

내가 사용하는 코드베이스의 Imputation 종류들

wiki를 정독해보면 unit imputation, item imputation가 있다고 한다.
그러나 이런 원론적인 부분은 지금 내가 시간이 없기 때문에 ㅠ

나는 크게 두가지 방법을 사용하는데,

  1. Single Imputation
  2. MICE Imputation

을 비교적 자주 사용하고 있다.

MICE는 Multiveriate Imputation by Chained Equations의 약자로, MI(다중대치법) 중 하나이다.

따라서 굳이 나누자면 원래는

  1. Single Imputation
  2. Multiveriate Imputation

이게 맞아? 이게 맞냐고?!! ... 원래는 이게 맞다. ;ㅅ;

그러나 코드로 구현하다보면 한계점이 와서 쓰던 것만 쓰게 되는 것 같다 (?)
왜냐면 저거 열심히 다 바꾼다고 크게 점수가 달라지진 않았기 때문에 ㅜ 적당히 아주 성능이 별로이지 않은 결측치 대체방식을 택해왔던 것 같다.



Scikit-learn: Simple Imputer

scikit-learn에서 기본적인 Imputation 툴들을 제공한다.

https://scikit-learn.org/stable/modules/impute.html

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy = 'mean')

imputer.fit_transform(train_df)

기본적으로 함수들이나 모양은 scaler랑 비슷하게 생겨서 알기 쉽다.
저기 있는 strategy를 바꿔주면서 어떻게 결측값을 대체할 것인가를 선택하면 된다.

기본적으로 제공하는 것들은
1. mean - 평균값
2. median - 중앙값
3. most_frequent - 최빈값
4. constant - 정해진 값

'constant'를 사용할 땐 fill_value 파라미터를 통해서 어떠한 값으로 채워줄 것인지 정해주면 되는 것 같다!

나는 보통 최빈값을 택하는 편인데, 그 이유는 평균값과 중앙값이 최빈값에 비해 비교적 이상치에 민감하다고 생각하기 때문 (,,)
짧은 식견이지만 나이 칼럼에서 '0' 값이 너무 많을 때, 이것을 이상치로 보지 않고 null 값으로 본다는 마음을 가지고 있기 때문에(?) missing values에 대해서도 이 '0'값으로 넣어주는 식으로 진행하곤 하는데, 그래도 이것보다는 회귀로 예측해서 채워주는 걸 좀 더 좋아하는 편.

사실 데이터의 null값이 너무 많으면 가중치를 덜 주거나 imputation을 진행하지 않고 
dropna 하거나 column 자체를 떨궈도 별 차이가 없는 것 같다 ㅠ
그러나 데이터 갯수가 작으면 하나하나 소듕하니까... 스타트업 해커톤할 때 그랬지 ㅠ

https://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html#sklearn.impute.SimpleImputer


Scikit-learn: IterativeImputer

IterativeImputer도 딱 한번 사용해본 적이 있는데, 독스는 처음 읽어봤다..ㅋㅋ 미리 읽어볼걸 되게 좋은 툴이었다.

Round-robin 형식(기회를 동등하게 갖는 방식이라고 함)을 통해 iteration하며, regressor을 사용한다는 것을 보니 각 피처에 대해 회귀 분석을 진행해서 결측값을 예측하는 시스템인 것 같다.

from sklearn.impute import IterativeImputer

imputer = IterativeImputer(max_iter = 10, random_state = 0)
imputer.fit_transform(train_df)

max_iter과 random_state를 통해 파라미터를 줄 수 있다.
random_state를 imputer에 준다는 게 신기했다.

내게 수식적 지식이 있는 것은 아니지만, Docs의 설명에 따르면
R data science 생태계에는 Amelia, mi, mice, missForest 등 다양한 Missing values handling 기법들이 존재하는데,
하나의 예시로 이 IterativeImputer에서 예측하는 모델(Regressor)을 Random Forest로 사용하면, missForest 기법이 된다고 한다.

따라서 이 IterativeImputer를 다양하게 사용할 수 있는 장점이 있다고 한다. 오호 그래? 🙄

Univeriative vs. Multiveriative

앞서 sklearn에서는 다양한 imputation 기법을 제공한다.
아래의 것은 조금 더 원론적인 내용이므로 나중에 읽어보기.!!

https://www.missingdata.nl/missing-data/missing-data-methods/imputation-methods/



MICE

MICE에 대한 설명은 여기에 잘 나와 있다 (...)
그림이 굉장히 알기쉽게 되어 있어서 여기서 한번 읽어보기!

https://ichi.pro/ko/mice-mich-knn-nulag-gabs-daechi-python-eul-sayonghan-seolmyeong-mich-guhyeon-229919597786290

https://www.numpyninja.com/post/mice-algorithm-to-impute-missing-values-in-a-dataset

  • 이건 위의 것의 원문인듯하다. 출처 표기가 안되어있었다. 다른데 돌아다니다가 찾음..
필요할때마다 MICE를 찾아보는데, 찾아볼 때마다 꼭 한번씩 읽음 ㅋㅋㅋ 멍청이 ㅜㅜ 기록해두기
심지어 여기에도 있는 걸 보니 그냥 더 이상 지원하지 않는 툴이 되었나보다 ㅠ

https://www.geeksforgeeks.org/missing-data-imputation-with-fancyimpute/

⬆️ GeeksforGeeks를 보면 fancyimpute의 IterativeImputer가 MICE로.. 음 쓴다고 한다.

https://stackoverflow.com/questions/54059964/can-not-use-mice-from-fancyimputer-python

⬆️ 이 글을 보니 fancyImpute의 MICE는 IterativeImputer로 바뀌었고, complete()도 더이상 지원하지 않고, fit_transform으로 지원한다고 한다.

그래서 실행해보았다.

잘 된다. 하지만 유의해야 할 점은 array로 퐁 뱉어준다ㅠㅋㅋ 이점은 fit_transform이 그러하듯..똑같았다.
따라서 pd.DataFrame으로 묶어주는 작업이 필요하지 싶다!

그리고 적용해보니 int_feat에만 work 하는 것을 볼 수 있었다.
categorical features에 대해서는 다른 방안을 마련해야 할 것 같다.

https://autoimpute.readthedocs.io/en/latest/user_guide/imputers.html
https://pypi.org/project/autoimpute/

이건 categorical columns에도 work 한다는 autoimpute라 사용을 시도해보았는데 stackoverflow에도 나오지 않는 에러를 자꾸만 뱉어내서 실패했다.
Docs는 여기 있는 imputation tools중에 가장 열심히 읽었는데, 망했다 8ㅁ8


그래서 그냥 사랑하는 sklearn이 제공하는 IterativeImputer를 사용해서 MICE를 사용하는 방법을 찾아왔다,, 사실 아까 sklearn에 IterativeImputer 있는 거 처음 알았을 때 그냥 이걸로 하면 되겠는데 하긴 했었다 ㅋㅋㅋ.. 괜히 고생했다 8ㅁ8..



Scikit-learn으로 MICE 만들기

https://www.numpyninja.com/post/how-to-implement-mice-algorithm-using-iterative-imputer-to-handle-missing-values

감사합니다ㅜ ㅜ 어린 양을 살리셨어요...

그냥 이렇게 써야겠다,,,, MICE 관련 툴은 더 이상 정리된 게 없는 모양이다.
구글링 그만..

나는 파라미터 없이 그냥 MICE import해서 쓸 수 있는 툴을 원했지만,, 그거슨 없었다고 한다.
모두 IterativeImputer를 사용합시다! 는게 결론 ✨



Categorical feature imputation

그럼 여기서 질문이 하나 생겼다.
지금껏 int features에 대해서는 imputation한다는 거 알겠는데, 범주형 변수, categorical features에 대해서는 어떻게 imputation 하느냐? regression을 써도 되는걸까..?

개인적으로는 안될거라고..생각은 한다만 구글링을 해봤는데 확실히 자료가 없다.
범주형이어야 하는데 regression하면 그렇게 안나올거니까,,,? 사실 해본적 없다.

구글링을 아무리 해도 한국어로 된 자료는 고사하고 영어로 된 자료도 많지 않아서 그냥 돌아다니다가 참고할 만한 것들을 가져왔다.

Encode and KNN Impute All Categorical Features Fast

https://towardsdatascience.com/preprocessing-encode-and-knn-impute-all-categorical-features-fast-b05f50b4dfaa

쉽게 말해, fancyimpute의 KNN imputer를 통해 Encoding된 범주형 변수를 imputation하겠다는 것이다.
꽤 괜찮은 아이디어라고 생각한다. 다음에 필요하다면 시도해봐야겠다.

Strategies to handle the missing values in Categorical Variables

https://www.analyticsvidhya.com/blog/2021/04/how-to-handle-missing-values-of-categorical-variables/

음 뭐라하나, 내가 한번씩 생각했던 방법이다.
아직 방법론적으로 개발된 게 없는 것 같다.

  1. 값을 지워준다. (예?ㅠ)
  2. 최빈값으로 채워준다.
  3. classifier 모델을 통해 classify 해준다.
  4. 칼럼을 지워준다. (예?ㅠ)
  5. 비지도학습 알고리즘을 이용해본다. - 앞서 적은 KNN과 같은 방법들

개인적으로 3번이 조금 시도해볼만하다고 생각한다...

힘들다.! 지금 적용해보러 갈건데 코드로 구현해보게 되면 다시 추가할 예정이다.!!


⬇️ int feat에 대해서는 IterativeImputer, cat feat에 대해서는 SimpleImputer의 most_frequent value를 넣는 방법으로 시도했다.

from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import SimpleImputer, IterativeImputer

simp = SimpleImputer(strategy = 'most_frequent')
iterimp = IterativeImputer()

train_df[int_feat] = pd.DataFrame(iterimp.fit_transform(train_df[int_feat]), columns = int_feat)
train_df[cat_feat] = pd.DataFrame(simp.fit_transform(train_df[cat_feat]), columns = cat_feat)
profile
분명히 처음엔 데린이었는데,, 이제 개린이인가..

0개의 댓글