데이터를 수집하는 과정 중의 오류, 데이터를 병합하는 과정에서의 오류 등으로 인해서 중복되지 않아야 할 데이터가 중복이 되는 경우가 생길 수 있습니다.

 

특히, unique 한 'key' 값을 관리해야 하는 경우 중복(duplicates)이 발생하면 분석에 심각한 영향을 끼칠 수도 있습니다.  (관계형 DB라면 아례 데이터가 들어가지도 않겠지요...)

 

따라서 본격적인 데이터 분석에 들어가기 전에 반드시, 꼭, 예외없이, 무조건, Seriously 중복 데이터를 확인하고 처리하는 전처리 작업이 필요합니다.  (가령, 두 개의 DataFrame을 Left Join 으로 Merge했더니 row의 개수가 늘어났다 하면 그건 병합하는 DataFrame의 'key' 값에 중복이 있다는 얘기거든요.)

 

데이터 개수가 몇 개 안되면 눈으로 확인하고 중복 데이터 위치 indexing 해서 처리하면 되는데요, 데이터 개수가 많으면 육안으로 일일이 확인한다는게 사실상 불가능해집니다.

 

이때 중복이 존재하는지 확인할 때 사용할 수 있는 것이 Python pandas의 duplicated() method 입니다.  그리고 중복값을 처리하는 것이 drop_duplicates() method 이구요.

 

 - 중복 여부 확인 : DataFrame.duplicated()

 - 중복이 있으면 처음과 마지막 값 중 무엇을 남길 것인가? : keep = 'first', 'last', False

 - 중복값 처리(unique한 1개의 key만 남기고 나머지 중복은 제거) : DataFrame.drop_duplicates()

 

 

 

 

먼저 pandas 모듈을 불러오고, 예제로 사용할 '중복(duplicate entries)'이 있는 DataFrame을 만들어보겠습니다.

 



In [1]: import pandas as pd


In [2]: data = {'key1':['a', 'b', 'b', 'c', 'c'],
   ...: 'key2':['v', 'w', 'w', 'x', 'y'],
   ...: 'col':[1, 2, 3, 4, 5]}


In [3]: df = pd.DataFrame(data, columns=['key1','key2','col'])


In [4]: df
Out[4]:
  key1 key2  col
0    a    v    1
1    b    w    2
2    b    w    3
3    c    x    4
4    c    y    5




 

 

 

  (1) 중복 데이터가 있는지 확인하기 : DataFrame.duplicated()

 

'key1'을 기준으로 하면 index 1, 2 ('b')가 중복, index 3, 4 ('c')가 서로 중복입니다.

'key1'과 'key2'를 동시에 기준으로 하면 index 1, 2 ('b', 'w') 가 서로 중복입니다.

 

DataFrame.duplicated() method는 True, False 의 boolean 형태의 Series를 반환합니다.

 



In [5]: df.duplicated(['key1'])
Out[5]:
0    False
1    False
2     True
3    False
4     True
dtype: bool





In [6]: df.duplicated(['key1', 'key2'])
Out[6]:
0    False
1    False
2     True
3    False
4    False
dtype: bool




 

 

 

  (2) 중복이 있으면 처음과 끝 중 무슨 값을 남길 것인가? : keep = 'first', 'last', False

 

keep='first'가 default 이며, 중복값이 있으면 첫번째 값을 duplicated 여부를 False로 반환하고, 나머지 중복값에 대해서는 True를 반환하게 됩니다. keep='last'는 그 반대이겠지요.

 

keep=False는 처음이나 끝값인지 여부는 고려를 안하고 중복이면 무조건 True를 반환합니다 (=> 나중에 drop_duplicates() 에서 keep 할 생각이 없다는 뜻입니다).

 



In [7]: df.duplicated(['key1'], keep='first') # by default
Out[7]:
0    False
1    False
2     True
3    False
4     True
dtype: bool




 

In [8]: df.duplicated(['key1'], keep='last')
Out[8]:
0    False
1     True
2    False

3     True
4    False
dtype: bool
 
 


In [9]: df.duplicated(['key1'], keep=False)
Out[9]:
0    False
1     True
2     True
3     True
4     True

dtype: bool




 

 

 

  (3) 중복값 처리(unique한 1개의 key만 남기고 나머지 중복은 제거) 
       : DataFrame.drop_duplicates()

 

drop_duplicates() method는 중복값을 keep='first', 'last', False argument에 따라서 unique한 1개의 key값만 남기고 나머지 중복은 제거를 한 후의 DataFrame을 반환합니다.

 



# drop duplic
In [10]: df.drop_duplicates(['key1'], keep='first')
Out[10]:
  key1 key2  col
0    a    v    1
1    b    w    2
3    c    x    4





In [11]: df.drop_duplicates(['key1'], keep='last')
Out[11]:
  key1 key2  col
0    a    v    1
2    b    w    3
4    c    y    5





In [12]: df.drop_duplicates(['key1'], keep=False)
Out[12]:
  key1 key2  col
0    a    v    1



 

 

 

이상으로 중복 여부 확인, 중복값 처리를 위한 Python pandas의 DataFrame.duplicated(), DataFrame.drop_duplicates() method에 대한 소개를 마치겠습니다.

 

 

 

 

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

 

 

 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python pandas의

 

 - fillna() method를 사용한 결측값 대체

 - dropna() method를 사용한 결측값 있는 행, 열 제거

 - interpolate() method를 사용한 결측값 보간

 

하는 방법을 알아보았습니다.

 

이번 포스팅에서는 Python pandas의 replace() method를 사용해서

 

 - 결측값 혹은 원래의 값을 다른 값으로 교체(replacing generic values)

 - List 를 다른 List로 교체

 - mapping dict 로 교체

 - DataFrame의 특정 칼럼 값 교체

 

하는 방법을 소개하겠습니다.

 

fillna() 를 사용한 결측값 대체와 replace()를 사용한 결측값 교체가 유사한면이 있구요, 다만 replace() 는 결측값 말고도 모든 값을 대상으로 할 수 있고, list, mapping dict 등으로 좀더 유연하고 포괄적으로 사용할 수 있는 장점이 있습니다.

 

 

 

 

replace() method를 가지고 결측값, 실측값 모두를 대상으로 교체(replacement)하는 예를 들어보겠습니다.  필요한 모듈을 importing하고, 결측값을 포함한 간단한 Series 먼저 시작하겠습니다.

 

 

In [1]: import pandas as pd


In [2]: import numpy as np


In [3]: from pandas import DataFrame, Series

 

In [4]: ser = Series([1, 2, 3, 4, np.nan])

 

In [5]: ser

Out[5]:

0    1.0
1    2.0
2    3.0
3    4.0
4    NaN
dtype: float64

 

 

 

 

  (1) 결측값, 실측값을 다른 값으로 교체하기 : replace(old_val, new_val)

 

 

# replacing a value with other value

In [6]: ser.replace(2, 20)

Out[6]:

0     1.0
1    20.0
2     3.0
3     4.0
4     NaN
dtype: float64

 

# replacing missing value(NaN) with other value

In [7]: ser.replace(np.nan, 5)

Out[7]:

0    1.0
1    2.0
2    3.0
3    4.0
4    5.0
dtype: float64

 

 

 

 

 

  (2) list 를 다른 list 값으로 교체하기 : replace([old1, old2, ...], [new1, new2, ...])

 

 

In [8]: ser

Out[8]:

0    1.0
1    2.0
2    3.0
3    4.0
4    NaN
dtype: float64

 

In [9]: ser.replace([1, 2, 3, 4, np.nan], [6, 7, 8, 9, 10])

Out[9]:

0     6.0
1     7.0
2     8.0
3     9.0
4    10.0
dtype: float64

 

 

 

 

  (3) mapping dict 로 원래 값, 교체할 값 매핑 : replace({old1 : new1, old2: new2})

 

 

In [10]: ser

Out[10]:

0    1.0
1    2.0
2    3.0
3    4.0
4    NaN
dtype: float64

 

In [11]: ser.replace({1: 6,

    ...: 2: 7,

    ...: 3: 8,

    ...: 4: 9,

    ...: np.nan: 10})

Out[11]:

0     6.0
1     7.0
2     8.0
3     9.0
4    10.0
dtype: float64

 

 

 

 

  (4) DataFrame의 특정 칼럼 값 교체하기 : df.replace({'col1': old_val}, {'col1': new_val})

 

DataFrame 의 값을 교체하려면 dict { } 안에 원래와 교체할 'column name'과 'value' 를 짝을 이루어서 매핑해주면 됩니다.

 

 

In [13]: df = DataFrame({'C1': ['a_old', 'b', 'c', 'd', 'e'],

    ...: 'C2': [1, 2, 3, 4, 5],

    ...: 'C3': [6, 7, 8, 9, np.nan]})


In [14]: df

Out[14]:

      C1  C2   C3
a_old   1  6.0
1      b   2  7.0
2      c   3  8.0
3      d   4  9.0
4      e   5  NaN

 

In [15]: df.replace({'C1': 'a_old'}, {'C1': 'a_new'})

Out[15]:

      C1  C2   C3
a_new   1  6.0
1      b   2  7.0
2      c   3  8.0
3      d   4  9.0
4      e   5  NaN


In [16]: df.replace({'C3': np.nan}, {'C3': 10})

Out[16]:

      C1  C2    C3
0  a_old   1   6.0
1      b   2   7.0
2      c   3   8.0
3      d   4   9.0
4      e   5  10.0

 

 

 

이상으로 Python pandas의 replace() method를 사용해서 결측값, 원래 값을 다른 값으로 교체하기 소개를 마치겠습니다.

 

결측값을 그룹별로 그룹별 평균으로 대체하기는 http://rfriend.tistory.com/402 를 참고하세요. 


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

 

 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python pandas 의 dropna() method를 사용해서

 

 - 결측값이 들어있는 행 전체 제거하기

 - 결측값이 들어있는 열 전체 제거하기

 

방법을 알아보았습니다.

 

 

이번 포스팅에서는 Python pandas의 interpolate() method를 사용해서 결측값을 보간하는 방법(interpolation of missing values)에 대해서 소개하겠습니다.

 

 - (1) 시계열데이터의 값에 선형으로 비례하는 방식으로 결측값 보간

       (interpolate TimeSeries missing values linearly)

 - (2) 시계열 날짜 index를 기준으로 결측값 보간

       (interploate TimeSeries missing values along with Time-index)

 - (3) DataFrame 값에 선형으로 비례하는 방식으로 결측값 보간

       (interpolate DataFrame missing values linearly)

 - (4) 결측값 보간 개수 제한하기

       (limit the number of consecutive interpolation)

 

 

시계열 데이터 분석이라든지 (X, Y, Z) 좌표 공간 상에서 측정된 데이터(근접한 공간 상 데이터 간 상관성/연속성이 있는 경우) 등에서 사용하면 매우 유용하고 편리한 method 입니다.

 

 

이전 포스팅의 결측값 대체는 '특정의 동일 값'으로 채우는 방식(filling, imputation)이었던 반면에, 이번 포스팅의 결측값 보간(interploation)은 실측값과 실측값 사이의 결측값을 마치 '그라데이션(gradation)' 기법으로 색깔을 조금씩 변화시켜가면서 부드럽게 채워나가는 방법이라고 이해하시면 되겠습니다.

 

 

 

자, 그럼 필요한 모듈을 import 하고, 결측값이 들어있는 TimeSeries 데이터를 만든 후에, 결측값을 보간해보겠습니다.

 

  (1) 시계열데이터의 값에 선형으로 비례하는 방식으로 결측값 보간
       (interpolate TimeSeries missing values linearly) : ts.interpolate()

 



# importing modules 
In [1]: import pandas as pd


In [2]: import numpy as np


In [3]: from pandas import DataFrame, Series


In [4]: from datetime import datetime


# making TimeSeries with missing values
In [5]: datestrs = ['12/1/2016', '12/03/2016', '12/04/2016', '12/10/2016']


In [6]: dates = pd.to_datetime(datestrs)


In [7]: dates
Out[7]: DatetimeIndex(['2016-12-01', '2016-12-03', '2016-12-04', '2016-12-10'], dtype='datetime64[ns]', freq=None)


In [8]: ts = Series([1, np.nan, np.nan, 10], index=dates)


In [9]: ts
Out[9]:
2016-12-01     1.0
2016-12-03     NaN
2016-12-04     NaN

2016-12-10    10.0
dtype: float64




 

 

ts.interpolote() 는 default 가 interpolate(method='values')로서 선형으로 비례하여 결측값을 보간해줍니다.

 



# interpolate missing values linearly 
In [10]: ts_intp_linear = ts.interpolate()


In [11]: ts_intp_linear
Out[11]:
2016-12-01     1.0
2016-12-03     4.0
2016-12-04     7.0
2016-12-10    10.0




 

 

 

  (2) 시계열 날짜 index를 기준으로 결측값 보간
       (interploate TimeSeries missing values along with Time-index)
       : ts.interploate(method='time')

 

위의 ts.interpolate() 결과와 아래의 ts.interpolate(method='time') 보간 결과를 유심히 비교해보시기 바랍니다.

 



In [12]: ts
Out[12]:
2016-12-01     1.0
2016-12-03     NaN
2016-12-04     NaN
2016-12-10    10.0



dtype: float64


In [13]: ts_intp_time = ts.interpolate(method='time')


In [14]: ts_intp_time
Out[14]:
2016-12-01     1.0
2016-12-03     3.0
2016-12-04     4.0
2016-12-10    10.0
dtype: float64




 

 

 

  (3) DataFrame 값에 선형으로 비례하는 방식으로 결측값 보간
      (interpolate DataFrame missing values linearly)

 



In [15]: df = DataFrame({'C1': [1, 2, np.nan, np.nan, 5],
    ...: 'C2': [6, 8, 10, np.nan, 20]})
    ...:


In [16]: df
Out[16]:
    C1    C2
0  1.0   6.0
1  2.0   8.0
NaN  10.0
NaN   NaN
4  5.0  20.0



In [17]: df_intp_values = df.interpolate(method='values')

In [18]: df_intp_values
Out[18]:
    C1    C2
0  1.0   6.0
1  2.0   8.0
3.0  10.0
4.0  15.0
4  5.0  20.0





# the same with : df.interpolate(method='values') = df.interpolate()
In [19]: df.interpolate() # by default : method='values'
Out[19]:
    C1    C2
0  1.0   6.0
1  2.0   8.0
3.0  10.0
4.0  15.0
4  5.0  20.0


 

 

 

 (4) 결측값 보간 개수 제한하기 (limit the number of consecutive interpolation) : limit

 

연속적으로 결측값을 보간하는 개수를 'limit=1'을 사용해서 제한해보겠습니다.  아래 예시처럼 df.ix[3, 'C1']의 NaN이 보간되지 않고 NaN 그대로 남아있습니다.

 



In [20]: df.interpolate(method='values', limit=1)
Out[20]:
    C1    C2
0  1.0   6.0
1  2.0   8.0
3.0  10.0
NaN  15.0
4  5.0  20.0




 

 

 

limit_direction='backward' 로 설정해주면 보간해주는 방향이 밑에서 위로 올라가면서 해주게 됩니다.

(아무런 설정을 안해주면 default 로 limit_direction='forward' 설정됨.  limit_direction='both' 도 있음)

 



In [29]: df.interpolate(method='values', limit=1, limit_direction='backward')
Out[29]:
    C1    C2
0  1.0   6.0
1  2.0   8.0
NaN  10.0
4.0  15.0
4  5.0  20.0



 

 

 

이상으로 Python pandas 의 interpolate() method를 활용한 결측값 보간법을 마치도록 하겠습니다.

 

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

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python pandas의 fillna() method를 사용한

 

 - 결측값 여부 확인하기

 - 결측값 채우기, 결측값 대체하기

 

에 대해서 알아보았습니다.

 

이번 포스팅에서는 Python pandas의 dropna() method를 사용해서

 

 - 결측값이 들어있는 행 전체 제거

   (delete row with missing values)

 - 결측값이 들어있는 열 전체를 제거

   (delete column with missing values)

 - 특정 행 또는 열 만을 대상으로 결측값이 들어있으면 제거

   (delete specific row or column with missing values)

 

하는 방법을 소개하겠습니다.

 

관측값이 아주 많고 결측값이 별로 없는 경우에는 결측값이 들어있는 행 전체를 삭제하고 분석을 진행해도 무리가 없고 편리할 수 있습니다.

 

혹은 특정 변수의 결측값 비율이 매우 높고, 결측값을 채워넣을 만한 마땅한 방법이 없는 경우에는 분석의 신뢰성 확보를 위해서 그 변수(행, 칼럼)을 삭제하고 분석을 진행할 필요도 있습니다.

 

이때 dropna() method 를 사용하면 됩니다.

 

 

 

 

먼저, 필요한 모듈을 불러오고, 결측값(None 또는 np.nan)이 들어있는 DataFrame을 만들어보겠습니다.

 

import numpy as np
import pandas as pd


# seeding the random generator for reproducibility
np.random.seed(1004) 

## making a sample DataFrame
df = pd.DataFrame(
    np.random.randn(5, 4), 
    columns=['C1', 'C2', 'C3', 'C4']
)

print(df)
#          C1        C2        C3        C4
# 0  0.594403  0.402609 -0.805162  0.115126
# 1 -0.753065 -0.784118  1.461576  1.576076
# 2 -0.171318 -0.914482  0.860139  0.358802
# 3  1.729657 -0.497648  1.761870  0.169013
# 4 -1.085237 -0.010652  1.115798 -1.264972


## adding the missing values (None or np.nan)
df.loc[[0, 1], 'C1'] = None
df.loc[2, 'C2'] = np.nan

print(df)
#          C1        C2        C3        C4
# 0       NaN  0.402609 -0.805162  0.115126
# 1       NaN -0.784118  1.461576  1.576076
# 2 -0.171318       NaN  0.860139  0.358802
# 3  1.729657 -0.497648  1.761870  0.169013
# 4 -1.085237 -0.010652  1.115798 -1.264972

 

 

 

이제 dropna() method를 사용해서 결측값이 들어있는 행 전체(axis = 0) 삭제, 혹은 결측값이 들어있는 열 전체(axis=1) 삭제해 보겠습니다.

 

  (1) 결측값이 들어있는 행 전체 삭제하기(delete row with NaN) : df.dropna(axis=0)

 

## deleting rows with missing values
df_drop_row = df.dropna(axis=0)

print(df_drop_row)
#          C1        C2        C3        C4
# 3  1.729657 -0.497648  1.761870  0.169013
# 4 -1.085237 -0.010652  1.115798 -1.264972

 

 

 

(2) 결측값이 들어있는 열 전체 삭제하기 (delete column with NaN) : df.dropna(axis=1)

 

print(df)
#          C1        C2        C3        C4
# 0       NaN  0.402609 -0.805162  0.115126
# 1       NaN -0.784118  1.461576  1.576076
# 2 -0.171318       NaN  0.860139  0.358802
# 3  1.729657 -0.497648  1.761870  0.169013
# 4 -1.085237 -0.010652  1.115798 -1.264972


## deleting columns with missing values
df_drop_column = df.dropna(axis=1)
print(df_drop_column)
#          C3        C4
# 0 -0.805162  0.115126
# 1  1.461576  1.576076
# 2  0.860139  0.358802
# 3  1.761870  0.169013
# 4  1.115798 -1.264972

 

 

 

(3) 특정 행 또는 열을 대상으로 결측값이 들어있으면 제거
       (delete specific row or column with missing values) : df[ ].dropna()

 

DataFrame의 행 또는 열을 indexing 한 후에 dropna() method를 적용하면 됩니다.  dropna() 와 dropna(axis=0)은 동일합니다 (즉, axis=0 은 생략 가능).

 

아래에 4가지의 예를 들어보았습니다.  원래의 df DataFrame을 어떻게 indexing 하느냐에 따라 결측값이 들어있는 행과 열이 제거되는지 유심히 살펴보시면 금방 이해가 될거예요.

 

 

[df DataFrame의 칼럼 'C1' 에서 결측값이 있는 행 제거하기 ==> return Series]

print(df)
#          C1        C2        C3        C4
# 0       NaN  0.402609 -0.805162  0.115126
# 1       NaN -0.784118  1.461576  1.576076
# 2 -0.171318       NaN  0.860139  0.358802
# 3  1.729657 -0.497648  1.761870  0.169013
# 4 -1.085237 -0.010652  1.115798 -1.264972


## deleting specific columns with missing values
df['C1'].dropna()
# 2   -0.171318
# 3    1.729657
# 4   -1.085237
# Name: C1, dtype: float64

 

 

[df DataFrame의 칼럼 'C1', 'C2', 'C3' 에서 결측값이 있는 행(axis=0, by default) 제거하기, 열(axis=1) 제거하기 ==> return DataFrame]

print(df[['C1', 'C2', 'C3']])
#          C1        C2        C3
# 0       NaN  0.402609 -0.805162
# 1       NaN -0.784118  1.461576
# 2 -0.171318       NaN  0.860139
# 3  1.729657 -0.497648  1.761870
# 4 -1.085237 -0.010652  1.11579



## dropping rows with missing values, the same with dropna(axis=0)
df[['C1', 'C2', 'C3']].dropna()
#          C1        C2        C3
# 3  1.729657 -0.497648  1.761870
# 4 -1.085237 -0.010652  1.115798


## dropping columns with missing values
df[['C1', 'C2', 'C3']].dropna(axis=1)
#          C3
# 0 -0.805162
# 1  1.461576
# 2  0.860139
# 3  1.761870
# 4  1.115798

 

 

[df DataFrame의 행 2, 4, 그리고 칼럼 'C1', 'C2', 'C3'에서 결측값이 있는 행 제거하기 ==> return DataFrame]

df.loc[[2, 4], ['C1', 'C2', 'C3']]
#          C1        C2        C3
# 2 -0.171318       NaN  0.860139
# 4 -1.085237 -0.010652  1.115798


## droping rows with missing values
df.loc[[2, 4], ['C1', 'C2', 'C3']].dropna(axis=0)
#          C1        C2        C3
# 4 -1.085237 -0.010652  1.115798

 

 

 

마지막으로 첨언하자면, 결측값이 들어있는 행 전체, 혹은 열 전체를 삭제하는 것은 데이터 소실, 혹은 데이터 모집단 왜곡의 위험도 있는 만큼 분석에 영향도를 한번 생각해보고 나서 결정하시면 좋겠습니다.

 

그리고 만약을 대비해서 원본 데이터 (source data)는 그대로 남겨 놓은 상태에서 행 삭제 혹은 열 삭제 후의 DataFrame은 다른 이름으로 copy 해서 사용하실 것을 권합니다. 

 

다음번 포스팅에서는 결측값 보간(interpolation)에 대해서 알아보겠습니다.

 

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

 

 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 결측값 여부 확인, 결측값 개수 세기 등을 해보았습니다.

 

이번 포스팅에서는 결측값을 채우고 대체하는 다양한 방법들로서,

 

 (1) 결측값을 특정 값으로 채우기  (replace missing valeus with scalar value)

 (2) 결측값을 앞 방향 혹은 뒷 방향으로 채우기 (fill gaps forward or backward)

 (3) 결측값 채우는 회수를 제한하기 (limit the amount of filling)

 (4) 결측값을 변수별 평균으로 대체하기 (filling missing values with mean values per columns)

 (5) 결측값을 다른 변수의 값으로 대체하기 (filling missing values with another columns' values)

 

등에 대해서 알아보도록 하겠습니다. 

모델링에 들어가기 전에 결측값을 확인하고 결측값을 처리하는 절차가 반드시 필요하고 매우 중요한 부분입니다. 

 

 

 

 

먼저 필요한 pandas, numpy 모듈을 불러오고, 결측값(missing values)을 포함하고 있는 DataFrame을 만들어보겠습니다.  결측값은 None 또는 np.nan 을 할당해주면 됩니다.

 

import numpy as np
import pandas as pd

## making a sample DataFrame
df = pd.DataFrame(np.random.randn(5, 3),
                  columns=['C1', 'C2', 'C3'])
                  
df

#          C1        C2        C3
# 0 -1.245527 -0.332396 -1.310678
# 1 -2.007546 -0.747846 -0.704082
# 2 -2.237326 -1.065539  1.086993
# 3 -1.179056  1.372350  0.388981
# 4  1.070266 -1.545383  0.996416


## replacing some values with None
df.loc[0, 'C1'] = np.nan
df.loc[1, 'C1'] = np.nan
df.loc[1, 'C3'] = np.nan
df.loc[2, 'C2'] = np.nan
df.loc[3, 'C2'] = np.nan
df.loc[4, 'C3'] = np.nan


df

#          C1        C2        C3
# 0       NaN  0.893384  2.388055
# 1       NaN  0.012155       NaN
# 2  0.099263       NaN -1.330312
# 3  2.468724       NaN  1.046972
# 4 -1.157316  1.465829       NaN

 

 

 

(1) 결측값을 특정 값으로 채우기 (replace missing values with scalar value) : df.fillna(0)

 

결측값을 '0' 으로 대체해보겠습니다.

 

## (1) 결측값을 특정 값으로 채우기
## (1-1) 결측값을 숫자 0 으로 채우기
df_0 = df.fillna(0)

print(df_0)

#          C1        C2        C3
# 0  0.000000  0.893384  2.388055
# 1  0.000000  0.012155  0.000000
# 2  0.099263  0.000000 -1.330312
# 3  2.468724  0.000000  1.046972
# 4 -1.157316  1.465829  0.000000

 

 

 

이번에는 결측값을 'missing' 이라는 string 값으로 채워보겠습니다.

 

## (1-2) 결측값을 문자열 'missing'으로 채우기
df_missing = df.fillna('missing')

print(df_missing)

#           C1         C2       C3
# 0    missing   0.893384  2.38805
# 1    missing  0.0121551  missing
# 2  0.0992629    missing -1.33031
# 3    2.46872    missing  1.04697
# 4   -1.15732    1.46583  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') 를 사용하면 됩니다.

 

## (2) 결측값을 앞 방향 혹은 뒷 방향으로 채우기

print(df)

#          C1        C2        C3
# 0       NaN  0.893384  2.388055
# 1       NaN  0.012155       NaN
# 2  0.099263       NaN -1.330312
# 3  2.468724       NaN  1.046972
# 4 -1.157316  1.465829       NaN


## (2-1) 결측값을 위에서 아래 방향으로 채우기 (forward filling)
df.fillna(method='ffill') 
#df.fillna(method='pad') # or equivalently

#          C1        C2        C3
# 0       NaN  0.893384  2.388055
# 1       NaN  0.012155  2.388055
# 2  0.099263  0.012155 -1.330312
# 3  2.468724  0.012155  1.046972
# 4 -1.157316  1.465829  1.046972

 

 

 

(2-2) 결측값을 뒷 방향으로 채워나가기 위해 fillna(method='bfill') 혹은 fillna(method='backfill')을 사용

 

## (2-2) 결측값을 아래에서 위 방향으로 채우기 (backward filling)

print(df)

#          C1        C2        C3
# 0       NaN  0.893384  2.388055
# 1       NaN  0.012155       NaN
# 2  0.099263       NaN -1.330312
# 3  2.468724       NaN  1.046972
# 4 -1.157316  1.465829       NaN


df.fillna(method='bfill')
#df.fillna(method='backfill') # or equivalently

#          C1        C2        C3
# 0  0.099263  0.893384  2.388055
# 1  0.099263  0.012155 -1.330312
# 2  0.099263  1.465829 -1.330312
# 3  2.468724  1.465829  1.046972
# 4 -1.157316  1.465829       NaN

 

 

 

  (3) 앞/뒤 방향으로 결측값 채우는 회수를 제한하기 (limit the amount of filling)
     : fillna(method='ffill', limit=number), fillna(method='bfill', limit=number)

 

앞 방향이나 뒷 방향으로 채워나갈 때 fillna(limit=1) 를 사용해서 결측값 채우는 '개수'를 '1'개로 한정해 보겠습니다. 

시계열 데이터 분석할 때 유용하게 사용하는 기능 중의 하나입니다.  

 

## (3) 앞/뒤 방향으로 결측값 채우는 회수를 제한하기 (limit the amount of filling)

df.fillna(method='ffill', limit=1) # fill values forward with limit

#          C1        C2        C3
# 0       NaN  0.893384  2.388055
# 1       NaN  0.012155  2.388055
# 2  0.099263  0.012155 -1.330312
# 3  2.468724       NaN  1.046972
# 4 -1.157316  1.465829  1.046972


df.fillna(method='bfill', limit=1) # fill values backward with limit

#          C1        C2        C3
# 0       NaN  0.893384  2.388055
# 1  0.099263  0.012155 -1.330312
# 2  0.099263       NaN -1.330312
# 3  2.468724  1.465829  1.046972
# 4 -1.157316  1.465829       NaN

 

 

 

 (4) 결측값을 변수별 평균으로 대체하기(filling missing values with mean per columns)
      : df.fillna(df.mean()), df.where(pd.notnull(df), df.mean(), axis='columns')

## (4) 결측값을 변수별 평균으로 대체하기

## mean values per columns
df.mean()

# C1    0.470224
# C2    0.790456
# C3    0.701572
# dtype: float64


## filling missing values with mean per columns
df.fillna(df.mean())
#df.where(pd.notnull(df), df.mean(), axis='columns') # or equivalently

#          C1        C2        C3
# 0  0.470224  0.893384  2.388055
# 1  0.470224  0.012155  0.701572
# 2  0.099263  0.790456 -1.330312
# 3  2.468724  0.790456  1.046972
# 4 -1.157316  1.465829  0.701572

 

 

위의 예시는 각 칼럼의 평균으로 -> 각 칼럼의 결측값을 대체하는 방식이었습니다.

아래 예시는 'C1' 칼럼의 평균을 가지고 'C1', 'C2', 'C3' 칼럼의 결측값을 대체하는 방법입니다.

 

##  'C1' 칼럼의 평균을 가지고 'C1', 'C2', 'C3' 칼럼의 결측값을 대체하는 방법
df.mean()['C1']

# 0.4702236218547502


df.fillna(df.mean()['C1'])

#          C1        C2        C3
# 0  0.470224  0.893384  2.388055
# 1  0.470224  0.012155  0.470224
# 2  0.099263  0.470224 -1.330312
# 3  2.468724  0.470224  1.046972
# 4 -1.157316  1.465829  0.470224

 

 

아래의 예시는 'C1'칼럼과 'C2' 칼럼에 대해서만 각 칼럼의 평균을 가지고 -> 각 칼럼에 있는 대체값을 대체하는 경우입니다.  좀 헷갈릴 수 있는데요, 위의 2개의 예시의 혼합 형태라고 보시면 되겠습니다.

('C3'의 NaN 값은 결측값 그대로 있습니다.)

 

## 'C1'칼럼과 'C2' 칼럼에 대해서만 각 칼럼의 평균을 가지고 -> 각 칼럼에 있는 결측값을 대체

## mean values of 'C1', 'C2'
df.mean()['C1':'C2']

# C1    0.470224
# C2    0.790456
# dtype: float64


## filling the missing value of 'C1', 'C2' with each mean values
df.fillna(df.mean()['C1':'C2'])

#          C1        C2        C3
# 0  0.470224  0.893384  2.388055
# 1  0.470224  0.012155       NaN
# 2  0.099263  0.790456 -1.330312
# 3  2.468724  0.790456  1.046972
# 4 -1.157316  1.465829       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' 칼럼의 값을 가져다가 결측값을 채워보겠습니다.

 

## (5) 결측값을 다른 변수의 값으로 대체하기
##    (filling missing values with another columns' values)

df_2 = pd.DataFrame({
    'C1': [1, 2, 3, 4, 5], 
    'C2': [6, 7, 8, 9, 10]})

## put NaNs as an example
df_2.loc[[1, 3], ['C2']] = np.nan

df_2

#    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
df_2['C2_New'] = np.where(pd.notnull(df_2['C2']) == True, 
                          df_2['C2'], df_2['C1'])

df_2

#    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 a long time
for i in df_2.index:
    if pd.notnull(df_2.loc[i, 'C2']) == True:
        df_2.loc[i, 'C2_New_2'] = df_2.loc[i, 'C2']
    else:
        df_2.loc[i, 'C2_New_2'] = df_2.loc[i, 'C1']
        
        
 
df_2

#    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 를 참고하세요. 

 

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

 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python pandas DataFrame 에서

 

 - 결측값이 있는지 여부 확인 : isnull(), notnull()

 - 열별 결측값 개수 : df.isnull().sum()

 - 행별 결측값 개수 : df.isnull().sum(1)

 

확인하는 방법을 소개하였습니다. (바로가지 ☞ http://rfriend.tistory.com/260)

 

 

이번 포스팅에서는 결측값 연산 (calculations with missing data) 에 대해서 알아보겠습니다.

 

 

 

 

먼저, 필요한 모듈 importing 하고, 결측값 들어있는 DataFrame을 만들어보겠습니다.

 



In [1]: import pandas as pd

In [2]: import numpy as np

In [3]: from pandas import DataFrame

In [4]: df = DataFrame(np.arange(10).reshape(5,2),
   ...: index=['a', 'b', 'c', 'd', 'e'],
   ...: columns=['C1', 'C2'])
   ...:

In [5]: df
Out[5]:
   C1  C2
a   0   1
b   2   3
c   4   5
d   6   7
e   8   9


In [6]: df.loc[['b', 'e'], ['C1']] = None

In [7]: df.loc[['b', 'c'], ['C2']] = None

In [8]: df
Out[8]:
    C1   C2
a  0.0  1.0
NaN  NaN
c  4.0  NaN
d  6.0  7.0
NaN  9.0


 

 

 

 

  (1) sum(), cumsum() methods 계산 시 : NaN은 '0'으로 처리

 


# When summing data, NA (missing) values will be treated as zero
In [9]: df.sum() # sum by columns
Out[9]:
C1    10.0
C2    17.0
dtype: float64



In [10]: df['C1'].sum() # sum of 'C1' column
Out[10]: 10.0

In [11]: df['C1'].cumsum() # cumulative sum of 'C1' column
Out[11]:
a     0.0
b     NaN
c     4.0
d    10.0
e     NaN
Name: C1, dtype: float64


 

 

 

  (2) mean(), std() 연산 시 : NaN은 분석 대상(population)에서 제외

 


In [8]: df
Out[8]:
    C1   C2
a  0.0  1.0
NaN  NaN
c  4.0  NaN
d  6.0  7.0
NaN  9.0



# As for mean(), std() methods, NA (missing) values will be ignored
In [12]: df.mean() # mean by columns
Out[12]:
C1    3.333333  # (0+4+6)/3 = 3.333333
C2    5.666667  # (1+7+9)/3 = 5.66667
dtype: float64



In [13]: df.mean(1) # mean by row
Out[13]:
a    0.5
b    NaN
c    4.0
d    6.5
e    9.0
dtype: float64

 
In [14]: df.std()
Out[14]:
C1    3.055050
C2    4.163332
dtype: float64

 

 

 

 

  (3) DataFrame 칼럼 간 연산 시 : NaN이 하나라도 있으면 NaN 반환

 

아래 예시의 'C3' 칼럼에서 'c', 'e' index 값을 유심히 보시기 바랍니다.  '+'을 하는 'C1'과 'C2' 칼럼의 값 중에서 하나라도 NaN 이면 'C3' 값은 NaN 입니다. (물론 'C1'과 'C2' 모두 NaN이면 이들의 합인 'C3'는 NaN이구요.)

 


In [8]: df
Out[8]:
    C1   C2
a  0.0  1.0
NaN  NaN
c  4.0  NaN
d  6.0  7.0
NaN  9.0



In [15]: df['C3'] = df['C1'] + df['C2']


In [16]: df
Out[16]:
    C1   C2    C3
a  0.0  1.0   1.0
b  NaN  NaN   NaN
c  4.0  NaN   NaN
d  6.0  7.0  13.0
e  NaN  9.0   NaN


 

 

 

  (4) DataFrame 간 연산 : 동일한 칼럼끼리는 NaN을 '0'으로 처리하여 연산,
      동일한 칼럼이 없는 경우(한쪽에만 칼럼이 있는 경우)는 모든 값을 NaN으로 반환

 

아래 예제의 df, df_2 의 두 개의 DataFrame에서는 'C1' 만이 동일한 (공통의) 칼럼이며, 나머지는 한쪽 DataFrame에만 있는 칼럼들로 구성이 되어 있습니다.

 

이들 df, df_2 두 개의 DataFrame을 '+' 하면 'C1' 만 제대로 덧셈 연산이 되고 ( NaN 은 '0'으로 처리하여 연산 ), 나머지 칼럼은 모든 값이 NaN으로 변환되었음을 알 수 있습니다.

 


In [17]: df_2 = DataFrame({'C1' : [1, 1, 1, 1, 1],
    ...: 'C4' : [1, 1, 1, 1, 1]},
    ...: index=['a', 'b', 'c', 'd', 'e'])
    ...:

In [18]: df
Out[18]:
    C1   C2    C3
0.0  1.0   1.0
NaN  NaN   NaN
4.0  NaN   NaN
6.0  7.0  13.0
NaN  9.0   NaN



In [19]: df_2
Out[19]:
   C1  C4
a   1   1
b   1   1
c   1   1
d   1   1
e   1   1


In [20]: df + df_2
Out[20]:
    C1  C2  C3  C4
1.0 NaN NaN NaN
NaN NaN NaN NaN
5.0 NaN NaN NaN
7.0 NaN NaN NaN
NaN NaN NaN NaN


 

 

이상으로 결측값 연산을 마치도록 하겠습니다.

 

다음번 포스팅에서결측값 채우기, 결측값 대체 대해서 알아보겠습니다.

 

728x90
반응형
Posted by Rfriend
,

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.loc[[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




 

 

 

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

 

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python pandas의 merge() 함수를 사용해서 Key를 기준으로 DataFrame을 합치는 방법을 소개하였습니다.

 

이번 포스팅에서는 pandas의 merge(), join() 함수를 사용해서 index를 기준으로 DataFrame을 합치는 방법을 소개하도록 하겠습니다.

 

SQL이나 R 사용자라면 index 사용하는게 좀 낯설을 수도 있을 것 같습니다.

 

 

 

먼저 필요한 Library를 importing하고, 간단한 DataFrame 을 예로 만들어 보겠습니다.

 

 

In [1]: import pandas as pd


In [2]: from pandas import DataFrame


In [3]: df_left = DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],

   ...: 'B': ['B0', 'B1', 'B2', 'B3']},

   ...: index=['K0', 'K1', 'K2', 'K3'])


In [4]: df_right = DataFrame({'C': ['C2', 'C3', 'C4', 'C5'],

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

   ...: index=['K2', 'K3', 'K4', 'K5'])

   ...:


In [5]: df_left

Out[5]:

     A   B
K0  A0  B0
K1  A1  B1
K2  A2  B2
K3  A3  B3


In [6]: df_right

Out[6]:

     C   D
K2  C2  D2
K3  C3  D3
K4  C4  D4
K5  C5  D5

 

 

 

 

index를 기준으로 DataFrame을 합치는 방법에는 pd.merge() 와 join() 두 가지 방법이 있는데요, join() 이 code가 간결한 편이며, code에 대한 가독성은 pd.merge()가 좀더 명확한 편입니다.

 

 

  (1) index를 기준으로 Left Join 하기 (Left join on index)

 

 

# Left joining on index

# way 1 : by merge()

In [7]: pd.merge(df_left, df_right,

   ...: left_index=True, right_index=True,

   ...: how='left')

Out[7]:

     A   B    C    D
K0  A0  B0  NaN  NaN
K1  A1  B1  NaN  NaN
K2  A2  B2   C2   D2
K3  A3  B3   C3   D3

 

# way 2 : by join

In [8]: df_left.join(df_right, how='left')

Out[8]:

     A   B    C    D
K0  A0  B0  NaN  NaN
K1  A1  B1  NaN  NaN
K2  A2  B2   C2   D2
K3  A3  B3   C3   D3

 

 

 

 

  (2) index를 기준으로 Right Join 하기 (Right join on index)

 


# Right join on index

# way 1 : merge()

In [9]: pd.merge(df_left, df_right,

   ...: left_index=True, right_index=True,

   ...: how='right')

Out[9]:

      A    B   C   D
K2   A2   B2  C2  D2
K3   A3   B3  C3  D3
K4  NaN  NaN  C4  D4
K5  NaN  NaN  C5  D5

 

# way 2 : join()

In [10]: df_left.join(df_right, how='right')

Out[10]:

      A    B   C   D
K2   A2   B2  C2  D2
K3   A3   B3  C3  D3
K4  NaN  NaN  C4  D4
K5  NaN  NaN  C5  D5

 

 

 

 

  (3) index를 기준으로 inner join 하기 (inner join on index)

 

 

# inner join on index

# way 1 : by merge()

In [11]: pd.merge(df_left, df_right,

    ...: left_index=True, right_index=True,

    ...: how='inner')

Out[11]:

     A   B   C   D
K2  A2  B2  C2  D2
K3  A3  B3  C3  D3

 


# way 2 : by join()

In [12]: df_left.join(df_right, how='inner')

Out[12]:

     A   B   C   D
K2  A2  B2  C2  D2
K3  A3  B3  C3  D3

 

 

 

 

  (4) index를 기준으로 outer join 하기 (outer join on index)

 

 

# outer join on index

# way 1 : by pd.merge()

In [13]: pd.merge(df_left, df_right,

    ...: left_index=True, right_index=True,

    ...: how='outer')

Out[13]:

      A    B    C    D
K0   A0   B0  NaN  NaN
K1   A1   B1  NaN  NaN
K2   A2   B2   C2   D2
K3   A3   B3   C3   D3
K4  NaN  NaN   C4   D4
K5  NaN  NaN   C5   D5

 


# way 2 : by join()

In [14]: df_left.join(df_right, how='outer')

Out[14]:

      A    B    C    D
K0   A0   B0  NaN  NaN
K1   A1   B1  NaN  NaN
K2   A2   B2   C2   D2
K3   A3   B3   C3   D3
K4  NaN  NaN   C4   D4
K5  NaN  NaN   C5   D5

 

 

 


위의 4개의 index 기준 DataFrame 병합 사례에서는 양쪽 DataFrame 모두 index를 사용했습니다. 

 

그런데 만약 한쪽 DataFrame은 index를 기준으로 하고, 나머지 한쪽 DataFrame에서는 Key 변수를 기준으로 해서 두 DataFrame을 합쳐야 한다면 어떻게 해야 할까요?

 

pd.merge()와 join() 두 가지 방법을 how='left' 의 경우만 예를 들어서 설명하겠습니다.  역시 join() 이 script가 간결한 반면, pd.merge()가 병합의 기준을 명시해줌으로써 가독성은 더 좋습니다. 뭘 사용할지는 개인의 취향에 따라 선택하시면 됩니다.

 

 

  (5) index와 Key를 혼합해서 DataFrame 합치기 (Joining key columns on an index)

 

먼저 df_left_2 는 'KEY' 를 가진 DataFrame으로 만들고, df_right_2는 index를 가진 DataFrame으로 만든 후에 이 둘을 'KEY'와 index를 혼합해서 사용해서 합쳐보겠습니다.

 

 

# making DataFrame 

In [15]: df_left_2 = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],

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

    ...: 'B': ['B0', 'B1', 'B2', 'B3']})

    ...:


In [16]: df_right_2 = DataFrame({'C': ['C2', 'C3', 'C4', 'C5'],

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

    ...: index=['K2', 'K3', 'K4', 'K5'])

    ...:


In [17]: df_left_2  # with 'KEY'

Out[17]:

    A   B KEY
0  A0  B0  K0
1  A1  B1  K1
2  A2  B2  K2
3  A3  B3  K3


In [18]: df_right_2  # with 'index'

Out[18]:

     C   D
K2  C2  D2
K3  C3  D3
K4  C4  D4
K5  C5  D5

 

 

 

 

이제 Key와 index를 혼합해서 두 DataFrame을 합쳐보겠습니다.

 

 

# joining key columns on an index

# way 1 : pd.merge()

In [19]: pd.merge(df_left_2, df_right_2,

    ...: left_on='KEY', right_index=True,

    ...: how='left')

    ...:

Out[19]:

    A   B KEY    C    D
0  A0  B0  K0  NaN  NaN
1  A1  B1  K1  NaN  NaN
2  A2  B2  K2   C2   D2
3  A3  B3  K3   C3   D3

 

 

# way 2 : join()

In [20]: df_left_2.join(df_right_2, on='KEY', how='left')

Out[20]:

    A   B KEY    C    D
0  A0  B0  K0  NaN  NaN
1  A1  B1  K1  NaN  NaN
2  A2  B2  K2   C2   D2
3  A3  B3  K3   C3   D3

 

 

 

이상으로 DataFrame을 index 기준으로 합치는 방법에 대한 소개를 마치겠습니다.

 

 

728x90
반응형
Posted by Rfriend
,

데이터 분석을 하다 보면 여기 저기 흩어져 있는 데이터를 특정한 Key를 기준으로 병합해서 분석해야 하는 경우가 매우 많습니다.

 

지난번 포스팅에서는 DataFrame을 pandas의 concat() 함수를 이용해서 합치는 방법, append() 함수를 사용해서 합치는 방법을 소개하였습니다.

 

이번 포스팅에서는 SQL을 사용해서 Database의 Table 들을 Join/Merge 하는 것과 유사하게 Python pandas의 pd.merge() 함수를 사용해서 DataFrame을 Key 기준으로 inner, outer, left, outer join 하여 합치는 방법을 소개하도록 하겠습니다.

 

SQL을 사용하는데 익숙한 분석가라면 매우 쉽고 빠르게 이해하실 수 있을 것입니다. 그리고 Python의 merge() 기능은 메모리 상에서 매우 빠르게 작동함으로 사용하는데 있어 불편함이 덜할 것 같습니다.

 

 

 

 

 

pandas merge 함수 설정값들은 아래와 같이 여러개가 있는데요, 이중에서  'how'와 'on'은 꼭 기억해두셔야 합니다.

 

 

pd.merge(left, right, # merge할 DataFrame 객체 이름
             how='inner', # left, rigth, inner (default), outer
             on=None, # merge의 기준이 되는 Key 변수
             left_on=None, # 왼쪽 DataFrame의 변수를 Key로 사용
             right_on=None, # 오른쪽 DataFrame의 변수를 Key로 사용
             left_index=False, # 만약 True 라면, 왼쪽 DataFrame의 index를 merge Key로 사용
             right_index=False, # 만약 True 라면, 오른쪽 DataFrame의 index를 merge Key로 사용
             sort=True, # merge 된 후의 DataFrame을 join Key 기준으로 정렬
             suffixes=('_x', '_y'), # 중복되는 변수 이름에 대해 접두사 부여 (defaults to '_x', '_y'
             copy=True, # merge할 DataFrame을 복사
             indicator=False) # 병합된 이후의 DataFrame에 left_only, right_only, both 등의 출처를 알 수 있는 부가 정보 변수 추가

 

 

 

먼저, pandas, DataFrame library를 importing 한 후에, 2개의 DataFrame을 만들어보겠습니다.

 

 

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': ['B0', 'B1', 'B2', 'B3']})

   ...:


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

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

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

   ...:


In [5]: df_left

Out[5]:

    A   B KEY
0  A0  B0  K0
1  A1  B1  K1
2  A2  B2  K2
3  A3  B3  K3


In [6]: df_right

Out[6]:

    C   D KEY
0  C2  D2  K2
1  C3  D3  K3
2  C4  D4  K4
3  C5  D5  K5

 

 

 

 

 

'how' 의 left, right, inner, outer 별로 위에서 만든 'df_left'와 'df_right' 두 개의 DataFrame을 'KEY' 변수를 기준으로 merge 해보겠습니다.  SQL join에 익숙하신 분이라면 쉽게 이해할 수 있을 것입니다.

 

 

  (1) Merge method : left (SQL join name : LEFT OUTER JOIN)

 

 

In [7]: df_merge_how_left = pd.merge(df_left, df_right,

   ...: how='left',

   ...: on='KEY')

   ...:


In [8]: df_merge_how_left

Out[8]:

     A   B   KEY   C     D
0  A0  B0  K0  NaN  NaN
1  A1  B1  K1  NaN  NaN
2  A2  B2  K2   C2   D2
3  A3  B3  K3   C3   D3 

 

 

 

 

  (2) Merge method : right (SQL join name : RIGHT OUTER JOIN)

 

 

In [9]: df_merge_how_right = pd.merge(df_left, df_right,

   ...: how='right',

   ...: on='KEY')


In [10]: df_merge_how_right

Out[10]:

     A    KEY   C   D
0   A2   B2  K2  C2  D2
1   A3   B3  K3  C3  D3
2  NaN  NaN  K4  C4  D4
3  NaN  NaN  K5  C5  D5

 

 

 

 

  (3) Merge method : inner (SQL join name : INNER JOIN)

 

 

In [11]: df_merge_how_inner = pd.merge(df_left, df_right,

    ...: how='inner', # default

    ...: on='KEY')

    ...:


In [12]: df_merge_how_inner

Out[12]:

    A   B KEY   C   D
0  A2  B2  K2  C2  D2
1  A3  B3  K3  C3  D3

 

 

 

 

  (4) Merge method : outer (SQL join name : FULL OUTER JOIN)

 

 

In [13]: df_merge_how_outer = pd.merge(df_left, df_right,

    ...: how='outer',

    ...: on='KEY')

    ...:


In [14]: df_merge_how_outer

Out[14]:

     A    B  KEY    C    D
0   A0   B0  K0  NaN  NaN
1   A1   B1  K1  NaN  NaN
2   A2   B2  K2   C2   D2
3   A3   B3  K3   C3   D3
4  NaN  NaN  K4   C4   D4
5  NaN  NaN  K5   C5   D5

 

 

 

[참고] Hive 조인 문 : INNER JOIN, LEFT OUTER JOIN, RIGHT OUTER JOIN, FULL JOIN, CARTESIAN PRODUCT JOIN, MAP-SIDE JOIN, UNION ALL : http://rfriend.tistory.com/216

 

 

 

  (5) indicator = True : 병합된 이후의 DataFrame에 left_only, right_only, both 등의 

                              출처를 알 수 있는 부가정보 변수 추가

 

 

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

    ...: indicator=True)

Out[15]:

     A    B KEY    C    D      _merge
0   A0   B0  K0  NaN  NaN   left_only
1   A1   B1  K1  NaN  NaN   left_only
2   A2   B2  K2   C2   D2        both
3   A3   B3  K3   C3   D3        both
4  NaN  NaN  K4   C4   D4  right_only
5  NaN  NaN  K5   C5   D5  right_only

 

 

 

위에서는 indicator=True로 했더니 '_merge'라는 새로운 변수가 생겼습니다.

 

이 방법 외에도, 아래처럼 indicator='변수 이름(예: indicator_info)'을 설정해주면, 새로운 변수 이름에 indicator 정보가 반환됩니다.

 

 

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

    ...: indicator='indicator_info')

Out[16]:

     A    B KEY    C    D indicator_info
0   A0   B0  K0  NaN  NaN      left_only
1   A1   B1  K1  NaN  NaN      left_only
2   A2   B2  K2   C2   D2           both
3   A3   B3  K3   C3   D3           both
4  NaN  NaN  K4   C4   D4     right_only
5  NaN  NaN  K5   C5   D5     right_only

 

 

 

 

  (6) 변수 이름이 중복될 경우 접미사 붙이기 : suffixes = ('_x', '_y')

 

'B'와 'C' 의 변수 이름이 동일하게 있는 두 개의 DataFrame을 만든 후에, KEY를 기준으로 합치기(merge)를 해보겠습니다.  변수 이름이 중복되므로 Data Source를 구분할 수 있도록 suffixes = ('string', 'string') 을 사용해서 중복되는 변수의 뒷 부분에 접미사를 추가해보겠습니다.  default는 suffixes = ('_x', '_y') 입니다.

 

 

# making DataFrames with overlapping columns

In [17]: df_left_2 = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],

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

    ...: 'B': ['B0', 'B1', 'B2', 'B3'],

    ...: 'C': ['C0', 'C1', 'C2', 'C3']})


In [18]: df_right_2 = DataFrame({'KEY': ['K0', 'K1', 'K2', 'K3'],

    ...: 'B': ['B0_2', 'B1_2', 'B2_2', 'B3_2'],

    ...: 'C': ['C0_2', 'C1_2', 'C2_2', 'C3_2'],

    ...: 'D': ['D0_2', 'D1_2', 'D2_2', 'D3_3']})

    ...:


In [19]: df_left_2

Out[19]:

    A   B   C KEY
0  A0  B0  C0  K0
1  A1  B1  C1  K1
2  A2  B2  C2  K2
3  A3  B3  C3  K3


In [20]: df_right_2

Out[20]:

      B     C     D KEY
0  B0_2  C0_2  D0_2  K0
1  B1_2  C1_2  D1_2  K1
2  B2_2  C2_2  D2_2  K2
3  B3_2  C3_2  D3_3  K3

 


# adding string suffixes to apply to overlapping columns

In [21]: pd.merge(df_left_2, df_right_2, how='inner', on='KEY',

    ...: suffixes=('_left', '_right'))

    ...:

Out[21]:

    A B_left C_left KEY B_right C_right     D
0  A0     B0     C0  K0    B0_2    C0_2  D0_2
1  A1     B1     C1  K1    B1_2    C1_2  D1_2
2  A2     B2     C2  K2    B2_2    C2_2  D2_2
3  A3     B3     C3  K3    B3_2    C3_2  D3_3

 


# suffixes defaults to ('_x', '_y') 

In [22]: pd.merge(df_left_2, df_right_2, how='inner', on='KEY')

    ...:

Out[22]:

    A B_x C_x KEY   B_y   C_y     D
0  A0  B0  C0  K0  B0_2  C0_2  D0_2
1  A1  B1  C1  K1  B1_2  C1_2  D1_2
2  A2  B2  C2  K2  B2_2  C2_2  D2_2
3  A3  B3  C3  K3  B3_2  C3_2  D3_3

 

 

 

 

 

left_on, right_on, left_index, right_index 에 대해서는 다음번 포스팅에서 소개하도록 하겠습니다.

 

728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 DataFrame을 Python pandas 라이브러리의 pd.concat() 함수를 사용해서 상+하로 합치기, 좌+우로 합치기를 해보았습니다.

 

이번 포스팅에서는 이어서 DataFrame과 Series를 pd.concat() 함수, append() 함수를 사용해서 합치기를 소개하겠습니다.  

 

DataFrame 끼리 합치기 대비 해서 DataFrame + Series 가 index 관련해서 좀 헷갈리는게 있습니다만, 아래의 간단한 예시를 참고하면 어렵지 않게 이해할 수 있을 것입니다.

 

 

 

pandas, DataFrame, Series importing 부터 시작해 보시죠.

 

 

# importing libraries

 

In [1]: import pandas as pd

   ...: from pandas import DataFrame

   ...: from pandas import Series

 

 

 

 

  (1) DataFrame에 Series '좌+우' 합치기 : pd.concat([df, Series], axis=1)

 

DataFrame과 Series가 합쳐지면 DataFrame이 됩니다. axis=1 을 설정하면 '좌+우' 형태로 열(column)이 오른쪽 옆으로 늘어납니다. 

 

새로 합쳐지는 DataFrame의 열 이름(column name)을 유심히 살펴보세요.  Series의 이름(name)이 새로운 DataFrame의 변수 이름이 됩니다.

 

 

In [2]: df_1 = pd.DataFrame({'A': ['A0', 'A1', 'A2'],

   ...: 'B': ['B0', 'B1', 'B2'],

   ...: 'C': ['C0', 'C1', 'C2'],

   ...: 'D': ['D0', 'D1', 'D2']},

   ...: index=[0, 1, 2])


In [3]: df_1

Out[3]:

    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2


In [4]: Series_1 = pd.Series(['S1', 'S2', 'S3'], name='S')


In [5]: Series_1

Out[5]:

0    S1
1    S2
2    S3
Name: S, dtype: object

 

# Concatenating DataFrame and Series along columns (from left to right)

# concatenated column name of the new DataFrame will be the same name of Series

In [6]: pd.concat([df_1, Series_1], axis=1)

Out[6]:

    A    B    C   D   S
0  A0  B0  C0  D0  S1
1  A1  B1  C1  D1  S2
2  A2  B2  C2  D2  S3

 

 

 

 

  (2) DataFrame에 Series를 '좌+우'로 합칠 때

      열 이름(column name) 무시하고 정수 번호 자동 부여 : ignore_index=True

 

 

In [7]: pd.concat([df_1, Series_1], axis=1, ignore_index=True)

Out[7]:

    0    1    2    3    4
0  A0  B0  C0  D0  S1
1  A1  B1  C1  D1  S2
2  A2  B2  C2  D2  S3

 

 

 

 

  (3) Series 끼리 '좌+우'로 합치기 : pd.concat([Series1, Series2, ...], axis=1)

 

만약 Series의 이름(name)이 있으면 합쳐진 DataFrame의 열 이름(column name)으로 사용됩니다. Series에 이름이 없다면 정수 0, 1, 2, ... 가 자동 부여 됩니다.

 

 

In [8]: Series_1 = pd.Series(['S1', 'S2', 'S3'], name='S')


In [9]: Series_2 = pd.Series([0, 1, 2]) # without name


In [10]: Series_3 = pd.Series([3, 4, 5]) # without name


In [11]: Series_1

Out[11]:

0    S1
1    S2
2    S3

Name: S, dtype: object


In [12]: Series_2

Out[12]:

0    0
1    1
2    2
dtype: int64


In [13]: Series_3

Out[13]:

0    3
1    4
2    5
dtype: int64

 

# name of Series will be used as the column name of concatenated DataFrame

In [14]: pd.concat([Series_1, Series_2, Series_3], axis=1)

Out[14]:

     S  0  1
0  S1  0  3
1  S2  1  4
2  S3  2  5

 

 

 

 

  (4) Series 끼리 합칠 때 열 이름(column name) 덮어 쓰기 : keys = ['xx', 'xx', ...]

 

 

In [15]: pd.concat([Series_1, Series_2, Series_3], axis=1, keys=['C0', 'C1', 'C1'])

Out[15]:

   C0  C1  C1
0  S1   0   3
1  S2   1   4
2  S3   2   5

 

 

 

 

  (5) DataFrame에 Series를 '위+아래'로 합치기 : df.append(Series, ignore_index=True)

 

ignore_index=True 를 설정해주도록 합니다. 

 

 

In [16]: df_1

Out[16]:

    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2


In [17]: Series_4 = pd.Series(['S1', 'S2', 'S3', 'S4'], index=['A', 'B', 'C', 'E'])


In [18]: Series_4

Out[18]:

A    S1
B    S2
C    S3
E
    S4
dtype: object


In [19]: df_1.append(Series_4, ignore_index=True)

Out[19]:

    A    B   C    D    E
0  A0  B0  C0   D0  NaN
1  A1  B1  C1   D1  NaN
2  A2  B2  C2   D2  NaN
3  S1  S2  S3  NaN   S4

 

 

 

 

ignore_index=True 를 설정해주지 않으면 아래처럼 'TypeError' 가 발생합니다.

 

 

In [20]: df_1.append(Series_4) # TypeError without 'ignore_index=True'

Traceback (most recent call last):


File "<ipython-input-20-ca24d6ef8563>", line 1, in <module>

df_1.append(Series_4) # TypeError without 'ignore_index=True'


File "C:\Anaconda3\lib\site-packages\pandas\core\frame.py", line 4314, in append

raise TypeError('Can only append a Series if ignore_index=True'


TypeError: Can only append a Series if ignore_index=True or if the Series has a name

 

 

 

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

 

 

728x90
반응형
Posted by Rfriend
,