Skip to main content

자료형 변환

데이터를 올바르게 분석하려면 각 열의 자료형(dtype)이 데이터의 성격에 맞아야 합니다. 숫자로 저장된 문자열, 잘못된 float 타입의 정수 열, 메모리를 낭비하는 64비트 타입 등을 적절히 변환하면 연산 정확성과 메모리 효율 모두 개선됩니다.

학습 목표

  • astype으로 기본 자료형을 변환할 수 있다
  • to_numeric, to_datetime으로 안전하게 형 변환을 수행할 수 있다
  • 카테고리형(Categorical)으로 변환하여 메모리를 절약할 수 있다
  • 메모리 사용량을 프로파일링하고 최적화할 수 있다

왜 중요한가

CSV에서 데이터를 읽으면 Pandas가 자동으로 dtype을 추론하지만, 항상 올바르지는 않습니다. 숫자가 문자열로 읽히면 수학 연산이 불가능하고, 카테고리 데이터가 object로 저장되면 불필요한 메모리를 소비합니다. sklearn의 많은 모델은 수치형 입력만 받으므로, 올바른 형 변환은 ML 파이프라인의 필수 단계입니다.

astype 기본 변환

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'age': ['25', '30', '28'],
    'salary': [4000.0, 4500.0, 3800.0],
    'is_active': [1, 0, 1]
})

# 문자열 → 정수
df['age'] = df['age'].astype(int)

# float → int (소수점이 없는 경우만)
df['salary'] = df['salary'].astype(int)

# 정수 → 불리언
df['is_active'] = df['is_active'].astype(bool)

# 메모리 절약: int64 → int32
df['age'] = df['age'].astype('int32')

print(df.dtypes)

to_numeric — 안전한 숫자 변환

# 변환 불가능한 값이 포함된 경우
s = pd.Series(['100', '200', 'N/A', '400', '-'])

# errors='coerce' → 변환 불가 시 NaN
result = pd.to_numeric(s, errors='coerce')
print(result)
# 0    100.0
# 1    200.0
# 2      NaN
# 3    400.0
# 4      NaN

# errors='ignore' → 변환 실패 시 원본 유지 (비권장)
# errors='raise' → 오류 발생 (기본값)

# downcast로 메모리 최적화
result = pd.to_numeric(s, errors='coerce', downcast='integer')
print(result.dtype)  # int16 (값에 맞는 최소 크기)
astype(int)은 NaN이 있으면 오류가 발생합니다. 결측치가 있을 수 있는 열에는 pd.to_numeric(errors='coerce')를 먼저 사용하세요. Pandas 1.0+에서는 pd.Int32Dtype() 등 Nullable Integer를 사용하면 NaN과 정수를 함께 저장할 수 있습니다.

카테고리형 (Categorical)

반복되는 문자열이 많은 열(부서, 등급, 지역 등)은 카테고리형으로 변환하면 메모리를 크게 절약할 수 있습니다.
df = pd.DataFrame({
    'department': ['개발', '마케팅', '개발', '인사', '개발'] * 10000
})

# object → category
print(f"변환 전: {df['department'].memory_usage(deep=True) / 1024:.1f} KB")
df['department'] = df['department'].astype('category')
print(f"변환 후: {df['department'].memory_usage(deep=True) / 1024:.1f} KB")
# 약 10배 이상 절약

# 순서가 있는 카테고리
grade_type = pd.CategoricalDtype(
    categories=['Bronze', 'Silver', 'Gold', 'Platinum'],
    ordered=True
)
df['grade'] = pd.Series(['Gold', 'Silver', 'Bronze']).astype(grade_type)
print(df['grade'].min())  # Bronze (순서 기반)

메모리 프로파일링

df = pd.DataFrame({
    'id': range(100000),
    'name': [f'user_{i}' for i in range(100000)],
    'score': np.random.randn(100000),
    'category': np.random.choice(['A', 'B', 'C'], 100000)
})

# 열별 메모리 사용량
print(df.memory_usage(deep=True))

# 전체 메모리
print(f"전체: {df.memory_usage(deep=True).sum() / 1024**2:.2f} MB")
# 자동 다운캐스트 함수
def optimize_dtypes(df):
    """DataFrame의 dtype을 자동으로 최적화합니다."""
    for col in df.columns:
        col_type = df[col].dtype
        if col_type == 'float64':
            df[col] = pd.to_numeric(df[col], downcast='float')
        elif col_type == 'int64':
            df[col] = pd.to_numeric(df[col], downcast='integer')
        elif col_type == 'object':
            if df[col].nunique() / len(df) < 0.5:  # 고유값 비율 50% 미만
                df[col] = df[col].astype('category')
    return df

AI/ML에서의 활용

  • 피처 전처리: 문자열로 저장된 수치 데이터를 숫자로 변환하여 모델에 입력합니다
  • 메모리 최적화: dtype 다운캐스트로 대규모 데이터셋의 메모리 사용량을 줄입니다
  • 카테고리 인코딩: 카테고리형은 One-Hot/Label 인코딩의 전처리 단계입니다
  • Nullable 타입: Int32, Float32, boolean 등 Nullable 타입으로 결측치를 안전하게 처리합니다
object는 Python 객체(문자열 포함)를 저장하는 범용 타입이고, StringDtype(pd.StringDtype())은 문자열 전용 타입입니다. StringDtype은 결측치를 pd.NA로 처리하며, 문자열 연산에 최적화되어 있습니다.
pd.to_numeric(downcast='integer')는 값의 범위를 확인하고 가장 작은 dtype을 선택합니다. 값이 범위를 벗어나면 다운캐스트하지 않으므로 안전합니다. 다만, 나중에 더 큰 값이 추가될 수 있다면 주의하세요.

체크리스트

  • astype으로 기본 형 변환을 수행할 수 있다
  • to_numeric(errors=‘coerce’)로 안전한 숫자 변환을 할 수 있다
  • 카테고리형 변환으로 메모리를 최적화할 수 있다
  • memory_usage(deep=True)로 메모리 사용량을 확인할 수 있다
  • Nullable 정수 타입의 필요성을 설명할 수 있다

다음 문서