데이터 분석의 이해·과정·EDA
학습개요
데이터 분석은 데이터에서 의미 있는 패턴과 통찰을 발견하여 합리적 의사결정을 지원하는 기술입니다. 이를 위해서는 “도구 사용”을 넘어 분석 절차(파이프라인)와 분석 방법론(규칙 기반→통계→기계학습→딥러닝)을 올바르게 이해해야 합니다. 본 차시에서는 데이터 분석의 정의와 필요성, 순환적 분석 과정을 정리하고, 탐색적 데이터 분석(EDA, Exploratory Data Analysis)의 역할과 자동화 도구 활용 관점을 실무 중심으로 다룹니다.
약어 정리
- EDA: Exploratory Data Analysis(탐색적 데이터 분석)
- LLM: Large Language Model(대규모 언어 모델)
- CNN: Convolutional Neural Network(합성곱 신경망)
- RNN/LSTM/GRU: Recurrent/Long Short-Term Memory/Gated Recurrent Unit(순환/장단기 메모리/게이트 순환)
- NLP: Natural Language Processing(자연어 처리)
학습목표
- 데이터 분석의 개념과 필요성을 설명할 수 있다.
- 데이터 분석 주요 과정(문제 정의→수집→전처리→EDA→모델링→시각화·해석)의 역할을 설명할 수 있다.
- EDA의 중요성을 이해하고, 기본 기법과 자동화 도구를 활용할 수 있다.
1. 데이터 분석의 이해
1.1 왜 필요한가
- 의사결정 품질 향상: 가설이 아닌 데이터로 판단
- 근본 원인 규명: 표면 현상 뒤의 구조 파악
- 예측과 최적화: 미래 수요, 위험, 성과 개선
1.2 현실의 난제
- 품질 문제: 결측·오류·중복·불일치
- 복잡성 증가: 5V(Volume·Velocity·Variety·Veracity·Value)
- 도구·방법 한계: 구조화/비구조화, 고성능 컴퓨팅, 방법 선택의 어려움
2. 데이터 분석 과정(순환·반복)
한 번에 완벽하지 않습니다. 가설↔검증을 반복하며 품질을 끌어올립니다.
- 문제 정의: 성공 지표, 제약, 기대 산출물 명확화
- 데이터 수집: 원천 식별(내부/외부), 접근·권한, 스키마
- 데이터 전처리: 정합성·형식 통일, 결측·이상치 처리
- EDA: 분포·관계·패턴 탐색, 가설 도출/수정
- 모델링: 기준선(Baseline)→특징공학→튜닝→검증
- 시각화·해석: 이해관계자에게 설명 가능한 결과 제시
→ 필요 시 1)로 되돌아가 범위/가설을 조정
3. 분석 방법론의 발전과 선택 기준
3.1 규칙 기반 분석
- 개념: 도메인 지식을 if-then 규칙으로 명시
- 장점: 투명성·해석 용이
- 단점: 복잡·비선형 패턴, 일반화에 약함
- 예시(간략):
def loan_approval_rule(age: int, income: int, credit_score: int) -> str:
# age: 신청자 나이(정수), income: 연소득(원, 정수), credit_score: 신용점수(정수)
if age < 18: return "거부: 미성년자"
if income < 6_000_000: return "거부: 소득 불충분"
if credit_score < 600: return "거부: 신용 점수 불충분"
if income >= 100_000_000 and credit_score >= 950: return "승인: 최우선 고객"
if income >= 70_000_000 and credit_score >= 900: return "승인: 우수 고객"
if income >= 20_000_000 and credit_score >= 850: return "승인: 일반 고객"
return "추가 검토 필요"
3.2 통계적 모형
- 기술통계: 평균·중앙·분산·왜도·첨도 → 데이터 파악의 출발점
- 추론통계: 가설검정·신뢰구간·회귀 등으로 일반화
- 주의: 분포 가정 위반 시 신뢰도 저하 → 로버스트 기법 병행
3.3 기계학습(ML)
- 지도학습: 분류/회귀(성능 지표로 검증)
- 비지도학습: 군집·차원축소·연관규칙(구조 발견)
- 강화학습: 보상 최대화 정책 학습(순차 의사결정)
3.4 딥러닝(DL)
- 특징: 다층 신경망으로 복잡한 패턴 자동 학습
- 모델군: CNN(비전), RNN/LSTM/GRU(시계열·텍스트), 트랜스포머/LLM(NLP 전반)
- 장단: 확장성·표현력↑ / 데이터·연산·해석 비용↑
선택 기준 요약: 데이터 크기/형태·해석 필요성·제약(시간·비용)·성능 목표에 따라 규칙↔통계↔ML↔DL 간 균형을 잡습니다.
4. 탐색적 데이터 분석(EDA)
4.1 핵심 목적
- 품질 점검: 결측·이상치·코딩 오류
- 패턴 발견: 분포·상관·군집·추세·계절성
- 가설 수립/수정: 무엇을 예측/설명할지 선명화
4.2 기본 접근
- 기술통계: describe, 그룹 요약, 분포 요약
- 시각화: 히스토그램·박스플롯·산점도·시계열 라인
- 관계 탐색: 상관 행렬, 페어플롯, 교차표 등
4.3 자동화 도구(예: ydata-profiling)
- 반복적 기초 분석을 몇 줄로 리포트화 → 핵심 이슈를 빠르게 훑고, 본 분석에 집중
# 역할/타입 주석 포함 예시
from ydata_profiling import ProfileReport # 자동 EDA 리포트 생성기
import pandas as pd
def quick_profile(df: pd.DataFrame, title: str = "EDA Report") -> None:
"""
df: 분석 대상 DataFrame
title: 리포트 제목(기본 'EDA Report')
Side effect: HTML 리포트 파일 생성
"""
profile = ProfileReport(df, title=title)
profile.to_file("eda_profile.html")
5. 실무형 EDA 워크플로우(템플릿 코드)
import pandas as pd
import numpy as np
from pathlib import Path
def load_csv(path: str | Path, parse_dates: list[str] | None = None) -> pd.DataFrame:
"""
path: CSV 경로
parse_dates: 날짜 변환할 열 목록(없으면 None)
return: 로드된 DataFrame
"""
return pd.read_csv(path, parse_dates=parse_dates)
def summarize(df: pd.DataFrame) -> None:
"""
df: 요약할 DataFrame
side effect: info/describe/결측 요약 출력
"""
print("=== INFO ===")
print(df.info())
print("\n=== DESCRIBE (numeric) ===")
print(df.describe())
print("\n=== MISSING ===")
miss = df.isna().sum()
print(miss[miss > 0].sort_values(ascending=False))
def add_datetime_parts(df: pd.DataFrame, col: str) -> pd.DataFrame:
"""
col: 날짜열 이름(문자열 또는 datetime)
return: 날짜 파생열(year, month, dow 등)이 추가된 DataFrame
"""
out = df.copy()
out[col] = pd.to_datetime(out[col], errors="coerce")
out[f"{col}_year"] = out[col].dt.year
out[f"{col}_month"] = out[col].dt.month
out[f"{col}_dow"] = out[col].dt.dayofweek # 0=월, 6=일
out[f"{col}_is_wknd"] = out[f"{col}_dow"].isin([5, 6]).astype(int)
return out
def iqr_clip(s: pd.Series, k: float = 3.0) -> pd.Series:
"""
s: 수치형 시리즈
k: IQR 배수(기본 3.0; 보수적 클리핑)
return: IQR 범위를 벗어난 값이 경계값으로 잘린 시리즈
"""
q1, q3 = s.quantile([0.25, 0.75])
iqr = q3 - q1
lo, hi = q1 - k*iqr, q3 + k*iqr
return s.clip(lower=lo, upper=hi)
def safe_merge(left: pd.DataFrame, right: pd.DataFrame, on: list[str] | str,
how: str = "inner", validate: str | None = None) -> pd.DataFrame:
"""
validate 예: 'one_to_one', 'one_to_many', 'many_to_one', 'many_to_many'
잘못된 카디널리티(N:N 폭발)를 초기에 잡기 위함
"""
return pd.merge(left, right, on=on, how=how, validate=validate)
# 사용 예시
# df = load_csv("data/train.csv", parse_dates=["date"])
# summarize(df)
# df = add_datetime_parts(df, "date")
# df["sales_clipped"] = iqr_clip(df["sales"])
실무 팁
- 재현성: 전처리 함수화/버전고정(리포지토리·환경)
- 데이터 누수 방지: 시계열/학습-평가 분리 시 “미래 정보”가 학습에 섞이지 않도록 주의
- 해석 가능성: 성능과 설명력의 균형(규칙·통계·ML·DL 혼합)
6. 정리
- 데이터 분석은 의사결정 지원이 목적이며, 문제 정의→수집→전처리→EDA→모델링→시각화·해석의 순환 과정으로 진행됩니다.
- 규칙 기반은 투명하지만 일반화가 약하고, 통계·ML·DL로 갈수록 표현력·성능은 커지나 해석·자원 비용이 늘어납니다.
- EDA는 실무의 출발점입니다. 품질을 점검하고 패턴을 발견해 가설을 세우고, 자동화 도구로 반복 작업을 줄여 핵심 분석에 집중합니다.
7. 다음 학습·실습 제안
- 제공된 데이터(또는 Kaggle 공개 데이터)로 위 EDA 템플릿을 즉시 실행
- 자동화 리포트(ydata-profiling)로 빠르게 스캔 → 의문점 목록 작성
- 그 의문점에 맞는 추가 시각화/검정/특징공학 설계
- 기준선 모델(선형회귀/로지스틱/트리·부스팅)로 베이스라인 확보 → 개선 사이클 반복
오픈소스 기반 데이터 분석 8강 — 데이터 분석 방법론
Data-Analysis-with-Open-Source/오픈소스_데이터_분석_8강.ipynb at main · mors119/Data-Analysis-with-Open-Source
Data Analysis with Open Source. Contribute to mors119/Data-Analysis-with-Open-Source development by creating an account on GitHub.
github.com
8-1 통계적 모형을 활용한 분석 (statsmodels)
# --- OLS(최소제곱) 선형회귀: formula API 예제 ---
# 역할: y ~ X 공식을 사용해 단순 선형회귀 적합 및 요약 출력
# 포인트:
# - 회귀계수/유의확률/신뢰구간/잔차진단 지표(DW 등)까지 한 번에 확인
# - 범주형은 C(var)로 자동 더미 인코딩
# 대안:
# - [import statsmodels.api as sm] 직접 설계행렬(X, add_constant) 생성 후 sm.OLS(y, X).fit()
# - [import patsy] dmatrices로 설계행렬 생성
# - [import sklearn] LinearRegression은 예측/평가 중심(요약통계는 직접 계산 필요)
import pandas as pd, random
import statsmodels.formula.api as smf
random.seed(1)
X = list(range(1, 11))
y = [2*x + 1 + random.gauss(0, 1) for x in X]
data = pd.DataFrame({'X': X, 'y': y})
model = smf.ols('y ~ X', data=data).fit() # 옵션: robust_cov='HC3' 등으로 강건표준오차
print(model.summary())
# 추가: 진단 플롯(잔차 vs 적합치, Q-Q)
# [import statsmodels.api]
# import statsmodels.api as sm
# sm.graphics.plot_regress_exog(model, 'X'); plt.show()
8-2 기계학습을 활용한 분석 (scikit-learn)
# --- 분류(RandomForest) 기본 흐름 ---
# 역할: 데이터 로드 → 학습/검증 분할 → 모델 학습 → 예측/정확도 평가
# 포인트:
# - random_state 고정으로 재현성 확보
# - 기본 하이퍼파라미터로도 강건하지만, 과적합 방지를 위해 max_depth, n_estimators 조정
# 대안:
# - [import sklearn.svm] SVC, [import xgboost] XGBClassifier, [import lightgbm] LGBMClassifier
# - 교차검증/그리드탐색: [import sklearn.model_selection] GridSearchCV, cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
X, y = cancer.data, cancer.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y # stratify로 클래스 비율 유지
)
model = RandomForestClassifier(
n_estimators=300, # 트리 수(일반적으로 ↑ → 성능/안정 ↑, 속도/메모리 비용)
max_depth=None, # 깊이 제한(과적합 시 8~20 고려)
min_samples_leaf=1, # 리프 최소 샘플(과적합 방지에 영향)
random_state=42, n_jobs=-1
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print("Accuracy:", accuracy_score(y_test, y_pred))
print(classification_report(y_test, y_pred))
# 파이프라인(스케일링/특징선택 포함) 예:
# [import sklearn.pipeline, sklearn.preprocessing]
# from sklearn.pipeline import Pipeline
# from sklearn.preprocessing import StandardScaler
# pipe = Pipeline([('scaler', StandardScaler()), ('rf', RandomForestClassifier())])
# pipe.fit(X_train, y_train)
8-3 딥러닝을 활용한 분석 (TensorFlow/Keras)
# --- 전이학습(Transfer Learning) with MobileNetV2 on CIFAR-10 ---
# 역할: 사전학습 백본을 고정하고 분류기 헤드만 학습하여 빠르게 준수한 성능 확보
# 포인트:
# - CIFAR-10(32x32)을 160x160으로 리사이즈 → ImageNet 백본 입력과 호환
# - from_logits=True이면 마지막 Dense는 활성함수 없음(softmax는 평가 시 내부 처리)
# 대안:
# - [import tensorflow.keras.applications] EfficientNetB0 등 다른 백본
# - 미세조정(fine-tuning): base_model.trainable=True 후 상위 block만 학습률 낮게
# - [import torch] PyTorch로 동일 구조 구현 가능
import tensorflow as tf
from sklearn.metrics import classification_report
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
x_train, x_test = x_train/255.0, x_test/255.0 # 정규화
y_train, y_test = y_train.squeeze(), y_test.squeeze() # (N,1) → (N,)
base_model = tf.keras.applications.MobileNetV2(
weights='imagenet', include_top=False, input_shape=(160, 160, 3)
)
base_model.trainable = False # 전이학습 1단계: 백본 동결
model = tf.keras.Sequential([
tf.keras.layers.Resizing(160, 160),
base_model,
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dropout(0.2), # 과적합 방지(옵션)
tf.keras.layers.Dense(10) # from_logits=True와 호환
])
model.compile(
optimizer=tf.keras.optimizers.Adam(1e-3),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
model.fit(x_train, y_train, epochs=3, batch_size=128, validation_split=0.2)
y_pred = tf.argmax(model.predict(x_test), axis=1).numpy()
test_acc = model.evaluate(x_test, y_test, verbose=0)[1]
print("\nTest Accuracy:", test_acc)
print("\nClassification Report:\n", classification_report(y_test, y_pred))
# 미세조정 단계(선택):
# base_model.trainable = True
# for layer in base_model.layers[:-20]:
# layer.trainable = False
# model.compile(optimizer=tf.keras.optimizers.Adam(1e-4), ...)
# model.fit(..., epochs=2)
8-4 EDA 예시
# --- 당뇨병 회귀 데이터셋 EDA(상관행렬/기술통계) ---
# 역할: 기초 통계량과 변수 간 상관관계를 시각화하여 탐색적 인사이트 확보
# 포인트:
# - 상관계수는 선형관계 강도 측정 → 비선형/상호작용은 별도 탐색 필요
# 대안:
# - pairplot으로 쌍변수 관계 확인: [import seaborn] sns.pairplot(df.sample(500))
import pandas as pd, matplotlib.pyplot as plt, seaborn as sns
from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
df = pd.DataFrame(diabetes.data, columns=diabetes.feature_names)
df['target'] = diabetes.target
print(df.describe())
plt.figure(figsize=(12,8))
corr_matrix = df.corr(numeric_only=True) # pandas ≥2.0에서 권장
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt=".2f")
plt.title('Diabetes Dataset Correlation Matrix')
plt.show()
8-5 기술통계량 분석
# --- Iris 데이터셋: 기술통계/그룹통계 ---
# 역할: 전체 요약 + 품종별 평균 비교로 기본 분포 파악
# 포인트:
# - groupby.agg로 여러 통계를 한 번에 계산 가능
# 대안:
# - [import scipy] stats.ttest_ind로 집단 간 평균 차이 검정
import pandas as pd, seaborn as sns
df = sns.load_dataset('iris')
print("=== 기본 통계량 ===")
print(df.describe())
print("\n=== 데이터 미리보기 ===")
print(df.head())
print("\n=== 품종별 평균값 ===")
print(df.groupby('species', as_index=True).mean(numeric_only=True))
# 다중 통계:
# df.groupby('species').agg({'sepal_length':['mean','std'], 'petal_length':['median','max']})
8-6 데이터 시각화 분석
# --- Iris: 분포/산점/박스플롯/상관 히트맵(서브플롯) ---
# 역할: 단변량/이변량/다변량 시각화로 구조적 차이 파악
# 포인트:
# - 한글 폰트 경고는 실행 환경 의존(Colab은 나눔 폰트 설치 필요)
# - cmap/색상 팔레트는 인지부하↓를 위해 일관성 유지
import matplotlib.pyplot as plt, seaborn as sns
df = sns.load_dataset('iris')
plt.figure(figsize=(9, 12))
plt.suptitle('Iris 데이터셋 시각화', y=1.02, fontsize=14)
# 1) 히스토그램
plt.subplot(4, 1, 1)
sns.histplot(df['petal_length'], bins=10)
plt.title('꽃잎 길이 분포'); plt.xlabel('꽃잎 길이 (cm)')
# 2) 산점도(품종별 색상)
plt.subplot(4, 1, 2)
species_list = df['species'].unique()
colors = {'setosa':'red', 'versicolor':'green', 'virginica':'blue'}
for sp in species_list:
tmp = df[df['species'] == sp]
plt.scatter(tmp['petal_length'], tmp['petal_width'], c=colors[sp], label=sp)
plt.title('꽃잎 길이 vs 너비'); plt.xlabel('꽃잎 길이 (cm)'); plt.ylabel('꽃잎 너비 (cm)')
plt.legend()
# 3) 박스플롯(품종별 꽃받침 길이)
plt.subplot(4, 1, 3)
sns.boxplot(x='species', y='sepal_length', data=df)
plt.title('품종별 꽃받침 길이 비교'); plt.xlabel('품종'); plt.ylabel('꽃받침 길이 (cm)')
# 4) 상관 히트맵
plt.subplot(4, 1, 4)
corr = df.select_dtypes(include='number').corr()
sns.heatmap(corr, annot=True, cmap='coolwarm', fmt=".2f")
plt.title('숫자형 특성 간 상관관계')
plt.tight_layout()
plt.show()
# 대안:
# - pairplot(쌍플롯): sns.pairplot(df, hue='species', corner=True)
# - jointplot: sns.jointplot(x='sepal_length', y='sepal_width', data=df, kind='kde')
8-7 자동화된 EDA 도구
# --- ydata_profiling(구 pandas-profiling)로 자동 EDA ---
# 역할: 결측/분포/상관/경고 등 자동 리포트 HTML 생성
# 포인트:
# - 큰 데이터셋은 time/memory 소모 → minimal=True, correlations off 등으로 경량화
# 대안:
# - [import sweetviz] Sweetviz(report).show_html()
# - [import autovizwidget] AutoViz(노트북 대화식)
# 설치: !pip install ydata_profiling (노트북/Colab에서 한 번만)
import seaborn as sns
from ydata_profiling import ProfileReport
df = sns.load_dataset('iris')
profile = ProfileReport(
df, title='Iris Profiling Report',
minimal=False, # True면 빠르게/간단히
correlations={"pearson": {"calculate": True}},
explorative=True
)
profile.to_file('report.html') # HTML 리포트 저장
# 노트북에서 바로 보기: profile.to_notebook_iframe()