EDA 마스터하기: 타이타닉 데이터로 배우는 탐색적 데이터 분석 실전 기법

들어가며

이전 편에서 Kaggle 데이터셋을 활용한 실무 프로젝트의 전체 로드맵을 살펴보았습니다. 이번 편에서는 본격적으로 데이터 분석의 첫걸음인 탐색적 데이터 분석(EDA, Exploratory Data Analysis)을 타이타닉 데이터셋으로 실습하며 실무에서 바로 쓸 수 있는 기법들을 익혀보겠습니다.

EDA는 단순히 데이터를 확인하는 것이 아닙니다. 데이터의 구조를 파악하고, 패턴을 발견하며, 이상치를 탐지하고, 변수 간 관계를 이해하는 데이터 분석의 핵심 과정입니다. 실무에서 좋은 EDA는 프로젝트 성공의 80%를 결정한다고 해도 과언이 아닙니다.

타이타닉 데이터셋 이해하기

데이터셋 개요

타이타닉 데이터셋은 1912년 타이타닉호 침몰 사고에서 승객들의 생존 여부를 담은 데이터로, 머신러닝 입문자들에게 가장 인기 있는 데이터셋입니다. 실무 관점에서 보면 이는 이진 분류(Binary Classification) 문제이자, 고객 이탈 예측이나 리스크 분석과 유사한 구조를 가지고 있습니다.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False

# 데이터 로드
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')

print(f"Train shape: {train.shape}")
print(f"Test shape: {test.shape}")
train.head()

주요 변수 설명

변수명 설명 타입
Survived 생존 여부 (0=사망, 1=생존) Target
Pclass 객실 등급 (1=1등석, 2=2등석, 3=3등석) Categorical
Name 승객 이름 Text
Sex 성별 Categorical
Age 나이 Numerical
SibSp 동승한 형제/배우자 수 Numerical
Parch 동승한 부모/자녀 수 Numerical
Ticket 티켓 번호 Text
Fare 운임 Numerical
Cabin 객실 번호 Text
Embarked 승선 항구 (C=Cherbourg, Q=Queenstown, S=Southampton) Categorical

체계적인 EDA 프로세스

1단계: 데이터 품질 체크

실무에서 가장 먼저 해야 할 일은 데이터 품질 확인입니다. 결측치, 중복, 데이터 타입 오류 등을 점검합니다.

# 기본 정보 확인
print("=== 데이터 기본 정보 ===")
train.info()

# 결측치 비율 계산
print("\n=== 결측치 현황 ===")
missing = pd.DataFrame({
    '결측치_수': train.isnull().sum(),
    '결측치_비율(%)': (train.isnull().sum() / len(train) * 100).round(2)
})
missing = missing[missing['결측치_수'] > 0].sort_values('결측치_비율(%)', ascending=False)
print(missing)

# 중복 데이터 확인
print(f"\n중복 행 개수: {train.duplicated().sum()}")

실무 Tip: 결측치가 77%인 Cabin 변수는 삭제를 고려하거나, “있음/없음”으로 이진화하는 것이 일반적입니다. Age의 19% 결측은 중앙값/평균 대체나 머신러닝 기반 imputation을 활용합니다.

2단계: 단변량 분석(Univariate Analysis)

각 변수를 개별적으로 분석하여 분포와 특성을 파악합니다.

# 범주형 변수 분석
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 생존율
sns.countplot(data=train, x='Survived', ax=axes[0, 0])
axes[0, 0].set_title('생존 분포')
axes[0, 0].set_xticklabels(['사망', '생존'])

# 객실 등급
sns.countplot(data=train, x='Pclass', ax=axes[0, 1])
axes[0, 1].set_title('객실 등급 분포')

# 성별
sns.countplot(data=train, x='Sex', ax=axes[1, 0])
axes[1, 0].set_title('성별 분포')

# 승선 항구
sns.countplot(data=train, x='Embarked', ax=axes[1, 1])
axes[1, 1].set_title('승선 항구 분포')

plt.tight_layout()
plt.show()

# 수치형 변수 분포
fig, axes = plt.subplots(1, 3, figsize=(15, 4))

train['Age'].hist(bins=30, ax=axes[0], edgecolor='black')
axes[0].set_title('나이 분포')
axes[0].set_xlabel('Age')

train['Fare'].hist(bins=30, ax=axes[1], edgecolor='black')
axes[1].set_title('운임 분포 (왜도 주의)')
axes[1].set_xlabel('Fare')

train['Fare'].apply(lambda x: np.log1p(x)).hist(bins=30, ax=axes[2], edgecolor='black')
axes[2].set_title('운임 분포 (로그 변환)')
axes[2].set_xlabel('log(Fare + 1)')

plt.tight_layout()
plt.show()

# 기술 통계량
print(train[['Age', 'Fare', 'SibSp', 'Parch']].describe())

실무 인사이트:
– Fare는 심한 우편향(right-skewed) → 로그 변환 필요
– Age는 비교적 정규분포에 가까움
– 생존율은 약 38% (불균형 데이터, 실무에서는 SMOTE 등 고려)

3단계: 이변량 분석(Bivariate Analysis)

타겟 변수(Survived)와 각 feature의 관계를 분석합니다. 이 단계가 feature engineering의 힌트를 제공합니다.

# 범주형 변수 vs 생존율
fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# 성별별 생존율
sns.barplot(data=train, x='Sex', y='Survived', ax=axes[0, 0], ci=None)
axes[0, 0].set_title('성별 생존율')
axes[0, 0].set_ylabel('생존율')

# 객실 등급별 생존율
sns.barplot(data=train, x='Pclass', y='Survived', ax=axes[0, 1], ci=None)
axes[0, 1].set_title('객실 등급별 생존율')

# 승선 항구별 생존율
sns.barplot(data=train, x='Embarked', y='Survived', ax=axes[1, 0], ci=None)
axes[1, 0].set_title('승선 항구별 생존율')

# 가족 크기별 생존율 (파생 변수)
train['FamilySize'] = train['SibSp'] + train['Parch'] + 1
sns.barplot(data=train, x='FamilySize', y='Survived', ax=axes[1, 1], ci=None)
axes[1, 1].set_title('가족 크기별 생존율')

plt.tight_layout()
plt.show()

# 수치형 변수 vs 생존 여부 (박스플롯)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

sns.boxplot(data=train, x='Survived', y='Age', ax=axes[0])
axes[0].set_title('생존 여부별 나이 분포')
axes[0].set_xticklabels(['사망', '생존'])

sns.boxplot(data=train, x='Survived', y='Fare', ax=axes[1])
axes[1].set_title('생존 여부별 운임 분포')
axes[1].set_xticklabels(['사망', '생존'])

plt.tight_layout()
plt.show()

핵심 발견:
1. 여성의 생존율이 압도적으로 높음 (74% vs 19%) → “Women and children first” 정책 반영
2. 1등석 승객의 생존율이 높음 (63% vs 47% vs 24%) → 사회경제적 지위의 영향
3. 가족 크기 2-4명일 때 생존율 최고 → 혼자이거나 대가족은 불리
4. 운임이 높을수록 생존율 증가 → Pclass와 상관관계

4단계: 다변량 분석(Multivariate Analysis)

여러 변수 간의 상호작용을 분석합니다.

# 상관계수 히트맵
plt.figure(figsize=(10, 8))
corr_data = train[['Survived', 'Pclass', 'Age', 'SibSp', 'Parch', 'Fare', 'FamilySize']].corr()
sns.heatmap(corr_data, annot=True, cmap='coolwarm', center=0, fmt='.2f')
plt.title('변수 간 상관관계')
plt.show()

# 성별 + 객실 등급 조합 분석
plt.figure(figsize=(10, 6))
sns.barplot(data=train, x='Pclass', y='Survived', hue='Sex', ci=None)
plt.title('객실 등급 + 성별 조합의 생존율')
plt.xlabel('객실 등급')
plt.ylabel('생존율')
plt.legend(title='성별')
plt.show()

# 나이 그룹 + 성별 분석
train['AgeGroup'] = pd.cut(train['Age'], bins=[0, 12, 18, 35, 60, 100], 
                            labels=['어린이', '청소년', '청년', '중년', '노년'])

plt.figure(figsize=(12, 6))
sns.barplot(data=train, x='AgeGroup', y='Survived', hue='Sex', ci=None)
plt.title('연령대 + 성별 조합의 생존율')
plt.xlabel('연령대')
plt.ylabel('생존율')
plt.legend(title='성별')
plt.show()

실무 적용 포인트:
– Fare와 Pclass는 강한 음의 상관관계(-0.55) → multicollinearity 주의
– 모든 객실 등급에서 여성의 생존율이 남성보다 높음
– 어린 여자 어린이의 생존율이 가장 높음 → Age * Sex interaction feature 고려

5단계: 이상치 탐지

# Fare 이상치 확인
Q1 = train['Fare'].quantile(0.25)
Q3 = train['Fare'].quantile(0.75)
IQR = Q3 - Q1
outlier_threshold = Q3 + 1.5 * IQR

print(f"Fare 이상치 기준: {outlier_threshold:.2f}")
print(f"이상치 개수: {(train['Fare'] > outlier_threshold).sum()}개")

# 이상치 데이터 확인
outliers = train[train['Fare'] > outlier_threshold][['Name', 'Pclass', 'Fare', 'Survived']]
print("\n주요 고운임 승객:")
print(outliers.sort_values('Fare', ascending=False).head(10))

실무 판단: 고운임 승객은 실제 1등석 VIP이므로 이상치가 아닌 정상 데이터입니다. 무조건 제거하지 말고 도메인 지식으로 판단해야 합니다.

실무에서 바로 쓰는 EDA 체크리스트

필수 확인 항목

  1. 데이터 품질
    – [ ] 결측치 비율 및 패턴 확인
    – [ ] 중복 데이터 제거
    – [ ] 데이터 타입 검증
    – [ ] 이상치 탐지 및 처리 방침 결정

  2. 분포 이해
    – [ ] 타겟 변수 불균형 확인
    – [ ] 수치형 변수의 왜도/첨도
    – [ ] 범주형 변수의 클래스 불균형

  3. 관계 파악
    – [ ] Feature-Target 상관관계
    – [ ] Feature 간 다중공선성
    – [ ] 비선형 관계 탐색

  4. 인사이트 도출
    – [ ] 비즈니스적으로 해석 가능한 패턴
    – [ ] Feature engineering 아이디어
    – [ ] 모델링 전략 수립

마무리

이번 편에서는 타이타닉 데이터를 통해 실무 EDA의 전 과정을 살펴보았습니다. 핵심은 단순히 그래프를 그리는 것이 아니라, 데이터에서 의미 있는 패턴을 발견하고 이를 모델링에 활용할 수 있는 인사이트로 전환하는 능력입니다.

오늘 배운 내용:
– 체계적인 EDA 4단계 프로세스 (품질 체크 → 단변량 → 이변량 → 다변량)
– 실무에서 자주 쓰는 시각화 기법 (분포도, 박스플롯, 히트맵, 교차 분석)
– 도메인 지식을 활용한 이상치 판단
– Feature engineering 힌트 발견 (FamilySize, AgeGroup 등)

다음 편에서는 오늘 발견한 인사이트를 바탕으로 고객 이탈 예측 프로젝트를 진행하며, 전처리부터 모델링까지 전체 머신러닝 파이프라인을 구축해보겠습니다. 실무에서 바로 쓸 수 있는 코드 템플릿과 함께 준비했으니 기대해주세요!

데이터분석가를 꿈꾸는 취준생을 위한 실제 업무와 유사한 kaggle데이터를 활용한 실무데이터분석 프로젝트 시리즈 (2/6편)
데이터분석가를 꿈꾸는 취준생을 위한 실제 업무와 유사한 kaggle데이터를 활용한 실무데이터분석 프로젝트 시리즈 (2/6편)

이 글이 도움이 되셨나요?

Buy me a coffee

코멘트

“EDA 마스터하기: 타이타닉 데이터로 배우는 탐색적 데이터 분석 실전 기법” 에 하나의 답글

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

TODAY 136 | TOTAL 136