[Pandas 기초] 데이터프레임 합치기(merge, join, concat)

업데이트:

개요

jpg

이번 포스팅에서는 다수의 데이터프레임(dataframe) 또는 시리즈(Series)를 결합하거나 key값을 활용해 매칭하는 방법에 대해 알아보자.
데이터를 다루면서 아마 가장 기본적이고 많이 사용하게 될 부분이고, 데이터프레임의 구조에 대한 이해가 필수적이다.


1. 데이터 프레임 붙이기 : pd.concat()

pd.concat()함수는 데이터프레임을 말그대로 물리적으로 이어 붙여주는 함수로,
pd.concat(데이터프레임리스트)로 사용한다.

두가지의 데이터프레임을 만들어보자.

import pandas as pd

df1 = pd.DataFrame({'a':['a0','a1','a2','a3'],
                   'b':['b0','b1','b2','b3'],
                   'c':['c0','c1','c2','c3']},
                  index = [0,1,2,3])

df2 = pd.DataFrame({'a':['a2','a3','a4','a5'],
                   'b':['b2','b3','b4','b5'],
                   'c':['c2','c3','c4','c5'],
                   'd':['d2','d3','d4','d5']},
                   index = [2,3,4,5])

print(df1, '\n')
print(df2)
[Output]
    a   b   c
0  a0  b0  c0
1  a1  b1  c1
2  a2  b2  c2
3  a3  b3  c3 

    a   b   c   d
2  a2  b2  c2  d2
3  a3  b3  c3  d3
4  a4  b4  c4  d4
5  a5  b5  c5  d5 

1-1. pd.concat()의 옵션

두 데이터프레임은 행인덱스와 열인덱스가 다르다.

result1 = pd.concat([df1,df2])
print(result1)
[Output]
    a   b   c    d
0  a0  b0  c0  NaN
1  a1  b1  c1  NaN
2  a2  b2  c2  NaN
3  a3  b3  c3  NaN
2  a2  b2  c2   d2
3  a3  b3  c3   d3
4  a4  b4  c4   d4
5  a5  b5  c5   d5 

defalut값으로 axis=0이 적용되기 때문에 행방향(위아래)으로 데이터프레임을 이어붙인다.
그런데 df1에는 d열이 없으므로 NaN값이 채워진 것을 알 수 있다.

그냥 이어붙이니 행 인덱스번호도 그대로 가져왔기때문에, ignore_index=True을 줘서 인덱스를 재배열 할 수 있다.

result2 = pd.concat([df1,df2], ignore_index=True)
print(result2)
[Output]
    a   b   c    d
0  a0  b0  c0  NaN
1  a1  b1  c1  NaN
2  a2  b2  c2  NaN
3  a3  b3  c3  NaN
4  a2  b2  c2   d2
5  a3  b3  c3   d3
6  a4  b4  c4   d4
7  a5  b5  c5   d5 

이번에는 열방향axis=1(좌우)으로 이어붙여보자.

result3 = pd.concat([df1,df2],axis=1)
print(result3)
[Output]
     a    b    c    a    b    c    d
0   a0   b0   c0  NaN  NaN  NaN  NaN
1   a1   b1   c1  NaN  NaN  NaN  NaN
2   a2   b2   c2   a2   b2   c2   d2
3   a3   b3   c3   a3   b3   c3   d3
4  NaN  NaN  NaN   a4   b4   c4   d4
5  NaN  NaN  NaN   a5   b5   c5   d5 

pd.concat()함수는 또한 default로 outer를 가진다.
이어붙이는 방식을 outer는 합집합, inner는 교집합을 의미한다.

그러면 이번에는 inner옵션을 줘서 이어붙일 두 데이터에 모두 존재하는 행인덱스만 가져와보자.

result3_in = pd.concat([df1,df2],axis=1, join='inner')   #열방향(axis=1), 교집합(inner)
print(result3_in)
[Output]
    a   b   c   a   b   c   d
2  a2  b2  c2  a2  b2  c2  d2
3  a3  b3  c3  a3  b3  c3  d3 

1-2. 시리즈를 데이터프레임에 붙이기

시리즈 객체도 마찬가지로 이어붙이기가 가능하다.
시리즈 객체를 생성할때 주는 옵션 name이, 시리즈가 데이터프레임에 결합되었을 떄의 열이름이 된다.

sr1 = pd.Series(['e0','e1','e2','e3'], name = 'e')
sr2 = pd.Series(['f0','f1','f2'], name = 'f', index = [3,4,5])
sr3 = pd.Series(['g0','g1','g2','g3'], name = 'g')
result4 = pd.concat([df1,sr1], axis=1)
print(result4, '\n')

result5 = pd.concat([df2,sr2], axis=1)
print(result5, '\n')
[Output]
    a   b   c   e
0  a0  b0  c0  e0
1  a1  b1  c1  e1
2  a2  b2  c2  e2
3  a3  b3  c3  e3 

    a   b   c   d    f
2  a2  b2  c2  d2  NaN
3  a3  b3  c3  d3   f0
4  a4  b4  c4  d4   f1
5  a5  b5  c5  d5   f2 

sr2의 name은 f, index는 3,4,5로 지정해줬으므로, 해당 행인덱스에 맞춰 이어붙여졌음을 볼 수 있다.

1-3. 시리즈 끼리 붙이기

result6 = pd.concat([sr1, sr3], axis = 1)  #열방향 연결, 데이터프레임
print(result6)
print(type(result6), '\n')

result7 = pd.concat([sr1, sr3], axis = 0)  #행방향 연결, 시리즈
print(result7)
print(type(result7), '\n') 
[Output]
    e   g
0  e0  g0
1  e1  g1
2  e2  g2
3  e3  g3
<class 'pandas.core.frame.DataFrame'> 

0    e0
1    e1
2    e2
3    e3
0    g0
1    g1
2    g2
3    g3
dtype: object
<class 'pandas.core.series.Series'> 

sr1에는 인덱스를 지정해주지 않았으므로 자동으로 0,1,2,3이 들어갔을 것이다. 그래서 바로 열방향 concat이 되었고,
두 시리즈를 행방향(axis=0)연결을 하게되면 여전히 시리즈객체로 반환됨을 알 수 있다.


2. 데이터프레임 병합 : pd.merge()

merge()함수는 두 데이터프레임을 각 데이터에 존재하는 고유값(key)을 기준으로 병합할때 사용한다.
pd.merge(df_left, df_right, how='inner', on=None)이 default이다.

두 데이터를 불러오자

import pandas as pd

pd.set_option('display.max_columns', 10)                       # 출력할 열의 최대개수
pd.set_option('display.max_colwidth', 20)                      # 출력할 열의 너비
pd.set_option('display.unicode.east_asian_width', True)        # 유니코드 사용 너비 조정

df1 = pd.read_excel("stock price.xlsx")
df2 = pd.read_excel("stock valuation.xlsx")

print(df1, '\n')
print(df2)
       id    stock_name          value   price
0  128940      한미약품   59385.666667  421000
1  130960        CJ E&M   58540.666667   98900
2  138250    엔에스쇼핑   14558.666667   13200
3  139480        이마트  239230.833333  254500
4  142280  녹십자엠에스     468.833333   10200
5  145990        삼양사   82750.000000   82000
6  185750        종근당   40293.666667  100500
7  192400    쿠쿠홀딩스  179204.666667  177500
8  199800          툴젠   -2514.333333  115400
9  204210  모두투어리츠    3093.333333    3475 

       id              name           eps     bps        per       pbr
0  130960            CJ E&M   6301.333333   54068  15.695091  1.829178
1  136480              하림    274.166667    3551  11.489362  0.887074
2  138040    메리츠금융지주   2122.333333   14894   6.313806  0.899691
3  139480            이마트  18268.166667  295780  13.931338  0.860437
4  145990            삼양사   5741.000000  108090  14.283226  0.758627
5  161390        한국타이어   5648.500000   51341   7.453306  0.820007
6  181710   NHN엔터테인먼트   2110.166667   78434  30.755864  0.827447
7  185750            종근당   3990.333333   40684  25.185866  2.470259
8  204210      모두투어리츠     85.166667    5335  40.802348  0.651359
9  207940  삼성바이오로직스   4644.166667   60099  89.790059  6.938551

아무 옵션을 적용하지 않으면, on=None이므로 두 데이터의 공통 열이름(id)을 기준으로 inner(교집합) 조인을 하게 된다.

merge_inner = pd.merge(df1, df2)
merge_inner
id stock_name value price name eps bps per pbr
0 130960 CJ E&M 58540.666667 98900 CJ E&M 6301.333333 54068 15.695091 1.829178
1 139480 이마트 239230.833333 254500 이마트 18268.166667 295780 13.931338 0.860437
2 145990 삼양사 82750.000000 82000 삼양사 5741.000000 108090 14.283226 0.758627
3 185750 종근당 40293.666667 100500 종근당 3990.333333 40684 25.185866 2.470259
4 204210 모두투어리츠 3093.333333 3475 모두투어리츠 85.166667 5335 40.802348 0.651359

2-1. merge()함수의 옵션

이번엔 outer옵션을 줘서 id를 기준으로 합치되, 어느한쪽에라도 없는 데이터가 있는 경우 NaN값이 지정된다.

merge_outer = pd.merge(df1,df2, how='outer',on='id')
merge_outer
id stock_name value price name eps bps per pbr
0 128940 한미약품 59385.666667 421000.0 NaN NaN NaN NaN NaN
1 130960 CJ E&M 58540.666667 98900.0 CJ E&M 6301.333333 54068.0 15.695091 1.829178
2 138250 엔에스쇼핑 14558.666667 13200.0 NaN NaN NaN NaN NaN
3 139480 이마트 239230.833333 254500.0 이마트 18268.166667 295780.0 13.931338 0.860437
4 142280 녹십자엠에스 468.833333 10200.0 NaN NaN NaN NaN NaN
5 145990 삼양사 82750.000000 82000.0 삼양사 5741.000000 108090.0 14.283226 0.758627
6 185750 종근당 40293.666667 100500.0 종근당 3990.333333 40684.0 25.185866 2.470259
7 192400 쿠쿠홀딩스 179204.666667 177500.0 NaN NaN NaN NaN NaN
8 199800 툴젠 -2514.333333 115400.0 NaN NaN NaN NaN NaN
9 204210 모두투어리츠 3093.333333 3475.0 모두투어리츠 85.166667 5335.0 40.802348 0.651359
10 136480 NaN NaN NaN 하림 274.166667 3551.0 11.489362 0.887074
11 138040 NaN NaN NaN 메리츠금융지주 2122.333333 14894.0 6.313806 0.899691
12 161390 NaN NaN NaN 한국타이어 5648.500000 51341.0 7.453306 0.820007
13 181710 NaN NaN NaN NHN엔터테인먼트 2110.166667 78434.0 30.755864 0.827447
14 207940 NaN NaN NaN 삼성바이오로직스 4644.166667 60099.0 89.790059 6.938551

왼쪽에 입력한 데이터프레임 기준(how='left')으로, 각각의 key값에 해당하는 열을 지정해주자.

merge_left = pd.merge(df1,df2, how='left', left_on='stock_name', right_on='name')
merge_left
id_x stock_name value price id_y name eps bps per pbr
0 128940 한미약품 59385.666667 421000 NaN NaN NaN NaN NaN NaN
1 130960 CJ E&M 58540.666667 98900 130960.0 CJ E&M 6301.333333 54068.0 15.695091 1.829178
2 138250 엔에스쇼핑 14558.666667 13200 NaN NaN NaN NaN NaN NaN
3 139480 이마트 239230.833333 254500 139480.0 이마트 18268.166667 295780.0 13.931338 0.860437
4 142280 녹십자엠에스 468.833333 10200 NaN NaN NaN NaN NaN NaN
5 145990 삼양사 82750.000000 82000 145990.0 삼양사 5741.000000 108090.0 14.283226 0.758627
6 185750 종근당 40293.666667 100500 185750.0 종근당 3990.333333 40684.0 25.185866 2.470259
7 192400 쿠쿠홀딩스 179204.666667 177500 NaN NaN NaN NaN NaN NaN
8 199800 툴젠 -2514.333333 115400 NaN NaN NaN NaN NaN NaN
9 204210 모두투어리츠 3093.333333 3475 204210.0 모두투어리츠 85.166667 5335.0 40.802348 0.651359

위의 결과는 how='left'옵션을 주었기 때문에, df1데이터의 고유값을 기준으로 한다. 한번 확인해보자.

set(df1['stock_name'].unique()) - set(df2['name'].unique())
[Output]
{'녹십자엠에스', '엔에스쇼핑', '쿠쿠홀딩스', '툴젠', '한미약품'}

unique()함수를 이용해 각 데이터의 key값을 확인하고 set자료형 변환을 통해 차집합을 생성했다.
실제로 이값들은 df1에만 있고 df2에는 없어서 NaN이 생성됐음을 알 수 있다.

오른쪽 데이터를 기준으로 병합하는 방법도 마찬가지다.

merge_right = pd.merge(df1, df2, how='right', left_on='stock_name', right_on='name')
merge_right
id_x stock_name value price id_y name eps bps per pbr
0 130960.0 CJ E&M 58540.666667 98900.0 130960 CJ E&M 6301.333333 54068 15.695091 1.829178
1 139480.0 이마트 239230.833333 254500.0 139480 이마트 18268.166667 295780 13.931338 0.860437
2 145990.0 삼양사 82750.000000 82000.0 145990 삼양사 5741.000000 108090 14.283226 0.758627
3 185750.0 종근당 40293.666667 100500.0 185750 종근당 3990.333333 40684 25.185866 2.470259
4 204210.0 모두투어리츠 3093.333333 3475.0 204210 모두투어리츠 85.166667 5335 40.802348 0.651359
5 NaN NaN NaN NaN 136480 하림 274.166667 3551 11.489362 0.887074
6 NaN NaN NaN NaN 138040 메리츠금융지주 2122.333333 14894 6.313806 0.899691
7 NaN NaN NaN NaN 161390 한국타이어 5648.500000 51341 7.453306 0.820007
8 NaN NaN NaN NaN 181710 NHN엔터테인먼트 2110.166667 78434 30.755864 0.827447
9 NaN NaN NaN NaN 207940 삼성바이오로직스 4644.166667 60099 89.790059 6.938551

df1에 없는 고유key값 확인

set(df2['name'].unique()) - set(df1['stock_name'].unique())
{'NHN엔터테인먼트', '메리츠금융지주', '삼성바이오로직스', '하림', '한국타이어'}


3 .데이터프레임 결합 : join()

join함수는 merge()함수를 기반으로 만들어졌기 때문에 기본 작동방식이 비슷하다. 하지만 join()은 행 인덱스를 기준으로 결합한다는 점에서 차이가 있다. 그래도 이 함수도 on=keys 옵션이 존재한다.
Dataframe1.join(Dataframe2. how='left')이 default값이다.

join()함수 적용을 위해 각 데이터의 id열을 행인덱스로 지정해주자.

df1.set_index('id', inplace=True)
df2.set_index('id', inplace=True)
df1.join(df2)
stock_name value price name eps bps per pbr
id
128940 한미약품 59385.666667 421000 NaN NaN NaN NaN NaN
130960 CJ E&M 58540.666667 98900 CJ E&M 6301.333333 54068.0 15.695091 1.829178
138250 엔에스쇼핑 14558.666667 13200 NaN NaN NaN NaN NaN
139480 이마트 239230.833333 254500 이마트 18268.166667 295780.0 13.931338 0.860437
142280 녹십자엠에스 468.833333 10200 NaN NaN NaN NaN NaN
145990 삼양사 82750.000000 82000 삼양사 5741.000000 108090.0 14.283226 0.758627
185750 종근당 40293.666667 100500 종근당 3990.333333 40684.0 25.185866 2.470259
192400 쿠쿠홀딩스 179204.666667 177500 NaN NaN NaN NaN NaN
199800 툴젠 -2514.333333 115400 NaN NaN NaN NaN NaN
204210 모두투어리츠 3093.333333 3475 모두투어리츠 85.166667 5335.0 40.802348 0.651359

default값이 how=’left’, 이므로 df1의 행 인덱스(id)를 기준으로 결합된다.

how='inner'옵션도 마찬가지다.

df1.join(df2, how='inner')
stock_name value price name eps bps per pbr
id
130960 CJ E&M 58540.666667 98900 CJ E&M 6301.333333 54068 15.695091 1.829178
139480 이마트 239230.833333 254500 이마트 18268.166667 295780 13.931338 0.860437
145990 삼양사 82750.000000 82000 삼양사 5741.000000 108090 14.283226 0.758627
185750 종근당 40293.666667 100500 종근당 3990.333333 40684 25.185866 2.470259
204210 모두투어리츠 3093.333333 3475 모두투어리츠 85.166667 5335 40.802348 0.651359

merge함수와 별 차이가 없어서, 둘중 하나만 사용해도 무방한 것 같다.


Reference

도서 [파이썬 머신러닝 판다스 데이터 분석]을 공부하며 작성하였습니다.

댓글남기기