All Articles

Pandas에서 여러 DataFrame다루기

지금까지는 하나의 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 를 하게 되면 어떻게 될까요? 두 테이블 모두 동일한 idcolumn 을 보유하기 때문에 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_onright_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 하게 된다면 orderid column 과 customersid 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_xid_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 정보를 수집합니다. 두 회사는 같은 고객을 보유하기도 하는데요, 각 회사의 고객 정보 테이블은 다음과 같습니다.

두 테이블을 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 email 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 email 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 MergeLeft Merge의 반대입니다. 즉 오른쪽 테이블의 모든 row 를 포함하지만 왼쪽 테이블에선 매칭되는 row 만을 포함합니다.

# company_a를 먼저 제공함으로써, company_a는 매칭되는 row만 포함됩니다.
# company_b를 나중에 제공함으로써, company_b의 모든 row가 포함됩니다.
pd.merge(company_a, company_b, how='right')

위 코드의 결과는 다음과 같습니다.

name email 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 and right_on
  • We can combine DataFrames whose rows don’t all match using left, right, and outer merges and the howkeyword argument
  • We can stack or concatenate DataFrames with the same columns using pd.concat

공부하는 주기가 줄어들고 있습니다..😅 초심을 잃지 않고 계속해서 공부합시다 👍

References