[에러 해결] [Pandas] ‘AttributeError’ (리스트/딕셔너리 객체에 .items() 사용 오류)와 효율적인 데이터 매핑 방법

개발 과정에서 Pandas DataFrame의 특정 컬럼 값을 다른 데이터셋의 값으로 매핑해야 할 때가 있습니다. 이때 파이썬의 기본 자료구조와 Pandas 객체의 차이를 혼동하거나, 비효율적인 반복문을 사용하면 예상치 못한 오류에 직면할 수 있습니다. 특히, AttributeError는 이러한 혼동에서 자주 발생합니다.

이번 글에서는 스택오버플로우 질문을 통해 접수된 문제 상황을 분석하고, AttributeError의 발생 원인을 명확히 파악하며, Pandas의 강력한 기능을 활용하여 데이터를 효율적으로 매핑하는 방법을 자세히 설명합니다.

1. 에러 발생 상황

사용자는 'train'이라는 데이터셋의 ‘Address’ 컬럼에 있는 주소 값을, 'Address_df_2'라는 다른 데이터셋의 ‘Valued_Address’ 컬럼에 있는 인코딩된 값으로 매핑하려는 목표를 가지고 있습니다. 즉, train 데이터셋의 주소들을 Address_df_2에 정의된 매핑 규칙에 따라 숫자 값으로 바꾸고자 합니다.

이를 위해 아래와 유사한 코드를 작성했습니다.

import pandas as pd

# 'a', 'b', 'c'는 편의상 문자열 변수로 가정합니다.
a, b, c = 'Street A', 'Street B', 'Street C'

train={
'Address':[a,b,a,c]
}
Address_df_2={
'Address':[a,b,c],
'Valued_Address':[1,2,3]
}

for item,id_ in train.Address.items(): # 문제의 코드
    for item2,val in Address_df_2['Address'].items():
        if id_==val:
           print(format(item),'',format(item2)) 
           print(train['Address'][format(item)],'--',Address_df_2['Valued_Address'][format(item2)])
           train.Address[format(item)]=Address_df_2.Valued_Address[format(item2)]
        else:
          i=i+1 # 'i'가 초기화되지 않아 NameError 발생 가능

이 코드에서 사용자는 train.Address.items()와 같은 방식으로 반복문을 시도하며, item 변수가 인덱스를, id_ 변수가 값을 나타낼 것으로 기대합니다. 그러나 실제 실행 시 여러 가지 문제가 발생하며, 특히 AttributeError와 같은 오류에 직면하게 됩니다.

2. 명확한 발생 원인

위 코드에서 발생하는 주요 문제는 파이썬의 기본 딕셔너리와 리스트를 Pandas DataFrame/Series 객체처럼 다루려고 시도하면서 발생합니다.

2.1. Pandas 객체 사용 오류 (AttributeError)

사용자가 제시한 코드에서 trainAddress_df_2는 파이썬의 딕셔너리입니다. 파이썬 딕셔너리는 train['Address']와 같이 키-값을 통해 접근해야 합니다. 그러나 코드에서는 train.Address.items()와 같이 점(.) 연산자를 사용하여 Address라는 ‘속성’에 접근하려 합니다. 파이썬 딕셔너리에는 Address라는 속성이 없으므로, 이 부분에서 AttributeError: 'dict' object has no attribute 'Address'가 발생합니다.

만약 train['Address']로 올바르게 접근하더라도, train['Address']는 단순히 [a, b, a, c]와 같은 파이썬 ‘리스트’입니다. 리스트에는 .items()라는 메서드가 존재하지 않습니다. 따라서 train['Address'].items()를 시도할 경우 AttributeError: 'list' object has no attribute 'items'가 발생하게 됩니다.

.items() 메서드는 파이썬 딕셔너리나 Pandas Series/DataFrame에서 (인덱스, 값) 또는 (키, 값) 쌍을 얻을 때 사용됩니다. 리스트의 경우 인덱스와 값을 함께 얻으려면 일반적으로 enumerate() 함수를 사용해야 합니다.

2.2. 비효율적이고 잘못된 인덱싱 방식

중첩된 for 루프는 Pandas와 같은 데이터 처리 라이브러리에서는 매우 비효율적인 접근 방식입니다. 특히 대량의 데이터를 처리할 때는 성능 저하의 주범이 됩니다.

또한, print(format(item))이나 train['Address'][format(item)]과 같이 format() 함수를 사용하여 변수를 문자열로 변환하는 방식은 불필요하거나 오해의 소지가 있습니다. 만약 item이 숫자인덱스(예: 0, 1, 2)라면 format(item)은 단순히 ‘0’, ‘1’, ‘2’와 같은 문자열을 반환합니다. 파이썬 리스트는 정수 인덱스로 접근하는 것이 일반적이므로, 문자열로 변환하는 것은 혼동을 줄 수 있습니다.

3. 해결 방법 및 코드 예시

이 문제의 가장 효율적이고 Pandas스러운 해결책은 바로 .map() 메서드를 사용하는 것입니다. .map()은 Series의 값들을 다른 Series, 딕셔너리 또는 함수를 사용하여 매핑할 때 사용되는 강력한 기능입니다.

3.1. Pandas DataFrame으로 데이터 초기화

먼저, 데이터를 Pandas DataFrame으로 올바르게 초기화해야 합니다.

import pandas as pd

# 'a', 'b', 'c'는 문자열 리터럴로 직접 사용합니다.
train_df = pd.DataFrame({
    'Address': ['Street A', 'Street B', 'Street A', 'Street C']
})

# 매핑 정보를 담을 DataFrame도 생성합니다.
address_mapping_df = pd.DataFrame({
    'Address': ['Street A', 'Street B', 'Street C'],
    'Valued_Address': [1, 2, 3]
})

print("초기 train_df:")
print(train_df)
print("\n매핑 정보 address_mapping_df:")
print(address_mapping_df)

3.2. .map() 메서드를 사용한 해결

이제 .map() 메서드를 사용하여 train_df의 ‘Address’ 컬럼 값을 address_mapping_df의 ‘Valued_Address’로 변환할 수 있습니다.

.map() 메서드는 매핑에 사용할 딕셔너리 또는 Series를 인자로 받습니다. 우리는 address_mapping_df에서 ‘Address’를 키로, ‘Valued_Address’를 값으로 하는 Series를 생성하여 매핑 딕셔너리로 활용할 수 있습니다.

# 1. 매핑을 위한 Series 생성 (Address를 인덱스로, Valued_Address를 값으로)
address_value_map = address_mapping_df.set_index('Address')['Valued_Address']

print("\n생성된 매핑 Series (address_value_map):")
print(address_value_map)

# 2. train_df의 'Address' 컬럼에 .map() 적용
train_df['Encoded_Address'] = train_df['Address'].map(address_value_map)

print("\n매핑 후 train_df:")
print(train_df)

이 코드는 train_df의 각 ‘Address’ 값을 address_value_map에서 찾아 해당하는 ‘Valued_Address’로 대체합니다. 매핑되지 않는 값(예: address_value_map에 없는 주소)이 있다면 기본적으로 NaN으로 처리됩니다.

만약 ‘Address’ 컬럼 자체를 변경하고 싶다면 train_df['Address'] = train_df['Address'].map(address_value_map)와 같이 사용하면 됩니다.

4. 향후 예방을 위한 팁

  • 파이썬 기본 자료구조와 Pandas 객체 구분 명확히 하기

    Pandas를 사용할 때는 파이썬의 list, dict와 Pandas의 Series, DataFrame이 다르다는 점을 항상 인지해야 합니다. 각 객체가 제공하는 메서드와 속성이 다르므로, 작업하려는 데이터가 어떤 타입인지 정확히 파악하는 것이 중요합니다.

  • Pandas 내장 함수 및 메서드 적극 활용

    데이터 조작 및 분석 시 for 루프보다는 .map(), .apply(), .merge(), .groupby() 등 Pandas가 제공하는 벡터화된 연산 및 내장 메서드를 우선적으로 고려하세요. 이는 코드의 효율성을 높이고, 가독성을 개선하며, 잠재적인 오류를 줄이는 가장 좋은 방법입니다.

  • 에러 메시지 꼼꼼히 읽기

    AttributeErrorKeyError와 같은 에러 메시지는 문제의 단서를 제공합니다. “'dict' object has no attribute 'Address'“와 같은 메시지는 해당 객체에 ‘Address’라는 속성이 없다는 것을 명확히 알려주므로, 어떤 객체를 다루고 있는지, 그리고 그 객체에 어떤 메서드나 속성이 있는지 다시 한번 확인하는 습관을 들이세요.

이러한 원칙들을 따른다면 Pandas를 사용하여 데이터를 훨씬 더 효율적이고 안정적으로 처리할 수 있을 것입니다.

댓글 남기기