개발고생일지/파이썬

pandas Dataframe(데이터프레임) 행, 열 합치기 (병합)

Fartist 2023. 3. 26. 10:00
- 목차
들어가는 말
1. .concat() 메서드로 데이터프레임 행, 열 합치기
2. .merge() 메서드로 데이터프레임 합치기
3. .concat() 에서도 join을 사용
4. .merge() 메서드에서 index로 병합하기
갈무리

[그림 1] 데이터프레임 행, 열 병합
[그림 1] 데이터프레임 행, 열 병합


들어가는 말

 데이터프레임을 다루다 보면 다른 곳에서 생성된 데이터프레임을 기존에 사용하던 데이터프레임과 병합해야 하는 상황이 생깁니다. 유형에 따라서는 행간 결합이 될 수 있고, 열간 결합이 될 수도 있습니다. 합치는 방식에 따라서 사용하는 메서드가 달라질 수 있고, 각 메서드마다 필요로 하는 인자가 달라 사용자가 메서드를 사용하기 전에 데이터프레임을 사전에 가공해야 할 수 있습니다. 여기서 다루는 pandas 데이터프레임을 합치는 방법 살펴보고, 본인이 필요한 합치기 방식이 무엇인지 확인해 보시기 바랍니다.

 

import pandas as pd

name = ['홍길동', '김철수', '나일류']
ID = ['apple', 'banaba', 'coconut']
mail = ['apple@gaegosaeng.com', 'banana@gaegosaeng.com', 'coconut@gaegosaeng.com']

name2 = ['민지선', '강민지', '고목나']
ID2 = ['jazz', 'kiwi', 'lemon']
mail2 = ['jazz@gaegosaeng.com', 'kiwi@gaegosaeng.com', 'lemon@gaegosaeng.com']

df_01 = pd.DataFrame({'ID': ID, 'name': name, 'E-mail': mail})
df_02 = pd.DataFrame({'ID': ID2, 'name': name2, 'E-mail': mail2})

[코드 1] 결합할 두 개의 데이터프레임

[그림 2] 결합 연습용 2개의 데이터프레임
[그림 2] 결합 연습용 2개의 데이터프레임


1.  .concat() 메서드로 데이터프레임 행, 열 합치기

1) .concat()의 디폴트 값

    i) 데이터프레임 인자

pd.concat([데이터프레임1, 데이터프레임2,...])

[코드 2] concat() 메서드의 인자1 - 데이터프레임

 concat() 메서드는 합치고픈 데이터프레임을 리스트의 형태[]로 묶어서 인자로 받습니다. 


    ii) 행/열 설정 인자

pd.concat([데이터프레임1, 데이터프레임2], axis=0)

[코드 3] concat() 메서드의 인자2 - 행, 열 지정

 concat() 메서드는 'axis=' 인자를 사용하여 행간 결합과 열간 결합을 지정할 수 있습니다. 만약 axis값을 지정하지 않으면 기본 디폴트 값은 'axis=0'으로 행간 결합을 합니다. 'axis=1'로 지정하면 열간 결합을 합니다. 그러므로 concat() 메서드는 기본 디폴트 설정은 행간 결합을 하는 메서드입니다.

[그림 3] 행간 결합과 열간 결합
[그림 3] 행간 결합과 열간 결합


    iii) 인덱스 설정 인자

pd.concat([데이터프레임1, 데이터프레임2], axis=0, ignore_index=Bool)

[코드 4] concat() 메서드의 인자3 - 인덱스 설정

 concat() 메서드에는 'ignore_index=Bool타입' 인자를 사용하여 결합한 결과물의 index를 어떻게 지정할지 결정할 수 있습니다. 'ignore_index=True'로 설정하면 합쳐진 데이터프레임은 새롭게 만들어진 통합된 index를 갖게 됩니다. 'ignore_index=False'로 설정하거나, 아예 인자를 기입하지 않으면 합쳐지기 전 각 데이터프레임이 가지고 있던 index값을 유지한 채로 결합을 하게 됩니다. 그러므로 concat() 메서드는 디폴트 설정으로 행간 결합 시, 각 데이터프레임이 가지고 있던 고유 index를 유지한 채 결합하는 메서드입니다.

[그림 4] 인덱스 설정 인자 적용 시
[그림 4] 인덱스 설정 인자 적용 시


2) .concat() 메서드를 활용한 데이터프레임 행간 결합

 concat() 메서드에 합칠 데이터프레임을 리스트[] 형태의 인자를 받고 선택사항으로  axis=0을 인자로 받으면 데이터프레임을 행간 결합할 수 있습니다. 이때 'ignore_index=True'를 인자로 넣어주면 합쳐진 데이터프레임은 새롭게 통합된 '0'부터 시작하는 index를 부여받게 됩니다. 만약 결합된 데이터프레임을 나중에도 사용하고 싶다면 결합된 데이터프레임을 변수에 할당해야 합니다. [그림 5]는 변수에 할당한 데이터프레임을 보여줍니다.

[그림 5] concat()을 활용한 행간 결합
[그림 5]concat()을 활용한 행간 결합


3) .concat() 메서드를 활용한 데이터프레임 열간 결합

 concat() 메서드에 합칠 데이터프레임을 리스트[] 형태로, 추가로 axis=1을 인자로 받으면 데이터프레임을 열간 결합할 수 있습니다. 이때 'ignore_index=True'를 인자로 넣어주면 합쳐진 데이터프레임은 새롭게 통합된 '0'부터 시작하는 index를 부여받게 됩니다. 

 열간 결합할 때, 만약 한쪽의 행 데이터값이 더 많다면 [그림 6]처럼 데이터가 없는 열은 일괄적으로 'NaN'값이 할당됩니다.

[그림 6] 두 데이터프레임의 행열 값이 다를 때 결합
[그림 6] 두 데이터프레임의 행열 값이 다를 때 결합


 열간 결합 시 주의할 점은 각 행의 값이 index값을 기준으로 대응한다는 것입니다. 만약 추가하는 하나 이상의 열이 다른 데이터프레임과 전혀 상관없는 index체계를 가지고 있다면 올바르게 데이터프레임을 결합할 수 없습니다. [그림 7]은 index값으로 1을 갖는 1행짜리 데이터프레임 df_05를 df_01과 결합했을 때 나오는 결과물입니다. 만약  df_05의 데이터 값이 '홍길동' 행 뒤에 붙어있길 원했다면 결과물은 올바르지 않은 결합 결과가 됩니다.

[그림 7] 인덱스 값을 기준으로 묶인 데이터프레임 열간 결합
[그림 7] 인덱스 값을 기준으로 묶인 데이터프레임 열간 결합

 [그림 7]로 알 수 있는 사실은 concat()의 열간 결합은 기본적으로 'index'값을 기준으로 실행된다는 것입니다. index가 아닌 다른 데이터 값(열)을 기준으로 데이터프레임을 결합하고 싶다면 다음에 나오는 join() 메서드를 활용해야 합니다.


2.  .merge() 메서드로 데이터프레임 합치기

 merge() 메서드는 concat() 메서드보다 데이터프레임 병합과 관련해서 세세한 설정 인자를 제공합니다. merge()에는 'inner'와 'outer'라는 개념이 존재하여 '병합 조건'을 추가하여 병합을 보다 자유롭게 활용합니다.

inner join = 병합 기준으로 잡은 index의 데이터 값을 두 데이터프레임이 모두 가지고 있는 경우 병합하는 것. (교집합)
outter join = 두 데이터프레임이 가진 모든 데이터 값을 병합하고 각 데이터프레임이 가지지 못한 데이터는 NaN으로 표기 (합집합)

 inner는 교집합 병합, outer는 합집합 병합입니다. 실제 예시는 merge() 메서드의 기본 모형을 살펴 본 후에 알아보도록 합시다.

- merge() 메서드의 기본 모형

pd.merge(left=데이터프레임1, right=데이터프레임2, how=결합방식, on=기준이 되는 열 이름)

[코드 5] merge() 메서드의 기본 모형

 left와 right에는 병합하고픈 데이터프레임 이름이 각각 들어갑니다. how는 결합방식을 선언하는 인자로, 들어갈 수 있는 값으로는 'inner', 'outer', 'left', 'right'가 있습니다. on은 병합 기준을 설정하는 인자입니다. 만약 기준이 되는 열 이름이 '주소'라면 on='주소'의 형태로 기입하면 됩니다.

기본적인 merge() 메서드의 골격은 이것으로 마칩니다. 사용하는 옵션에 따라 조금씩 변화할 수 있지만 큰 골자는 같습니다. 이제부터는 실제 사용예시를 살펴보면서 merge() 메서드로 데이터프레임을 합쳐보겠습니다. 


addr = ['서울', '경기', '제주']
ID = ['apple', 'banaba', 'maple']
phone = ['010-1234-1234', '010-1234-1235', '010-1234-1236']

df_new= pd.DataFrame({'주소': addr, 'ID': ID, '전화번호': phone})

[코드 6] merge() 메서드 연습을 위한 새로운 데이터프레임

[그림 8] merge() 연습을 위한 새로운 데이터프레임
[그림 8] merge() 연습을 위한 새로운 데이터프레임


1) inner join ( 교집합 병합 )

#Inner Join

pd.merge(left=df_01, right=df_new, how="inner", on="ID")

[코드 7] inner join 예시

[그림 9] inner join 결과
[그림 9] inner join 결과

 [그림 10]을 보면 df_01과 df_new가 공통으로 가진 'ID'를 기준으로 교집합 병합이 이루어졌습니다. 그 결과 [그림 9]처럼 ID ' apple'과 'banana'의 행만을 출력해주고 있습니다.

[그림 10] 병합 재료


2) outer join ( 합집합 병합 )

pd.merge(left=df_01, right=df_new, how="outer", on="ID")

[코드 8] outer join 예시

[그림 11] outer join 결과
[그림 11] outer join 결과

 outer join은 합집합 병합이라고 앞서 설명해 드렸습니다. [그림 11]을 보면 각 데이터프레임이 가지고 있던 'ID'열의 고유 데이터값들이 모두 들어가 있습니다. 또한 서로 가지지 못한 데이터 열은 모두 NaN으로 처리되어 있는 것을 알 수 있습니다.


3) left, right outer join ( 합집합 병합 옵션 )

  outer join에는 두 가지 옵션이 있습니다. 하나는 좌변 합집합 병합, 나머지 하나는 우변 합집합 병합입니다. 좌변 합집합 병합은 좌변에 입력된 데이터프레임을 기준으로 합집합 병합을 한다는 뜻입니다. left=에 입력된 데이터프레임이 기준이 되며 이를 좌변 데이터프레임이라고 부르겠습니다. 좌변 데이터프레임의 기준 열 데이터는 모두 보존하고 우변 데이터프레임(right=에 입력된)의 데이터를 추가합니다. 이때 우변 데이터프레임만 가지고 있는 데이터는 소실됩니다. 우변 데이터프레임이 가진 데이터 중에서 좌변 데이터프레임이 가진 데이터만 보존됩니다. 설명이 너무 복잡하지요? 예시를 보면서 다시 한번 천천히 살펴보겠습니다.

#left outer join
pd.merge(left=df_01, right=df_new, how="left", on="ID")

[코드 9] left outer join 코드

 [코드 9]를 보면 how=안에 'left'가 설정 인자로 들어가 있습니다. 이는 left outer join을 뜻하는 것으로 좌변 합집합 병합입니다. 그 결과 [그림 12]처럼 좌변 데이터프레임을 기준으로 우변 데이터프레임이 공통으로 가진 데이터만 병합됩니다.

[그림 12] left outer join
[그림 12] left outer join


 그렇다면 반대로 right outer join(우변 합집합 병합)은 어떨까요? 

#right outer join
pd.merge(left=df_01, right=df_new, how="right", on="ID")

[코드 10] right outer join 

[그림 13] right outer join
[그림 13] right outer join

 right outer join의 결과 [그림 13]처럼 마지막 index(2)의 행이 [그림 12]와 달라졌습니다. 확실히 우변에 입력된 데이터프레임의 데이터값이 보존되고 좌변 데이터프레임이 공통으로 가진 데이터값만 병합되었습니다. 


 

3.  concat() 에서도 join을 사용

앞서 merge()를 소개하면서 concat()에서는 join 옵션을 사용하지 못하는 것처럼 언급했지만 사실 concat() 메서드에서도 join 옵션을 사용할 수 있습니다. 하지만 기본적인 사용법에서 두 메서드가 join을 기준으로 다소 차이를 보이는 것이 사실이라 설명의 편의를 위해 그렇게 소개하였습니다. 이번 챕터에서는 concat() 메서드에서 inner join을 하는 방법을 소개하겠습니다.

#ID를 기준으로 새로운 인덱스 설정
df_01_newIndex = df_01.set_index('ID')

[코드 11] index를 가지고 있는 열 중에서 선택하여 교체

 [코드 11]은 기존에 df_01이 가진 index값 '0, 1, 2'를 df_01이 가진 열(column)의 데이터값으로 교체하는 내용입니다. .set_index()메서드를 사용해 index를 재설정합니다. [코드 11]에서는 방금까지 merge() 메서드의 병합 기준이 되었던 'ID'열을 새로운 index값으로 설정합니다.

[그림 14] df_01의 새로운 index값
[그림 14] df_01의 새로운 index값

[그림 14]를 보면 index 위치에 숫자가 사라지고 'ID'열의 데이터값이 자리한 것을 알 수 있습니다. 동일한 방법으로 이번에는 df_new의 index를 'ID'열로 바꾸어 보겠습니다.

#ID를 기준으로 새로운 인덱스 설정
df_new_newIndex = df_new.set_index('ID')

[코드 12] index를 가지고 있는 열 중에서 선택하여 교체

[그림 15] df_new의 새로운 index값
[그림 15] df_new의 새로운 index값

 이제 concat() 메서드로 inner  join을 할 준비가 끝났습니다. 


#concat()을 사용한 join
pd.concat([df_01_newIndex, df_new_newIndex], axis=1, join='inner')

[코드 13] concat() 메서드를 사용한 inner join

[그림 16] concat()을 사용한 inner join
[그림 16] concat()을 사용한 inner join

 [그림 16]을 보면 제대로 교집합 병합이 이루어진 것을 확인할 수 있습니다. concat() 안에서 쓰인 join인자는 'inner'와 'outer'를 값으로 받습니다. merge() 메서드가  how=에서 'inner', 'outer', 'left', 'right' 총 4 가지를 값으로 받았던 것과 비교하면 차이가 있습니다.


4. .merge() 메서드에서 index로 병합하기

 우리는 앞서 merge() 메서드로 두 데이터프레임을 병합할 때 기준이 되는 열을 'on=' 인자에 넣어서 설정했습니다. 하지만 메서드 안에서 별로의 특수한 인자로 설정하면 merge() 메서드에서 index값을 기준으로 데이터프레임을 병합할 수 있습니다. 

#merge()에서 index값을 기준으로 join
pd.merge(left=df_01_newIndex, right=df_new_newIndex, left_index = True, right_index = True, how = "inner")

[코드 14] merge()에서 index값을 기준으로 join

 앞서 새롭게 index를 설정한 df_01_newIndex와 df_new_newIndex를 사용하겠습니다. 새로운 index가 고유하고 두 데이터프레임을 유의미하게 비교가능해야 하기 때문에 set_index()메서드로 index를 공통된 열로 변환해 줄 필요가 있습니다. 

 merge() 메서드에서 바뀐 점이 있다면 인자로 left_index=True와 right_index=True를 가진다는 점, 그리고  on=인자가 사라진 점입니다. 본래 기준 열을 제시하는 on= 인자를 없애고, 그 기능을 left, right_index=인자가 맡았습니다. 각 인덱스를 비교인자로 사용할 것인지 결정하는 것으로 모두 True를 설정함으로써 두 데이터프레임을 비교 병합할 수 있게 됩니다.

[그림 17] merge() 메서드에서 index를 기준으로 병합하기
[그림 17] merge() 메서드에서 index를 기준으로 병합하기

 [그림 17]을 보면 [코드 14]의 결과로 inner join이 index를 기준으로 잘 작동했습니다.


갈무리

- 전체 코드

#결합 연습할 2개의 데이터프레임 선언

import pandas as pd

name = ['홍길동', '김철수', '나일류']
ID = ['apple', 'banaba', 'coconut']
mail = ['apple@gaegosaeng.com', 'banana@gaegosaeng.com', 'coconut@gaegosaeng.com']

name2 = ['민지선', '강민지', '고목나']
ID2 = ['jazz', 'kiwi', 'lemon']
mail2 = ['jazz@gaegosaeng.com', 'kiwi@gaegosaeng.com', 'lemon@gaegosaeng.com']

df_01 = pd.DataFrame({'ID': ID, 'name': name, 'E-mail': mail})
df_02 = pd.DataFrame({'ID': ID2, 'name': name2, 'E-mail': mail2})

# 행간 결합
pd.concat([df_01, df_02])

#열간 결합
pd.concat([df_01, df_02], axis=1)

#인덱스 설정 인자 미설정
pd.concat([df_01, df_02], axis=0)

#인덱스 설정 인자 설정
pd.concat([df_01, df_02], axis=0, ignore_index=True)

#concat()을 활용한 행간 결합
df_03 = pd.concat([df_01, df_02], axis=0, ignore_index=True)

#df_02에 행 하나 추가
df_02.loc[3] = ['melon', '이진아', 'melon@gaegosaeng.com']

#행의 개수가 맞지 않는 두 데이터프레임의 열간 결합
df_04 = pd.concat([df_01, df_02], axis=1)

# 고유한 인덱스를 가진 1행짜리 데이터프레임
df_05 = pd.DataFrame([['night', '강지은', 'night@gaegosaeng.com']], 
                     columns=['ID','name','E-mail'],index=[1])

#concat() 열간 결합은 index를 기준으로 합니다.
pd.concat([df_01, df_05], axis=1)

#merge() 연습용 데이터프레임
addr = ['서울', '경기', '제주']
ID = ['apple', 'banaba', 'maple']
phone = ['010-1234-1234', '010-1234-1235', '010-1234-1236']

df_new= pd.DataFrame({'주소': addr, 'ID': ID, '전화번호': phone})

#Inner Join
pd.merge(left=df_01, right=df_new, how="inner", on="ID")

#outer join
pd.merge(left=df_01, right=df_new, how="outer", on="ID")

#left outer join
pd.merge(left=df_01, right=df_new, how="left", on="ID")

#right outer join
pd.merge(left=df_01, right=df_new, how="right", on="ID")

#ID를 기준으로 새로운 인덱스 설정
df_01_newIndex = df_01.set_index('ID')

#ID를 기준으로 새로운 인덱스 설정
df_new_newIndex = df_new.set_index('ID')

#concat()을 사용한 join
pd.concat([df_01_newIndex, df_new_newIndex], axis=1, join='outer')

#merge()를 사용한 join
pd.merge(left=df_01_newIndex, right=df_new_newIndex, left_index = True, right_index = True, how = "inner")

 이상으로 pandas Dataframe에서 두 데이터프레임을 병합하는 방법에 대한 글을 마치겠습니다.

반응형