1 개요
Business Understanding에서 풀어야 할 문제를 정의했다면, 다음 질문은 “우리가 가진 데이터가 그 문제를 실제로 풀 수 있는 원재료인가?”이다.
역사 데이터는 대부분 현재 분석 목적과 다른 이유로 수집됐다. 고객 데이터베이스는 청구를 위해 만들어졌고, 거래 로그는 회계 감사를 위해 생성됐다. 이 데이터가 이탈 예측이나 사기 탐지에 적합한지는 별개의 문제다.
Data Understanding 단계는 두 가지를 확인한다:
- 문제-데이터 정합성: 비즈니스 문제에서 정의한 타겟 변수와 피처가 데이터에 실제로 존재하는가?
- 데이터 품질: 존재하더라도 신뢰할 수 있는가?
이 두 질문 중 하나라도 No가 나오면 Business Understanding으로 되돌아가 문제 정의를 수정해야 한다. Data Understanding은 탐색이면서 동시에 검증이다.
2 데이터가 문제 방향을 바꾸는 경우
Data Understanding이 단순히 “데이터를 살펴보는” 단계가 아님을 보여주는 사례가 있다.
신용카드 사기 탐지와 메디케어 사기 탐지는 둘 다 “사기 탐지” 문제로 보인다. 하지만 데이터를 들여다보면 근본적으로 다른 문제다 (Provost & Fawcett, 2013, Ch.2):
| 항목 | 신용카드 사기 | 메디케어 사기 |
|---|---|---|
| 사기범과 정상 이용자 | 별개의 인물 | 동일인 (의료 제공자가 정상 청구자이면서 사기범) |
| 타겟 레이블 | 명확함 (고객이 이의 제기) | 불명확 (사기를 인정하는 제3자 없음) |
| 방법론 | 지도 분류 | 비지도 (프로파일링, 이상 탐지, 클러스터링) |
두 문제는 같은 이름을 가지고 있지만, 데이터 이해 과정에서 전혀 다른 접근법이 필요하다는 것이 드러난다. 겉으로 같아 보이는 문제가 데이터 안에서는 전혀 다른 구조를 가질 수 있다. Business Understanding에서 정의한 문제가 Data Understanding에서 수정되는 것은 실패가 아니라 과정의 일부다.
3 EDA: 탐색적 데이터 분석
EDA(Exploratory Data Analysis, 탐색적 데이터 분석)는 데이터의 구조·패턴·이상·변수 간 관계를 시각화와 요약 통계로 파악하는 과정이다.
EDA는 가설을 생성하고, CDA(Confirmatory Data Analysis, 확증적 분석)는 가설을 검정한다. EDA에서 나온 “고객 연령과 이탈 사이에 패턴이 있어 보인다”는 관찰은 CDA에서 통계 검정으로 확인된다. EDA를 마친 후 CDA 없이 패턴을 사실로 단정하는 것은 오버피팅의 씨앗이다.
3.1 단변량 분석 (Univariate Analysis)
각 변수를 독립적으로 탐색한다. 목적은 변수의 기본 특성을 파악하고 이상을 탐지하는 것이다.
수치형 변수:
| 통계량 | 확인 내용 | 이상 신호 |
|---|---|---|
| 평균 vs 중앙값 | 분포의 왜도 | 평균 >> 중앙값 → 우편향, 이상치 의심 |
| 표준편차 | 분산 수준 | 극단적으로 크면 이상치 존재 |
| 최솟값·최댓값 | 범위 | 음수 나이, 200% 할인율 등 불가능한 값 |
| 왜도(skewness) | 분포 비대칭성 | |왜도| > 1이면 변환 검토 |
| 결측 비율 | 완전성 | > 30%이면 피처 제거 또는 전략 수립 |
범주형 변수:
- 카디널리티(cardinality): 고유값 수가 너무 많으면(예: 사용자 ID) 직접 피처로 쓸 수 없다
- 빈도 분포: 특정 범주에 극단적으로 치우쳐 있지 않은가?
- 예상치 못한 값: “기타” 항목이 50%를 넘거나, 오타로 인한 변종 범주
3.2 이변량 분석 (Bivariate Analysis)
두 변수 간의 관계를 탐색한다.
피처 vs 타겟 변수:
- 수치형 피처 vs 이진 타겟: 이탈 고객과 잔류 고객의 피처 분포를 비교 (박스플롯, KDE 플롯)
- 범주형 피처 vs 이진 타겟: 범주별 이탈률 비교 (막대 그래프, 크로스탭)
- 수치형 피처 vs 수치형 타겟: 산점도, 상관계수
상관계수 해석 시 주의점: 피어슨 상관계수 \(r\) 은 선형 관계만 포착한다. \(r = 0\) 이라도 비선형 관계는 존재할 수 있다. 항상 산점도로 시각적으로 확인한다.
피처 vs 피처:
- 강한 상관관계(multicollinearity)는 선형 모델에서 계수 불안정을 유발한다
- 상관행렬(correlation matrix)로 전체 피처 쌍의 관계를 한 번에 확인한다
3.3 다변량 분석 (Multivariate Analysis)
세 개 이상의 변수 간 관계를 탐색한다.
- 상호작용 효과: “고령 고객 중 고사용량 고객만 이탈률이 높다” 같은 조건부 패턴
- 클러스터 시각화: PCA, t-SNE로 고차원 데이터를 2D에 투영하여 자연적 군집 확인
- 결측 패턴: 결측이 특정 조합의 변수에서 함께 발생하는지 (연관 결측, MCAR vs MAR vs MNAR)
4 데이터 품질 6차원
데이터가 존재한다고 해서 사용 가능한 것은 아니다. 6가지 차원으로 품질을 진단한다.
| 차원 | 정의 | 탐지 방법 | 대응 |
|---|---|---|---|
| 완전성 (Completeness) | 필요한 값이 모두 있는가? | 결측 비율 계산 | 대체(imputation), 제거, 보완 수집 |
| 정확성 (Accuracy) | 값이 실제를 올바르게 반영하는가? | 도메인 규칙 검증, 이상치 탐지 | 원천 데이터 재확인, 수동 검수 |
| 일관성 (Consistency) | 동일 개체가 여러 소스에서 일관되게 표현되는가? | 동일 ID의 속성 값 불일치 탐지 | 레코드 링키지(record linkage), 마스터 데이터 |
| 시의성 (Timeliness) | 데이터가 현재 상태를 반영하는가? | 최근 업데이트 날짜 확인 | 실시간 수집 파이프라인 구축 |
| 유일성 (Uniqueness) | 중복 레코드가 없는가? | 중복 ID, 동일 거래 반복 탐지 | 중복 제거, 중복 발생 원인 차단 |
| 유효성 (Validity) | 값이 허용된 형식·범위 내에 있는가? | 날짜 형식, 음수 연령, 비정상 코드 탐지 | 데이터 수집 단계의 유효성 검사 강화 |
품질 문제가 여러 차원에서 동시에 발견될 때, 대응 우선순위는 다음과 같다:
- 정확성 — 잘못된 값이 모델에 들어가면 예측 자체가 틀린다
- 완전성 — 결측이 많으면 학습에 사용 가능한 샘플이 줄어든다
- 일관성 — 소스 간 불일치는 피처 엔지니어링을 복잡하게 만든다
- 유일성 — 중복 학습 데이터는 모델을 특정 패턴에 과적합시킨다
- 시의성·유효성 — 형식 문제는 파이프라인에서 비교적 쉽게 처리 가능하다
모든 것을 완벽하게 고치려 하면 Data Preparation이 끝나지 않는다. 비즈니스 목적에 결정적인 차원부터 집중한다.
5 레이블 품질 검토
지도학습에서 타겟 변수의 품질은 모든 피처보다 중요하다. 피처가 불완전해도 모델이 학습하지만, 레이블이 잘못되면 모델이 틀린 것을 학습한다.
5.1 클래스 불균형 (Class Imbalance)
이진 분류에서 두 클래스의 비율이 극단적으로 다른 경우다.
| 불균형 수준 | 비율 예시 | 문제 | 대응 전략 |
|---|---|---|---|
| 경미 | 70:30 | 거의 없음 | 기본 모델로 시작 |
| 중간 | 90:10 | 정확도 역설 | 층화 샘플링, 클래스 가중치 |
| 심각 | 99:1 (사기 탐지) | 소수 클래스 무시 | SMOTE, focal loss, 언더샘플링 |
| 극심 | 99.9:0.1 (의료 희귀 질환) | 소수 클래스 학습 불가 | 이상 탐지, 비지도 접근 검토 |
정확도 역설(Accuracy Paradox): 99%가 정상인 데이터에서 “항상 정상”으로 예측하면 정확도 99%가 나온다. 그러나 이 모델은 사기를 하나도 잡지 못한다. 클래스 불균형이 심할 때 정확도(accuracy)는 모델 품질 지표로 적합하지 않다. Precision, Recall, F1-score, AUC-ROC를 사용한다.
5.2 레이블 노이즈 (Label Noise)
레이블이 체계적 또는 무작위로 잘못 붙어 있는 경우다.
- 무작위 노이즈: 어노테이터의 실수, 측정 오류 → 데이터가 충분하면 어느 정도 허용 가능
- 체계적 노이즈: 특정 조건에서 레이블이 일관되게 틀림 → 모델이 체계적으로 잘못된 패턴을 학습
체계적 노이즈의 예: 의료 기록에서 “입원” 여부를 치료 성공의 레이블로 쓸 경우, 경증 환자는 외래 치료로 성공해도 레이블이 없고, 중증 환자는 입원 후 사망해도 “입원” 레이블이 붙어 “치료 성공”으로 오분류될 수 있다.
6 데이터 충분성 평가
데이터가 존재하고 품질이 양호해도, 양이 충분한지 확인해야 한다.
6.1 샘플 수 vs 모델 복잡도
일반적 경험칙: - 단순 선형 모델: 피처당 10~20개 샘플 - 트리 기반 모델(Random Forest, XGBoost): 수천 ~ 수만 개 - 딥러닝: 수십만 ~ 수백만 개 (도메인과 태스크에 따라 큰 차이)
이 수치는 엄격한 규칙이 아니다. 학습 곡선(learning curve)으로 실제 확인한다. 학습 데이터를 늘릴수록 검증 성능이 계속 향상되면 더 많은 데이터가 필요하다는 신호다.
6.2 클래스별 충분성
전체 샘플 수가 충분해도, 소수 클래스의 샘플 수가 부족하면 해당 클래스 예측 성능이 낮다.
- 분류 문제에서 클래스당 최소 수백 개 이상을 목표로 한다
- 부족하면: 추가 수집, 데이터 증강, 전이 학습, 비지도 접근 검토
6.3 시간적 충분성
시계열 데이터에서는 데이터가 많아도 기간이 중요하다. 계절성이 있는 문제(예: 쇼핑몰 매출)에서 1년치 미만의 데이터는 계절 패턴을 학습하지 못한다.
7 코드 예시
Data Understanding의 핵심 EDA 작업을 파이썬으로 구현한다.
7.1 Step 1: 순수 Python 구현 — 기술 통계
def describe_column(data, col):
"""단변량 기술 통계를 계산한다."""
values = [x for x in data[col] if x is not None]
n = len(values)
n_missing = len(data[col]) - n
mean = sum(values) / n
sorted_v = sorted(values)
median = sorted_v[n // 2] if n % 2 == 1 else (sorted_v[n//2 - 1] + sorted_v[n//2]) / 2
variance = sum((x - mean) ** 2 for x in values) / (n - 1)
std = variance ** 0.5
skewness = (sum((x - mean) ** 3 for x in values) / n) / (std ** 3)
return {
"n": n,
"missing_rate": n_missing / (n + n_missing),
"mean": round(mean, 4),
"median": round(median, 4),
"std": round(std, 4),
"min": min(values),
"max": max(values),
"skewness": round(skewness, 4),
}
# 왜도가 1보다 크면 로그 변환 검토
stats = describe_column(df_dict, "monthly_charges")
if abs(stats["skewness"]) > 1:
print(f"왜도 {stats['skewness']:.2f} → 로그 변환 검토")
if stats["missing_rate"] > 0.3:
print(f"결측 {stats['missing_rate']:.1%} → 피처 제거 또는 대체 전략 필요")7.2 Step 2: pandas/seaborn 구현 — 실무 EDA
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# ── 1. 기본 개요 ──────────────────────────────────────────────
print("Shape:", df.shape)
print("\n결측 비율:")
print((df.isnull().mean() * 100).sort_values(ascending=False).head(10).round(2))
# ── 2. 수치형 변수 분포 ───────────────────────────────────────
numeric_cols = df.select_dtypes(include="number").columns
fig, axes = plt.subplots(len(numeric_cols), 2, figsize=(12, len(numeric_cols) * 3))
for i, col in enumerate(numeric_cols):
# 히스토그램 — 분포 모양 확인
axes[i, 0].hist(df[col].dropna(), bins=40, edgecolor="white")
axes[i, 0].set_title(f"{col} — 분포")
axes[i, 0].axvline(df[col].mean(), color="red", linestyle="--", label="평균")
axes[i, 0].axvline(df[col].median(), color="blue", linestyle="--", label="중앙값")
axes[i, 0].legend()
# 박스플롯 — 이상치 탐지
axes[i, 1].boxplot(df[col].dropna(), vert=False)
axes[i, 1].set_title(f"{col} — 이상치")
plt.tight_layout()
plt.show()
# ── 3. 타겟 변수 vs 피처 관계 (이탈 예측 예시) ──────────────
target = "churn"
fig, axes = plt.subplots(1, len(numeric_cols), figsize=(6 * len(numeric_cols), 4))
for i, col in enumerate(numeric_cols):
for label, group in df.groupby(target)[col]:
axes[i].hist(group.dropna(), bins=30, alpha=0.5, label=f"churn={label}")
axes[i].set_title(f"{col} by {target}")
axes[i].legend()
plt.tight_layout()
plt.show()
# ── 4. 상관행렬 — 다중공선성 확인 ────────────────────────────
corr = df[numeric_cols].corr()
mask = np.triu(np.ones_like(corr, dtype=bool)) # 상삼각 마스킹
plt.figure(figsize=(10, 8))
sns.heatmap(corr, mask=mask, annot=True, fmt=".2f",
cmap="coolwarm", center=0, vmin=-1, vmax=1)
plt.title("상관행렬 — |r| > 0.8이면 다중공선성 의심")
plt.tight_layout()
plt.show()
# ── 5. 클래스 불균형 확인 ────────────────────────────────────
target_counts = df[target].value_counts()
ratio = target_counts.min() / target_counts.max()
print(f"\n클래스 분포:\n{target_counts}")
print(f"소수:다수 비율 = {ratio:.3f}")
if ratio < 0.1:
print("심각한 클래스 불균형 — SMOTE, focal loss, 재샘플링 검토")8 Data Understanding 체크리스트
Data Understanding 단계를 완료했다고 판단하는 기준:
9 결측치 패턴 분류 (MCAR / MAR / MNAR)
결측치가 있다는 사실만큼 중요한 것은 왜 결측이 발생했는가다. 결측 메커니즘에 따라 처리 방법이 달라진다.
9.1 MCAR (Missing Completely At Random)
결측이 다른 어떤 변수와도 관련이 없는 순수한 랜덤 결측이다.
- 예: 실험실 기계 오작동으로 무작위 샘플의 측정값이 빠짐
- 특성: 결측 데이터를 제거해도 편향이 생기지 않는다
- 확인 방법: Little’s MCAR test (귀무가설: MCAR)
- 처리: 완전 제거(listwise deletion) 또는 단순 대체 가능
9.2 MAR (Missing At Random)
결측이 다른 관측된 변수에 의존하지만, 결측된 변수 자체의 값에는 의존하지 않는 경우다. 이름과 달리 “랜덤”이 아니다.
- 예: 소득 데이터에서 고령 응답자일수록 소득 항목을 더 자주 건너뜀 (연령이 결측을 설명)
- 특성: 관측된 다른 변수를 조건으로 했을 때 결측이 랜덤 → 다중 대체(MICE) 가능
- 처리: 다중 대체법(Multiple Imputation), 모델 기반 대체
9.3 MNAR (Missing Not At Random)
결측이 결측된 값 자체와 관련된 경우다. 가장 까다롭다.
- 예: 소득이 매우 높거나 매우 낮은 사람일수록 소득 항목을 안 적음
- 특성: 단순 대체나 다중 대체가 편향을 제거하지 못한다
- 처리: 결측 자체를 별도 피처로 추가 (
is_missing인디케이터), 도메인 전문가 개입
MNAR인 데이터를 평균으로 대체하면, 극단값이 많이 결측된 경우 평균이 왜곡된다. 예를 들어 초고소득자의 소득이 결측인데 전체 평균으로 대체하면, 모델이 “고소득 → 소득 정보 없음”이라는 역 관계를 학습하지 못하게 된다. 결측 메커니즘 파악이 먼저다.
10 자주 발생하는 실수
10.1 실수 1: EDA를 Data Preparation과 혼동
EDA는 데이터를 이해하는 단계다. 이상치를 발견했다고 바로 제거하거나, 결측치를 발견했다고 바로 채우면 안 된다. 먼저 이해하고(Data Understanding), 그 다음 처리 전략을 수립한다(Data Preparation). 처리 결정은 항상 “왜”에서 시작해야 한다.
10.2 실수 2: 타겟 변수의 분포를 확인하지 않음
피처 EDA에 집중하면서 타겟 변수 자체의 분포를 보지 않는 경우가 있다. 이진 분류에서 클래스 비율이 99:1이라는 것을 모델을 만든 후에 발견하면, 성능 평가 지표 선택부터 다시 해야 한다.
10.3 실수 3: 단변량 분석만 하고 끝냄
수치형 변수의 분포와 결측 비율만 확인하고 EDA를 마치는 경우다. 단변량으로는 문제없어 보이는 피처들이 이변량 분석에서 타겟과 전혀 관계가 없거나, 서로 강하게 상관된(다중공선성) 문제가 드러나기도 한다. EDA는 단변량 → 이변량 → 다변량 순서를 밟아야 한다.
10.4 실수 4: 훈련/검증 데이터를 합쳐서 EDA
EDA를 전체 데이터셋으로 수행하면, 검증 세트의 통계 정보(평균, 표준편차, 분포)가 훈련 과정에 간접적으로 누출된다. EDA와 전처리는 훈련 세트 기준으로 수행하고, 검증/테스트 세트에는 동일한 변환을 적용(transform)해야 한다.
11 관련 주제
선행 단계
- Business Understanding — 문제 정의와 ROI 계산 — 타겟 변수와 피처의 원천 정의
후속 단계
- Data Preparation — 결측치 처리, 피처 엔지니어링, 인코딩 (예정)
연관 포스트
- 성능 지표 — F1, Precision, Recall, AUC — 클래스 불균형 시 사용할 평가 지표
- A/B 테스트 개요 — 레이블 획득을 위한 실험 설계