Skip to content

데이터 전처리


학습 목표

  1. 결측치(NaN)를 탐색하고 처리하는 방법을 이해한다
  2. 오브젝트 타입을 수치형으로 변환하는 방법을 익힌다
  3. 문자열 메서드(str.replace, str.strip)로 텍스트를 정제할 수 있다
  4. 불필요한 컬럼을 제거하여 메모리를 절약하는 방법을 이해한다
  5. 데이터 단위 변환과 새 컬럼 생성을 할 수 있다

전체 구조


1. 결측치(NaN) 탐색

결측치는 NaN(Not a Number)으로 표시되며, 데이터가 없는 상태를 의미한다.

결측치 확인 방법

python
import pandas as pd

df_last = pd.read_csv("data/분양가격.csv", encoding="cp949")

# 방법 1: isnull()로 True/False 확인
df_last.isnull()

# 방법 2: isnull().sum()으로 컬럼별 결측치 개수
df_last.isnull().sum()

# 방법 3: isna()도 동일한 기능
df_last.isna().sum()

출력 예시:

지역명           0
규모구분          0
연도             0
월              0
분양가격(제곱미터)  277

결측치 필터링

python
# 결측치가 없는 데이터만 가져오기
price = df_last.loc[df_last["평당분양가격"].notnull(), "평당분양가격"]

# 또는 DataFrame 전체에서 특정 컬럼 기준
df_not_null = df_last[df_last["평당분양가격"].notnull()]

2. 타입 변환 - to_numeric

분양가격이 숫자처럼 보이지만 object 타입인 경우가 있다. 공백 문자가 섞여 있으면 astype(int)로 직접 변환할 수 없다.

python
# astype으로 변환 시도 → ValueError 발생
# df_last["분양가격(제곱미터)"].astype(int)

# to_numeric으로 강제 변환 (변환 불가 값은 NaN 처리)
df_last["분양가격"] = pd.to_numeric(
    df_last["분양가격(제곱미터)"],
    errors="coerce"  # 변환 불가 → NaN
)

# 결과: float64 타입 (NaN이 float이므로)
print(df_last["분양가격"].dtype)
# float64

errors 옵션 정리: "raise"는 에러 발생, "coerce"는 NaN으로 강제 변환, "ignore"는 변환 불가 값을 원본 그대로 유지한다. 실무에서는 "coerce"를 가장 많이 사용한다.

NaN이 float인 이유

python
import numpy as np

print(type(np.nan))
# <class 'float'>

NaN은 float 타입이기 때문에, NaN이 포함된 컬럼은 자동으로 float64로 변환된다.


3. 문자열 정제 - str 메서드

str.replace로 텍스트 치환

Series.replace는 완전 일치만 치환하지만, str.replace는 부분 문자열도 치환할 수 있다.

python
# 원본: "전용면적 60초과 85이하"

# Series.replace → 완전 일치만 (동작 안 함)
df_last["규모구분"].replace("전용면적", "")  # 변화 없음

# str.replace → 부분 문자열 치환 (동작함)
df_last["전용면적"] = df_last["규모구분"].str.replace("전용면적", "")
df_last["전용면적"] = df_last["전용면적"].str.replace("초과", "~")
df_last["전용면적"] = df_last["전용면적"].str.replace("이하", "")

str.strip으로 공백 제거

python
# 앞뒤 공백 제거
df_last["전용면적"] = df_last["전용면적"].str.strip()

# 중간 공백도 제거
df_last["전용면적"] = df_last["전용면적"].str.replace(" ", "")

4. 단위 변환과 새 컬럼 생성

데이터셋마다 단위가 다를 수 있다. 제곱미터당 가격을 평당 가격으로 변환하는 예시:

python
# 제곱미터 → 평 (1평 = 3.3제곱미터)
df_last["평당분양가격"] = df_last["분양가격"] * 3.3

# 확인
df_last.head(1)

주의: 새 값을 DataFrame에 남기려면 연산 결과를 컬럼에 다시 할당해야 한다. df["컬럼"] * 3.3만 실행하면 계산 결과는 보이지만 원본 컬럼은 바뀌지 않는다.


5. 불필요한 컬럼 제거 - drop

python
# 제거 전 메모리 확인
print(df_last.info())  # 271 KB

# 컬럼 제거 (axis=1 필수)
df_last = df_last.drop(["규모구분", "분양가격(제곱미터)"], axis=1)

# 제거 후 메모리 확인
print(df_last.info())  # 203 KB

핵심 주의사항

  1. axis=0은 행 삭제(기본값), axis=1은 열 삭제
  2. 보통 결과를 다시 변수에 할당한다: df = df.drop(...)
  3. 사용하지 않는 컬럼 제거는 메모리 절약에 효과적이다

6. 결측치 시각화와 처리 전략

python
# 결측치가 많은 컬럼 순으로 정렬
null_counts = df.isnull().sum()
null_sorted = null_counts.sort_values(ascending=False)
null_sorted.head(10)

# 결측치 시각화
null_sorted.plot.barh(figsize=(7, 8))

결측치 처리 전략

python
# 결측치 행 제거
df_clean = df.dropna(subset=["분양가격"])

# 평균값으로 대체
df["분양가격"] = df["분양가격"].fillna(df["분양가격"].mean())

# 특정 값으로 대체
df["분양가격"] = df["분양가격"].fillna(0)

핵심 정리

  • **isnull().sum()**으로 컬럼별 결측치 개수를 빠르게 파악한다
  • **pd.to_numeric(errors="coerce")**로 문자열을 숫자로 안전하게 변환한다
  • str.replace는 부분 문자열을, Series.replace는 완전 일치를 치환한다
  • 새 컬럼 생성: df["새컬럼"] = df["기존컬럼"] * 3.3
  • **drop(axis=1)**로 불필요 컬럼을 제거하고, 보통 결과를 다시 할당한다
  • 메모리 사용량은 불필요 컬럼 제거만으로도 크게 줄어든다