BioPlayground

🧬
목록으로

데이터 정제 — 지저분한 데이터를 쓸 수 있게

실전 데이터의 결측값, 중복, 이상치를 어떻게 찾고 처리하는지 pandas로 배웁니다.

입문
|
8
|
검증 완료 (2026-07)
데이터 정제결측값이상치중복 제거데이터 품질
진행률0/6 (0%)

데이터 정제 — 지저분한 데이터를 쓸 수 있게

이 토픽을 마치면

실전 데이터에서 흔히 발생하는 결측값, 중복, 이상치 문제를 식별하고 처리할 수 있습니다.


왜 데이터 정제가 필요한가

현실 세계의 데이터는 깨끗하지 않습니다. 설문 응답이 빠져 있고, 같은 사람이 두 번 입력되어 있고, 나이가 -5살인 행이 있습니다. 이런 데이터를 그대로 분석하면 결과를 신뢰할 수 없습니다.

데이터 분석 업무의 60~80%는 정제에 들어간다고 합니다. 화려한 분석 기법보다 이 단계가 훨씬 중요합니다.


1단계: 데이터 전체 파악

python
import pandas as pd
df = pd.read_csv("patients.csv")
# 기본 정보
print(df.shape) # (1000, 8) — 1000행, 8열
print(df.dtypes) # 각 열의 데이터 타입
print(df.info()) # 결측값 현황 포함한 요약
# 첫/끝 확인
print(df.head()) # 상위 5행
print(df.tail()) # 하위 5행
# 통계 요약
print(df.describe()) # 수치형 열의 평균, 표준편차, 최소/최대 등

df.info()를 실행하면 Non-Null Count가 나옵니다. 전체 행 수보다 적으면 결측값이 있다는 뜻입니다. df.describe()에서 min/max가 상식적 범위를 벗어나면 이상치를 의심합니다.


2단계: 결측값 처리

python
# 결측값 확인
print(df.isnull().sum())
# name 0
# age 15
# blood_type 3
# weight 42
# 전략 1: 삭제 — 결측 행이 소수일 때
df_clean = df.dropna(subset=["blood_type"]) # blood_type 없는 3행 삭제
# 전략 2: 채우기 — 수치형 열
df["age"] = df["age"].fillna(df["age"].median()) # 중앙값으로
df["weight"] = df["weight"].fillna(df["weight"].mean()) # 평균으로
# 전략 3: 특정 값으로 채우기
df["blood_type"] = df["blood_type"].fillna("미확인")

어떤 전략을 쓸지는 맥락에 따라 다릅니다.

  • 결측 비율이 5% 미만이면 삭제가 가장 안전합니다
  • 수치형은 중앙값(이상치에 강함) 또는 평균으로 채웁니다
  • 범주형(혈액형, 성별)은 최빈값이나 "미확인"으로 채웁니다

3단계: 중복 제거

python
# 중복 확인
print(df.duplicated().sum()) # 23개 중복행
# 중복 내용 보기
print(df[df.duplicated(keep=False)]) # 원본 + 중복 모두 표시
# 중복 제거 (첫 번째만 유지)
df = df.drop_duplicates()
# 특정 열 기준 중복 제거
df = df.drop_duplicates(subset=["patient_id"], keep="last")

keep="first"는 첫 번째 행을 유지하고 나머지를 삭제합니다. keep="last"는 마지막을 유지합니다. 데이터가 시간순이라면 last가 최신 기록을 남깁니다.


4단계: 이상치 탐지

python
# 기본 통계로 확인
print(df["age"].describe())
# min: -5 ← 비정상
# max: 200 ← 비정상
# 범위 필터링
df = df[(df["age"] >= 0) & (df["age"] <= 120)]
# IQR 방식 — 통계적 이상치 탐지
Q1 = df["weight"].quantile(0.25)
Q3 = df["weight"].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df["weight"] < lower) | (df["weight"] > upper)]
print(f"이상치 {len(outliers)}개 발견")
# 이상치 제거
df = df[(df["weight"] >= lower) & (df["weight"] <= upper)]

이상치를 무조건 삭제하면 안 됩니다. 나이 -5는 명백한 오류이므로 삭제하지만, 체중 150kg은 실제로 존재할 수 있습니다. 도메인 지식이 이상치 판단의 핵심입니다.


5단계: 타입 변환과 형식 통일

python
# 문자열로 된 숫자 → 숫자형으로
df["age"] = pd.to_numeric(df["age"], errors="coerce")
# 날짜 문자열 → datetime
df["visit_date"] = pd.to_datetime(df["visit_date"])
# 문자열 정리 — 앞뒤 공백, 대소문자 통일
df["name"] = df["name"].str.strip()
df["blood_type"] = df["blood_type"].str.upper()

errors="coerce"는 변환 불가능한 값(예: "N/A")을 NaN으로 처리합니다. 에러를 내지 않고 진행할 수 있어서 대량 데이터에 유용합니다.


핵심 정리

단계확인 사항주요 도구
전체 파악행/열 수, 타입, 결측 현황info(), describe()
결측값비율 확인 → 삭제 or 채우기isnull(), fillna(), dropna()
중복동일 행 또는 키 기준 중복duplicated(), drop_duplicates()
이상치상식 범위 + IQRdescribe(), 범위 필터링
타입/형식숫자, 날짜, 문자열 통일to_numeric(), to_datetime(), str.strip()

데이터 정제는 반복적이고 지루한 작업이지만, 이 단계를 건너뛰면 이후의 모든 분석 결과가 흔들립니다. "Garbage in, garbage out" — 입력이 쓰레기면 출력도 쓰레기입니다.