IT/언어

[python/pandas] DataFrame 합치기 : merge, join(열, 인덱스 기준)

개발자 두더지 2021. 7. 13. 00:21
728x90

 날짜나 이름등 공통 데이터 열을 가지고 있는 여러 개의 pandas.DataFrame을 그 공통된 열을 기준으로 결합하기 위해서는 pandas.merge()함수 혹은 pandas.DataFrame의 merge()메소드를 사용한다.

 인덱스 열을 기준으로 결합할 경우에는 pandas.merge()를 사용할 수 있고, pandas.DataFrame의 join()메소드도 사용할 수 있다.

 이 포스팅에서는 아래의 내용에 대해 설명하고자한다.

  • pd.merge(), pd.DataFrame.merge()의 기본적인 사용법
  • 키가 되는 열을 지정: 인수on, left_on, right_on
  • 결합 방법을 지정: 인수how
    • inner_join: how='inner'
    • left_join: how='left'
    • right_join: how='right'
    • outer_join: how='outer'
  • 데이터의 정보를 취득: 인수indicator
  • 열 이름이 중복되어 있는 경우의 접미사를 지정: 인수suffixes
  • 여러 개의 열이 키가 되는 경우
  • 키가 되는 열로 정렬: 인수sort
  • 인덱스를 키로 지정: 인수left_index, right_index
  • pd.DataFrame.join()의 기본적인 사용방법

 예제로 아래의 두 개의 pandas.DataFrame을 사용할 것이다.

import pandas as pd

df_ab = pd.DataFrame({'a': ['a_1', 'a_2', 'a_3'], 'b': ['b_1', 'b_2', 'b_3']})
df_ac = pd.DataFrame({'a': ['a_1', 'a_2', 'a_4'], 'c': ['c_1', 'c_2', 'c_4']})

print(df_ab)
#      a    b
# 0  a_1  b_1
# 1  a_2  b_2
# 2  a_3  b_3

print(df_ac)
#      a    c
# 0  a_1  c_1
# 1  a_2  c_2
# 2  a_4  c_4

 참고로 pandas.DataFrame을 종횡으로 연결하기 위해서는 pandas.concat()함수를 사용한다. 

 

 

pd.merge(), pd.DataFrame.merge()의 기본적인 사용법


 pd.merge()함수에서는 첫 번째 인수 left와 두 번째 인수 right를 결합하는 두 개의 pandas.DataFrame을 지정한다.

print(pd.merge(df_ab, df_ac))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2

 merger()메소드의 경우는 left에 해당하는 pandas.DataFrame에서 부터 메소드를 호출하고, right에 해당하는 pandas.DataFrame를 인수로써 지정한다.

print(df_ab.merge(df_ac))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2

 어느쪽의 방법이든 결합된 pandas.DataFrame가 반환된다.

 아래에서 설명하는 인수는 pd.merge()메소드에서도 merge()메소드에도 동일하다.

 

 

키가 되는 열을 지정: 인수on, left_on, right_on


 기본적으로 2개의 pandas.DataFrame에 공통되는 열 이름을 키로 하여 결합 처리가 이루어진다.

 명시적으로 지정하는 경우에는 인수 on을 사용한다. 생략해도 문제가 없지만, 명시해두는 것이 나중에 다시 봤을 때에도 알기 쉬울 것이다.

print(pd.merge(df_ab, df_ac, on='a'))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2

 인수 left_on, right_on으로 각각의 pandas.DataFrame의 열 이름을 각각 지정하는 것도 가능하다.

df_ac_ = df_ac.rename(columns={'a': 'a_'})
print(df_ac_)
#     a_    c
# 0  a_1  c_1
# 1  a_2  c_2
# 2  a_4  c_4

print(pd.merge(df_ab, df_ac_, left_on='a', right_on='a_'))
#      a    b   a_    c
# 0  a_1  b_1  a_1  c_1
# 1  a_2  b_2  a_2  c_2

 이 경우, 두 개의 열이 남으므로, 필요하지 않는 열에 대해서는 drop()메소드를 이용해서 삭제해줘야한다. 사용법은 다음과 같다.

print(pd.merge(df_ab, df_ac_, left_on='a', right_on='a_').drop(columns='a_'))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2

 on, left_on, right_on에 열 이름의 리스트를 짖어하여 여러 개의 열을 키로써 지정하는 것이 가능하다. 그에 대한 내용은 뒤에서 설명하도록 하겠다.

 

 

결합 방법을 지정: 인수how


 결합방법은 인수 how의 문자열로 지정한다. 기본적으로는 how='inner'이다. 데이터가 없는 요소는 결손값 Nan이 된다. 

inner_join : how='inner'

print(pd.merge(df_ab, df_ac, on='a', how='inner'))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2

left_join : how='left'

print(pd.merge(df_ab, df_ac, on='a', how='left'))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2
# 2  a_3  b_3  NaN

right_join : how='right'

print(pd.merge(df_ab, df_ac, on='a', how='right'))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2
# 2  a_4  NaN  c_4

outer_join : how='outer'

print(pd.merge(df_ab, df_ac, on='a', how='outer'))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2
# 2  a_3  b_3  NaN
# 3  a_4  NaN  c_4

 

 

데이터의 정보를 취득 : 인수indicator


인수 indicator를 True로 하면, 원래 데이터의 정보를 포함한 열이 추가된다.

기본적으로 _merge이라는 행이 추가되어, both, left_only, right_only중 하나로 분류된다.

print(pd.merge(df_ab, df_ac, on='a', how='inner', indicator=True))
#      a    b    c _merge
# 0  a_1  b_1  c_1   both
# 1  a_2  b_2  c_2   both

print(pd.merge(df_ab, df_ac, on='a', how='outer', indicator=True))
#      a    b    c      _merge
# 0  a_1  b_1  c_1        both
# 1  a_2  b_2  c_2        both
# 2  a_3  b_3  NaN   left_only
# 3  a_4  NaN  c_4  right_only

_merge가 아닌 임의의 열 명으로 하고 싶은 경우에는 인수 indicator에 문자열을 지정한다.

print(pd.merge(df_ab, df_ac, on='a', how='outer', indicator='indicator'))
#      a    b    c   indicator
# 0  a_1  b_1  c_1        both
# 1  a_2  b_2  c_2        both
# 2  a_3  b_3  NaN   left_only
# 3  a_4  NaN  c_4  right_only

 

 

열 명이 중복되어 있는 경우의 접미사를 지정 : 인수suffixes


 left와 right를 키로하는 열 이외의 열 명이 중복되어 있는 경우, 기본적으로는 _x, _y이라는 접미사를 붙일 수 있다.

df_ac_b = df_ac.rename(columns={'c': 'b'})
print(df_ac_b)
#      a    b
# 0  a_1  c_1
# 1  a_2  c_2
# 2  a_4  c_4

print(pd.merge(df_ab, df_ac_b, on='a'))
#      a  b_x  b_y
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2

 임의의 접미사를 붙이고 싶은 경우는 인수 suffixes에 [left용 접미사, right용 접미사]와 같은 리스트나 튜플을 지정한다.

print(pd.merge(df_ab, df_ac_b, on='a', suffixes=['_left', '_right']))
#      a b_left b_right
# 0  a_1    b_1     c_1
# 1  a_2    b_2     c_2

 

 

여러 개의 열을 키로 지정하는 경우


 여기까지의 열은 키가 하나인 경우였다. 여러 개의 열을 키로 하고 싶은 경우에 대해서 설명한다. 아래의 2개의 pandas.DataFrame을 예로 살펴보자.

df_abx = df_ab.assign(x=['x_2', 'x_2', 'x_3'])
df_acx = df_ac.assign(x=['x_1', 'x_2', 'x_2'])

print(df_abx)
#      a    b    x
# 0  a_1  b_1  x_2
# 1  a_2  b_2  x_2
# 2  a_3  b_3  x_3

print(df_acx)
#      a    c    x
# 0  a_1  c_1  x_1
# 1  a_2  c_2  x_2
# 2  a_4  c_4  x_2

 공통되는 열 이름의 열이 여러 개 존재하는 경우, 기본적으로 모든 열이 키가 되어 처리된다. 명시적으로 지정하는 경우에는 인수 on에 열 이름의 리스트를 지정한다.

print(pd.merge(df_abx, df_acx))
#      a    b    x    c
# 0  a_2  b_2  x_2  c_2

print(pd.merge(df_abx, df_acx, on=['a', 'x']))
#      a    b    x    c
# 0  a_2  b_2  x_2  c_2

 공통되는 열 이름의 열이 여러개 존재하고 있어도 한 쪽의 열만 키로 하는 것도 가능하다. 위의 예와 같이 접미사를 붙일 수도 있다.

print(pd.merge(df_abx, df_acx, on='a'))
#      a    b  x_x    c  x_y
# 0  a_1  b_1  x_2  c_1  x_1
# 1  a_2  b_2  x_2  c_2  x_2

 다른 열 이름의 열을 공통 키로 하고 싶은 경우에 인수 left_on, right_on에 각각의 열에 각각의 열 이름 리스트를 지정하면 된다.

df_acx_ = df_acx.rename(columns={'x': 'x_'})
print(df_acx_)
#      a    c   x_
# 0  a_1  c_1  x_1
# 1  a_2  c_2  x_2
# 2  a_4  c_4  x_2

print(pd.merge(df_abx, df_acx_, left_on=['a', 'x'], right_on=['a', 'x_']))
#      a    b    x    c   x_
# 0  a_2  b_2  x_2  c_2  x_2

 인수 how는 하나의 열을 키로 했을 때 작성했던 것과 동일하다.

print(pd.merge(df_abx, df_acx, on=['a', 'x'], how='inner'))
#      a    b    x    c
# 0  a_2  b_2  x_2  c_2

print(pd.merge(df_abx, df_acx, on=['a', 'x'], how='left'))
#      a    b    x    c
# 0  a_1  b_1  x_2  NaN
# 1  a_2  b_2  x_2  c_2
# 2  a_3  b_3  x_3  NaN

print(pd.merge(df_abx, df_acx, on=['a', 'x'], how='right'))
#      a    b    x    c
# 0  a_2  b_2  x_2  c_2
# 1  a_1  NaN  x_1  c_1
# 2  a_4  NaN  x_2  c_4

print(pd.merge(df_abx, df_acx, on=['a', 'x'], how='outer'))
#      a    b    x    c
# 0  a_1  b_1  x_2  NaN
# 1  a_2  b_2  x_2  c_2
# 2  a_3  b_3  x_3  NaN
# 3  a_1  NaN  x_1  c_1
# 4  a_4  NaN  x_2  c_4

 

 

키 열로 정렬(sort) : 인수sort


열을 기준으로 정렬하고 싶은 경우에는 sort인수를 True로 하면 된다.

print(pd.merge(df_abx, df_acx, on=['a', 'x'], how='outer', sort=True))
#      a    b    x    c
# 0  a_1  NaN  x_1  c_1
# 1  a_1  b_1  x_2  NaN
# 2  a_2  b_2  x_2  c_2
# 3  a_3  b_3  x_3  NaN
# 4  a_4  NaN  x_2  c_4

 

 

인덱스를 키로 지정 : 인수 left_index, right_index


 인덱스(행 라벨)을 키로 지정하는 경우는 인수 left_index, right_index를 True로 한다.

 인수 left_on, right_on과 함께 사용하는 것도 가능하다.

df_ac_i = df_ac.set_index('a')
print(df_ac_i)
#        c
# a       
# a_1  c_1
# a_2  c_2
# a_4  c_4

print(pd.merge(df_ab, df_ac_i, left_on='a', right_index=True))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2

 인수 left_index, right_index를 양쪽 다 True로 지정하는 것도 물론 가능하다.

df_ab_i = df_ab.set_index('a')
print(df_ab_i)
#        b
# a       
# a_1  b_1
# a_2  b_2
# a_3  b_3

print(pd.merge(df_ab_i, df_ac_i, left_index=True, right_index=True))
#        b    c
# a            
# a_1  b_1  c_1
# a_2  b_2  c_2

 인덱스를 키로하는 경우에는 바로 다음에 설명할 join() 메소드를 사용해도 ok이다.

 

 

pd.DataFrame.join()의 기본적인 사용방법


인덱스를 키로 하는 경우에는 pandas.DataFrame의 join()메소드를 사용해 결합할 수 있다. 

join()은 merge()와 같이 pandas.join()로 바로 사용할 수 있는 것이아니라, pandas.DataFrame의 메소드만 존재하므로 주의할 필요가 있다.

merge()와 달리 왼쪽 결합이 기본이다(how='left')

print(df_ab_i)
#        b
# a       
# a_1  b_1
# a_2  b_2
# a_3  b_3

print(df_ac_i)
#        c
# a       
# a_1  c_1
# a_2  c_2
# a_4  c_4

print(df_ab_i.join(df_ac_i))
#        b    c
# a            
# a_1  b_1  c_1
# a_2  b_2  c_2
# a_3  b_3  NaN

print(df_ab_i.join(df_ac_i, how='inner'))
#        b    c
# a            
# a_1  b_1  c_1
# a_2  b_2  c_2

 호출하는 쪽의 pandas.DataFrame의 키로하는 열을 인수 on으로 지정할 수 있다. 인수를 지정하는 쪽의 pandas.DataFrame은 항상 인덱스가 키가 된다.

print(df_ab)
#      a    b
# 0  a_1  b_1
# 1  a_2  b_2
# 2  a_3  b_3

print(df_ab.join(df_ac_i, on='a'))
#      a    b    c
# 0  a_1  b_1  c_1
# 1  a_2  b_2  c_2
# 2  a_3  b_3  NaN

 join()의 인수 on은 어느쪽의 pandas.DataFrame에 대해 지정하는 것인지가 까다롭기 때문에, 인덱스를 키로 지정하지 않는 경우는 merge()를 사용하는 것이 더 알기 쉽다.

 join()의 첫 번째 인수에는 pandas.DataFrame()의 리스트를 지정하는 것도 가능하다.

df_ad_i = pd.DataFrame({'a': ['a_1', 'a_4', 'a_5'], 'd': ['d_1', 'd_4', 'd_5']}).set_index('a')
print(df_ad_i)
#        d
# a       
# a_1  d_1
# a_4  d_4
# a_5  d_5

print(df_ab_i.join([df_ac_i, df_ad_i]))
#        b    c    d
# a                 
# a_1  b_1  c_1  d_1
# a_2  b_2  c_2  NaN
# a_3  b_3  NaN  NaN

print(df_ac_i.join([df_ad_i, df_ab_i]))
#        c    d    b
# a                 
# a_1  c_1  d_1  b_1
# a_2  c_2  NaN  b_2
# a_4  c_4  d_4  NaN

참고자료

https://note.nkmk.me/python-pandas-merge-join/

728x90