데이터 분석 과정에서 결측치(Missing Value) 처리는 가장 기본적이면서도 필수적인 단계 중 하나입니다. 결측치는 데이터 수집 과정에서 누락되거나 잘못 기록된 값으로, 그대로 방치할 경우 분석 결과에 왜곡을 초래하거나 모델 성능을 저하시킬 수 있기 때문에 반드시 적절히 다뤄야 합니다. 하지만 결측치 처리에 관련된 통계 이론이나 복잡한 방법론들은 초보자나 실무자에게 부담이 될 수 있습니다.
이번 가이드는 그런 복잡한 이론을 배제하고, 현업에서 가장 많이 사용되고 검증된 안전한 Pandas 라이브러리 기반의 결측치 처리 방법에 초점을 맞췄습니다. 특히 ‘바로 써먹을 수 있는 실용적인 루틴’과 ‘5가지 대표적인 결측치 처리 패턴’을 중심으로 구성하여, 데이터 분석 업무를 수행하는 누구나 손쉽게 적용할 수 있도록 체계적이고 깔끔하게 정리했습니다.
초보자부터 경험자까지 폭넓게 참고할 수 있도록, 각 패턴별 적용 상황과 장단점도 함께 안내해 드려 실제 프로젝트에 즉시 활용 가능하며, 분석 품질과 신뢰도를 높이는 데 큰 도움을 드릴 것입니다. 이 가이드만 있으면 결측치 때문에 고민하던 시간을 줄이고, 데이터 전처리 단계를 빠르고 정확하게 완성할 수 있을 것입니다.
시작 3분 루틴 (가장 쉽고 안전한 기본기)
결측치를 처리하기 전에, 먼저 데이터의 상태를 파악하는 것이 중요합니다.
- 결측치 확인:
df.isna().sum()
을 사용해 각 컬럼에 결측치가 얼마나 있는지 확인하고,df.isna().mean()
으로 전체 대비 결측 비율을 파악합니다. - 영향도 판단:
- 수치형 데이터는 평균(
mean
)이나 중앙값(median
)으로 대체하는 것이 일반적입니다. - 범주형 데이터는 최빈값(
mode
)이나'Unknown'
같은 새로운 라벨로 대체하는 것이 안전합니다.
- 수치형 데이터는 평균(
- 처리 방식 결정:
- 행 제거: 결측치가 전체 데이터에서 소수이거나, 특정 행의 결측치가 너무 많아 복구가 불가능할 때 사용합니다.
- 단순 대체: 평균, 중앙값, 최빈값 등 대표값으로 빈 값을 채웁니다.
- 그룹별 대체: 특정 그룹(예: ‘지역’별) 내에서 대표값으로 채우면 데이터 왜곡을 줄일 수 있습니다.
- 시계열 보간: 날짜/시간 데이터는 앞뒤 값의 경향을 따라 값을 채우는 보간(
interpolate
) 방식을 사용합니다.
- 처리 후 검증:
df.isna().sum()
으로 결측치가 모두 사라졌는지 확인하고,df.describe()
나df.value_counts()
로 값의 분포가 이상하지 않은지 다시 한번 점검합니다.
가장 많이 쓰는 5가지 패턴 (코드 포함)
아래 5가지 패턴은 Pandas로 결측치를 처리할 때 가장 많이 사용되는 실전 코드입니다.
- 행/열 단순 제거:
dropna()
df.dropna()
: 결측치가 하나라도 있는 행 전체를 제거합니다.df.dropna(subset=['col1', 'col2'])
:col1
또는col2
에 결측치가 있는 행만 제거합니다.df.dropna(axis=1)
: 결측치가 있는 열을 제거합니다.
- 수치형 단순 대체:
fillna()
df['age'].fillna(df['age'].median(), inplace=True)
:age
컬럼의 결측치를 중앙값으로 채웁니다. 이상치에 덜 민감해 평균보다 안전합니다.df['price'].fillna(df['price'].mean(), inplace=True)
:price
컬럼을 평균값으로 채웁니다.
- 범주형 대체:
fillna()
mode_val = df['city'].mode().iat[0]
df['city'].fillna(mode_val, inplace=True)
:city
컬럼의 결측치를 최빈값으로 채웁니다.df['gender'].fillna('Unknown', inplace=True)
:gender
컬측치를'Unknown'
이라는 새로운 라벨로 채웁니다.
- 그룹별 대표값으로 채우기 (권장):
groupby()
와transform()
df['sales'] = df.groupby('category')['sales'].transform(lambda s: s.fillna(s.median()))
category
별로 그룹을 묶은 뒤, 각 그룹의 중앙값으로 결측치를 채웁니다. 데이터의 왜곡을 최소화할 수 있는 매우 효과적인 방법입니다.
- 시계열 데이터 보간:
interpolate()
df['temp'] = df['temp'].interpolate(method='linear')
:temp
컬럼의 결측치를 선형 보간하여 채웁니다.df['temp'] = df['temp'].ffill().bfill()
: 결측치를 바로 앞의 값(ffill
)이나 바로 뒤의 값(bfill
)으로 채웁니다. 시계열 데이터의 추세를 반영할 수 있어 유용합니다.
현업에서 바로 쓰는 미니 레시피
복잡한 데이터에서도 결측치를 일괄적으로 처리할 수 있는 파이프라인 예시입니다.
- 결측 비율 기준 열 일괄 제거:
Python
na_ratio = df.isna().mean() drop_cols = na_ratio[na_ratio > 0.6].index # 결측 비율 60% 이상인 열 제거 df = df.drop(columns=drop_cols)
- 수치형/범주형 컬럼 일괄 채우기:
Python
num_cols = df.select_dtypes(include='number').columns cat_cols = df.select_dtypes(exclude='number').columns df[num_cols] = df[num_cols].apply(lambda s: s.fillna(s.median())) for c in cat_cols: mode_val = df[c].mode().iat[0] if not df[c].mode().empty else 'Unknown' df[c] = df[c].fillna(mode_val)
미니 예제 (끝까지 돌아가는 코드)
아래 코드는 결측치가 포함된 데이터프레임을 생성하고, 위에서 배운 방법을 적용해 결측치를 처리하는 전체 과정입니다.
Python
import pandas as pd
# 1. 예제 데이터 생성
data = {
'date': pd.date_range('2025-08-01', periods=5, freq='D'),
'category': ['A', 'A', 'B', 'B', None],
'sales': [10, None, 30, None, 50],
'city': ['서울', None, '부산', '부산', None]
}
df = pd.DataFrame(data)
print("원본 데이터프레임:\n", df)
print("\n결측치 개수:\n", df.isna().sum())
# 2. 범주형 결측치 처리 (category 컬럼)
df['category'] = df['category'].fillna('Unknown')
print("\ncategory 컬럼 처리 후:\n", df)
# 3. 그룹별 수치형 결측치 처리 (sales 컬럼)
df['sales'] = df.groupby('category')['sales'].transform(lambda s: s.fillna(s.median()))
print("\nsales 컬럼 처리 후:\n", df)
# 4. 범주형 결측치 처리 (city 컬럼)
mode_city = df['city'].mode()
df['city'] = df['city'].fillna(mode_city.iat[0] if not mode_city.empty else 'Unknown')
print("\ncity 컬럼 처리 후:\n", df)
print("\n최종 결측치 개수:\n", df.isna().sum())
Disclaimer: 본 블로그의 정보는 개인의 단순 참고 및 기록용으로 작성된 것이며, 개인적인 조사와 생각을 담은 내용이기에 오류가 있거나 편향된 내용이 있을 수 있습니다.