달나라 노트

Python Pandas : merge (2개의 DataFrame join하기. DataFrame join, DataFrame left join, DataFrame right join, DataFrame outer join) 본문

Python/Python Pandas

Python Pandas : merge (2개의 DataFrame join하기. DataFrame join, DataFrame left join, DataFrame right join, DataFrame outer join)

CosmosProject 2020. 11. 3. 14:10
728x90
반응형

 

 

 

DataFrame.merge

sql에서의 window function과 비슷한 기능을 pandas에서 어떻게 사용할 수 있는지를 알아봅시다.

먼저 test용 DataFrame을 생성합니다.

import pandas as pd
dict_name = {
    'item_id': [1, 2, 4],
    'item_name': ['a', 'b', 'd']
}

dict_price = {
    'item_id': [1, 2, 3],
    'price': [1000, 2000, 3000]
}

df_name = pd.DataFrame(dict_name)
df_price = pd.DataFrame(dict_price)

print(df_name)
print(type(df_name))

print(df_price)
print(type(df_price))
 
- Output 1
   item_id item_name
0        1         a
1        2         b
2        4         d
<class 'pandas.core.frame.DataFrame'>

   item_id  price
0        1   1000
1        2   2000
2        3   3000
<class 'pandas.core.frame.DataFrame'>
  

item_id별 이름과, item_id별 가격을 담고있는 2개의 DataFrame을 생성했습니다.




DataFrame의 merge는 아래처럼 사용할 수 있습니다.

sql의 join과 동일하다고 생각하시면 됩니다.

 

left_df -> 왼쪽에 위치할 DataFrame

 

right_df -> 오른쪽에 위치할 DataFrame

 

how -> inner, left, right, outer 4개의 옵션 중 1개 선택하여 사용가능

 

left_on -> 왼쪽에 위치할 DataFrame에서 merge시에 기준이 될 컬럼이름(List의 형태로 1개 이상의 컬럼 이름 전달 가능)

 

right_on -> 오른쪽에 위치할 DataFrame에서 merge시에 기준이 될 컬럼이름(List의 형태로 1개 이상의 컬럼 이름 전달 가능)

pd.merge(left_df, right_df, how='inner/left/right/outer', left_on=[left_df_column], right_on=[right_df_column])




how에 들어갈 수 있는 4개의 옵션 각각에 대해 예시 DataFrame을 merge하며 살펴봅시다.

 

1. inner 옵션은 merge 기준이 되는 item_id 컬럼에 대해 2개의 DataFrame이 모두 가진 값만을 합쳐서 반환합니다.

 

2. left 옵션은 merge 기준이 되는 item_id 컬럼에 대해 left DataFrame이 모두 가진 값을 보존한 후 거기에 right DataFrame을 합쳐서 반환합니다.

item_id = 4인 상품은 df_price에 price정보가 없었으므로 NaN값으로 출력됨을 알 수 있습니다.

 

3. right 옵션은 merge 기준이 되는 item_id 컬럼에 대해 right DataFrame이 모두 가진 값을 보존한 후 거기에 left DataFrame을 합쳐서 반환합니다.

item_id = 3인 상품은 df_name에 item_name정보가 없었으므로 NaN값으로 출력됨을 알 수 있습니다.

 

4. outer 옵션은 merge 기준이 되는 item_id 컬럼에 대해 left DataFrame과 right DataFrame이 가진 모든 행을 보존한 후 거기에 동일한 item_id끼리 연결하여 반환합니다.

item_id = 4인 상품은 df_price에 price정보가 없었으므로 NaN값으로 출력됨을 알 수 있습니다.

item_id = 3인 상품은 df_name에 item_name정보가 없었으므로 NaN값으로 출력됨을 알 수 있습니다.

df_new = pd.merge(df_name, df_price, how='inner', left_on=['item_id'], right_on=['item_id'])
print(df_new)

- Output
   item_id item_name  price
0        1         a   1000
1        2         b   2000
df_new = pd.merge(df_name, df_price, how='left', left_on=['item_id'], right_on=['item_id'])
print(df_new)

- Output
   item_id item_name   price
0        1         a  1000.0
1        2         b  2000.0
2        4         d     NaN
df_new = pd.merge(df_name, df_price, how='right', left_on=['item_id'], right_on=['item_id'])
print(df_price)

- Output
   item_id item_name  price
0        1         a   1000
1        2         b   2000
2        3       NaN   3000
df_new = pd.merge(df_name, df_price, how='outer', left_on=['item_id'], right_on=['item_id'])
print(df_new)

- Output
   item_id item_name   price
0        1         a  1000.0
1        2         b  2000.0
2        4         d     NaN
3        3       NaN  3000.0




이제 한번 left_on, right_on의 조건에 2개의 컬럼을 조건으로 넣어봅시다.

 

이를 위해 테스트용 DataFrame을 생성합니다.

dict_daily_price = {
    'basis_dy': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'],
    'price': [
        1000, 1000, 1010,
        2000, 2100, 2050,
        3000, 3100, 2950,
        4000, 3950, 3900, 3980],
}

dict_daily_sales = {
    'basis_dy': [
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103,
        20200101, 20200102, 20200103, 20200104],
    'item_id': [
        1, 1, 1,
        2, 2, 2,
        3, 3, 3,
        4, 4, 4, 4],
    'item_name': [
        'a', 'a', 'a',
        'b', 'b', 'b',
        'c', 'c', 'c',
        'd', 'd', 'd', 'd'],
    'quantity': [
        100, 105, 98,
        50, 51, 55,
        201, 200, 220,
        30, 40, 38, 50
    ]
}

df_daily_price = pd.DataFrame(dict_daily_price)
df_daily_sales = pd.DataFrame(dict_daily_sales)

print(df_daily_price)
print(type(df_daily_price))
print(df_daily_sales)
print(type(df_daily_sales))

- Output
    basis_dy  item_id item_name  price
0   20200101        1         a   1000
1   20200102        1         a   1000
2   20200103        1         a   1010
3   20200101        2         b   2000
4   20200102        2         b   2100
5   20200103        2         b   2050
6   20200101        3         c   3000
7   20200102        3         c   3100
8   20200103        3         c   2950
9   20200101        4         d   4000
10  20200102        4         d   3950
11  20200103        4         d   3900
12  20200104        4         d   3980

    basis_dy  item_id item_name  quantity
0   20200101        1         a       100
1   20200102        1         a       105
2   20200103        1         a        98
3   20200101        2         b        50
4   20200102        2         b        51
5   20200103        2         b        55
6   20200101        3         c       201
7   20200102        3         c       200
8   20200103        3         c       220
9   20200101        4         d        30
10  20200102        4         d        40
11  20200103        4         d        38
12  20200104        4         d        50
  




위에서 생성한 2개의 DataFrame 중하나는 item들의 일자별 price 정보를 담고있으며 -> df_daily_price

 

다른 하나는 item들의 일자별 판매 개수를 담고있습니다. -> df_daily_sales

 

이 2개의 DataFrame을 합쳐서 일자별 가격과 판매개수 정보를 모두 담고있는 새로운 DataFrame을 생성해봅시다.

df_new = pd.merge(df_daily_price, df_daily_sales, how='inner', left_on=['item_id', 'basis_dy'], right_on=['item_id', 'basis_dy'])

print(df_new)

- Output
    basis_dy  item_id item_name_x  price item_name_y  quantity
0   20200101        1           a   1000           a       100
1   20200102        1           a   1000           a       105
2   20200103        1           a   1010           a        98
3   20200101        2           b   2000           b        50
4   20200102        2           b   2100           b        51
5   20200103        2           b   2050           b        55
6   20200101        3           c   3000           c       201
7   20200102        3           c   3100           c       200
8   20200103        3           c   2950           c       220
9   20200101        4           d   4000           d        30
10  20200102        4           d   3950           d        40
11  20200103        4           d   3900           d        38
12  20200104        4           d   3980           d        50

위처럼 left_on과 right_on의 조건에 2개의 컬럼을 명시하면 merge가 진행될 때 item_id와 basis_dy가 모두 동일한 값을 가진 행(row)끼리 합쳐짐을 알 수 있습니다.

 

그리고 결과에서 보면 item_name컬럼이 item_name_x와 item_name_y 2개가 존재함을 알 수 있죠.

 

item_name_x는 df_daily_price에 있던 item_name컬럼의 이름에 _x라는 접미어가 붙은 것이며

item_name_y는 df_daily_sales에 있던 item_name컬럼의 이름에 _y라는 접미어가 붙은 것입니다.

 

이렇게 item_name 컬럼에만 접미어가 붙은 이유는 merge 대상인 두 DataFrame이 동일한 이름의 컬럼을 가지고 있었기 때문입니다.

 

또한 여기서 왼쪽 DataFrame에 있는 컬럼에는 _x라는 접미어가 붙고, 오른쪽 DataFrame에 있는 컬럼에는 _y라는 접미어가 붙는다는 것도 알 수 있죠.

 

근데 뭔가 이상합니다.

두 DataFrame에는 item_id와 basis_dy라는 중복된 컬럼도 존재하는데 왜 이 두 컬럼에는 _x, _y등의 접미어각 붙지 않았을까요?

그것을 바로 item_id, basis_dy 이 두 컬럼은 merge의 조건(left_on, right_on)으로 쓰였기 때문입니다.




또한 merge 내에서 loc를 이용하여 일부의 DataFrame에 대해 merge가 가능합니다.

 

아래와 같은 형식은 left_df의 key가 되는 컬럼(이 예시에서는 item_id와 basis_dy가 되겠네요.)을 기준으로 right_df의 데이터를 붙여넣으면서 중복된 컬럼도 생기지 않게 하기 위한 방법으로 사용하기 좋겠네요.

df_new = pd.merge(df_daily_price.loc[:, ['item_id', 'basis_dy']], df_daily_sales, how='inner', left_on=['item_id', 'basis_dy'], right_on=['item_id', 'basis_dy'])

print(df_new)

- Output
    item_id  basis_dy item_name  quantity
0         1  20200101         a       100
1         1  20200102         a       105
2         1  20200103         a        98
3         2  20200101         b        50
4         2  20200102         b        51
5         2  20200103         b        55
6         3  20200101         c       201
7         3  20200102         c       200
8         3  20200103         c       220
9         4  20200101         d        30
10        4  20200102         d        40
11        4  20200103         d        38
12        4  20200104         d        50



 

 

 

 

 

728x90
반응형
Comments