Skip to main content

단변량 분석

단변량 분석(Univariate Analysis)은 한 번에 하나의 변수를 분석하여 분포 형태, 중심 경향, 이상치를 파악하는 과정입니다. 모든 EDA의 출발점으로, 각 변수의 특성을 이해해야 다변량 분석과 전처리 전략을 수립할 수 있습니다.

학습 목표

  • 수치형 변수의 분포를 히스토그램과 KDE로 파악할 수 있다
  • 범주형 변수의 빈도와 비율을 시각화할 수 있다
  • IQR 방법과 Z-score로 이상치를 탐지할 수 있다
  • 변수 유형에 맞는 분석 방법을 선택할 수 있다

왜 중요한가

단변량 분석 결과는 전처리의 근거입니다. 편향된 분포는 로그 변환이 필요하고, 이상치는 모델에 큰 영향을 줍니다. 범주형 변수의 불균형은 샘플링 전략에 영향을 미칩니다.

수치형 변수 분석

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

np.random.seed(42)
df = pd.DataFrame({
    'salary': np.concatenate([
        np.random.normal(4500, 800, 180),
        np.random.normal(12000, 2000, 20)  # 고소득 그룹
    ]),
    'experience': np.random.uniform(1, 25, 200),
    'performance': np.random.normal(75, 10, 200),
    'department': np.random.choice(['개발', '영업', '마케팅', '인사'], 200),
    'satisfaction': np.random.choice(['높음', '보통', '낮음'], 200, p=[0.4, 0.35, 0.25])
})

# 수치형 변수 요약
numeric_cols = ['salary', 'experience', 'performance']
summary = df[numeric_cols].describe().T
summary['skew'] = df[numeric_cols].skew()
summary['kurtosis'] = df[numeric_cols].kurtosis()
print(summary.round(2))

분포 시각화

fig, axes = plt.subplots(2, 3, figsize=(15, 8))

for i, col in enumerate(numeric_cols):
    # 히스토그램 + KDE
    sns.histplot(df[col], kde=True, ax=axes[0, i], color='#4a9eca', alpha=0.7)
    axes[0, i].axvline(df[col].mean(), color='red', linestyle='--', label='평균')
    axes[0, i].axvline(df[col].median(), color='green', linestyle='-', label='중앙값')
    axes[0, i].set_title(f'{col} 분포')
    axes[0, i].legend(fontsize=8)

    # 박스플롯
    sns.boxplot(y=df[col], ax=axes[1, i], color='#4a9eca')
    axes[1, i].set_title(f'{col} 박스플롯')

plt.suptitle('수치형 변수 단변량 분석')
plt.tight_layout()
plt.show()

분포 형태 판단

특성판단 기준전처리 제안
정규 분포왜도 약 0, 종 모양StandardScaler
오른쪽 치우침왜도 > 1, 긴 오른쪽 꼬리로그 변환, 제곱근 변환
왼쪽 치우침왜도 < -1, 긴 왼쪽 꼬리제곱 변환
다봉 분포2개 이상의 봉우리그룹 분리 후 분석
균등 분포평평한 형태MinMaxScaler

범주형 변수 분석

cat_cols = ['department', 'satisfaction']

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

for ax, col in zip(axes, cat_cols):
    # 빈도 + 비율 시각화
    counts = df[col].value_counts()
    bars = ax.bar(counts.index, counts.values, color='#4a9eca', alpha=0.7)

    # 비율 레이블 추가
    for bar, count in zip(bars, counts.values):
        pct = count / len(df) * 100
        ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 2,
                f'{pct:.1f}%', ha='center', fontsize=10)

    ax.set_title(f'{col} 빈도 분포')
    ax.set_ylabel('빈도')

plt.tight_layout()
plt.show()

# 빈도 테이블
for col in cat_cols:
    print(f"\n{col} 빈도:")
    freq = pd.DataFrame({
        '빈도': df[col].value_counts(),
        '비율(%)': (df[col].value_counts(normalize=True) * 100).round(1)
    })
    print(freq)

범주형 변수 체크포인트

for col in cat_cols:
    n_unique = df[col].nunique()
    top_value = df[col].value_counts().index[0]
    top_pct = df[col].value_counts(normalize=True).values[0] * 100

    print(f"{col}:")
    print(f"  고유값 수: {n_unique}")
    print(f"  최빈값: {top_value} ({top_pct:.1f}%)")

    # 클래스 불균형 경고
    if top_pct > 80:
        print(f"  ⚠ 클래스 불균형 주의: {top_value}{top_pct:.0f}% 차지")

이상치 탐지

IQR 방법

def detect_outliers_iqr(series, k=1.5):
    """IQR 방법으로 이상치를 탐지합니다."""
    q1 = series.quantile(0.25)
    q3 = series.quantile(0.75)
    iqr = q3 - q1
    lower = q1 - k * iqr
    upper = q3 + k * iqr
    outliers = series[(series < lower) | (series > upper)]
    return outliers, lower, upper

for col in numeric_cols:
    outliers, lower, upper = detect_outliers_iqr(df[col])
    print(f"{col}: 이상치 {len(outliers)}개 ({len(outliers)/len(df)*100:.1f}%)")
    print(f"  정상 범위: [{lower:.0f}, {upper:.0f}]")

Z-score 방법

from scipy import stats

def detect_outliers_zscore(series, threshold=3):
    """Z-score 방법으로 이상치를 탐지합니다."""
    z_scores = np.abs(stats.zscore(series))
    outliers = series[z_scores > threshold]
    return outliers

for col in numeric_cols:
    outliers = detect_outliers_zscore(df[col])
    print(f"{col}: Z-score 이상치 {len(outliers)}개 (|Z| > 3)")

이상치 시각화

fig, ax = plt.subplots(figsize=(10, 5))

# salary의 이상치 시각화
outliers_iqr, lower, upper = detect_outliers_iqr(df['salary'])
normal = df['salary'][(df['salary'] >= lower) & (df['salary'] <= upper)]

ax.hist(normal, bins=30, color='#4a9eca', alpha=0.7, label='정상 데이터')
ax.hist(outliers_iqr, bins=10, color='#e6a23c', alpha=0.7, label='이상치')
ax.axvline(lower, color='red', linestyle='--', label=f'하한: {lower:.0f}')
ax.axvline(upper, color='red', linestyle='--', label=f'상한: {upper:.0f}')
ax.set_title('salary 이상치 탐지 (IQR 방법)')
ax.legend()
plt.tight_layout()
plt.show()
방법기준장점단점
IQRQ1-1.5IQR ~ Q3+1.5IQR분포 가정 없음, 강건비대칭 분포에서 한쪽만 감지
Z-scoreabs(Z) > 3직관적, 표준화 기반정규분포 가정 필요
Modified ZMAD 기반이상치에 강건계산 복잡

AI/ML에서의 활용

  • 피처 전처리: 분포 형태에 따라 변환(로그, 제곱근)과 스케일링 전략을 결정합니다
  • 이상치 처리: 이상치를 제거할지, 대체할지, 그대로 둘지 결정합니다
  • 클래스 불균형: 타겟 변수의 불균형 비율을 파악하여 샘플링 전략을 결정합니다
  • 피처 엔지니어링: 다봉 분포는 그룹 변수를 생성하고, 편향 분포는 비닝(binning)을 고려합니다
아닙니다. 이상치가 데이터 입력 오류인 경우 제거하지만, 실제로 극단적인 값(예: VIP 고객의 높은 구매액)인 경우 중요한 정보일 수 있습니다. 도메인 지식을 바탕으로 판단하세요.

체크리스트

  • 수치형 변수의 분포를 시각화하고 형태를 판단할 수 있다
  • 범주형 변수의 빈도와 비율을 분석할 수 있다
  • IQR과 Z-score로 이상치를 탐지할 수 있다
  • 분석 결과를 전처리 전략으로 연결할 수 있다

다음 문서