아미(아름다운미소)

두 데이터프레임 df와 df2에서 'a'와 'b' 컬럼을 비교하여 매핑이 다른 행을 찾는 예제 본문

랭귀지/pandas

두 데이터프레임 df와 df2에서 'a'와 'b' 컬럼을 비교하여 매핑이 다른 행을 찾는 예제

유키공 2025. 4. 14. 09:29

예제1

import pandas as pd

# --------------------------------------------
# 1. 샘플 데이터 생성
# --------------------------------------------
df = pd.DataFrame({
    'a': [1, 2, 3, 4],
    'b': ['A', 'B', 'C', 'D'],
    'c': [10, 20, 30, 40]
})

df2 = pd.DataFrame({
    'a': [1, 2, 3, 5],
    'b': ['A', 'X', 'C', 'E'],
    'c': [10, 20, 30, 50]
})

# --------------------------------------------
# 2. 비교 방법 구현
# --------------------------------------------
def method_merge():
    """방법 1: merge + query (가장 직관적)"""
    result = (
        pd.merge(df, df2, on='a', how='left', suffixes=('', '_df2'))
        .query("b != b_df2")[['a', 'b']]
    )
    print("▣ 방법 1 결과 (merge):\n", result)

def method_set():
    """방법 2: 집합(set) 비교 (고속 처리)"""
    diff = set(zip(df['a'], df['b'])) - set(zip(df2['a'], df2['b']))
    result = df[pd.Series(zip(df['a'], df['b'])).isin(diff)][['a', 'b']]
    print("\n▣ 방법 2 결과 (집합 비교):\n", result)

def method_boolean():
    """방법 3: boolean 인덱싱 (메모리 효율적)"""
    mask = (
        df.set_index('a')['b']
        .ne(df2.set_index('a')['b'].reindex(df['a']))
        .reset_index(drop=True)
    )
    result = df[mask][['a', 'b']]
    print("\n▣ 방법 3 결과 (boolean 인덱싱):\n", result)

# --------------------------------------------
# 3. 실행 및 결과 출력
# --------------------------------------------
method_merge()
method_set()
method_boolean()

예제2

import pandas as pd

# 샘플 데이터 생성
df = pd.DataFrame({
    'a': [1, 2, 3, 4],
    'b': ['A', 'B', 'C', 'D'],
    'c': [10, 20, 30, 40]
})

df2 = pd.DataFrame({
    'a': [1, 2, 3, 5],
    'b': ['A', 'X', 'C', 'E'],
    'c': [10, 20, 30, 50]
})

# --------------------------
# 방법 1: merge 사용 (보강 버전)
# --------------------------
mismatch_merge = pd.merge(df, df2, on=['a'], how='outer', suffixes=('_df', '_df2'))
mismatch_merge = mismatch_merge[mismatch_merge['b_df'] != mismatch_merge['b_df2']]

print("▣ 방법 1: merge 방식 (양쪽 데이터 비교)")
print(mismatch_merge[['a', 'b_df', 'b_df2']].rename(columns={
    'b_df': 'df_b',
    'b_df2': 'df2_b'
}))

# --------------------------
# 방법 2: concat 사용 (보강 버전)
# --------------------------
df_compare = pd.concat([
    df.set_index('a')[['b', 'c']], 
    df2.set_index('a')[['b', 'c']]
], axis=1, keys=['df', 'df2'])

mismatch_concat = df_compare[df_compare['df']['b'] != df_compare['df2']['b']]

print("\n▣ 방법 2: concat 방식 (인덱스 기반 비교)")
print(mismatch_concat[['df', 'df2']].stack(level=0).unstack())

# --------------------------
# 방법 3: boolean 인덱싱 (보강 버전)
# --------------------------
# 불일치하는 a 값 찾기
mismatch_idx = ~df.set_index('a')['b'].eq(df2.set_index('a')['b']).reindex(df['a']).values

# 원본 df에서 불일치 행 추출 + df2의 b 값 추가
result = df[mismatch_idx].copy()
result['df2_b'] = result['a'].map(df2.set_index('a')['b'])

print("\n▣ 방법 3: boolean 인덱싱 방식 (df 기준 + df2 값 매핑)")
print(result[['a', 'b', 'df2_b']].rename(columns={'b': 'df_b'}))

성능체크

import pandas as pd
import numpy as np
import timeit

# 큰 데이터프레임 생성 (100만 행)
np.random.seed(42)
size = 1_000_000
df_large = pd.DataFrame({
    'a': np.arange(size),
    'b': np.random.choice(['A', 'B', 'C', 'D'], size),
    'c': np.random.randint(1, 100, size)
})

df2_large = df_large.copy()
# 10%의 행을 무작위로 변경
changed_indices = np.random.choice(size, size//10, replace=False)
df2_large.loc[changed_indices, 'b'] = np.random.choice(['X', 'Y', 'Z'], len(changed_indices))

# 방법 1: merge
def method_merge():
    mismatch = pd.merge(df_large, df2_large, on=['a'], how='outer', suffixes=('', '_df2'))
    return mismatch[mismatch['b'] != mismatch['b_df2']]

# 방법 2: concat
def method_concat():
    df_compare = pd.concat([df_large.set_index('a'), df2_large.set_index('a')], axis=1, keys=['df1', 'df2'])
    return df_compare[df_compare['df1']['b'] != df_compare['df2']['b']]

# 방법 3: boolean 인덱싱
def method_boolean():
    return df_large[~df_large.set_index('a')['b'].eq(df2_large.set_index('a')['b']).reindex(df_large['a']).values]

# 성능 측정
print("merge 방식:", timeit.timeit(method_merge, number=3))
print("concat 방식:", timeit.timeit(method_concat, number=3))
print("boolean 방식:", timeit.timeit(method_boolean, number=3))

'랭귀지 > pandas' 카테고리의 다른 글

type 함수  (0) 2025.04.03
errors='coerce'로 NaT 변환 후 처리  (0) 2025.04.01
index.duplicated() 메서드 사용  (0) 2025.03.31
pandas_profiling (대규모 데이터 분석)  (0) 2025.03.28
메모리 사용량을 상세히 분석  (0) 2025.03.28
Comments