이전 포스팅에서 Pandas 의 함수를 활용해서 결측값을 채우거나 행을 제거하기, GroupBy operator를 사용해서 그룹별 (가중)평균을 구하는 방법을 소개했었습니다. 


이번 포스팅에서는 이전 포스팅의 내용들을 결합하여 '결측값을 그룹 별 평균값으로 채우기 (Fill missing values using the group means)' 를 해보겠습니다. 





먼저 예제로 사용할 'a'와 'b' 두 개의 그룹을 가지고 있고, 'col_1'과 'col_2'의 두 개의 칼럼을 가지고 있는 간단한 데이터프레임을 만들어보겠습니다. 



In [1]: import numpy as np

   ...: import pandas as pd


In [2]: np.random.seed(123) # for reproducibility


In [3]: df = pd.DataFrame({'grp': ['a', 'a', 'a', 'a', 'b', 'b', 'b', 'b'],

   ...: 'col_1': np.random.randn(8),

   ...: 'col_2': np.random.randn(8)})

   ...:

   ...: df

Out[3]:

col_1 col_2 grp

0 -1.085631 1.265936 a

1 0.997345 -0.866740 a

2 0.282978 -0.678886 a

3 -1.506295 -0.094709 a

4 -0.578600 1.491390 b

5 1.651437 -0.638902 b

6 -2.426679 -0.443982 b

7 -0.428913 -0.434351 b

 




다음으로 그룹 'a'와 'b'별로 'col_1'과 'col_2' 칼럼에 각각 하나씩 결측값(missing value, NaN)을 집어넣어보겠습니다. 



In [4]: df.loc[[1, 6], ['col_1', 'col_2']] = np.nan

   ...: df

Out[4]:

col_1 col_2 grp

0 -1.085631 1.265936 a

1 NaN NaN a

2 0.282978 -0.678886 a

3 -1.506295 -0.094709 a

4 -0.578600 1.491390 b

5 1.651437 -0.638902 b

6 NaN NaN b

7 -0.428913 -0.434351 b

 



'a'와 'b' 그룹별 'col_1'과 'col_2' 칼럼의 평균을 계산해보니 아래와 같군요. 이 그룹별 칼럼별 평균 값으로 결측값을 대체하려는 것입니다. 



In [5]: df.groupby('grp').mean()

   ...:

Out[5]:

      col_1       col_2

grp

a -0.769649    0.164114

b  0.214641    0.139379

 



자, 이제 준비가 되었으니 GroupBy operator lambda 함수, 그리고 apply() 를 사용해서 그룹별 칼럼별 평균을 가지고 결측값을 채워(imputation)보겠습니다. 



In [6]: fill_mean_func = lambda g: g.fillna(g.mean())


In [7]: df.groupby('grp').apply(fill_mean_func)

Out[7]:

            col_1         col_2       grp

grp

a     0  -1.085631    1.265936    a

      1  -0.769649    0.164114   a

      2   0.282978   -0.678886    a

      3  -1.506295   -0.094709    a

b    4  -0.578600    1.491390    b

      5   1.651437   -0.638902    b

      6   0.214641    0.139379    b

      7  -0.428913   -0.434351    b

 





만약에 각 "그룹별"로 "특정 값(specific value)"을 가지고 결측값을 대체하고 싶다면 아래의 코드를 참고하세요. 



In [8]: fill_values = {'a': 1.0, 'b': 0.5}


In [9]: fill_func = lambda d: d.fillna(fill_values[d.name])


In [10]: df.groupby('grp').apply(fill_func)

Out[10]:

      col_1            col_2 grp

0   -1.085631       1.265936 a

1    1.000000       1.000000 a

2    0.282978      -0.678886 a

3   -1.506295      -0.094709 a

4   -0.578600       1.491390 b

5    1.651437      -0.638902 b

6    0.500000       0.500000 b

7   -0.428913      -0.434351 b

 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. ^^



Posted by R Friend R_Friend

댓글을 달아 주세요

  1. Das 2019.05.11 18:15  댓글주소  수정/삭제  댓글쓰기

    정말 많은 도움이 됐습니다 ^^

  2. RRR 2019.08.24 21:54  댓글주소  수정/삭제  댓글쓰기

    안녕하세요!! R을 사용하는데 정말 많은 도움이 되고 있습니다!!
    혹시 R에서 결측값을 그룹별 평균값으로 채우기 위해서는 어떤 코드를 이용해야할까요?

  3. RRR 2019.08.24 22:11  댓글주소  수정/삭제  댓글쓰기

    답변 감사합니다!!
    그런데 저는 변수별 평균으로 대체하는 방법이 아닌 위의 내용처럼 그룹별 평균으로 대체하는 방법이 필요해서요..

    • R Friend R_Friend 2019.08.24 22:53 신고  댓글주소  수정/삭제

      R을 사용해서 그룹별 평균으로 대체하는 방법은 아래의 dplyr 의 mutate 함수 사용하는 방법 참고하세요.

      library(dplyr)
      df %>%
      group_by(grp) %>%
      mutate(val = ifelse(is.na(val), mean(val, na.rm=TRUE), val))

    • RRR 2019.08.24 23:13  댓글주소  수정/삭제

      와 너무 감사합니다!! 아무리 구글링을 해도 못찾았었는데 너무 큰 도움이 되었어요!!

    • R Friend R_Friend 2019.08.24 23:17 신고  댓글주소  수정/삭제

      잘 해결되었다니 다행입니다. R 결측값 처리 포스팅의 본문 내용 업데이트 해놔야겠네요.

DataFrame을 가지고 분석을 진행하다 보면 대부분의 경우 결측값(missing value)이 골치거리로 따라 다닙니다.

 

데이터가 원래 수집 혹은 측정이 안되었을 수도 있고, 다수의 DataFrame을 서로 병합하는 과정에서 결측값이 생길 수도 있으며, index를 재설정(reindex)하는 경우에도 결측값이 생길 수 있습니다.

 

이처럼 다양한 이유로 인해서 생기는 결측값은 분석 오류가 발생시키거나 혹은 왜곡시킬 위험이 있습니다. 따라서 분석할 DataFrame을 생성했으면 결측값(missing value)이 있는지 여부에 대해서 꼭 확인하고 조치하여야 합니다.

 

이번 포스팅에서는 Python pandas의 isnull(), notnull() 메소드를 활용해서 결측값이 있는지 여부를 확인하는 방법을 소개하겠습니다.

 

 

 

 

Python pandas에서는 결측값을 'NaN' 으로 표기하며, 'None'도 결측값으로 인식합니다.

 

 

먼저 결측값이 있는 DataFrame을 만들어보겠습니다.

 

 

# making DataFrame with missing values

In [1]: import pandas as pd


In [2]: from pandas import DataFrame


In [3]: df_left = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],

   ...: 'A': ['A0', 'A1', 'A2', 'A3'],

   ...: 'B': [0.5, 2.2, 3.6, 0.4]})


In [4]: df_right = DataFrame({'KEY': ['K2', 'K3', 'K4', 'K5'],

   ...: 'C': ['C2', 'C3', 'C4', 'C5'],

   ...: 'D': ['D2', 'D3', 'D4', 'D5']})


In [5]: df_all = pd.merge(df_left, df_right, how='outer', on='KEY')


In [6]: df_all

Out[6]:

     A    B KEY    C    D
0   A0  0.5  K0  NaN  NaN
1   A1  2.2  K1  NaN  NaN
2   A2  3.6  K2   C2   D2
3   A3  0.4  K3   C3   D3
NaN  NaN  K4   C4   D4
NaN  NaN  K5   C5   D5

 

 

 

 

  (1) DataFrame 전체의 결측값 여부 확인 : df.isnull(), isnull(df), df.notnull(), notnull(df)

 

isnull() 메소드는 관측치가 결측이면 True, 결측이 아니면 False boollean 값을 반환합니다.

notnull() 메소드는 관측치가 결측이면 False, 결측이 아니면 True를 반환합니다.(isnull() 과 정반대)

 

isnull(DataFrame) 과 DataFrame.isnull() 은 동일한 값을 반환하며, notnull(DataFrame)과 DataFrame.notnull() 역시 동일한 의미의 script 입니다.

 

 

In [7]: pd.isnull(df_all)

Out[7]:

       A      B    KEY      C      D
0  False  False  False   True   True
1  False  False  False   True   True
2  False  False  False  False  False
3  False  False  False  False  False
4   True   True  False  False  False
5   True   True  False  False  False


In [8]: df_all.isnull()

Out[8]:

       A      B    KEY      C      D
0  False  False  False   True   True
1  False  False  False   True   True
2  False  False  False  False  False
3  False  False  False  False  False
4   True   True  False  False  False
5   True   True  False  False  False


In [9]: pd.notnull(df_all)

Out[9]:

       A      B   KEY      C      D
0   True   True  True  False  False
1   True   True  True  False  False
2   True   True  True   True   True
3   True   True  True   True   True
False  False  True   True   True
False  False  True   True   True

 

In [10]: df_all.notnull()

Out[10]:

       A      B   KEY      C      D
0   True   True  True  False  False
1   True   True  True  False  False
2   True   True  True   True   True
3   True   True  True   True   True
False  False  True   True   True
False  False  True   True   True

 

 

 

  (2) 특정 변수, 원소에 결측값 추가하기, 결측값 여부 확인하기 : indexing & None

 

아래 예시의 'df_all' DataFrame 에서 ['A', 'B'] 칼럼의 ['0', '1'] index 위치에 있는 관측치에 'None'을 할당하여 결측치를 만들어보았습니다.

 

'A'칼럼의 경우 'string' 데이터 형식인데요, 'None'을 할당하니 'None'으로 입력되었습니다.  반면에, 'B' 칼럼의 경우 'float' 데이터 형식인데요, 'None'을 할당하니 'NaN'으로 자동으로 입력되었습니다.

 

DataFrame의 행, 열을 기준으로 indexing을 하고 싶을 때는 DataFrame.ix[[row1, row2], ['col1', 'col2']] 을 사용하면 됩니다.  아래 예시를 참고하세요.

 

 

In [11]: df_all

Out[11]:

     A        B  KEY    C    D
0   A0      0.5  K0  NaN  NaN
1   A1      2.2  K1  NaN  NaN
2   A2      3.6  K2   C2   D2
3   A3      0.4  K3   C3   D3
NaN  NaN  K4   C4   D4
NaN  NaN  K5   C5   D5

 

In [12]: df_all.ix[[0, 1], ['A', 'B']] = None


In [13]: df_all

Out[13]:

      A         KEY    C      D
None  NaN  K0  NaN  NaN
None  NaN  K1  NaN  NaN
2    A2     3.6   K2   C2   D2
3    A3     0.4   K3   C3   D3
4   NaN  NaN  K4   C4   D4
5   NaN  NaN  K5   C5   D5


In [14]: df_all[['A', 'B']].isnull()

Out[14]:

       A      B
0   True   True
1   True   True
2  False  False
3  False  False
4   True   True
5   True   True

 

 

 

 

  (3) 칼럼별 결측값 개수 구하기 : df.isnull().sum()

 

 

# counting missing value numbers for all columns

In [15]: df_all.isnull().sum()

Out[15]:

A      2
B      2
KEY    0
C      2
D      2
dtype: int64

 

# counting missing value numbers for 'A' column

In [16]: df_all['A'].isnull().sum()

Out[16]: 2

 

 

 

 

반대로, 칼럼별 결측값이 아닌 값의 개수를 구하려면 df.notnull().sum() 을 사용하면 됩니다.

 

 

# counting notnull value numbers for all columns

In [17]: df_all.notnull().sum()

Out[17]:
A      4
B      4
KEY    6
C      4
D      4
dtype: int64

 

 

 

 

  (4) 행(row) 단위로 결측값 개수 구하기 : df.isnull().sum(1)

       행(row) 단위로 실측값 개수 구하기 : df.notnull().sum(1)

 

 

In [18]: df_all

Out[18]:

     A    B KEY    C    D
0   A0  0.5  K0  NaN  NaN
1   A1  2.2  K1  NaN  NaN
2   A2  3.6  K2   C2   D2
3   A3  0.4  K3   C3   D3
4  NaN  NaN  K4   C4   D4
5  NaN  NaN  K5   C5   D5


In [19]: df_all['NaN_cnt'] = df_all.isnull().sum(1)


In [20]: df_all['NotNull_cnt'] = df_all.notnull().sum(1)


In [21]: df_all

Out[21]:

     A      B    KEY   C     D     NaN_cnt   NotNull_cnt
0   A0    0.5   K0  NaN  NaN      2            4
1   A1    2.2   K1  NaN  NaN      2            4
2   A2    3.6   K2   C2    D2        0            6
3   A3    0.4   K3   C3    D3        0            6
4  NaN  NaN  K4   C4    D4        2            4
5  NaN  NaN  K5   C5    D5        2            4

 

 

 

 

다음번 포스팅에서는 결측값 연산에 대해서 소개하겠습니다.

 

 

Posted by R Friend R_Friend

댓글을 달아 주세요