커리큘럼 홈
Phase 2

데이터 분석 & 컨설팅

Phase 2: 데이터 분석 & 컨설팅 (8주)

목표: 비즈니스 문제를 정의하고, 데이터에서 인사이트를 추출하며, ML 모델로 예측/분류/이상탐지를 수행하고, 경영진에게 효과적으로 전달할 수 있다.

기간: 2개월 (8주)

포트폴리오: 데이터 분석 프로젝트 + 경영진 보고서


🚀 Phase 2를 시작하며

Phase 1에서 데이터를 수집, 처리, 저장하는 파이프라인을 구축할 수 있게 되었습니다.

하지만 FDE는 단순히 "데이터를 다루는 사람"이 아닙니다. 비즈니스 문제를 정의하고, 데이터로 해결책을 찾는 사람입니다.

Phase 2에서는:

  • "무엇을" 분석할지 정의하는 방법 (5 Whys, MECE)
  • "어떻게" 인사이트를 도출할지 (EDA, ML)
  • "어떻게" 전달할지 (Pyramid Principle, 경영진 발표)

를 배웁니다.

기술적 역량(Phase 1) + 비즈니스 역량(Phase 2) = FDE의 차별화된 가치


💡 Phase 2의 핵심: "왜" + "어떻게"

Phase 1에서 데이터 엔지니어링 기초를 배웠다면, Phase 2에서는 비즈니스 문제 해결 관점을 추가합니다.

Phase 1: "데이터를 어떻게 다루는가?" (기술)
Phase 2: "왜 분석하는가?" + "어떻게 인사이트를 도출하는가?" (기술 + 비즈니스)

FDE vs 일반 데이터 분석가

역할시작점핵심 질문
데이터 분석가"이 데이터 분석해주세요"How?
FDE"매출이 떨어지고 있어요"Why? What? Then How?

Month 3: 문제 정의 & EDA


Week 9: 비즈니스 문제 정의 & EDA 기초

학습 목표

  • 비즈니스 문제와 증상을 구분할 수 있다
  • 5 Whys, MECE로 문제를 구조화할 수 있다
  • 데이터의 분포, 중심 경향, 산포도를 분석할 수 있다
  • 효과적인 시각화를 통해 인사이트를 전달할 수 있다

핵심 개념

1. 문제 정의 (분석 전 필수!)

❌ 잘못된 접근:
"Python 배웠어요" → "Pandas 배웠어요" → "근데 뭘 분석해야 하죠?"

✅ 올바른 접근:
"고객 이탈이 문제예요" → "어떤 데이터가 있죠?" → "가설을 세워봅시다"
→ "이걸 검증하려면 이런 분석이 필요해요" → "Python으로 구현합니다"

문제 vs 증상

증상: "매출이 20% 감소했어요"
     ↓ Why?
문제: "신규 고객 유입은 동일한데 재구매율이 급감"
     ↓ Why?
근본 원인: "경쟁사 대비 배송 시간이 2배 → 고객 만족도 하락"

5 Whys 기법

1. Why: 대시보드 데이터가 업데이트 안 됨 → ETL 작업 실패
2. Why: ETL 작업이 왜 실패? → 소스 DB 연결 타임아웃
3. Why: 왜 타임아웃? → 쿼리 실행 시간 30분 초과
4. Why: 왜 쿼리가 느려졌나? → 테이블 10배 증가, 인덱스 없음
5. Why: 왜 인덱스가 없나? → 초기 설계 시 예측 실패

→ 근본 원인: 데이터 모델링/용량 계획 부재
→ 해결책: 인덱스 추가 + 파티셔닝 + 모니터링

MECE (Mutually Exclusive, Collectively Exhaustive)

문제: "왜 고객이 이탈하는가?"

MECE 분해:
┌─────────────────────────────────────────────┐
│              고객 이탈 원인                  │
├─────────────┬─────────────┬─────────────────┤
│   제품 문제  │  서비스 문제 │   외부 요인     │
├─────────────┼─────────────┼─────────────────┤
│ • 품질 불만  │ • 배송 지연  │ • 경쟁사 프로모션│
│ • 가격 불만  │ • CS 불만족  │ • 경기 침체     │
│ • 기능 부족  │ • 반품 어려움│ • 계절적 요인   │
└─────────────┴─────────────┴─────────────────┘

✅ 상호 배타적: 각 카테고리가 겹치지 않음
✅ 전체 포괄적: 모든 가능한 원인 포함

2. 통계 기초

import pandas as pd
import numpy as np

# 중심 경향
df['value'].mean()      # 평균 - 이상치에 민감
df['value'].median()    # 중앙값 - 이상치에 강건
df['value'].mode()      # 최빈값 - 범주형에 유용

# 산포도
df['value'].std()       # 표준편차
df['value'].var()       # 분산
df['value'].quantile([0.25, 0.5, 0.75])  # 사분위수
df['value'].max() - df['value'].min()    # 범위

# 분포 형태
from scipy.stats import skew, kurtosis
skew(df['value'])       # 왜도 (0: 대칭, >0: 오른쪽 꼬리)
kurtosis(df['value'])   # 첨도 (0: 정규분포)

# 상관관계
df.corr()               # 피어슨 상관계수
df.corr(method='spearman')  # 스피어만 (순위 기반)
지표의미사용 시점
평균 vs 중앙값차이 크면 왜곡된 분포이상치 존재 파악
표준편차데이터 퍼짐 정도변동성 분석
왜도분포 비대칭성로그 변환 필요성 판단
상관계수변수 간 선형 관계피처 선택

2. 시각화 (matplotlib, seaborn, plotly)

import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px

# 분포 시각화
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
sns.histplot(df['value'], kde=True, ax=axes[0])  # 히스토그램
sns.boxplot(x=df['value'], ax=axes[1])           # 박스플롯
sns.violinplot(x=df['value'], ax=axes[2])        # 바이올린

# 관계 시각화
sns.scatterplot(x='feature1', y='target', hue='category', data=df)
sns.pairplot(df[numerical_cols])  # 모든 조합 산점도

# 상관관계 히트맵
plt.figure(figsize=(10, 8))
sns.heatmap(df.corr(), annot=True, cmap='RdBu_r', center=0)

# 인터랙티브 시각화 (Plotly)
fig = px.scatter(df, x='feature1', y='target', color='category',
                 hover_data=['id', 'date'], title='Feature vs Target')
fig.show()
차트 유형용도변수 유형
히스토그램분포수치형 1개
박스플롯분포 + 이상치수치형 1개
산점도관계수치형 2개
히트맵상관관계수치형 다수
막대 그래프카운트/비교범주형
라인 차트추세시계열

3. 결측치 분석

# 결측치 현황
df.isnull().sum()
df.isnull().sum() / len(df) * 100  # 비율

# 결측치 시각화
import missingno as msno
msno.matrix(df)        # 결측 패턴 매트릭스
msno.heatmap(df)       # 결측 상관관계
msno.dendrogram(df)    # 결측 클러스터링

# 결측 유형 판단
# MCAR (완전 무작위): 결측이 다른 변수와 무관
# MAR (무작위): 관측된 변수와 관련
# MNAR (비무작위): 결측값 자체와 관련
결측 유형특징처리 방법
MCAR완전 무작위삭제 또는 대체
MAR다른 변수와 관련조건부 대체
MNAR결측값 자체와 관련도메인 지식 필요

4. 이상치 탐지 (기초)

# IQR 방법
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df['value'] < lower) | (df['value'] > upper)]

# Z-score 방법
from scipy import stats
z_scores = np.abs(stats.zscore(df['value']))
outliers = df[z_scores > 3]

# 시각화
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.boxplot(x=df['value'], ax=axes[0])
axes[0].set_title('Boxplot - Outliers')
sns.scatterplot(x=range(len(df)), y=df['value'], ax=axes[1])
axes[1].axhline(upper, color='r', linestyle='--')
axes[1].axhline(lower, color='r', linestyle='--')

실습 과제

과제: 실제 데이터셋 EDA 리포트

데이터 선택 (1개):
1. Kaggle: House Prices (주택 가격 예측)
2. Kaggle: Titanic (생존 예측)
3. UCI: Adult Income (소득 분류)
4. 자체 수집 데이터

요구사항:
1. 데이터 개요 (shape, dtypes, 샘플)
2. 수치형 변수 분석:
   - 기술 통계량 (mean, std, min, max, percentiles)
   - 분포 시각화 (히스토그램, 박스플롯)
   - 왜도/첨도 분석
3. 범주형 변수 분석:
   - 카테고리별 빈도
   - 막대 그래프
4. 결측치 분석:
   - 결측 비율
   - 결측 패턴 (missingno)
   - 결측 유형 추정
5. 이상치 분석:
   - IQR / Z-score 탐지
   - 이상치 특성 분석
6. 변수 간 관계:
   - 상관관계 히트맵
   - 타겟과의 관계 시각화
7. 인사이트 요약 (3-5개)

산출물:
- Jupyter Notebook (코드 + 마크다운 설명)
- 최소 10개 시각화
- 인사이트 요약 섹션

평가 기준

항목통과 기준배점
완성도7개 분석 항목 모두 포함30%
시각화 품질명확한 제목, 레이블, 해석25%
인사이트실행 가능한 발견 3개 이상25%
코드 품질함수화, 주석, 재사용성10%
프레젠테이션논리적 흐름10%

추천 자료

유형제목링크
Python for Data Analysis (Wes McKinney)-
코스Kaggle EDA Coursehttps://www.kaggle.com/learn/data-visualization
영상StatQuest - Statisticshttps://www.youtube.com/c/joshstarmer
도구ydata-profiling (자동 EDA)https://github.com/ydataai/ydata-profiling
참고Kaggle 우승자 EDA 노트북https://www.kaggle.com/notebooks?sortBy=voteCount

Week 10: 데이터 이해 & 전처리

학습 목표

  • 조직 내 데이터 소스를 매핑할 수 있다
  • 데이터 품질 6차원으로 평가할 수 있다
  • 결측치를 적절한 방법으로 처리할 수 있다
  • 이상치를 처리하는 다양한 전략을 적용할 수 있다

핵심 개념

1. 데이터 소스 매핑

┌─────────────────────────────────────────────────────────────┐
│                    조직 데이터 맵                            │
├─────────────────────────────────────────────────────────────┤
│  [운영 시스템]                                               │
│  ├── ERP (SAP, Oracle)        → 재무, 재고, 구매            │
│  ├── CRM (Salesforce)         → 고객, 영업, 마케팅          │
│  ├── WMS (창고관리)           → 물류, 배송                  │
│  └── POS (매장)               → 거래, 결제                  │
│                                                              │
│  [웹/앱]                                                     │
│  ├── 웹 로그 (GA, Adobe)      → 행동, 전환                  │
│  ├── 앱 이벤트 (Amplitude)    → 사용 패턴                   │
│  └── A/B 테스트 (Optimizely)  → 실험 결과                   │
│                                                              │
│  [데이터 웨어하우스]                                         │
│  ├── Snowflake / BigQuery     → 통합 분석                   │
│  └── Data Lake (S3)           → 원시 데이터                 │
└─────────────────────────────────────────────────────────────┘

2. 데이터 품질 6차원

차원질문체크 방법
완전성 (Completeness)필요한 데이터가 다 있나?NULL 비율 체크
정확성 (Accuracy)데이터가 실제와 일치하나?샘플링 검증
일관성 (Consistency)시스템 간 데이터가 일치하나?A vs B 비교
적시성 (Timeliness)데이터가 최신인가?최종 업데이트 확인
유효성 (Validity)데이터 형식이 올바른가?정규식 체크
유일성 (Uniqueness)중복이 없나?중복 레코드 탐지
"""데이터 품질 기본 프로파일링"""
def profile_dataframe(df):
    profile = {
        'row_count': len(df),
        'column_count': len(df.columns),
        'columns': {}
    }

    for col in df.columns:
        col_profile = {
            'dtype': str(df[col].dtype),
            'null_pct': df[col].isnull().mean() * 100,
            'unique_count': df[col].nunique(),
        }

        # 수치형 컬럼 추가 통계
        if df[col].dtype in ['int64', 'float64']:
            col_profile.update({
                'min': df[col].min(),
                'max': df[col].max(),
                'mean': df[col].mean(),
            })

        profile['columns'][col] = col_profile

    return profile

# 품질 이슈 탐지
for col, stats in profile['columns'].items():
    if stats['null_pct'] > 10:
        print(f"⚠️ {col}: NULL 비율 {stats['null_pct']:.1f}%")

3. 결측치 처리

# 삭제
df.dropna()                        # 전체 행 삭제
df.dropna(thresh=len(df)*0.5)      # 50% 이상 결측 시 삭제
df.drop(columns=['col_with_many_na'])  # 컬럼 삭제

# 대체 - 단순
df['num'].fillna(df['num'].mean())     # 평균
df['num'].fillna(df['num'].median())   # 중앙값
df['cat'].fillna(df['cat'].mode()[0])  # 최빈값
df['num'].fillna(method='ffill')       # 앞 값으로 (시계열)

# 대체 - 고급
from sklearn.impute import KNNImputer, SimpleImputer
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer

# KNN 대체
imputer = KNNImputer(n_neighbors=5)
df_imputed = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)

# 다중 대체 (MICE)
imputer = IterativeImputer(random_state=42)
df_imputed = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)

# 그룹별 대체
df['value'] = df.groupby('category')['value'].transform(
    lambda x: x.fillna(x.median())
)
방법장점단점사용 시점
삭제단순데이터 손실결측 5% 미만, MCAR
평균/중앙값단순분산 감소수치형, 적은 결측
KNN관계 고려느림중간 규모 데이터
MICE불확실성 반영복잡다변량 결측

2. 이상치 처리

# Winsorizing (극단값 클리핑)
from scipy.stats import mstats
df['value_winsorized'] = mstats.winsorize(df['value'], limits=[0.05, 0.05])

# Clipping (경계값으로 제한)
lower = df['value'].quantile(0.01)
upper = df['value'].quantile(0.99)
df['value_clipped'] = df['value'].clip(lower, upper)

# 로그 변환 (오른쪽 꼬리 축소)
df['value_log'] = np.log1p(df['value'])  # log(1+x) - 0 처리

# 삭제 (명확한 오류)
df = df[df['age'] <= 120]  # 비현실적 값 제거

# 별도 처리 (분리 모델링)
outliers = df[df['value'] > threshold]
normal = df[df['value'] <= threshold]
방법장점단점사용 시점
제거단순데이터 손실명확한 오류
Winsorize데이터 보존정보 손실극단값 축소
로그 변환분포 정규화해석 변화오른쪽 꼬리
별도 모델정보 보존복잡이상치가 의미 있을 때

3. 데이터 변환

from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
from sklearn.preprocessing import PowerTransformer

# 표준화 (Z-score): 평균 0, 표준편차 1
scaler = StandardScaler()
df['value_std'] = scaler.fit_transform(df[['value']])

# 정규화 (Min-Max): 0~1 범위
scaler = MinMaxScaler()
df['value_norm'] = scaler.fit_transform(df[['value']])

# 로버스트 스케일링: 이상치에 강건 (중앙값, IQR 사용)
scaler = RobustScaler()
df['value_robust'] = scaler.fit_transform(df[['value']])

# 로그 변환
df['value_log'] = np.log1p(df['value'])

# Box-Cox / Yeo-Johnson: 정규분포에 가깝게
pt = PowerTransformer(method='yeo-johnson')  # 음수 포함 가능
df['value_transformed'] = pt.fit_transform(df[['value']])
변환사용 시점알고리즘
StandardScaler대부분SVM, 선형 모델, 신경망
MinMaxScaler0-1 범위 필요이미지, 신경망
RobustScaler이상치 존재대부분
Log/Box-Cox왜곡된 분포선형 모델

4. 범주형 인코딩

from sklearn.preprocessing import LabelEncoder, OneHotEncoder
import category_encoders as ce

# Label Encoding (순서 있는 범주)
le = LabelEncoder()
df['size_encoded'] = le.fit_transform(df['size'])  # S=0, M=1, L=2

# One-Hot Encoding (순서 없는 범주)
df_encoded = pd.get_dummies(df, columns=['color'], drop_first=True)

# Target Encoding (타겟과의 관계 활용)
encoder = ce.TargetEncoder(cols=['category'])
df['category_encoded'] = encoder.fit_transform(df['category'], df['target'])

# Frequency Encoding (빈도 기반)
freq = df['category'].value_counts(normalize=True)
df['category_freq'] = df['category'].map(freq)

# Binary Encoding (고카디널리티)
encoder = ce.BinaryEncoder(cols=['high_cardinality_col'])
df_encoded = encoder.fit_transform(df)
인코딩카디널리티장점단점
One-Hot낮음 (<10)정보 보존차원 폭발
Label순서형단순순서 가정
Target높음예측력 좋음과적합 위험
Binary높음차원 절약정보 손실

실습 과제

과제: 지저분한 데이터 정제 파이프라인

데이터: Kaggle "Spaceship Titanic" 또는 유사한 지저분한 데이터

요구사항:
1. 결측치 처리:
   - 결측 패턴 분석 후 전략 선택
   - 수치형: KNN 또는 그룹별 중앙값
   - 범주형: 최빈값 또는 별도 카테고리
2. 이상치 처리:
   - 탐지 (IQR, Z-score)
   - 전략 선택 및 적용 (근거 포함)
3. 데이터 변환:
   - 왜곡된 변수 식별
   - 적절한 변환 적용
   - 스케일링 적용
4. 인코딩:
   - 범주형 변수 분석
   - 적절한 인코딩 방법 선택
5. sklearn Pipeline으로 구성

산출물:
- Jupyter Notebook
- 처리 전/후 비교 시각화
- sklearn Pipeline 코드
- 각 결정에 대한 근거 문서화

평가 기준

항목통과 기준배점
결측치 처리적절한 전략 + 근거25%
이상치 처리탐지 + 처리 + 근거25%
변환/인코딩올바른 방법 선택20%
Pipeline재사용 가능한 코드20%
문서화결정 근거 명확10%

추천 자료

유형제목링크
Feature Engineering for ML (Zheng & Casari)O'Reilly
코스Kaggle Feature Engineeringhttps://www.kaggle.com/learn/feature-engineering
문서sklearn Preprocessinghttps://scikit-learn.org/stable/modules/preprocessing.html
라이브러리category_encodershttps://contrib.scikit-learn.org/category_encoders/

Week 11: Feature Engineering

학습 목표

  • 수치형 피처에서 새로운 피처를 생성할 수 있다
  • 범주형 피처를 효과적으로 변환할 수 있다
  • 시간 피처를 추출하고 활용할 수 있다
  • 텍스트에서 피처를 추출할 수 있다

핵심 개념

1. 수치형 피처 엔지니어링

# Binning (구간화)
df['age_group'] = pd.cut(df['age'], bins=[0, 18, 35, 55, 100],
                         labels=['청소년', '청년', '중년', '노년'])

# Polynomial Features (다항 특성)
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False)
poly_features = poly.fit_transform(df[['x1', 'x2']])
# x1, x2, x1^2, x1*x2, x2^2

# 상호작용 피처
df['price_per_sqft'] = df['price'] / df['sqft']
df['room_ratio'] = df['bedrooms'] / df['total_rooms']

# 집계 피처 (Groupby)
df['user_avg_spend'] = df.groupby('user_id')['amount'].transform('mean')
df['user_order_count'] = df.groupby('user_id')['order_id'].transform('count')
df['category_median_price'] = df.groupby('category')['price'].transform('median')

Kaggle Grandmaster 팁 (자료조사 반영):

# Groupby Aggregations - 가장 강력한 기법
agg_features = df.groupby('user_id').agg({
    'amount': ['mean', 'std', 'min', 'max', 'sum', 'count'],
    'category': ['nunique'],
    'timestamp': ['min', 'max']
}).reset_index()
agg_features.columns = ['user_id'] + [f'user_{a}_{b}' for a, b in agg_features.columns[1:]]

# 순위 피처
df['amount_rank'] = df.groupby('category')['amount'].rank(pct=True)

2. 범주형 피처 엔지니어링

# 희귀 카테고리 처리
freq = df['category'].value_counts(normalize=True)
rare_categories = freq[freq < 0.01].index
df['category_grouped'] = df['category'].replace(rare_categories, 'Other')

# 카테고리 조합
df['location_type'] = df['city'] + '_' + df['property_type']

# Target 통계 (주의: 데이터 누수 방지)
from sklearn.model_selection import KFold

def target_encode_cv(df, col, target, n_splits=5):
    kf = KFold(n_splits=n_splits, shuffle=True, random_state=42)
    df[f'{col}_target_enc'] = 0

    for train_idx, val_idx in kf.split(df):
        means = df.iloc[train_idx].groupby(col)[target].mean()
        df.loc[val_idx, f'{col}_target_enc'] = df.loc[val_idx, col].map(means)

    return df

3. 시간 피처

df['datetime'] = pd.to_datetime(df['timestamp'])

# 기본 추출
df['year'] = df['datetime'].dt.year
df['month'] = df['datetime'].dt.month
df['day'] = df['datetime'].dt.day
df['dayofweek'] = df['datetime'].dt.dayofweek  # 0=월요일
df['hour'] = df['datetime'].dt.hour
df['is_weekend'] = df['dayofweek'].isin([5, 6]).astype(int)

# 주기성 인코딩 (순환 피처)
df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24)
df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24)
df['month_sin'] = np.sin(2 * np.pi * df['month'] / 12)
df['month_cos'] = np.cos(2 * np.pi * df['month'] / 12)

# Lag 피처 (시계열)
df['sales_lag_1'] = df.groupby('product')['sales'].shift(1)
df['sales_lag_7'] = df.groupby('product')['sales'].shift(7)
df['sales_rolling_7'] = df.groupby('product')['sales'].transform(
    lambda x: x.shift(1).rolling(7).mean()
)

4. 텍스트 피처

from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer

# 기본 통계
df['text_len'] = df['text'].str.len()
df['word_count'] = df['text'].str.split().str.len()
df['avg_word_len'] = df['text'].apply(lambda x: np.mean([len(w) for w in x.split()]))

# TF-IDF
tfidf = TfidfVectorizer(max_features=100, ngram_range=(1, 2))
tfidf_features = tfidf.fit_transform(df['text'])
tfidf_df = pd.DataFrame(tfidf_features.toarray(),
                        columns=[f'tfidf_{w}' for w in tfidf.get_feature_names_out()])

# 특정 키워드 포함 여부
keywords = ['urgent', 'sale', 'free']
for kw in keywords:
    df[f'has_{kw}'] = df['text'].str.lower().str.contains(kw).astype(int)

실습 과제

과제: Kaggle 스타일 Feature Engineering

데이터: Kaggle "House Prices" 또는 유사 테이블 데이터

요구사항:
1. 원본 피처 분석 (EDA 간략히)
2. 수치형 피처 엔지니어링:
   - 비율/조합 피처 5개 이상
   - Binning 2개 이상
   - Groupby 집계 3개 이상
3. 범주형 피처 엔지니어링:
   - 희귀 카테고리 처리
   - 카테고리 조합
   - Target Encoding (CV 방식)
4. 시간 피처 (해당 시):
   - 기본 추출
   - 순환 인코딩
5. 피처 중요도 분석:
   - 상관관계
   - RandomForest feature_importances_
6. 최종 피처 셋 선정 및 근거

산출물:
- Jupyter Notebook
- 피처 50개 이상 생성
- 최종 선정 피처 목록 + 근거
- 베이스라인 모델 성능 비교 (피처 전/후)

평가 기준

항목통과 기준배점
피처 수50개 이상 생성20%
피처 품질의미 있는 피처 비율25%
기법 다양성4가지 유형 이상 활용20%
성능 개선베이스라인 대비 개선25%
문서화피처 설명 및 근거10%

추천 자료

유형제목링크
노트북Kaggle 우승 FE 사례https://www.kaggle.com/code
Feature Engineering for MLO'Reilly
영상Abhishek Thakur - FE Tipshttps://www.youtube.com/c/AbhishekThakurAbhi
블로그NVIDIA Kaggle Grandmaster Tipshttps://developer.nvidia.com/blog/kaggle-grandmasters-unveil-winning-strategies/

Week 12: Feature Selection & 차원 축소

학습 목표

  • Filter, Wrapper, Embedded 방법으로 피처를 선택할 수 있다
  • PCA를 적용하여 차원을 축소할 수 있다
  • t-SNE, UMAP으로 고차원 데이터를 시각화할 수 있다
  • 적절한 피처 수를 결정할 수 있다

핵심 개념

1. Filter Methods

from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif

# 분산 기반
from sklearn.feature_selection import VarianceThreshold
selector = VarianceThreshold(threshold=0.01)
X_filtered = selector.fit_transform(X)

# 상관관계 기반
correlation_matrix = X.corr().abs()
upper = correlation_matrix.where(np.triu(np.ones(correlation_matrix.shape), k=1).astype(bool))
to_drop = [col for col in upper.columns if any(upper[col] > 0.95)]

# 통계 검정 (ANOVA F-value)
selector = SelectKBest(f_classif, k=20)
X_selected = selector.fit_transform(X, y)

# Mutual Information
selector = SelectKBest(mutual_info_classif, k=20)
X_selected = selector.fit_transform(X, y)

2. Wrapper Methods

from sklearn.feature_selection import RFE, RFECV
from sklearn.ensemble import RandomForestClassifier

# Recursive Feature Elimination
estimator = RandomForestClassifier(n_estimators=100, random_state=42)
selector = RFE(estimator, n_features_to_select=20, step=1)
selector.fit(X, y)
selected_features = X.columns[selector.support_]

# RFE with Cross-Validation (최적 피처 수 자동 결정)
selector = RFECV(estimator, step=1, cv=5, scoring='accuracy')
selector.fit(X, y)
print(f"최적 피처 수: {selector.n_features_}")

# Sequential Feature Selection (Forward/Backward)
from sklearn.feature_selection import SequentialFeatureSelector
sfs = SequentialFeatureSelector(estimator, n_features_to_select=20, direction='forward')
sfs.fit(X, y)

3. Embedded Methods

from sklearn.linear_model import LassoCV
from sklearn.ensemble import RandomForestClassifier

# Lasso (L1 Regularization)
lasso = LassoCV(cv=5, random_state=42)
lasso.fit(X, y)
selected_features = X.columns[lasso.coef_ != 0]

# Tree-based Feature Importance
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X, y)
importance = pd.DataFrame({
    'feature': X.columns,
    'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)

# Permutation Importance (더 신뢰성 있음)
from sklearn.inspection import permutation_importance
perm_importance = permutation_importance(rf, X_test, y_test, n_repeats=10)
방법장점단점사용 시점
Filter빠름, 모델 무관피처 상호작용 무시전처리, 빠른 감소
Wrapper정확느림, 과적합 가능작은 피처셋
Embedded빠름, 정확모델 의존대부분 상황

4. 차원 축소

from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
import umap

# PCA
pca = PCA(n_components=0.95)  # 95% 분산 유지
X_pca = pca.fit_transform(X_scaled)
print(f"원본: {X.shape[1]} → PCA: {X_pca.shape[1]}")

# Explained Variance
plt.plot(np.cumsum(pca.explained_variance_ratio_))
plt.xlabel('Number of Components')
plt.ylabel('Cumulative Explained Variance')

# t-SNE (시각화용, 2D/3D)
tsne = TSNE(n_components=2, perplexity=30, random_state=42)
X_tsne = tsne.fit_transform(X_scaled)
plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=y, cmap='viridis')

# UMAP (t-SNE보다 빠르고 구조 보존 좋음)
reducer = umap.UMAP(n_components=2, random_state=42)
X_umap = reducer.fit_transform(X_scaled)
방법용도특징
PCA피처 압축, 노이즈 제거선형, 빠름
t-SNE시각화 (2D/3D)비선형, 느림, 클러스터 강조
UMAP시각화 + 피처비선형, 빠름, 전역 구조 보존

실습 과제

과제: 고차원 데이터 피처 선택 & 시각화

데이터: 고차원 데이터 (피처 50개 이상)
- Kaggle 대회 데이터
- 유전자 발현 데이터
- 센서 데이터

요구사항:
1. Filter Methods:
   - 분산, 상관관계 기반 필터링
   - 결과 비교 (제거된 피처 목록)
2. Wrapper Methods:
   - RFE 또는 SFS 적용
   - 최적 피처 수 결정
3. Embedded Methods:
   - Lasso 또는 Tree Importance
   - Permutation Importance 비교
4. 차원 축소:
   - PCA 적용 (explained variance plot)
   - t-SNE / UMAP 시각화
   - 클러스터 해석
5. 종합 비교:
   - 각 방법으로 선택된 피처 벤 다이어그램
   - 모델 성능 비교 (전체 vs 선택된 피처)

산출물:
- Jupyter Notebook
- 피처 선택 비교 표
- 2D 시각화 (t-SNE/UMAP)
- 최종 추천 피처셋

평가 기준

항목통과 기준배점
Filter 적용2가지 이상 방법20%
Wrapper 적용RFE/SFS 중 1개20%
Embedded 적용Importance 분석20%
차원 축소PCA + 시각화25%
종합 분석성능 비교 및 결론15%

추천 자료

유형제목링크
문서sklearn Feature Selectionhttps://scikit-learn.org/stable/modules/feature_selection.html
영상StatQuest - PCAhttps://www.youtube.com/watch?v=FgakZw6K1QQ
논문UMAP Paperhttps://arxiv.org/abs/1802.03426
도구UMAP Documentationhttps://umap-learn.readthedocs.io/

Month 4: ML 모델링 & 이상탐지


Week 13: 가설 기반 분석 & 분류/회귀

학습 목표

  • 가설 기반 분석 접근법을 적용할 수 있다
  • 상관관계와 인과관계를 구분할 수 있다
  • 분류/회귀 문제를 구분하고 적절한 알고리즘을 선택할 수 있다
  • XGBoost, LightGBM을 효과적으로 사용할 수 있다

핵심 개념

1. 가설 기반 분석 (Hypothesis-Driven)

모델링 전에 "왜 이 모델을 만드는가?"를 명확히 해야 합니다.

┌─────────────────────────────────────────────────────────────┐
│                  가설 기반 분석 프로세스                      │
├─────────────────────────────────────────────────────────────┤
│  1. 문제 정의                                                │
│     "재구매율이 30% 하락한 이유는?"                          │
│              │                                               │
│              ▼                                               │
│  2. 가설 수립                                                │
│     H1: 배송 시간 증가 → 만족도 하락 → 재구매 감소          │
│     H2: 경쟁사 프로모션 → 고객 이탈                         │
│     H3: 제품 품질 하락 → 반품 증가 → 재구매 감소            │
│              │                                               │
│              ▼                                               │
│  3. 데이터 분석 (검증)                                       │
│     각 가설에 대한 증거 수집                                 │
│              │                                               │
│              ▼                                               │
│  4. 결론 도출                                                │
│     "H1 지지: 배송 시간 3일→5일, 재구매율 -25%p 상관"       │
└─────────────────────────────────────────────────────────────┘

좋은 가설의 조건

  • ✅ 구체적: "배송 3일 이상이면 재구매율 20% 하락"
  • ✅ 측정 가능: 데이터로 검증 가능
  • ✅ 행동 가능: 검증 결과에 따라 행동 가능
  • ❌ 나쁜 예: "고객이 불만족해서 그렇다" (너무 모호)

2. 상관관계 vs 인과관계

# 상관관계 ≠ 인과관계
# 예시: 아이스크림 판매량과 익사 사고 (상관계수 0.95)
# 하지만 인과관계? ❌
# 진짜 원인: 여름 (더운 날씨 → 수영 증가 + 아이스크림 소비 증가)

# 인과관계 판단 기준
# 1. 시간적 선후관계: 원인이 결과보다 먼저 발생
# 2. 상관관계: 통계적 연관성 존재
# 3. 다른 설명 배제: 교란변수 통제
# 4. 실험: 무작위 통제 실험 (Gold Standard)

3. 분류 알고리즘

from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
import xgboost as xgb
import lightgbm as lgb

# Logistic Regression
lr = LogisticRegression(max_iter=1000, random_state=42)
lr.fit(X_train, y_train)

# Random Forest
rf = RandomForestClassifier(
    n_estimators=100,
    max_depth=10,
    min_samples_split=5,
    random_state=42,
    n_jobs=-1
)

# XGBoost
xgb_model = xgb.XGBClassifier(
    n_estimators=100,
    max_depth=6,
    learning_rate=0.1,
    subsample=0.8,
    colsample_bytree=0.8,
    random_state=42,
    use_label_encoder=False,
    eval_metric='logloss'
)

# LightGBM (가장 빠름)
lgb_model = lgb.LGBMClassifier(
    n_estimators=100,
    max_depth=6,
    learning_rate=0.1,
    num_leaves=31,
    random_state=42
)

2. 회귀 알고리즘

from sklearn.linear_model import Ridge, Lasso, ElasticNet
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor

# Ridge Regression (L2)
ridge = Ridge(alpha=1.0)

# Lasso (L1, 피처 선택 효과)
lasso = Lasso(alpha=0.1)

# ElasticNet (L1 + L2)
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5)

# XGBoost Regressor
xgb_reg = xgb.XGBRegressor(
    n_estimators=100,
    max_depth=6,
    learning_rate=0.1,
    random_state=42
)

3. 교차 검증 & 튜닝

from sklearn.model_selection import cross_val_score, GridSearchCV, RandomizedSearchCV
from sklearn.model_selection import StratifiedKFold
import optuna

# K-Fold Cross Validation
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=cv, scoring='f1')
print(f"F1: {scores.mean():.4f} (+/- {scores.std()*2:.4f})")

# Grid Search
param_grid = {
    'n_estimators': [100, 200],
    'max_depth': [5, 10, 15],
    'learning_rate': [0.01, 0.1]
}
grid_search = GridSearchCV(xgb_model, param_grid, cv=5, scoring='f1', n_jobs=-1)
grid_search.fit(X_train, y_train)

# Optuna (더 효율적)
def objective(trial):
    params = {
        'n_estimators': trial.suggest_int('n_estimators', 50, 300),
        'max_depth': trial.suggest_int('max_depth', 3, 15),
        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3, log=True),
    }
    model = lgb.LGBMClassifier(**params, random_state=42)
    score = cross_val_score(model, X_train, y_train, cv=5, scoring='f1').mean()
    return score

study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)

4. 평가 지표

from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    roc_auc_score, confusion_matrix, classification_report,
    mean_squared_error, mean_absolute_error, r2_score
)

# 분류 지표
y_pred = model.predict(X_test)
y_prob = model.predict_proba(X_test)[:, 1]

print(classification_report(y_test, y_pred))
print(f"ROC-AUC: {roc_auc_score(y_test, y_prob):.4f}")

# Confusion Matrix 시각화
from sklearn.metrics import ConfusionMatrixDisplay
ConfusionMatrixDisplay.from_predictions(y_test, y_pred)

# 회귀 지표
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.4f}")
print(f"MAE: {mean_absolute_error(y_test, y_pred):.4f}")
print(f"R²: {r2_score(y_test, y_pred):.4f}")
문제 유형지표사용 시점
분류 (균형)Accuracy, F1클래스 균형
분류 (불균형)F1, AUC, Recall이상탐지, 사기탐지
회귀RMSE, MAE연속값 예측
랭킹NDCG, MRR추천, 검색

실습 과제

과제: 고객 이탈 예측 모델

데이터: Telco Customer Churn 또는 유사 데이터

요구사항:
1. 데이터 전처리 (Week 10-12 기법 적용)
2. 베이스라인 모델 (Logistic Regression)
3. 앙상블 모델:
   - Random Forest
   - XGBoost
   - LightGBM
4. 하이퍼파라미터 튜닝 (Optuna 권장)
5. 모델 비교:
   - 5-Fold CV 성능
   - ROC Curve 비교
   - 혼동 행렬
6. 피처 중요도 분석
7. 최종 모델 선택 및 근거

산출물:
- Jupyter Notebook
- 모델 성능 비교 표
- ROC Curve 그래프
- 비즈니스 인사이트 (이탈 요인 분석)

평가 기준

항목통과 기준배점
전처리체계적 파이프라인15%
모델링3개 이상 모델25%
튜닝CV + 하이퍼파라미터25%
평가적절한 지표 + 비교20%
해석비즈니스 인사이트15%

추천 자료

유형제목링크
문서XGBoost Documentationhttps://xgboost.readthedocs.io/
문서LightGBM Documentationhttps://lightgbm.readthedocs.io/
문서Optuna Tutorialhttps://optuna.org/
영상StatQuest - XGBoosthttps://www.youtube.com/watch?v=OtD8wVaFm6E

Week 14: 클러스터링 & 세그멘테이션

학습 목표

  • K-means 클러스터링을 적용하고 최적 K를 결정할 수 있다
  • 계층적 클러스터링과 덴드로그램을 해석할 수 있다
  • DBSCAN으로 밀도 기반 클러스터링을 수행할 수 있다
  • RFM 분석으로 고객을 세그멘테이션할 수 있다

핵심 개념

1. K-means

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

# Elbow Method
inertias = []
silhouettes = []
K_range = range(2, 11)

for k in K_range:
    kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
    kmeans.fit(X_scaled)
    inertias.append(kmeans.inertia_)
    silhouettes.append(silhouette_score(X_scaled, kmeans.labels_))

# 시각화
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].plot(K_range, inertias, 'bo-')
axes[0].set_title('Elbow Method')
axes[1].plot(K_range, silhouettes, 'ro-')
axes[1].set_title('Silhouette Score')

# 최종 모델
kmeans = KMeans(n_clusters=optimal_k, random_state=42)
df['cluster'] = kmeans.fit_predict(X_scaled)

2. 계층적 클러스터링

from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from sklearn.cluster import AgglomerativeClustering

# Dendrogram
Z = linkage(X_scaled, method='ward')
plt.figure(figsize=(12, 6))
dendrogram(Z, truncate_mode='lastp', p=30)
plt.title('Hierarchical Clustering Dendrogram')

# 클러스터 추출
clusters = fcluster(Z, t=4, criterion='maxclust')  # 4개 클러스터

# sklearn 방식
agg = AgglomerativeClustering(n_clusters=4, linkage='ward')
df['cluster'] = agg.fit_predict(X_scaled)

3. DBSCAN

from sklearn.cluster import DBSCAN
from sklearn.neighbors import NearestNeighbors

# eps 결정 (k-distance plot)
k = 5
nn = NearestNeighbors(n_neighbors=k)
nn.fit(X_scaled)
distances, _ = nn.kneighbors(X_scaled)
distances = np.sort(distances[:, k-1])
plt.plot(distances)
plt.title('K-Distance Plot (Elbow = eps)')

# DBSCAN
dbscan = DBSCAN(eps=0.5, min_samples=5)
df['cluster'] = dbscan.fit_predict(X_scaled)

# 노이즈 포인트
noise_points = df[df['cluster'] == -1]
print(f"노이즈: {len(noise_points)} ({len(noise_points)/len(df)*100:.1f}%)")
알고리즘장점단점사용 시점
K-means빠름, 확장성K 사전 지정, 구형 가정대부분 상황
계층적덴드로그램, K 불필요느림, 대용량 불가소규모, 탐색적
DBSCAN임의 형태, 이상치 탐지밀도 차이에 약함노이즈 있는 데이터

4. RFM 분석

# RFM 계산
snapshot_date = df['order_date'].max() + pd.Timedelta(days=1)

rfm = df.groupby('customer_id').agg({
    'order_date': lambda x: (snapshot_date - x.max()).days,  # Recency
    'order_id': 'count',                                      # Frequency
    'amount': 'sum'                                           # Monetary
}).rename(columns={
    'order_date': 'Recency',
    'order_id': 'Frequency',
    'amount': 'Monetary'
})

# 점수화 (1-5)
rfm['R_Score'] = pd.qcut(rfm['Recency'], 5, labels=[5,4,3,2,1])  # 낮을수록 좋음
rfm['F_Score'] = pd.qcut(rfm['Frequency'].rank(method='first'), 5, labels=[1,2,3,4,5])
rfm['M_Score'] = pd.qcut(rfm['Monetary'].rank(method='first'), 5, labels=[1,2,3,4,5])

rfm['RFM_Score'] = rfm['R_Score'].astype(str) + rfm['F_Score'].astype(str) + rfm['M_Score'].astype(str)

# 세그먼트 정의
def segment(row):
    r, f, m = int(row['R_Score']), int(row['F_Score']), int(row['M_Score'])
    if r >= 4 and f >= 4:
        return 'Champions'
    elif r >= 3 and f >= 3:
        return 'Loyal'
    elif r >= 4 and f <= 2:
        return 'New Customers'
    elif r <= 2 and f >= 3:
        return 'At Risk'
    elif r <= 2 and f <= 2:
        return 'Lost'
    else:
        return 'Others'

rfm['Segment'] = rfm.apply(segment, axis=1)

실습 과제

과제: 고객 세그멘테이션 프로젝트

데이터: 이커머스 주문 데이터

요구사항:
1. RFM 분석:
   - RFM 점수 계산
   - 세그먼트 정의 (5개 이상)
   - 세그먼트별 특성 분석
2. 클러스터링:
   - K-means (Elbow, Silhouette)
   - 계층적 (Dendrogram)
   - DBSCAN (eps 결정)
3. 클러스터 프로파일링:
   - 각 클러스터 특성 요약
   - 시각화 (PCA/t-SNE 2D)
4. RFM vs 클러스터링 비교
5. 비즈니스 액션 제안:
   - 세그먼트별 마케팅 전략
   - 우선순위 고객 그룹

산출물:
- Jupyter Notebook
- 세그먼트 프로파일 테이블
- 클러스터 시각화
- 마케팅 전략 제안서

평가 기준

항목통과 기준배점
RFM 분석점수화 + 세그먼트25%
클러스터링3가지 방법 비교25%
프로파일링클러스터 특성 명확20%
시각화2D 시각화 + 해석15%
전략 제안실행 가능한 제안15%

추천 자료

유형제목링크
영상StatQuest - Clusteringhttps://www.youtube.com/watch?v=4b5d3muPQmA
문서sklearn Clusteringhttps://scikit-learn.org/stable/modules/clustering.html
블로그RFM Analysis Guidehttps://clevertap.com/blog/rfm-analysis/

Week 15: 이상 탐지 (Anomaly Detection)

학습 목표

  • 다양한 이상탐지 기법을 이해하고 적용할 수 있다
  • Isolation Forest로 이상치를 탐지할 수 있다
  • Autoencoder 기반 이상탐지를 구현할 수 있다
  • 적절한 임계값을 설정할 수 있다

핵심 개념

1. 통계적 방법

# Z-score
from scipy import stats
z_scores = np.abs(stats.zscore(df[numerical_cols]))
outliers = (z_scores > 3).any(axis=1)

# IQR
Q1 = df[numerical_cols].quantile(0.25)
Q3 = df[numerical_cols].quantile(0.75)
IQR = Q3 - Q1
outliers = ((df[numerical_cols] < (Q1 - 1.5 * IQR)) |
            (df[numerical_cols] > (Q3 + 1.5 * IQR))).any(axis=1)

# Mahalanobis Distance (다변량)
from scipy.spatial.distance import mahalanobis
mean = df[numerical_cols].mean()
cov = df[numerical_cols].cov()
cov_inv = np.linalg.inv(cov)
df['mahalanobis'] = df[numerical_cols].apply(
    lambda x: mahalanobis(x, mean, cov_inv), axis=1
)

2. 밀도 기반 (LOF, DBSCAN)

from sklearn.neighbors import LocalOutlierFactor

# Local Outlier Factor
lof = LocalOutlierFactor(n_neighbors=20, contamination=0.05)
df['lof_outlier'] = lof.fit_predict(X_scaled)  # -1: 이상, 1: 정상
df['lof_score'] = -lof.negative_outlier_factor_  # 점수 (높을수록 이상)

# DBSCAN (노이즈 = 이상)
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=0.5, min_samples=5)
df['dbscan_outlier'] = dbscan.fit_predict(X_scaled)
outliers = df[df['dbscan_outlier'] == -1]

3. Isolation Forest

from sklearn.ensemble import IsolationForest

# 학습
iso_forest = IsolationForest(
    n_estimators=100,
    contamination=0.05,  # 예상 이상치 비율
    random_state=42
)
df['iso_outlier'] = iso_forest.fit_predict(X_scaled)  # -1: 이상
df['iso_score'] = -iso_forest.decision_function(X_scaled)  # 점수

# 시각화
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[0].scatter(X_scaled[:, 0], X_scaled[:, 1],
                c=df['iso_outlier'], cmap='RdBu')
axes[0].set_title('Isolation Forest Results')

# 점수 분포
axes[1].hist(df['iso_score'], bins=50)
axes[1].axvline(np.percentile(df['iso_score'], 95), color='r', linestyle='--')
axes[1].set_title('Anomaly Score Distribution')

4. Autoencoder

import tensorflow as tf
from tensorflow.keras import layers, Model

# Autoencoder 정의
class Autoencoder(Model):
    def __init__(self, input_dim, latent_dim=8):
        super().__init__()
        self.encoder = tf.keras.Sequential([
            layers.Dense(32, activation='relu'),
            layers.Dense(16, activation='relu'),
            layers.Dense(latent_dim, activation='relu')
        ])
        self.decoder = tf.keras.Sequential([
            layers.Dense(16, activation='relu'),
            layers.Dense(32, activation='relu'),
            layers.Dense(input_dim, activation='sigmoid')
        ])

    def call(self, x):
        encoded = self.encoder(x)
        decoded = self.decoder(encoded)
        return decoded

# 학습 (정상 데이터만)
autoencoder = Autoencoder(input_dim=X_train.shape[1])
autoencoder.compile(optimizer='adam', loss='mse')
autoencoder.fit(X_train_normal, X_train_normal,
                epochs=50, batch_size=32, validation_split=0.1)

# 이상 탐지 (재구성 오차)
reconstructed = autoencoder.predict(X_test)
mse = np.mean((X_test - reconstructed) ** 2, axis=1)
threshold = np.percentile(mse, 95)
df['ae_outlier'] = (mse > threshold).astype(int)

실무 사례 (자료조사 반영):

기업방법성과
MastercardDecision Intelligence탐지율 300% 향상, 오탐 85% 감소
PayPalIsolation Forest수백만 사용자 보호
제조업Autoencoder다운타임 40% 감소

실습 과제

과제: 금융 사기 탐지 시스템

데이터: Kaggle Credit Card Fraud Detection

요구사항:
1. 데이터 분석:
   - 클래스 불균형 확인
   - 정상 vs 사기 특성 비교
2. 이상탐지 모델:
   - Z-score / IQR (베이스라인)
   - Isolation Forest
   - LOF
   - Autoencoder
3. 모델 비교:
   - Precision, Recall, F1 (특히 Recall 중요)
   - ROC-AUC
   - Precision-Recall Curve
4. 임계값 조정:
   - 비즈니스 요구사항에 맞는 임계값
   - Precision-Recall 트레이드오프
5. 앙상블:
   - 여러 모델 결합
   - 투표 또는 스태킹

산출물:
- Jupyter Notebook
- 모델 성능 비교 표
- 임계값 분석 그래프
- 운영 가이드 (임계값 조정 방법)

평가 기준

항목통과 기준배점
데이터 분석불균형 처리 전략15%
모델 구현4개 방법 모두30%
평가적절한 지표 사용20%
임계값비즈니스 관점 분석20%
앙상블성능 개선15%

추천 자료

유형제목링크
데이터Credit Card Fraudhttps://www.kaggle.com/mlg-ulb/creditcardfraud
문서PyOD Libraryhttps://pyod.readthedocs.io/
논문Isolation Foresthttps://cs.nju.edu.cn/zhouzh/zhouzh.files/publication/icdm08b.pdf
영상Autoencoder for ADhttps://www.youtube.com/watch?v=2K3ScZp1dXQ

Week 16: 시계열 분석

학습 목표

  • 시계열 데이터의 구성 요소를 분해할 수 있다
  • 정상성을 검정하고 변환할 수 있다
  • Prophet으로 예측 모델을 구축할 수 있다
  • ML 기반 시계열 예측을 수행할 수 있다

핵심 개념

1. 시계열 분해

from statsmodels.tsa.seasonal import seasonal_decompose

# 시계열 분해
result = seasonal_decompose(df['value'], model='additive', period=7)

fig, axes = plt.subplots(4, 1, figsize=(12, 10))
result.observed.plot(ax=axes[0], title='Original')
result.trend.plot(ax=axes[1], title='Trend')
result.seasonal.plot(ax=axes[2], title='Seasonal')
result.resid.plot(ax=axes[3], title='Residual')
plt.tight_layout()

2. 정상성 검정 & 변환

from statsmodels.tsa.stattools import adfuller, kpss

# ADF 검정 (귀무가설: 비정상)
result = adfuller(df['value'])
print(f"ADF Statistic: {result[0]:.4f}")
print(f"p-value: {result[1]:.4f}")  # < 0.05면 정상

# 차분 (differencing)
df['value_diff'] = df['value'].diff()
df['value_diff2'] = df['value'].diff().diff()

# 로그 변환 + 차분
df['value_log_diff'] = np.log(df['value']).diff()

3. Prophet

from prophet import Prophet

# 데이터 준비 (Prophet 형식)
df_prophet = df.rename(columns={'date': 'ds', 'value': 'y'})

# 모델 학습
model = Prophet(
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False,
    changepoint_prior_scale=0.05  # 트렌드 유연성
)
model.fit(df_prophet)

# 예측
future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)

# 시각화
model.plot(forecast)
model.plot_components(forecast)

# 휴일 효과 추가
holidays = pd.DataFrame({
    'holiday': 'custom_holiday',
    'ds': pd.to_datetime(['2024-01-01', '2024-12-25']),
    'lower_window': 0,
    'upper_window': 1
})
model = Prophet(holidays=holidays)

4. ML 기반 시계열 (Lag Features)

# Lag Features 생성
def create_lag_features(df, target_col, lags):
    for lag in lags:
        df[f'{target_col}_lag_{lag}'] = df[target_col].shift(lag)
    return df

df = create_lag_features(df, 'value', [1, 7, 14, 30])

# Rolling Features
df['value_rolling_7_mean'] = df['value'].shift(1).rolling(7).mean()
df['value_rolling_7_std'] = df['value'].shift(1).rolling(7).std()
df['value_rolling_30_mean'] = df['value'].shift(1).rolling(30).mean()

# 시간 특성
df['dayofweek'] = df['date'].dt.dayofweek
df['month'] = df['date'].dt.month
df['is_weekend'] = df['dayofweek'].isin([5, 6]).astype(int)

# TimeSeriesSplit
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)

scores = []
for train_idx, val_idx in tscv.split(X):
    X_train, X_val = X.iloc[train_idx], X.iloc[val_idx]
    y_train, y_val = y.iloc[train_idx], y.iloc[val_idx]

    model.fit(X_train, y_train)
    pred = model.predict(X_val)
    scores.append(mean_absolute_error(y_val, pred))

자료조사 반영 - 도구 비교:

도구특징추천 상황
Prophet쉬움, 휴일/이벤트 처리빠른 베이스라인
NeuralProphetProphet + Deep Learning더 정확한 예측
TimeGPTZero-shot, 사전학습매우 빠른 프로토타입
LightGBM + Lag유연성, 해석 가능복잡한 피처 필요 시

실습 과제

과제: 수요 예측 모델 (포트폴리오 #2)

데이터: 소매 판매 데이터 (Kaggle Store Sales 또는 유사)

요구사항:
1. 시계열 분석:
   - 분해 (추세, 계절성, 잔차)
   - 정상성 검정
   - ACF/PACF 분석
2. 전통 모델:
   - Prophet (기본)
   - Prophet + 휴일/이벤트
3. ML 모델:
   - Lag/Rolling 피처 생성
   - LightGBM + TimeSeriesSplit
4. 모델 비교:
   - MAE, MAPE
   - 시각화 (예측 vs 실제)
5. 비즈니스 활용:
   - 재고 관리 관점 해석
   - 신뢰 구간 활용

산출물:
- **포트폴리오 #2: 수요 예측 모델**
- Jupyter Notebook
- 모델 성능 비교
- 예측 대시보드 (Streamlit 선택)

평가 기준

항목통과 기준배점
EDA분해 + 정상성 분석20%
Prophet기본 + 휴일 모델25%
ML 모델Lag 피처 + 교차검증25%
비교 분석적절한 지표 + 시각화20%
비즈니스실행 가능한 인사이트10%

추천 자료

유형제목링크
문서Prophet Documentationhttps://facebook.github.io/prophet/
문서NeuralProphethttps://neuralprophet.com/
영상StatQuest - Time Serieshttps://www.youtube.com/watch?v=DeORzP0go5I
KaggleStore Sales Competitionhttps://www.kaggle.com/c/store-sales-time-series-forecasting

💬 데이터 커뮤니케이션 (Phase 2 전체 적용)

Phase 2의 모든 실습 과제에는 발표/보고서 요소가 포함됩니다.

기술 → 비즈니스 번역

❌ 기술 언어:
"Random Forest 모델의 AUC가 0.85로 Logistic Regression 대비
0.12 향상되었습니다."

✅ 비즈니스 언어:
"고객 이탈 예측 정확도가 85%입니다. 이탈 위험 고객 10명 중 8.5명을
정확히 식별할 수 있어, 선제적 리텐션 캠페인이 가능합니다."

Pyramid Principle (결론 먼저)

전통적 방식 ❌:
"데이터를 수집했고, 정제했고, 분석했고... 결론은 X입니다."

Pyramid 방식 ✅:
"결론: 배송 시간을 2일 단축하면 재구매율이 15% 증가합니다.

왜냐하면:
1. 배송 시간과 재구매율의 상관관계 -0.6 (강한 음의 상관)
2. 고객 설문: 불만족 1위 = 배송 지연 (43%)
3. 경쟁사 비교: 우리 5일 vs 경쟁사 3일"

이해관계자별 커뮤니케이션

대상관심사형식
경영진ROI, 리스크1페이지 요약, 3분 발표
현업 팀실행 가능성구체적 가이드, 데모
기술 팀구현 방법기술 문서, 아키텍처

Phase 2 완료 기준

필수 산출물

  1. Week 9: 문제 정의서 + EDA 리포트
  2. Week 10: 데이터 품질 평가 + 정제 파이프라인
  3. Week 11: Feature Engineering 노트북
  4. Week 12: 피처 선택 & 차원 축소 분석
  5. Week 13: 가설 검증 + 고객 이탈 예측 모델
  6. Week 14: 고객 세그멘테이션 + 마케팅 전략 제안
  7. Week 15: 이상탐지 시스템
  8. Week 16: 포트폴리오 #2 - 수요 예측 모델 + 경영진 보고서

역량 체크리스트

분석 역량

  • 비즈니스 문제를 구조화하여 정의할 수 있다 (5 Whys, MECE)
  • 가설 기반 분석 접근법을 적용할 수 있다
  • 데이터에서 인사이트를 추출하고 시각화할 수 있다
  • 결측치, 이상치를 적절히 처리할 수 있다
  • 피처를 생성하고 선택할 수 있다

모델링 역량

  • 분류/회귀 모델을 구축하고 평가할 수 있다
  • 클러스터링으로 세그멘테이션을 수행할 수 있다
  • 이상탐지 시스템을 구축할 수 있다
  • 시계열 예측을 수행할 수 있다

커뮤니케이션 역량

  • 기술 내용을 비즈니스 언어로 번역할 수 있다
  • Pyramid Principle로 보고서를 작성할 수 있다
  • 이해관계자 맞춤 발표를 할 수 있다

Phase 2 완료 → Phase 3: Knowledge Graph로 이동