데이터를 받으면 보통 제일 먼저 뭘 하시나요? 아마 df.head()를 찍고, df.info()를 돌리고, df.describe()를 실행하는 것부터 시작할 것이다. 거기서 히스토그램 몇 개 그리고, 상관계수 확인하고, 결측값 처리하면 EDA가 끝난 것처럼 느껴집니다.
EDA는 코드 실행 순서가 아니라 질문의 순서입니다.
어떤 질문을 해야 하는지 알면, 어떤 코드를 써야 하는지는 자연스럽게 따라온다. 오늘은 그 사고 순서를 정리해보려 한다. 🔍
1. EDA가 실제로 하는 일

EDA(Exploratory Data Analysis, 탐색적 데이터 분석)를 한 줄로 정의하면 다음과 같다.
모델링 전에 데이터가 문제를 풀 수 있는 형태인지 확인하는 과정
여기서 핵심은 "모델링 전에"라는 부분이다. EDA는 분석의 준비 단계가 아니라 분석 그 자체다. 데이터를 제대로 이해하지 못한 채로 모델을 학습시키면 결과가 이상해도 왜 이상한지 설명할 수 없다. 좋은 EDA는 나중에 모델이 왜 그런 예측을 했는지, 왜 성능이 낮은지를 설명할 수 있는 근거가 된다.
1.1 EDA로 확인해야 할 세 가지
EDA에서 실제로 확인해야 하는 건 세 가지 질문으로 압축된다.
- 타겟 변수의 분포가 편향되어 있는가?
- 입력 변수와 타겟 사이에 관계가 있는가?
- 결측값과 이상값이 어떤 패턴으로 존재하는가?
이 세 질문에 답하는 과정이 EDA다. 코드는 이 질문들에 답하기 위한 도구일 뿐이다. 순서가 바뀌면 코드만 많고 인사이트는 없는 EDA가 된다.
2. 변수 타입별 탐색 전략

데이터의 모든 변수를 같은 방식으로 탐색할 수는 없다. 변수 타입에 따라 확인해야 할 것이 달라진다.
2.1 수치형 변수 (Numerical)
수치형 변수에서 먼저 봐야 할 건 분포 형태다.
- 정규분포에 가까운가, 아니면 한쪽으로 치우쳐 있는가 (왜도)
- 중앙값과 평균이 크게 차이 나는가 (이상값 존재 신호)
- 값의 범위가 적절한가 (스케일 차이 확인)
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 분포 확인 — 히스토그램 + KDE
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# 히스토그램
axes[0].hist(df['target'], bins=30, edgecolor='black')
axes[0].set_title('분포 확인 (Histogram)')
# Box plot — 이상값 시각화
axes[1].boxplot(df['target'], vert=True)
axes[1].set_title('이상값 확인 (Box Plot)')
plt.tight_layout()
plt.show()
# 기초 통계량
print(df['target'].describe())
print(f"\n왜도(Skewness): {df['target'].skew():.3f}")
print(f"첨도(Kurtosis): {df['target'].kurtosis():.3f}")
왜도(Skewness)가 ±1을 크게 벗어나면 분포가 심하게 치우쳐 있다는 뜻이다. 이 경우 로그 변환이나 Box-Cox 변환을 고려하게 된다. 박스 플롯에서 수염 밖에 찍히는 점들은 잠재적 이상값 후보다 — 이걸 바로 제거하는 게 아니라, 왜 저 위치에 있는지를 먼저 물어봐야 한다.
2.2 범주형 변수 (Categorical)
범주형 변수에서 먼저 봐야 할 건 클래스 불균형이다.
- 특정 카테고리에 데이터가 몰려 있지 않은가
- 고유값 수(cardinality)가 너무 많지 않은가
- 의미 없는 카테고리(오탈자, 대소문자 혼용)가 섞여 있지 않은가
# 범주형 변수 빈도 확인
print(df['category'].value_counts())
print(f"\n고유값 수: {df['category'].nunique()}")
# 시각화
df['category'].value_counts().plot(kind='bar', figsize=(10, 4))
plt.title('카테고리 분포')
plt.xticks(rotation=45)
plt.show()
타겟 변수가 범주형일 때 클래스 불균형은 모델 성능에 직접적인 영향을 준다. 예를 들어 사기 탐지 데이터에서 정상 거래가 99%, 사기 거래가 1%라면, 그냥 "전부 정상"이라고 예측해도 정확도가 99%가 나온다. EDA 단계에서 이걸 발견하지 못하면 모델 평가 결과를 신뢰하기 어렵다.
2.3 시계열 데이터
시계열 데이터는 수치형이지만 탐색 방식이 다르다. 분포보다 시간에 따른 패턴을 봐야 한다.
- 추세(Trend)가 있는가 — 전반적으로 올라가거나 내려가는 방향이 있는가
- 계절성(Seasonality)이 있는가 — 특정 주기로 반복되는 패턴이 있는가
- 이상 구간이 있는가 — 갑자기 튀는 시점이 있는가, 이유가 있는가
3. 결측값 탐색: 단순 카운트가 아니라 패턴을 봐야 한다
결측값은 개수보다 패턴이 중요하다. 결측값이 어떤 이유로 생겼는지에 따라 처리 방법이 완전히 달라진다.
3.1 결측값의 세 가지 유형
| 유형 | 의미 | 예시 | 처리 방향 |
|---|---|---|---|
| MCAR | 완전 무작위 결측 | 설문에서 일부 문항 빠뜨림 | 제거 또는 단순 대체 가능 |
| MAR | 다른 변수와 관련된 결측 | 나이가 많을수록 소득 미응답 | 관련 변수 활용한 대체 |
| MNAR | 결측값 자체가 정보를 담고 있음 | 소득이 낮을수록 미응답 | 결측 여부를 새 변수로 만들기 |
# 결측값 현황 파악
missing = df.isnull().sum()
missing_ratio = (missing / len(df) * 100).round(2)
missing_df = pd.DataFrame({
'결측 수': missing,
'결측 비율(%)': missing_ratio
}).query('결측 수 > 0').sort_values('결측 비율(%)', ascending=False)
print(missing_df)
결측 비율이 높다고 해서 무조건 해당 변수를 제거하는 건 좋지 않다. MNAR 케이스처럼 결측 자체가 신호일 수 있기 때문이다. 예를 들어 신용카드 데이터에서 소득 정보가 비어 있다면 그게 오히려 리스크 신호일 수 있다. 제거 전에 결측과 타겟 변수의 관계를 먼저 확인하는 습관이 필요하다.
4. 변수 간 관계 탐색
단변수 탐색이 끝나면 변수들 사이의 관계를 봐야 한다. 특히 입력 변수와 타겟 변수 사이의 관계가 핵심이다.
4.1 수치형 변수 간 관계 — 상관계수
import seaborn as sns
# 상관계수 히트맵
corr = df.select_dtypes(include='number').corr()
plt.figure(figsize=(10, 8))
sns.heatmap(
corr,
annot=True,
fmt='.2f',
cmap='coolwarm',
center=0,
square=True
)
plt.title('상관계수 히트맵')
plt.tight_layout()
plt.show()
히트맵에서 타겟 변수 행(또는 열)을 집중해서 본다. 타겟과 상관이 높은 변수들이 모델에서 중요하게 작용할 가능성이 높다. 동시에 입력 변수들끼리 상관이 너무 높은 경우(다중공선성)도 확인해야 한다. 선형 회귀 글에서 다뤘던 VIF 문제가 여기서 시작된다.
4.2 범주형 변수와 수치형 타겟의 관계
# 범주형 변수별 타겟 분포 비교
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Box plot — 그룹 간 분포 차이
df.boxplot(column='target', by='category', ax=axes[0])
axes[0].set_title('카테고리별 타겟 분포')
# 평균값 비교
df.groupby('category')['target'].mean().sort_values().plot(
kind='barh', ax=axes[1]
)
axes[1].set_title('카테고리별 평균 타겟값')
plt.tight_layout()
plt.show()
그룹 간 타겟 값의 분포가 다르다면 그 범주형 변수는 예측에 유용할 가능성이 높다. 반대로 그룹 간 차이가 거의 없다면 해당 변수는 모델에 크게 기여하지 않을 수 있다.
5. EDA를 마무리할 때 확인할 것들
EDA를 마칠 때는 다음 질문들에 대답할 수 있어야 한다.
| 확인 항목 | 확인 내용 |
|---|---|
| 타겟 변수 | 분포가 심하게 편향되어 있는가? 클래스 불균형이 있는가? |
| 결측값 | 결측 비율과 패턴 확인, MCAR / MAR / MNAR 분류 |
| 이상값 | 이상값인가 희귀 케이스인가? 도메인 맥락에서 판단 |
| 변수 관계 | 타겟과 상관 높은 변수, 변수 간 다중공선성 확인 |
| 스케일 | 변수 간 스케일 차이가 큰가? 표준화/정규화 필요 여부 |
| 데이터 타입 | 범주형이 수치로 저장되어 있거나, 반대인 경우는 없는가 |
이 체크리스트를 다 채웠다면 전처리 단계로 넘어갈 준비가 된 것이다. 여기서 발견한 문제들 — 결측값 처리, 이상값 판단, 인코딩, 스케일링 — 이 다음 글의 주제들이 된다.
정리
EDA는 코드 실행 순서가 아니라 질문의 순서다.
- 타겟 변수를 먼저 본다 — 편향, 불균형
- 변수 타입에 따라 탐색 방법을 다르게 한다 — 수치형, 범주형, 시계열
- 결측값은 개수가 아니라 패턴으로 판단한다 — MCAR / MAR / MNAR
- 변수 간 관계를 타겟 중심으로 탐색한다 — 상관계수, 그룹 비교
df.head() 찍는 것도 EDA의 일부지만, 그게 전부는 아니다. 코드를 돌리기 전에 "이 데이터에서 내가 무엇을 확인해야 하는가"라는 질문을 먼저 하는 게 더 중요하다.
다음 글에서는 이상값을 다룬다. 이상값은 무조건 제거해야 한다는 생각이 왜 위험한지, 어떤 기준으로 판단하고 어떻게 처리하는지 정리해보겠다. 🙂
'Data Analysis > EDA' 카테고리의 다른 글
| [데이터 전처리] 피벗 테이블, 파일 호출 / 저장 (0) | 2023.05.03 |
|---|---|
| [데이터 전처리] 그룹화 (0) | 2023.05.02 |
| [데이터 전처리] 데이터 결합 (0) | 2023.05.01 |
| [데이터 전처리] 레코드 칼럼 추가, 삭제, apply.map (0) | 2023.04.27 |
| [데이터 전처리] 결측값 처리 (0) | 2023.04.26 |
HELLO WORLD
안녕하세요. 데이터로 말하는 분석가 모모입니다.
데이터를 구조화하고 분석하는 과정과 실무에 활용되는 도구 중심의 내용을 기록합니다.