탐색적 데이터 분석(Exploratory Data Analysis, EDA)은 데이터 분석의 첫 단계로, 데이터셋의 주요 특성을 요약하고 이해하는 과정입니다. EDA는 주로 시각적 방법을 사용하여 데이터의 패턴, 관계, 이상치 등을 발견하는 것을 목표로 합니다.
EDA는 다음과 같은 이유로 중요합니다:
일반적인 EDA 프로세스는 다음 단계를 포함합니다:
1. 데이터 이해하기
2. 데이터 준비 및 정제
3. 기술 통계 분석
4. 데이터 시각화
5. 상관관계 분석
6. 그룹별 분석
7. 가설 생성 및 검정
데이터의 출처와 수집 방법을 이해하는 것은 EDA의 첫 단계입니다. 이는 데이터의 신뢰성과 한계를 파악하는 데 도움이 됩니다.
고려해야 할 사항:
데이터셋의 각 변수 유형을 식별하는 것은 적절한 분석 방법을 선택하는 데 중요합니다.
주요 변수 유형:
1. 수치형 변수
예시 코드 (Python):
import pandas as pd
# 데이터 로드
df = pd.read_csv('your_data.csv')
# 변수 유형 확인
print(df.dtypes)
# 범주형 변수의 고유값 확인
for col in df.select_dtypes(include=['object']).columns:
print(f"\n{col} 고유값:")
print(df[col].value_counts())
메타데이터는 '데이터에 대한 데이터'로, 각 변수의 의미, 단위, 제약 조건 등을 설명합니다.
검토해야 할 메타데이터:
메타데이터 검토는 데이터 해석 오류를 방지하고, 적절한 전처리 방법을 선택하는 데 도움이 됩니다.
첫 단계는 데이터를 분석 환경으로 가져오는 것입니다.
Python 예시:
import pandas as pd
# CSV 파일 로드
df = pd.read_csv('your_data.csv')
# Excel 파일 로드
# df = pd.read_excel('your_data.xlsx')
# 데이터 미리보기
print(df.head())
# 데이터 정보 확인
print(df.info())
결측치는 분석 결과를 왜곡할 수 있으므로 적절히 처리해야 합니다.
결측치 처리 방법:
1. 제거: 결측치가 있는 행 삭제
2. 대체: 평균, 중앙값, 최빈값 등으로 대체
3. 예측: 머신러닝 모델을 사용하여 결측치 예측
Python 예시:
# 결측치 확인
print(df.isnull().sum())
# 결측치 제거
df_cleaned = df.dropna()
# 결측치 대체 (수치형 변수는 평균, 범주형 변수는 최빈값으로)
df_imputed = df.copy()
for col in df.columns:
if df[col].dtype in ['int64', 'float64']:
df_imputed[col].fillna(df[col].mean(), inplace=True)
else:
df_imputed[col].fillna(df[col].mode()[0], inplace=True)
이상치는 데이터의 전반적인 패턴에서 크게 벗어난 값입니다.
이상치 탐지 방법:
1. 시각적 방법: 박스플롯, 산점도
2. 통계적 방법: Z-score, IQR(Interquartile Range)
Python 예시:
import numpy as np
import matplotlib.pyplot as plt
def detect_outliers(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return df[(df[column] < lower_bound) | (df[column] > upper_bound)]
# 이상치 탐지
outliers = detect_outliers(df, 'your_column')
# 박스플롯으로 이상치 시각화
plt.figure(figsize=(10, 6))
df.boxplot(column='your_column')
plt.title('Boxplot with Outliers')
plt.show()
적절한 분석을 위해 데이터 유형을 변환해야 할 수 있습니다.
주요 변환:
1. 범주형 -> 수치형: 원-핫 인코딩, 라벨 인코딩
2. 수치형 -> 범주형: 구간화(binning)
3. 날짜/시간 형식 변환
Python 예시:
# 원-핫 인코딩
df_encoded = pd.get_dummies(df, columns=['categorical_column'])
# 라벨 인코딩
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df['encoded_column'] = le.fit_transform(df['categorical_column'])
# 수치형 변수 구간화
df['binned_column'] = pd.cut(df['numeric_column'], bins=5, labels=['Very Low', 'Low', 'Medium', 'High', 'Very High'])
# 날짜 형식 변환
df['date_column'] = pd.to_datetime(df['date_column'])
중심 경향 측정은 데이터의 전형적인 또는 중심적인 값을 나타냅니다.
주요 측정 방법:
1. 평균(Mean): 모든 값의 합을 개수로 나눈 값
2. 중앙값(Median): 정렬된 데이터의 중앙에 위치한 값
3. 최빈값(Mode): 가장 자주 나타나는 값
Python 예시:
# 수치형 변수의 중심 경향 측정
numeric_cols = df.select_dtypes(include=[np.number]).columns
for col in numeric_cols:
print(f"\n{col}:")
print(f"Mean: {df[col].mean():.2f}")
print(f"Median: {df[col].median():.2f}")
print(f"Mode: {df[col].mode()[0]:.2f}")
# 범주형 변수의 최빈값
categorical_cols = df.select_dtypes(exclude=[np.number]).columns
for col in categorical_cols:
print(f"\n{col} Mode:")
print(df[col].mode()[0])
분산 측정은 데이터의 퍼짐 정도를 나타냅니다.
주요 측정 방법:
1. 범위(Range): 최대값과 최소값의 차이
2. 분산(Variance): 평균으로부터의 편차 제곱의 평균
3. 표준편차(Standard Deviation): 분산의 제곱근
4. 사분위수 범위(Interquartile Range, IQR): 제3사분위수와 제1사분위수의 차이
Python 예시:
for col in numeric_cols:
print(f"\n{col}:")
print(f"Range: {df[col].max() - df[col].min():.2f}")
print(f"Variance: {df[col].var():.2f}")
print(f"Standard Deviation: {df[col].std():.2f}")
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
print(f"IQR: {IQR:.2f}")
분포 분석은 데이터의 전반적인 형태와 특성을 이해하는 데 도움이 됩니다.
주요 분석 방법:
1. 왜도(Skewness): 분포의 비대칭성을 측정
2. 첨도(Kurtosis): 분포의 뾰족한 정도를 측정
3. 정규성 검정: 데이터가 정규 분포를 따르는지 확인
Python 예시:
from scipy import stats
for col in numeric_cols:
print(f"\n{col}:")
print(f"Skewness: {df[col].skew():.2f}")
print(f"Kurtosis: {df[col].kurtosis():.2f}")
# Shapiro-Wilk 정규성 검정
stat, p = stats.shapiro(df[col])
print(f"Shapiro-Wilk test - Statistic: {stat:.4f}, p-value: {p:.4f}")
if p > 0.05:
print("Data likely follows a normal distribution")
else:
print("Data likely does not follow a normal distribution")
단변량 시각화는 개별 변수의 분포를 보여줍니다.
주요 시각화 방법:
1. 히스토그램: 연속형 변수의 분포
2. 막대 그래프: 범주형 변수의 빈도
3. 박스플롯: 수치형 변수의 분포와 이상치
Python 예시:
import matplotlib.pyplot as plt
import seaborn as sns
# 히스토그램
plt.figure(figsize=(10, 6))
sns.histplot(data=df, x='numeric_column', kde=True)
plt.title('Histogram of Numeric Column')
plt.show()
# 막대 그래프
plt.figure(figsize=(10, 6))
sns.countplot(data=df, x='categorical_column')
plt.title('Bar Plot of Categorical Column')
plt.xticks(rotation=45)
plt.show()
# 박스플롯
plt.figure(figsize=(10, 6))
sns.boxplot(data=df, y='numeric_column')
plt.title('Box Plot of Numeric Column')
plt.show()
이변량 시각화는 두 변수 간의 관계를 보여줍니다.
주요 시각화 방법:
1. 산점도: 두 연속형 변수 간의 관계
2. 선 그래프: 시간에 따른 변화
3. 히트맵: 범주형 변수 간의 관계
Python 예시:
# 산점도
plt.figure(figsize=(10, 6))
sns.scatterplot(data=df, x='numeric_column1', y='numeric_column2')
plt.title('Scatter Plot')
plt.show()
# 선 그래프
plt.figure(figsize=(10, 6))
df.groupby('date_column')['numeric_column'].mean().plot(kind='line')
plt.title('Line Plot')
plt.show()
# 히트맵
plt.figure(figsize=(12, 10))
sns.heatmap(df.corr(), annot=True, cmap='coolwarm')
plt.title('Correlation Heatmap')
plt.show()
다변량 시각화는 세 개 이상의 변수 간의 관계를 보여줍니다.
주요 시각화 방법:
1. 페어플롯: 여러 변수 쌍 간의 관계
2. 3D 산점도: 세 변수 간의 관계
3. 평행 좌표 그래프: 여러 차원의 데이터 시각화
Python 예시:
# 페어플롯
sns.pairplot(df[['numeric_col1', 'numeric_col2', 'numeric_col3', 'category_col']], hue='category_col')
plt.title('Pair Plot')
plt.show()
# 3D 산점도
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(df['numeric_col1'], df['numeric_col2'], df['numeric_col3'])
ax.set_xlabel('Numeric Column 1')
ax.set_ylabel('Numeric Column 2')
ax.set_zlabel('Numeric Column 3')
plt.title('3D Scatter Plot')
plt.show()
# 평행 좌표 그래프
from pandas.plotting import parallel_coordinates
plt.figure(figsize=(12, 6))
parallel_coordinates(df, 'category_col')
plt.title('Parallel Coordinates Plot')
plt.show()
주요 상관계수:
1. Pearson 상관계수: 연속형 변수 간의 선형 관계
2. Spearman 상관계수: 순서형 변수나 비선형 관계에 적합
3. Kendall's tau: 순위 상관관계
Python 예시:
# Pearson 상관계수
pearson_corr = df[numeric_cols].corr(method='pearson')
print("Pearson Correlation:")
print(pearson_corr)
# Spearman 상관계수
spearman_corr = df[numeric_cols].corr(method='spearman')
print("\nSpearman Correlation:")
print(spearman_corr)
# Kendall's tau
kendall_corr = df[numeric_cols].corr(method='kendall')
print("\nKendall's tau Correlation:")
print(kendall_corr)
상관 행렬은 모든 변수 쌍 간의 상관계수를 보여주는 표입니다.
Python 예시:
# 상관 행렬 히트맵
plt.figure(figsize=(12, 10))
sns.heatmap(pearson_corr, annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlation Matrix Heatmap')
plt.show()
산점도 행렬은 여러 변수 쌍 간의 관계를 시각적으로 보여줍니다.
Python 예시:
# 산점도 행렬
sns.pairplot(df[numeric_cols])
plt.title('Scatter Plot Matrix')
plt.show()
시계열 분해는 데이터를 추세, 계절성, 잔차 성분으로 분리합니다.
Python 예시:
from statsmodels.tsa.seasonal import seasonal_decompose
# 시계열 데이터 준비 (날짜를 인덱스로 설정)
ts_data = df.set_index('date_column')['numeric_column']
# 시계열 분해
result = seasonal_decompose(ts_data, model='additive', period=12) # 월별 데이터의 경우
# 결과 시각화
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(10, 12))
result.observed.plot(ax=ax1)
ax1.set_title('Observed')
result.trend.plot(ax=ax2)
ax2.set_title('Trend')
result.seasonal.plot(ax=ax3)
ax3.set_title('Seasonal')
result.resid.plot(ax=ax4)
ax4.set_title('Residual')
plt.tight_layout()
plt.show()
추세는 데이터의 장기적인 변화를, 계절성은 주기적인 패턴을 나타냅니다.
Python 예시:
# 이동 평균을 사용한 추세 분석
rolling_mean = ts_data.rolling(window=12).mean()
plt.figure(figsize=(12, 6))
ts_data.plot()
rolling_mean.plot()
plt.title('Time Series with Rolling Mean')
plt.legend(['Original', 'Rolling Mean'])
plt.show()
# 계절성 분석
seasonal_avg = ts_data.groupby(ts_data.index.month).mean()
plt.figure(figsize=(10, 6))
seasonal_avg.plot(kind='bar')
plt.title('Monthly Seasonal Average')
plt.xlabel('Month')
plt.ylabel('Average Value')
plt.show()
자기상관은 시계열 데이터가 자신의 과거 값과 얼마나 관련이 있는지를 보여줍니다.
Python 예시:
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
# 자기상관함수(ACF) 그래프
plt.figure(figsize=(12, 6))
plot_acf(ts_data, lags=40)
plt.title('Autocorrelation Function')
plt.show()
# 부분 자기상관함수(PACF) 그래프
plt.figure(figsize=(12, 6))
plot_pacf(ts_data, lags=40)
plt.title('Partial Autocorrelation Function')
plt.show()
데이터를 그룹화할 수 있는 변수를 식별합니다. 주로 범주형 변수가 사용됩니다.
Python 예시:
# 범주형 변수 확인
categorical_cols = df.select_dtypes(include=['object', 'category']).columns
print("Categorical columns:", categorical_cols)
# 각 범주형 변수의 고유값 확인
for col in categorical_cols:
print(f"\n{col} unique values:")
print(df[col].value_counts())
그룹별로 통계량을 계산하고 비교합니다.
Python 예시:
# 그룹별 평균 계산
group_means = df.groupby('category_column')['numeric_column'].mean()
print("Group means:")
print(group_means)
# 그룹별 기술통계량 계산
group_stats = df.groupby('category_column')['numeric_column'].describe()
print("\nGroup statistics:")
print(group_stats)
# 그룹별 박스플롯
plt.figure(figsize=(12, 6))
sns.boxplot(x='category_column', y='numeric_column', data=df)
plt.title('Boxplot by Group')
plt.show()
분산 분석은 그룹 간 평균의 차이가 통계적으로 유의미한지 검정합니다.
Python 예시:
from scipy import stats
# 일원 분산분석 (One-way ANOVA)
groups = [group for name, group in df.groupby('category_column')['numeric_column']]
f_statistic, p_value = stats.f_oneway(*groups)
print("One-way ANOVA results:")
print(f"F-statistic: {f_statistic}")
print(f"p-value: {p_value}")
if p_value < 0.05:
print("There is a statistically significant difference between group means.")
else:
print("There is no statistically significant difference between group means.")
PCA는 데이터의 분산을 최대한 보존하면서 차원을 축소하는 기법입니다.
Python 예시:
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
# 데이터 준비
X = df[numeric_cols]
X_scaled = StandardScaler().fit_transform(X)
# PCA 수행
pca = PCA()
pca_result = pca.fit_transform(X_scaled)
# 설명된 분산 비율 확인
explained_variance_ratio = pca.explained_variance_ratio_
cumulative_variance_ratio = np.cumsum(explained_variance_ratio)
# 스크리 플롯
plt.figure(figsize=(10, 6))
plt.plot(range(1, len(explained_variance_ratio) + 1), cumulative_variance_ratio, 'bo-')
plt.xlabel('Number of Components')
plt.ylabel('Cumulative Explained Variance Ratio')
plt.title('Scree Plot')
plt.show()
# 첫 두 개의 주성분으로 산점도 그리기
plt.figure(figsize=(10, 8))
plt.scatter(pca_result[:, 0], pca_result[:, 1])
plt.xlabel('First Principal Component')
plt.ylabel('Second Principal Component')
plt.title('PCA Result')
plt.show()
t-SNE는 고차원 데이터를 2D 또는 3D로 시각화하는 데 효과적인 비선형 차원 축소 기법입니다.
Python 예시:
from sklearn.manifold import TSNE
# t-SNE 수행
tsne = TSNE(n_components=2, random_state=42)
tsne_result = tsne.fit_transform(X_scaled)
# 결과 시각화
plt.figure(figsize=(10, 8))
plt.scatter(tsne_result[:, 0], tsne_result[:, 1])
plt.xlabel('t-SNE feature 1')
plt.ylabel('t-SNE feature 2')
plt.title('t-SNE visualization')
plt.show()
UMAP은 대규모 데이터셋에 대해 빠르고 효과적인 차원 축소를 제공합니다.
Python 예시:
import umap
# UMAP 수행
reducer = umap.UMAP()
umap_result = reducer.fit_transform(X_scaled)
# 결과 시각화
plt.figure(figsize=(10, 8))
plt.scatter(umap_result[:, 0], umap_result[:, 1])
plt.xlabel('UMAP feature 1')
plt.ylabel('UMAP feature 2')
plt.title('UMAP visualization')
plt.show()
EDA 결과를 바탕으로 검증 가능한 가설을 수립합니다.
예시 가설:
1. "A 변수와 B 변수 사이에는 강한 양의 상관관계가 있다."
2. "C 그룹의 평균값은 D 그룹의 평균값보다 유의미하게 높다."
수립한 가설을 적절한 통계 검정 방법을 사용하여 검증합니다.
Python 예시 (독립표본 t-검정):
from scipy import stats
# 두 그룹의 데이터 준비
group1 = df[df['category_column'] == 'A']['numeric_column']
group2 = df[df['category_column'] == 'B']['numeric_column']
# 독립표본 t-검정 수행
t_statistic, p_value = stats.ttest_ind(group1, group2)
print("Independent t-test results:")
print(f"t-statistic: {t_statistic}")
print(f"p-value: {p_value}")
if p_value < 0.05:
print("There is a statistically significant difference between the two groups.")
else:
print("There is no statistically significant difference between the two groups.")
여러 가설을 동시에 검정할 때 발생할 수 있는 1종 오류의 증가를 제어하기 위한 방법들을 적용합니다.
Python 예시 (Bonferroni 교정):
from statsmodels.stats.multitest import multipletests
# 여러 검정의 p-value들
p_values = [0.01, 0.03, 0.05, 0.001, 0.02]
# Bonferroni 교정 적용
rejected, adjusted_p_values, _, _ = multipletests(p_values, method='bonferroni')
print("Original p-values:", p_values)
print("Adjusted p-values:", adjusted_p_values)
print("Null hypotheses rejected:", rejected)
자동화된 EDA 도구들은 빠르고 효율적인 초기 분석을 제공합니다.
주요 도구들:
1. Pandas Profiling
2. Sweetviz
3. AutoViz
4. D-Tale
Python 예시 (Pandas Profiling):
from pandas_profiling import ProfileReport
# EDA 리포트 생성
profile = ProfileReport(df, title="Pandas Profiling Report")
# HTML 파일로 저장
profile.to_file("your_report.html")
대규모 데이터셋을 다룰 때는 샘플링, 병렬 처리, 증분 분석 등의 전략이 필요합니다.
Python 예시 (랜덤 샘플링):
# 랜덤 샘플링
sample_size = 10000
df_sample = df.sample(n=sample_size, random_state=42)
# 샘플 데이터로 EDA 수행
# ...
텍스트 데이터는 특별한 EDA 기법이 필요합니다.
Python 예시 (단어 빈도 분석):
from collections import Counter
import nltk
from nltk.corpus import stopwords
nltk.download('stopwords')
stop_words = set(stopwords.words('english'))
def word_freq(text):
words = text.lower().split()
words = [word for word in words if word not in stop_words]
return Counter(words)
# 텍스트 컬럼에 대해 단어 빈도 분석
text_column = 'your_text_column'
word_frequencies = df[text_column].apply(word_freq).sum()
# 상위 10개 단어 출력
print(word_frequencies.most_common(10))
EDA 보고서는 명확하고 간결하며, 주요 발견사항과 인사이트를 강조해야 합니다.
보고서 구조 예시:
1. 요약
2. 데이터 개요
3. 데이터 품질 평가
4. 변수별 분석 결과
5. 다변량 분석 결과
6. 주요 발견사항 및 인사이트
7. 추가 분석 제안
데이터 기반 인사이트를 효과적으로 전달하기 위한 전략:
1. 스토리텔링 기법 활용
2. 적절한 시각화 사용
3. 비즈니스 맥락 연결
4. 기술적 용어 최소화
EDA 결과를 바탕으로 더 깊이 있는 분석이나 모델링을 위한 제안을 작성합니다.
제안 내용 예시:
1. 특정 변수 간의 관계에 대한 심층 분석
2. 시계열 예측 모델 개발
3. 고객 세그먼테이션을 위한 클러스터링 분석
4. 특정 결과를 예측하기 위한 머신러닝 모델 구축
EDA의 한계:
1. 인과관계 추론의 어려움
2. 과도한 패턴 찾기(과적합) 위험
3. 데이터 품질에 따른 결과 신뢰성 문제
주의사항:
1. 데이터 편향성 인지
2. 통계적 유의성과 실제적 중요성 구분
3. 가설 검정 결과의 해석 시 주의
EDA 이후 가능한 후속 단계:
1. 고급 통계 분석
2. 예측 모델링
3. 인과관계 분석
4. A/B 테스트 설계
5. 데이터 기반 의사결정
EDA는 반복적이고 지속적인 과정입니다. 새로운 데이터와 인사이트를 바탕으로 분석을 지속적으로 개선하고, 최신 기법과 도구를 학습하는 것이 중요합니다.
이것으로 EDA 가이드북을 마칩니다. 이 가이드북은 EDA의 기본 개념부터 고급 기법까지 폭넓게 다루고 있으며, 실제 데이터 분석 프로젝트에 적용할 수 있는 실용적인 예시들을 포함하고 있습니다. EDA는 데이터 과학 프로젝트의 핵심 단계로, 이를 통해 얻은 인사이트는 후속 분석과 모델링의 기반이 됩니다.