[Pandas 기초] 데이터프레임 합치기(merge, join, concat)
업데이트:
개요
이번 포스팅에서는 다수의 데이터프레임(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
도서 [파이썬 머신러닝 판다스 데이터 분석]을 공부하며 작성하였습니다.
댓글남기기