조인 (Joins)

서론

데이터 분석이 단일 데이터 프레임만 포함하는 경우는 드뭅니다. 일반적으로 여러 데이터 프레임이 있으며, 관심 있는 질문에 답하기 위해 이들을 결합(join)해야 합니다.

pandas에는 하나 이상의 데이터 프레임을 결합하기 위한 매우 풍부한 옵션들이 있으며, 그중 가장 중요한 두 가지는 이어 붙이기(concatenate)와 병합하기(merge)입니다. 이 장의 몇 가지 예제는 한 쌍의 데이터 프레임을 결합하는 방법을 보여줍니다. 다행히 두 쌍씩 결합함으로써 세 개의 데이터 프레임을 결합할 수 있으므로 이것으로 충분합니다.

사전 준비

이 장에서는 pandas 데이터 분석 패키지를 사용합니다.

이어 붙이기 (Concatenate)

동일한 인덱스나 동일한 열을 가진 두 개 이상의 데이터 프레임이 있는 경우, pd.concat()을 사용하여 하나의 데이터 프레임으로 붙일 수 있습니다.

동일한 열의 경우 axis=0을 전달하여 인덱스를 따라 붙이고, 동일한 인덱스의 경우 axis=1을 전달하여 열을 따라 붙입니다. 이어 붙이기 함수는 일반적으로 데이터 프레임 리스트에 사용됩니다.

최종 데이터 프레임에서 원본 데이터가 어디에서 왔는지 추적하고 싶다면 keys 키워드를 사용하세요.

다음은 두 개의 서로 다른 주(state) 인구 데이터를 사용하고 keys 옵션도 활용하는 예제입니다:

코드 보기
import pandas as pd

base_url = (
    "https://github.com/aeturrell/coding-for-economists/raw/refs/heads/main/data/"
)
state_codes = ["ca", "il"]
end_url = "pop.dta"

# 각 주에 대해 하나씩, 두 개의 데이터프레임을 가져옵니다.
list_of_state_dfs = [pd.read_stata(base_url + state + end_url) for state in state_codes]
# 데이터프레임 리스트의 첫 번째 항목 예시를 보여줍니다.
print(list_of_state_dfs[0])

# 데이터프레임 리스트를 이어 붙입니다.
df = pd.concat(list_of_state_dfs, keys=state_codes, axis=0)
df
        county      pop
0  Los Angeles  9878554
1       Orange  2997033
2      Ventura   798364
county pop
ca 0 Los Angeles 9878554
1 Orange 2997033
2 Ventura 798364
il 0 Cook 5285107
1 DeKalb 103729
2 Will 673586

keys 인수는 선택 사항이지만, 병합된 데이터 프레임 내에서 원본 데이터 프레임을 추적하는 데 유용합니다.

연습 문제

다음 두 데이터 프레임을 이어 붙여 보세요:

df1 = pd.DataFrame([['a', 1], ['b', 2]],
                   columns=['letter', 'number'])

df2 = pd.DataFrame([['c', 3], ['d', 4]],
                   columns=['letter', 'number'])

병합 (Merge)

pd.merge(left, right, on=..., how=...)를 사용하여 데이터 프레임을 병합하는 옵션이 너무 많아서 여기에서 모두 다룰 수는 없습니다. 가장 중요한 특징은 병합할 두 데이터 프레임, 어떤 변수(키라고도 하며 인덱스가 될 수 있습니다)를 기준으로 병합할지 결정하는 on=, 그리고 병합 방식(예: left, right, outer, inner)을 결정하는 how=입니다. 아래 다이어그램은 왼쪽 데이터 프레임의 키를 사용한 병합의 예를 보여줍니다:

how= 키워드는 다음과 같이 작동합니다: - how='left'는 왼쪽 데이터 프레임의 키만 병합에 사용합니다. - how='right'는 오른쪽 데이터 프레임의 키만 병합에 사용합니다. - how='inner'는 양쪽 데이터 프레임에 공통으로 나타나는 키를 사용합니다. - how='outer'는 양쪽 데이터 프레임에 있는 키의 데카르트 곱(cartesian product)을 사용하여 병합합니다.

이들 중 일부의 예를 살펴보겠습니다:

코드 보기
left = pd.DataFrame(
    {
        "key1": ["K0", "K0", "K1", "K2"],
        "key2": ["K0", "K1", "K0", "K1"],
        "A": ["A0", "A1", "A2", "A3"],
        "B": ["B0", "B1", "B2", "B3"],
    }
)
right = pd.DataFrame(
    {
        "key1": ["K0", "K1", "K1", "K2"],
        "key2": ["K0", "K0", "K0", "K0"],
        "C": ["C0", "C1", "C2", "C3"],
        "D": ["D0", "D1", "D2", "D3"],
    }
)
# 오른쪽 병합 (Right merge)
pd.merge(left, right, on=["key1", "key2"], how="right")
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K1 K0 A2 B2 C1 D1
2 K1 K0 A2 B2 C2 D2
3 K2 K0 NaN NaN C3 D3

K2와 K0의 키 조합이 왼쪽 데이터 프레임에는 존재하지 않았으므로 최종 데이터 프레임에서 해당 항목들은 NaN이 됩니다. 하지만 오른쪽 데이터 프레임의 키를 선택했기 때문에 항목 자체는 존재합니다.

내부 병합(inner merge)은 어떨까요?

코드 보기
pd.merge(left, right, on=["key1", "key2"], how="inner")
key1 key2 A B C D
0 K0 K0 A0 B0 C0 D0
1 K1 K0 A2 B2 C1 D1
2 K1 K0 A2 B2 C2 D2

이제 K2와 K0 조합이 제외된 것을 볼 수 있는데, 이는 양쪽 데이터 프레임의 공통 키 집합에 속하지 않았기 때문입니다.

마지막으로 indicator 키워드를 통해 추가 정보를 제공하는 외부 병합(outer merge)을 살펴보겠습니다:

코드 보기
pd.merge(left, right, on=["key1", "key2"], how="outer", indicator=True)
key1 key2 A B C D _merge
0 K0 K0 A0 B0 C0 D0 both
1 K0 K1 A1 B1 NaN NaN left_only
2 K1 K0 A2 B2 C1 D1 both
3 K1 K0 A2 B2 C2 D2 both
4 K2 K0 NaN NaN C3 D3 right_only
5 K2 K1 A3 B3 NaN NaN left_only

이제 모든 키 조합의 곱이 여기에 있습니다. indicator=True 옵션으로 인해 ’_merge’라는 추가 열이 생겼으며, 해당 행의 키가 어떤 데이터 프레임에서 왔는지 알려줍니다.

연습 문제

left_onright_on 키워드 인수를 사용하여 각각 lkeyrkey를 기준으로 조인하도록 다음 두 데이터 프레임을 병합해 보세요:

df1 = pd.DataFrame({'lkey': ['foo', 'bar', 'baz', 'foo'],
                    'value': [1, 2, 3, 5]})
df2 = pd.DataFrame({'rkey': ['foo', 'bar', 'baz', 'foo'],
                    'value': [5, 6, 7, 8]})

연습 문제

how="left"를 키워드 인수로 사용하여 "a"를 기준으로 다음 두 데이터 프레임을 병합해 보세요:

df1 = pd.DataFrame({'a': ['foo', 'bar'], 'b': [1, 2]})
df2 = pd.DataFrame({'a': ['foo', 'baz'], 'c': [3, 4]})

병합된 데이터 프레임의 .loc[1, "c"] 위치에 대해 무엇을 알 수 있나요?

병합 옵션에 대한 자세한 내용은 pandas의 포괄적인 병합 문서를 참고하세요.