2  탐색적 데이터 분석(EDA)

Keywords

탐색적 데이터 분석, EDA, 범주형 자료, 연속형 자료, 히스토그램, 박스플롯, 그룹별 자료 집계

탐색적 데이터 분석(Exploratory Data Analysis, EDA)은 데이터의 특성과 패턴을 파악하기 위해 시각화와 통계적 방법을 활용하는 과정이다. EDA를 통해 데이터의 분포, 변수 간 관계, 이상치, 결측치 등을 발견할 수 있으며, 이는 본격적인 분석이나 모델링 전략을 수립하는 데 중요한 기반이 된다. 이 장에서는 Python의 pandas, seaborn, matplotlib 라이브러리를 활용하여 체계적인 EDA를 수행하는 방법을 학습한다.

2.1 라이브러리 로드 및 데이터 불러오기

EDA를 수행하기 위해서는 데이터 처리를 위한 pandas, 시각화를 위한 seaborn과 matplotlib 라이브러리가 필요하다. 여기서는 seaborn에 내장된 penguins 데이터셋을 사용한다.

예제: 필수 라이브러리 로드 및 데이터 불러오기

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

# seaborn 내장 penguins 데이터셋 로드
df = sns.load_dataset("penguins")

# 데이터 미리보기
df.head()
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 Male
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 Female
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 Female
3 Adelie Torgersen NaN NaN NaN NaN NaN
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 Female

2.2 데이터 구조 확인

데이터 분석의 첫 단계는 전체 데이터의 구조를 파악하는 것이다. 데이터의 크기, 각 컬럼의 데이터 타입, 결측치 존재 여부 등을 확인하여 데이터의 전반적인 특성을 이해할 수 있다.

예제: 데이터 크기 확인

# 행과 열의 개수 확인
df.shape
(344, 7)

예제: 데이터 상세 정보 확인

# 컬럼별 데이터 타입 및 결측치 정보
df.info()
<class 'pandas.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 7 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   species            344 non-null    str    
 1   island             344 non-null    str    
 2   bill_length_mm     342 non-null    float64
 3   bill_depth_mm      342 non-null    float64
 4   flipper_length_mm  342 non-null    float64
 5   body_mass_g        342 non-null    float64
 6   sex                333 non-null    str    
dtypes: float64(4), str(3)
memory usage: 18.9 KB

위 결과에서 확인해야 할 주요 사항은 다음과 같다.

확인 항목 설명
행(row) 개수 전체 관측치의 수
열(column) 개수 변수의 수
변수 타입 수치형(int64, float64) 또는 범주형(object)
결측치 여부 Non-Null Count가 전체 행 수보다 적은 경우 결측치 존재

2.3 기초 통계량 확인

기술 통계량은 데이터의 중심 경향성과 분산 정도를 수치로 요약하여 제공한다. 수치형 변수와 범주형 변수는 서로 다른 방법으로 요약해야 한다.

2.3.1 수치형 변수 요약

수치형 변수는 평균, 표준편차, 최솟값, 최댓값, 사분위수 등의 통계량으로 요약할 수 있다.

예제: 수치형 변수 기술 통계량

# 수치형 컬럼의 기술 통계량
df.describe()
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
count 342.000000 342.000000 342.000000 342.000000
mean 43.921930 17.151170 200.915205 4201.754386
std 5.459584 1.974793 14.061714 801.954536
min 32.100000 13.100000 172.000000 2700.000000
25% 39.225000 15.600000 190.000000 3550.000000
50% 44.450000 17.300000 197.000000 4050.000000
75% 48.500000 18.700000 213.000000 4750.000000
max 59.600000 21.500000 231.000000 6300.000000

2.3.2 범주형 변수 요약

범주형 변수는 고유값의 개수, 최빈값, 빈도 등으로 요약할 수 있다.

예제: 범주형 변수 기술 통계량

# 범주형 컬럼의 기술 통계량
df.describe(include=["object", "str"])
species island sex
count 344 344 333
unique 3 3 2
top Adelie Biscoe Male
freq 152 168 168

위 결과에서 확인할 수 있는 정보는 다음과 같다.

통계량 설명
count 결측치를 제외한 데이터 개수
unique 고유한 값의 개수
top 최빈값(가장 자주 나타나는 값)
freq 최빈값의 빈도

2.4 결측치 탐색

결측치는 데이터 분석 결과를 왜곡할 수 있으므로 반드시 확인하고 적절히 처리해야 한다. 결측치의 개수와 비율을 파악하여 처리 전략을 수립할 수 있다.

예제: 컬럼별 결측치 개수 확인

# 각 컬럼의 결측치 개수
df.isna().sum()
species               0
island                0
bill_length_mm        2
bill_depth_mm         2
flipper_length_mm     2
body_mass_g           2
sex                  11
dtype: int64

예제: 컬럼별 결측치 비율 확인

# 각 컬럼의 결측치 비율 (%)
(df.isna().mean() * 100).round(1)
species              0.0
island               0.0
bill_length_mm       0.6
bill_depth_mm        0.6
flipper_length_mm    0.6
body_mass_g          0.6
sex                  3.2
dtype: float64

결측치가 5% 미만인 경우 해당 행을 제거하는 것이 일반적이며, 10% 이상인 경우 평균값이나 중앙값으로 대체하거나 별도의 결측치 처리 기법을 고려해야 한다.

2.5 범주형 변수 탐색

범주형 변수는 각 범주의 빈도를 확인하여 데이터의 분포를 파악한다. 시각화를 통해 불균형 여부를 쉽게 확인할 수 있다.

2.5.1 종(species) 분포

펭귄 종별 개체 수를 확인하여 데이터의 균형 상태를 파악한다.

예제: 종별 빈도 확인

# 종(species)별 개체 수
df["species"].value_counts()
species
Adelie       152
Gentoo       124
Chinstrap     68
Name: count, dtype: int64

예제: 종별 분포 시각화

# 종별 분포 막대 그래프
plt.figure(figsize=(6,4))
sns.countplot(data=df, x="species")
plt.title("Distribution of Penguin Species")
plt.xlabel("Species")
plt.ylabel("Count")
plt.show()

2.5.2 성별(sex) 분포

성별 분포를 확인하여 데이터 수집 과정에서 성별 편향이 있는지 파악한다.

예제: 성별 분포 시각화

# 성별 분포 막대 그래프
plt.figure(figsize=(6,4))
sns.countplot(data=df, x="sex")
plt.title("Distribution of Penguin Sex")
plt.xlabel("Sex")
plt.ylabel("Count")
plt.show()

2.6 수치형 변수 탐색

수치형 변수의 분포를 시각화하면 데이터의 중심, 퍼짐 정도, 왜도(skewness), 이상치 등을 직관적으로 파악할 수 있다.

2.6.1 히스토그램

히스토그램은 수치형 변수의 분포를 구간별 빈도로 나타낸 그래프이다. 데이터가 정규분포를 따르는지, 왜곡되어 있는지 등을 확인할 수 있다.

예제: 전체 수치형 변수 히스토그램

# 모든 수치형 변수의 히스토그램
df.hist(bins=20, figsize=(6,4))
plt.tight_layout()
plt.suptitle("Histograms of Numeric Variables", y=1.02)
plt.show()

2.6.2 박스플롯 (이상치 탐색)

박스플롯은 사분위수를 기반으로 데이터의 분포와 이상치를 시각화하는 도구이다. 박스 밖의 점들은 이상치 후보로 간주할 수 있다.

예제: 종별 체중 분포 및 이상치 확인

# 종별 체중 분포 박스플롯
plt.figure(figsize=(6,4))
sns.boxplot(data=df, x="species", y="body_mass_g")
plt.title("Body Mass Distribution by Species")
plt.xlabel("Species")
plt.ylabel("Body Mass (g)")
plt.show()

박스플롯에서 확인할 수 있는 정보는 다음과 같다.

요소 설명
박스 아래쪽 경계 제1사분위수(Q1, 25 백분위수)
박스 중앙선 중앙값(Q2, 50 백분위수)
박스 위쪽 경계 제3사분위수(Q3, 75 백분위수)
수염(whisker) Q1 - 1.5×IQR ~ Q3 + 1.5×IQR 범위
개별 점 이상치 후보

2.7 변수 간 관계 탐색

두 변수 간의 관계를 파악하면 변수 간 상관성, 패턴, 군집 등을 발견할 수 있다. 이는 예측 모델링이나 가설 검정의 기초 자료가 된다.

2.7.1 두 수치형 변수 관계

산점도는 두 수치형 변수 간의 관계를 점으로 표현한 그래프이다. 범주형 변수로 색상을 구분하면 그룹별 패턴을 동시에 확인할 수 있다.

예제: 부리 길이와 깊이의 관계

# 부리 길이와 깊이의 산점도 (종별 구분)
plt.figure(figsize=(6,4))
sns.scatterplot(
    data=df,
    x="bill_length_mm",
    y="bill_depth_mm",
    hue="species"
)
plt.title("Relationship between Bill Length and Depth")
plt.xlabel("Bill Length (mm)")
plt.ylabel("Bill Depth (mm)")
plt.legend(title="Species")
plt.show()

2.7.2 페어플롯

페어플롯은 모든 수치형 변수 쌍에 대한 산점도를 행렬 형태로 배치한 그래프이다. 대각선에는 각 변수의 히스토그램이나 커널 밀도 추정(KDE) 그래프가 표시된다.

예제: 전체 변수 간 관계 확인

# 모든 수치형 변수 쌍의 관계 시각화
sns.pairplot(
    df,
    hue="species",
    diag_kind="hist",
    height=2
)
plt.suptitle("Pairplot of Penguin Features", y=1.02)
plt.show()

페어플롯을 통해 다음을 파악할 수 있다.

  • 변수 간 선형 또는 비선형 관계
  • 그룹별 분포 차이
  • 다변량 군집 패턴
  • 변수 간 상관성

2.8 그룹별 통계 확인

그룹별 통계량을 비교하면 범주형 변수가 수치형 변수에 미치는 영향을 정량적으로 파악할 수 있다.

예제: 종별 평균 통계

# 종별 부리 길이와 체중의 평균
df.groupby("species")[["bill_length_mm", "body_mass_g"]].mean()
bill_length_mm body_mass_g
species
Adelie 38.791391 3700.662252
Chinstrap 48.833824 3733.088235
Gentoo 47.504878 5076.016260

추가적으로 표준편차, 중앙값 등 다양한 통계량을 함께 확인할 수 있다.

예제: 종별 다양한 통계량

# 종별 다양한 통계량 확인
df.groupby("species")[["bill_length_mm", "body_mass_g"]].agg(["mean",
 "std",
 "median",
 lambda x : x.dropna().quantile(0.75)-x.dropna().quantile(0.25)])
bill_length_mm body_mass_g
mean std median <lambda_0> mean std median <lambda_0>
species
Adelie 38.791391 2.663405 38.80 4.000 3700.662252 458.566126 3700.0 650.0
Chinstrap 48.833824 3.339256 49.55 4.725 3733.088235 384.335081 3700.0 462.5
Gentoo 47.504878 3.081857 47.30 4.250 5076.016260 504.116237 5000.0 800.0

2.9 요약

이 장에서는 탐색적 데이터 분석(EDA)의 기본 과정과 주요 기법을 학습했다. 주요 내용은 다음과 같다.

EDA 기본 단계

단계 주요 함수/기법 목적
데이터 구조 확인 shape, info() 데이터 크기, 타입, 결측치 파악
기초 통계량 확인 describe() 중심 경향성과 분산 이해
결측치 탐색 isna(), sum(), mean() 결측치 개수와 비율 확인
분포 확인 히스토그램, 박스플롯 데이터 분포와 이상치 탐색
관계 탐색 산점도, 페어플롯 변수 간 상관성과 패턴 발견
그룹 비교 groupby() 그룹별 통계적 차이 확인

주요 발견 사항 (펭귄 데이터 예시)

  • 펭귄 종(species)에 따라 부리 길이와 체중 분포가 명확하게 구분된다
  • Adelie 종은 다른 종에 비해 상대적으로 체중과 부리 길이가 작다
  • 일부 변수에 결측치가 존재하므로 본격적인 분석 전에 처리 전략이 필요하다
  • 성별과 종에 따른 신체 측정값의 차이가 관찰된다

EDA는 데이터의 특성을 이해하고 가설을 수립하는 필수 과정이다. 다음 장에서는 EDA를 통해 발견한 결측치와 이상치를 처리하는 데이터 전처리 기법을 학습할 것이다.