출처: https://rfriend.tistory.com/262 [R, Python 분석과 프로그래밍의 친구 (by R Friend):티스토리]
지난번 포스팅에서는 결측값 여부 확인, 결측값 개수 세기 등을 해보았습니다.
이번 포스팅에서는 결측값을 채우고 대체하는 다양한 방법들로서,
- 결측값을 특정 값으로 채우기
(replace missing valeus with scalar value)
- 결측값을 앞 방향 혹은 뒷 방향으로 채우기
(fill gaps forward or backward)
- 결측값 채우는 회수를 제한하기
(limit the amount of filling)
- 결측값을 변수별 평균으로 대체하기
(filling missing values with mean value per columns)
- 결측값을 다른 변수의 값으로 대체하기
(filling missing values with another columns' values)
등에 대해서 알아보도록 하겠습니다. 모델링에 들어가기 전에 결측값을 확인하고 결측값을 처리하는 절차가 반드시 필요하고 매우 중요한 부분입니다.
데이터 작성
먼저 필요한 pandas, numpy 모듈을 불러오고, 결측값(missing values)을 포함하고 있는 DataFrame을 만들어보겠습니다. 결측값은 df.ix[x, y] 에 None 또는 np.nan 을 할당해주면 됩니다.
#%% missing value imputation, filling missing value ## importing modules In [1]: import pandas as pd In [2]: import numpy as np ## making DataFrame In [3]: df = pd.DataFrame(np.random.randn(5, 3), ...: columns=['C1', 'C2', 'C3']) ...: In [4]: df Out[4]: C1 C2 C3 0 -0.905421 -0.228791 -0.850988 1 0.558819 0.564767 0.232641 2 -0.834515 -0.204626 -0.566917 3 0.242694 -0.317098 -0.673298 4 -0.497041 -0.301435 -1.265128 In [5]: df.ix[0, 0] = None In [6]: df.ix[1, ['C1', 'C3']] = np.nan In [7]: df.ix[2, 'C2'] = np.nan In [8]: df.ix[3, 'C2'] = np.nan In [9]: df.ix[4, 'C3'] = np.nan In [10]: df Out[10]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 NaN 2 -0.834515 NaN -0.566917 3 0.242694 NaN -0.673298 4 -0.497041 -0.301435 NaN |
(1) 결측값을 특정 값으로 채우기 (replace missing values with scalar value)
: df.fillna(0)
결측값을 '0' 으로 대체해보겠습니다.
In [11]: df_0 = df.fillna(0) In [12]: df_0 Out[12]: C1 C2 C3 0 0.000000 -0.228791 -0.850988 1 0.000000 0.564767 0.000000 2 -0.834515 0.000000 -0.566917 3 0.242694 0.000000 -0.673298 4 -0.497041 -0.301435 0.000000 |
이번에는 결측값을 'missing' 이라는 string 값으로 채워보겠습니다.
In [13]: df_missing = df.fillna('missing') In [14]: df_missing Out[14]: C1 C2 C3 C1 C2 C3 0 missing -0.228791 -0.850988 1 missing 0.564767 missing 2 -0.834515 missing -0.566917 3 0.242694 missing -0.673298 4 -0.497041 -0.301435 missing |
(2) 결측값을 앞 방향 혹은 뒷 방향으로 채우기 (fill gaps forward or backward)
: fillna(method='ffill' or 'pad'), fillna(method='bfill' or 'backfill')
(2-) 결측값을 앞 방향으로 채워나가려면(fill gaps forward) fillna(method='ffill') 혹은 fillna(method='pad') 를 사용하면 됩니다.
In [10]: df Out[10]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 NaN 2 -0.834515 NaN -0.566917 3 0.242694 NaN -0.673298 4 -0.497041 -0.301435 NaN In [15]: df.fillna(method='ffill') # Fill values forward Out[15]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 -0.850988 2 -0.834515 0.564767 -0.566917 3 0.242694 0.564767 -0.673298 4 -0.497041 -0.301435 -0.673298 In [16]: df.fillna(method='pad') # Fill values forward Out[16]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 -0.850988 2 -0.834515 0.564767 -0.566917 3 0.242694 0.564767 -0.673298 4 -0.497041 -0.301435 -0.673298 |
다음은 (2-2) 결측값을 뒷 방향으로 채워나가기 위해 fillna(method='bfill') 혹은 fillna(method='backfill')을 사용한 예시의 결과입니다.
In [10]: df Out[10]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 NaN 2 -0.834515 NaN -0.566917 3 0.242694 NaN -0.673298 4 -0.497041 -0.301435 NaN In [17]: df.fillna(method='bfill') # Fill values backward Out[17]: C1 C2 C3 0 -0.834515 -0.228791 -0.850988 1 -0.834515 0.564767 -0.566917 2 -0.834515 -0.301435 -0.566917 3 0.242694 -0.301435 -0.673298 4 -0.497041 -0.301435 NaN In [18]: df.fillna(method='backfill') # Fill values backward Out[18]: C1 C2 C3 0 -0.834515 -0.228791 -0.850988 1 -0.834515 0.564767 -0.566917 2 -0.834515 -0.301435 -0.566917 3 0.242694 -0.301435 -0.673298 4 -0.497041 -0.301435 NaN |
(3) 앞/뒤 방향으로 결측값 채우는 회수를 제한하기 (limit the amount of filling)
: fillna(method='ffill', limit=number), fillna(method='bfill', limit=number)
앞 방향이나 뒷 방향으로 채워나갈 때 fillna(limit=1) 를 사용해서 결측값 채우는 '개수'를 '1'개로 한정해 보겠습니다. 시계열 데이터 분석할 때 유용하게 사용하는 기능 중의 하나입니다.
아래 예에서 빨간색으로 밑줄 친 부분을 유심히 살펴보시면 이해하기 편하실 거예요.
In [10]: df Out[10]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 NaN 2 -0.834515 NaN -0.566917 3 0.242694 NaN -0.673298 4 -0.497041 -0.301435 NaN In [19]: df.fillna(method='ffill', limit=1) # fill values forward with limit Out[19]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 -0.850988 2 -0.834515 0.564767 -0.566917 3 0.242694 NaN -0.673298 4 -0.497041 -0.301435 -0.673298 In [20]: df.fillna(method='bfill', limit=1) # fill values backward with limit Out[20]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 -0.834515 0.564767 -0.566917 2 -0.834515 NaN -0.566917 3 0.242694 -0.301435 -0.673298 4 -0.497041 -0.301435 NaN |
(4) 결측값을 변수별 평균으로 대체하기(filling missing values with mean per columns)
: df.fillna(df.mean()), df.where(pd.notnull(df), df.mean(), axis='columns')
In [10]: df Out[10]: C1 C2 C3 0 NaN -0.228791 -0.850988 1 NaN 0.564767 NaN 2 -0.834515 NaN -0.566917 3 0.242694 NaN -0.673298 4 -0.497041 -0.301435 NaN # mean per columns In [21]: df.mean() Out[21]: C1 -0.362954 C2 0.011514 C3 -0.697068 dtype: float64 # filling missing values with mean per columns # way 1 In [22]: df.fillna(df.mean()) Out[22]: C1 C2 C3 0 -0.362954 -0.228791 -0.850988 1 -0.362954 0.564767 -0.697068 2 -0.834515 0.011514 -0.566917 3 0.242694 0.011514 -0.673298 4 -0.497041 -0.301435 -0.697068 # way 2 In [23]: df.where(pd.notnull(df), df.mean(), axis='columns') Out[23]: C1 C2 C3 0 -0.362954 -0.228791 -0.850988 1 -0.362954 0.564767 -0.697068 2 -0.834515 0.011514 -0.566917 3 0.242694 0.011514 -0.673298 4 -0.497041 -0.301435 -0.697068 |
위의 예시는 각 칼럼의 평균으로 -> 각 칼럼의 결측값을 대체하는 방식이었습니다.
아래 예시는 'C1' 칼럼의 평균을 가지고 'C1', 'C2', 'C3' 칼럼의 결측값을 대체하는 방법입니다.
In [24]: df.mean()['C1'] Out[24]: -0.36295411360962238 In [25]: df.fillna(df.mean()['C1']) Out[25]: C1 C2 C3 0 -0.362954 -0.228791 -0.850988 1 -0.362954 0.564767 -0.362954 2 -0.834515 -0.362954 -0.566917 3 0.242694 -0.362954 -0.673298 4 -0.497041 -0.301435 -0.362954 |
아래의 예시는 'C1'칼럼과 'C2' 칼럼에 대해서만 각 칼럼의 평균을 가지고 -> 각 칼럼에 있는 대체값을 대체하는 경우입니다. 좀 헷갈릴 수 있는데요, 위의 2개의 예시의 혼합 형태라고 보시면 되겠습니다.
('C3'의 NaN 값은 결측값 그대로 있습니다.)
In [26]: df.mean()['C1':'C2'] Out[26]: C1 -0.362954 C2 0.011514 dtype: float64 In [27]: df.fillna(df.mean()['C1':'C2']) Out[27]: C1 C2 C3 0 -0.362954 -0.228791 -0.850988 1 -0.362954 0.564767 NaN 2 -0.834515 0.011514 -0.566917 3 0.242694 0.011514 -0.673298 4 -0.497041 -0.301435 NaN |
(5) 결측값을 다른 변수의 값으로 대체하기
(filling missing values with another columns' values)
두가지 방법이 있는데요, 먼저 np.where()와 pd.notnumm() 를 사용해서 np.where(pd.notnull(df['C2']) == True, df['C2'], df['C1']) 처럼 'C2' 칼럼에서 결측값이 없으면 'C2' 칼럼의 값을 그대로 사용하고, 'C2'칼럼에 결측값이 있으면 'C1' 칼럼의 값을 가져다가 결측값을 채워보겠습니다.
In [28]: df_2 = pd.DataFrame({'C1': [1, 2, 3, 4, 5], ...: 'C2': [6, 7, 8, 9, 10]}) ...: In [29]: df_2.ix[[1, 3], ['C2']] = np.nan In [30]: df_2 Out[30]: C1 C2 0 1 6.0 1 2 NaN 2 3 8.0 3 4 NaN 4 5 10.0 ## making new column by filling missing values with another column's value # way 1 : by np.where => quick In [31]: df_2['C2_New'] = np.where(pd.notnull(df_2['C2']) == True, df_2['C2'], df_2['C1']) In [32]: df_2 Out[32]: C1 C2 C2_New 0 1 6.0 6.0 1 2 NaN 2.0 2 3 8.0 8.0 3 4 NaN 4.0 4 5 10.0 10.0 |
아래의 loop programming은 위와 동일한 결과를 반환하지만 시간은 훨~씬 오래 걸린다는 점 유의하시구요, 그냥 '아, 이렇게도 할 수 있구나...' 정도로만 참고하시기 바랍니다.
(당연히, 위의 np.where()와 pd.notnull() 을 사용하는 것이 속도도 빠르고 코드도 짧고 쉽기 때문에 추천)
# way 2 : by loop programming => takes long time In [33]: for i in df_2.index: ...: if pd.notnull(df_2.ix[i, 'C2']) == True: ...: df_2.ix[i, 'C2_New_2'] = df_2.ix[i, 'C2'] ...: else: ...: df_2.ix[i, 'C2_New_2'] = df_2.ix[i, 'C1'] ...: ...: In [34]: df_2 Out[34]: C1 C2 C2_New C2_New_2 0 1 6.0 6.0 6.0 1 2 NaN 2.0 2.0 2 3 8.0 8.0 8.0 3 4 NaN 4.0 4.0 4 5 10.0 10.0 10.0 |
결측값을 그룹별 평균으로 대체하기는 http://rfriend.tistory.com/402 를 참고하세요.
DataFrame 내 여러개의 칼럼별로 서로 다른 결측값 대체 전략을 사용하는 방법은 https://rfriend.tistory.com/542 를 참고하세요.
결측값을 선형회귀모형 추정값으로 대체하는 방법은 rfriend.tistory.com/636 를 참고하세요.
출처: https://rfriend.tistory.com/262 [R, Python 분석과 프로그래밍의 친구 (by R Friend):티스토리]
'파이썬' 카테고리의 다른 글
[파이썬]Unable to import required dependencies:numpy: (0) | 2022.08.22 |
---|---|
[Python] pandas 문자열 관련 함수 str (0) | 2022.08.18 |
[Pandas]데이터프레임 함수적용 pandas apply 사용법 및 apply lambda 설명 (0) | 2022.08.18 |
[Pandas]Pandas의 DataFrame에서 두 개의 텍스트 열 결합 (0) | 2022.08.18 |
[Pandas 기초] 데이터프레임 합치기(merge, join, concat) (0) | 2022.08.18 |
댓글