- Published on
Pandas에서 여러 DataFrame다루기
- Authors
- Name
- Eunsu Kim
- @eunsukimme
지금까지는 하나의 DataFrame을 다루는 방법들에 대해서 알아보았습니다. 그런데 모든 데이터를 하나의 테이블에 저장하는 경우는 현실에선 좀 처럼 쉽게 접할 수 없습니다. 그러므로 이번 포스팅에서는 여러 테이블, 즉 DataFrame들을 다루는 방법에 대해서 알아보도록 합시다.
Inner Merge
Pandas 에서는 여러 DataFrame을 연결짓는 Merge 라는 개념이 존재합니다. Merge 는 SQL 의 join 과 같은 원리로 동작합니다.
이해를 돕기 위해 우리가 e-Commerce 비즈니스를 운영한다고 생각해봅시다. 우리 데이터베이스에서 다음과 같은 테이블들을 저장한다고 가정해봅시다.
-
orders
- 각 주문들을 저장하는 테이블order_id customer_id product_id quantity timestamp 1 2 3 1 2017-01-01 2 2 2 3 2017-01-01 3 3 1 1 2017-01-01 4 3 2 2 2017-02-01 5 3 3 3 2017-02-01 6 1 4 2 2017-03-01 7 1 1 1 2017-02-02 8 1 4 1 2017-02-02 -
products
- 상품 정보를 저장하는 테이블product_id description price 1 thing-a-ma-jig 5 2 whatcha-ma-call-it 10 3 doo-hickey 7 4 gizmo 3 -
customers
- 사용자 정보를 저장하는 테이블customer_id customer_name address phone_number 1 John Smith 123 Main St. 212-123-4567 2 Jane Doe 456 Park Ave. 949-867-5309 3 Joe Schmo 798 Broadway 112-358-1321
위의 orders
테이블은 좀처럼 이해하기가 쉽지 않습니다. 주문을 한 사용자의 정보를 같이 확인하고 싶은데, 사용자의 정보를 함께 나타내 주면 좋을 것 같습니다. 이 때 사용할 수 있는게 바로 Merge 입니다.
Pandas 에서는 .merge()
라는 두 DataFrame을 엮어주는 메서드를 제공합니다. .merge()
함수는 두 DataFrame의 공통된 column 을 찾아낸 뒤 각 column 의 값이 동일한 row 들을 찾아냅니다. 그런 다음 매칭된 row 들을 포함하는 하나의 row 를 만들어 새로운 DataFrame에 추가한 후 이를 반환합니다.
new_df = pd.merge(orders, customers)
위 코드는 orders
테이블과 customers
테이블의 공통된 column 인 customer_id
의 값이 일치하는 모든 row 를 찾아냅니다. 즉, 사용자들이 주문한 주문내역을 확인할 수 있게됩니다.
Pandas 라이브러리가 제공하는 .merge()
뿐 만 아니라 DataFrame도 고유한 .merge()
함수를 가지고 있습니다. 예를 들어 위에서 작성한 코드는 다음과 같이 쓸 수도 있습니다.
new_df = orders.merge(customers) # pd.merge(orders, customers) 와 동일합니다.
둘은 완전히 동일한 기능을 하지만, 두 개 이상의 DataFrame을 merge 하는 경우 'chain'을 이룰 수 있기 때문에 DataFrame의 .merge()
를 사용합니다.
big_df = orders.merge(customers).merge(products)
Merge on Specific Columns
Merge 는 동일한 column 에 대해서 이뤄지기 때문에 위의 예에서는 별 문제없이 이뤄졌습니다. 그러나 우리 서비스의 데이터베이스 테이블이 다음과 같이 이뤄져있다고 가정해봅시다.
-
customers
id customer_name address phone_number 1 John Smith 123 Main St. 212-123-4567 2 Jane Doe 456 Park Ave. 949-867-5309 3 Joe Schmo 798 Broadway 112-358-1321 -
orders
id customer_id product_id quantity timestamp 1 2 3 1 2017-01-01 2 2 2 3 2017-01-01 3 3 1 1 2017-01-01 4 3 2 2 2017-02-01 5 3 3 3 2017-02-01 6 1 4 2 2017-03-01 7 1 1 1 2017-02-02 8 1 4 1 2017-02-02
만약 그냥 merge 를 하게 되면 어떻게 될까요? 두 테이블 모두 동일한 id
column 을 보유하기 때문에 id
가 같은 것들끼리 merge 가 되겠지만, 각각의 id
는 의미하는 바가 달라서 잘못된 결과를 만들어 낼 것입니다.
이를 해결할 수 있는 한 가지 방법은 .rename()
함수를 활용하여 merge 하는 테이블의 column 이름을 수정하는 것입니다. 즉, customers
테이블의 id
column 을 customer_id
로 수정하는 것입니다. 그리하여 customer_id
가 같은 row 를 매칭해줄 수 있습니다.
pd.merge(orders, customers.rename(column={'id': 'customer_id'}))
또 다른 방법은 merge 시키고자 하는 column 을 정해주는 것입니다. left_on
과 right_on
옵션을 부여함으로써 각 테이블의 어떤 column 을 기준으로 merge 할 것인지 정해줄 수 있습니다.
# left 테이블은 먼저 주어진 orders 입니다.
# right 테이블은 나중에 주어진 customers 입니다.
# orders.customer_id 와 customers.id 가 같은 row를 찾습니다.
pd.merge(
orders,
customers,
left_on='customer_id',
right_on='id')
만약 이렇게 merge 하게 된다면 order
의 id
column 과 customers
의 id
column 이 함께 merge 되기 때문에 한 row 에 두 개의 id
columns 이 존재하게 됩니다. Pandas 는 이러한 문제를 자체적으로 id_x
, id_y
와 같은 suffix 를 추가함으로써 해결해줍니다. 즉 merge 결과는 다음과 같습니다.
id_x | customer_id | product_id | quantity | timestamp | id_y | customer_name | address | phone_number |
---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 1 | 2017-01-01 00:00:00 | 2 | Jane Doe | 456 Park Ave | 949-867-5309 |
2 | 2 | 2 | 3 | 2017-01-01 00:00:00 | 2 | Jane Doe | 456 Park Ave | 949-867-5309 |
3 | 3 | 1 | 1 | 2017-01-01 00:00:00 | 3 | Joe Schmo | 789 Broadway | 112-358-1321 |
4 | 3 | 2 | 2 | 2016-02-01 00:00:00 | 3 | Joe Schmo | 789 Broadway | 112-358-1321 |
5 | 3 | 3 | 3 | 2017-02-01 00:00:00 | 3 | Joe Schmo | 789 Broadway | 112-358-1321 |
6 | 1 | 4 | 2 | 2017-03-01 00:00:00 | 1 | John Smith | 123 Main St. | 212-123-4567 |
7 | 1 | 1 | 1 | 2017-02-02 00:00:00 | 1 | John Smith | 123 Main St. | 212-123-4567 |
8 | 1 | 4 | 1 | 2017-02-02 00:00:00 | 1 | John Smith | 123 Main St. | 212-123-4567 |
Pandas 에서 column 이름의 충돌을 피하게 해준것은 고맙지만, id_x
와 id_y
는 이해하는데 전혀 도움이 되지 못합니다. 이를 해결하려면 .merge()
함수에 suffixes
옵션을 제공하여 x, y 대신 다른 suffixes 를 할당하도록 해줍니다.
# suffixes 옵션으로 충돌하는 column에 더할 suffix 배열을 전달합니다.
pd.merge(
orders,
customers,
left_on='customer_id',
right_on='id',
suffixes=['_order', '_customer']
)
그러면 결과는 다음과 같습니다.
id_order | customer_id | product_id | quantity | timestamp | id_customer | customer_name | address | phone_number |
---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 1 | 2017-01-01 00:00:00 | 2 | Jane Doe | 456 Park Ave | 949-867-5309 |
2 | 2 | 2 | 3 | 2017-01-01 00:00:00 | 2 | Jane Doe | 456 Park Ave | 949-867-5309 |
3 | 3 | 1 | 1 | 2017-01-01 00:00:00 | 3 | Joe Schmo | 789 Broadway | 112-358-1321 |
4 | 3 | 2 | 2 | 2016-02-01 00:00:00 | 3 | Joe Schmo | 789 Broadway | 112-358-1321 |
5 | 3 | 3 | 3 | 2017-02-01 00:00:00 | 3 | Joe Schmo | 789 Broadway | 112-358-1321 |
6 | 1 | 4 | 2 | 2017-03-01 00:00:00 | 1 | John Smith | 123 Main St. | 212-123-4567 |
7 | 1 | 1 | 1 | 2017-02-02 00:00:00 | 1 | John Smith | 123 Main St. | 212-123-4567 |
8 | 1 | 4 | 1 | 2017-02-02 00:00:00 | 1 | John Smith | 123 Main St. | 212-123-4567 |
Outer Merge
지금까지는 두 테이블 모두 동일한 column 에 대해 매칭되는 값이 항상 존재한 경우를 다뤄보았습니다. 그런데 만약 동일한 column 에 대해 한 쪽 테이블에는 값이 있는데, 다른 쪽 테이블에는 값이 존재하지 않는다면 어떻게 될까요? 이러한 경우 매치가 되지 않아 해당 row 를 잃게 됩니다. 이러한 merge 를 inner merge라고 합니다. 매칭되지 않는 row 가 없도록 하려면 다른 타입의 merge 를 사용해야 합니다.
이해를 돕기 위해 Company A, Company B 라는 두 회사가 있다고 생각해 봅시다. A 회사는 고객 정보를 수집할 때 name 과 emai l 정보를 수집하고, B 회사는 name 과 phone 정보를 수집합니다. 두 회사는 같은 고객을 보유하기도 하는데요, 각 회사의 고객 정보 테이블은 다음과 같습니다.
-
company_a
name email Sally Sparrow sally.sparrow@gmail.com Peter Grant pgrant@yahoo.com Leslie May leslie_may@gmail.com -
company_b
name phone Peter Grant 212-345-6789 Leslie May 626-987-6543 Aaron Burr 303-456-7891
두 테이블을 inner merge 하게 되면 Peter Grant 와 Lesile May 의 정보만이 남게 됩니다. Sally Sparrow 는 A 회사의 테이블에 저장되어있지만 B 회사에 없고, Aaron Burr 는 B 회사의 테이블에 저장되어있지만 A 회사에 없어서 매칭되지 않기 때문입니다.
만약 고객 정보의 손실이 없이 merge 를 하고자 한다면 Outer Merge를 사용할 수 있습니다. Outer Merge는 비록 매칭되는 row 가 없더라도 두 테이블의 모든 row 를 포함시킵니다. 이 때 매칭되지 않은 row 의 다른 column 은 None
또는 nan
으로 채워집니다. Merge 형태는 how
옵션을 부여함으로써 지정할 수 있습니다.
pd.merge(company_a, company_b, how='outer')
위 코드의 결과는 다음과 같습니다.
name | phone | |
---|---|---|
Sally Sparrow | sally.sparrow@gmail.com | nan |
Peter Grant | pgrant@yahoo.com | 212-345-6789 |
Leslie May | leslie_may@gmail.com | 626-987-6543 |
Aaron Burr | nan | 303-456-7891 |
Left and Right Merge
위에서 제시한 A, B 회사 예를 그대로 가져와 봅시다.
Left Merge
만약 고객들 중에서 phone 정보가 빠진 고객을 찾아내고 싶다고 가정해봅시다. 이 경우 Left Merge를 활용할 수 있습니다. Left Merge는 왼쪽 테이블의 모든 row 를 포함하지만 오른쪽 테이블에선 매칭되는 row 만을 포함합니다. .merge()
함수를 호출할 때 처음 주어지는 DataFrame이 왼쪽 테이블이 되고, 나중에 주어지는 DataFrame이 오른쪽 테이블이 됩니다.
# company_a를 먼저 제공함으로써, company_a의 모든 row가 포함됩니다.
# company_b를 나중에 제공함으로써, company_b는 매칭되는 row만 포함됩니다.
pd.merge(company_a, company_b, how='left')
위 코드의 결과는 다음과 같습니다.
name | phone | |
---|---|---|
Sally Sparrow | sally.sparrow@gmail.com | None |
Peter Grant | pgrant@yahoo.com | 212-345-6789 |
Leslie May | leslie_may@gmail.com | 626-987-6543 |
Right Merge
Right Merge는 Left Merge의 반대입니다. 즉 오른쪽 테이블의 모든 row 를 포함하지만 왼쪽 테이블에선 매칭되는 row 만을 포함합니다.
# company_a를 먼저 제공함으로써, company_a는 매칭되는 row만 포함됩니다.
# company_b를 나중에 제공함으로써, company_b의 모든 row가 포함됩니다.
pd.merge(company_a, company_b, how='right')
위 코드의 결과는 다음과 같습니다.
name | phone | |
---|---|---|
Peter Grant | pgrant@yahoo.com | 212-345-6789 |
Leslie May | leslie_may@gmail.com | 626-987-6543 |
Aaron Burr | None | 303-456-7891 |
Concatenate DateFrames
가끔 데이터셋이 여러 DataFrame으로 쪼개진 경우가 있습니다. 이렇게 쪼개진 DataFrame들을 하나의 DataFrame으로 묶기 위해선 .concat()
함수를 활용하면 됩니다.
pd.concat(['df1', 'df2'])
위 함수는 오직 column 이 동일한 DataFrame에 대해서만 적용할 수 있습니다.
Review
지금까지 Pandas 에서 여러 DataFrame을 다루는 방법에 대해서 알아보았습니다. 지금까지 배운 내용을 정리하면 다음곽 같습니다.
- Creating a DataFrame made by matching the common columns of two DataFrames is called a
merge
- We can specify which columns should be matches by using the keyword arguments
left_on
andright_on
- We can combine DataFrames whose rows don’t all match using
left
,right
, andouter
merges and thehow
keyword argument - We can stack or concatenate DataFrames with the same columns using
pd.concat
공부하는 주기가 줄어들고 있습니다..😅 초심을 잃지 않고 계속해서 공부합시다 👍