Seaborn ~ : )

아름다운 통계적 데이터 시각화, Seaborn 정복하기

작성자:   ~ : )
(with Gemini)
작성일: 2025년 5월 20일
수정일: 2025년 6월 17일

▼ 학습 시작하기 ▼

* 머리말

Seaborn의 세계에 오신 것을 환영합니다. Seaborn은 Matplotlib을 기반으로 하여 더 매력적이고 정보가 풍부한 통계 그래픽을 쉽게 만들 수 있도록 설계된 파이썬 시각화 라이브러리입니다.

이 문서는 Seaborn의 기본 개념, Matplotlib과의 관계, 다양한 통계적 플롯 유형, 그리고 실제 데이터셋(타이타닉)을 활용한 고급 시각화 예제까지 다룹니다. Seaborn을 통해 복잡한 데이터를 명확하고 아름답게 표현하는 방법을 배우실 수 있습니다.

- ~ : ) (with Gemini)

제 1 장: Seaborn이란?

Seaborn은 파이썬을 위한 통계 데이터 시각화 라이브러리입니다. Matplotlib 위에 구축되어 있으며, Pandas 데이터 구조와 긴밀하게 통합되어 있습니다. Seaborn의 목표는 데이터 탐색 및 이해를 위한 핵심 부분인 통계적 시각화를 더 쉽고 매력적으로 만드는 것입니다.

1.1. Seaborn의 주요 특징

제 2 장: Matplotlib과의 관계

Seaborn은 Matplotlib을 기반으로 합니다. 이는 Seaborn 플롯이 Matplotlib 객체이며, Matplotlib의 함수와 도구를 사용하여 Seaborn 플롯을 더욱 세밀하게 사용자 정의할 수 있다는 것을 의미합니다.

일반적으로 Seaborn은 다음과 같은 방식으로 Matplotlib을 보완합니다:

Seaborn을 사용할 때는 보통 Matplotlib의 pyplot 모듈도 함께 임포트하여 플롯을 표시(plt.show())하거나 그림 크기 조절, 축 레이블 설정 등 추가적인 사용자 정의를 수행합니다.

예시 코드: Seaborn과 Matplotlib 함께 사용

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

# 예시 데이터 생성
data = pd.DataFrame({
    'x': np.random.randn(100),
    'y': np.random.randn(100) * 2 + 0.5,
    'category': np.random.choice(['A', 'B', 'C'], 100)
})

# Seaborn으로 산점도 생성
plt.figure(figsize=(8, 6)) # Matplotlib으로 그림 크기 조절
sns.scatterplot(data=data, x='x', y='y', hue='category', style='category', s=100)

# Matplotlib으로 제목 및 레이블 추가
plt.title('Seaborn Scatter Plot with Matplotlib Customization')
plt.xlabel('X-Value (from DataFrame)')
plt.ylabel('Y-Value (from DataFrame)')
plt.grid(True)
plt.show()

예상 결과:

(x축과 y축에 해당하는 100개의 점들이 카테고리(A, B, C)에 따라 다른 색상과 모양으로 표시된 산점도가 나타납니다.
Matplotlib을 사용하여 그림 크기, 제목, 축 레이블, 그리드가 추가되었습니다.)

제 3 장: 주요 통계 플롯

Seaborn은 데이터의 관계, 분포, 범주를 시각화하기 위한 다양한 통계 플롯 함수를 제공합니다.

3.1 관계형 플롯 (Relational Plots)

두 변수 간의 관계를 시각화하는 데 사용됩니다. 대표적으로 scatterplot()lineplot()이 있습니다.

예시 코드: scatterplot()lineplot()

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

# 데이터 로드 (Seaborn 내장 'fmri' 데이터셋 사용)
fmri = sns.load_dataset("fmri")

plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1) # 1행 2열 중 첫 번째
sns.scatterplot(data=fmri, x="timepoint", y="signal", hue="event", style="region", s=50)
plt.title('Scatter Plot of fMRI Signal')

plt.subplot(1, 2, 2) # 1행 2열 중 두 번째
sns.lineplot(data=fmri, x="timepoint", y="signal", hue="event", style="region", errorbar=('ci', 95)) # ci: 신뢰구간
plt.title('Line Plot of fMRI Signal (with CI)')

plt.tight_layout()
plt.show()

예상 결과:

(두 개의 서브플롯이 나타납니다.
첫 번째는 fMRI 데이터의 timepoint에 따른 signal 값을 산점도로 표시하며, event와 region에 따라 색상과 마커 스타일이 구분됩니다.
두 번째는 동일한 데이터를 선 플롯으로 표시하며, event와 region에 따라 선의 색상과 스타일이 구분되고 95% 신뢰구간이 음영으로 표시됩니다.)

3.2 분포 플롯 (Distribution Plots)

단일 변수의 분포 또는 두 변수 간의 결합 분포를 시각화합니다. histplot()(히스토그램), kdeplot()(커널 밀도 추정), ecdfplot()(경험적 누적 분포 함수), rugplot() 등이 있습니다.

예시 코드: histplot()kdeplot()

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

# 데이터 로드 (Seaborn 내장 'penguins' 데이터셋 사용)
penguins = sns.load_dataset("penguins")

plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
sns.histplot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
plt.title('Histogram of Flipper Length by Species')

plt.subplot(1, 2, 2)
sns.kdeplot(data=penguins, x="flipper_length_mm", hue="species", fill=True, alpha=.5, linewidth=2)
plt.title('KDE Plot of Flipper Length by Species')

plt.tight_layout()
plt.show()

예상 결과:

(두 개의 서브플롯이 나타납니다.
첫 번째는 펭귄 종(species)에 따른 부리 길이(flipper_length_mm)의 분포를 누적 막대 히스토그램으로 표시합니다.
두 번째는 동일한 데이터의 분포를 커널 밀도 추정(KDE) 곡선으로 표시하며, 각 종별로 다른 색상의 부드러운 곡선이 그려지고 내부가 반투명하게 채워집니다.)

3.3 범주형 플롯 (Categorical Plots)

하나 이상의 범주형 변수와 수치형 변수 간의 관계를 시각화합니다. 점 플롯(stripplot, swarmplot), 분포 플롯(boxplot, violinplot, boxenplot), 추정 플롯(pointplot, barplot, countplot) 등 다양합니다.

예시 코드: boxplot()violinplot()

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

# 데이터 로드 (Seaborn 내장 'tips' 데이터셋 사용)
tips = sns.load_dataset("tips")

plt.figure(figsize=(14, 6))

plt.subplot(1, 2, 1)
sns.boxplot(data=tips, x="day", y="total_bill", hue="smoker")
plt.title('Total Bill by Day and Smoker (Box Plot)')

plt.subplot(1, 2, 2)
sns.violinplot(data=tips, x="day", y="total_bill", hue="smoker", split=True, inner="quart")
# split=True: smoker/non-smoker를 바이올린 양쪽에 분리, inner="quart": 사분위수 표시
plt.title('Total Bill by Day and Smoker (Violin Plot)')

plt.tight_layout()
plt.show()

예상 결과:

(두 개의 서브플롯이 나타납니다.
첫 번째는 요일(day) 및 흡연 여부(smoker)에 따른 총 식사 금액(total_bill)의 분포를 박스 플롯으로 보여줍니다. 각 요일별로 흡연자와 비흡연자의 박스가 나란히 그려집니다.
두 번째는 동일한 데이터를 바이올린 플롯으로 보여줍니다. 각 요일별로 흡연자와 비흡연자의 분포가 바이올린의 양쪽에 나뉘어 그려지고, 내부에는 사분위수가 표시됩니다.)

제 4 장: 다중 플롯 그리드

Seaborn은 데이터의 여러 하위 집합에 걸쳐 동일한 종류의 플롯을 생성하여 비교할 수 있는 강력한 다중 플롯 그리드 기능을 제공합니다. FacetGrid, PairGrid, JointGrid가 대표적입니다.

예시 코드: FacetGridpairplot()

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

# FacetGrid 예시 (tips 데이터셋 사용)
tips = sns.load_dataset("tips")
g = sns.FacetGrid(tips, col="time", row="sex", hue="smoker", margin_titles=True)
g.map(sns.scatterplot, "total_bill", "tip", alpha=.7, s=60)
g.add_legend()
g.fig.suptitle('FacetGrid: Tip vs Total Bill by Time, Sex, Smoker', y=1.03) # 전체 제목
plt.show()


# pairplot 예시 (penguins 데이터셋 사용)
penguins = sns.load_dataset("penguins").dropna() # 결측치 제거
sns.pairplot(penguins, hue="species", markers=["o", "s", "D"], corner=True)
# corner=True: 대각선 위쪽은 그리지 않음
plt.suptitle('Pair Plot of Penguin Features by Species', y=1.02)
plt.show()

예상 결과:

(FacetGrid 예시 결과:
식사 시간(time: Lunch, Dinner)을 열로, 성별(sex: Male, Female)을 행으로 하는 격자 형태의 서브플롯들이 나타납니다.
각 서브플롯은 해당 조건에 맞는 데이터의 total_bill과 tip 간의 관계를 산점도로 보여주며, 흡연 여부(smoker)에 따라 점의 색상이 구분됩니다.
범례와 전체 제목이 포함됩니다.)

(pairplot 예시 결과:
펭귄 데이터셋의 수치형 특성들 간의 모든 쌍별 관계를 보여주는 그리드가 나타납니다.
대각선에는 각 특성의 단일 변수 분포(히스토그램 또는 KDE)가 표시되고, 비대각선에는 두 특성 간의 산점도가 표시됩니다.
점의 색상과 마커는 펭귄 종(species)에 따라 구분됩니다. corner=True로 설정되어 그리드의 하단 삼각형 부분만 표시됩니다. 전체 제목이 포함됩니다.)

PairGridpairplot()보다 더 세밀한 제어가 가능하며, JointGrid는 두 변수 간의 결합 분포와 각 변수의 주변 분포를 함께 보여줍니다 (jointplot()의 객체 지향 인터페이스).

제 5 장: 스타일과 색상 팔레트

Seaborn은 플롯의 전반적인 미적 스타일과 색상 팔레트를 쉽게 변경할 수 있는 기능을 제공합니다.

예시 코드: 스타일과 팔레트 변경

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

data = np.random.multivariate_normal([0, 0], [[1, .5], [.5, 1]], size=100)
df = pd.DataFrame(data, columns=['x', 'y'])

# 스타일과 팔레트 설정
sns.set_theme(style="whitegrid", palette="viridis")
# 또는 sns.set_style("whitegrid")
#      sns.set_palette("viridis")

plt.figure(figsize=(7,5))
sns.scatterplot(data=df, x="x", y="y", hue=df["y"] > 0, s=70) # y > 0 인지 여부로 색상 구분
plt.title('Custom Style and Palette')
plt.show()

# 기본 스타일로 되돌리기 (필요시)
# sns.set_theme(style='darkgrid', palette='deep') # Seaborn 0.11+
# 또는 sns.set() # 이전 버전의 기본값

예상 결과:

(배경이 흰색 그리드로 처리되고, viridis 색상 팔레트가 적용된 산점도가 나타납니다.
점들은 y값이 0보다 큰지 여부에 따라 다른 색상(viridis 팔레트 내)으로 구분됩니다. 제목이 포함됩니다.)

제 6 장: 타이타닉 데이터 시각화 (Seaborn)

타이타닉 데이터셋을 Seaborn을 사용하여 더욱 다채롭고 통계적으로 의미 있는 방식으로 시각화해봅니다.

6.1 데이터 준비

Matplotlib 예제에서 사용한 것과 동일한 방식으로 Pandas를 사용하여 타이타닉 데이터를 불러오고 기본적인 전처리를 수행합니다.

예시 코드: (Matplotlib 예제와 동일)

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt # Seaborn과 함께 자주 사용

# Kaggle의 타이타닉 데이터셋 URL
url = 'https://web.stanford.edu/class/archive/cs/cs109/cs109.1166/stuff/titanic.csv'
try:
    df = pd.read_csv(url)
except Exception as e:
    print(f"Error loading data: {e}")
    data = {'Survived': [0, 1, 1, 1, 0, 0, 0, 0, 1, 1], 'Pclass': [3, 1, 3, 1, 3, 3, 1, 3, 3, 2],
            'Sex': ['male', 'female', 'female', 'female', 'male', 'male', 'male', 'male', 'female', 'female'],
            'Age': [22, 38, 26, 35, 35, np.nan, 54, 2, 27, 14], 'Fare': [7.25,71.28,7.92,53.1,8.05,8.45,51.86,21.07,11.13,30.07],
            'Embarked': ['S', 'C', 'S', 'S', 'S', 'Q', 'S', 'S', 'S', 'C'],
            'SibSp': [1,1,0,1,0,0,0,3,0,1], 'Parch': [0,0,0,0,0,0,0,1,2,0]}
    df = pd.DataFrame(data)


# Age 결측치를 평균으로 채우기
df['Age'].fillna(df['Age'].mean(), inplace=True)
# Embarked 결측치를 최빈값으로 채우기
if 'Embarked' in df.columns and df['Embarked'].isnull().any():
    df['Embarked'].fillna(df['Embarked'].mode()[0], inplace=True)

print("--- 데이터 준비 완료 (처음 3개 행) ---")
print(df.head(3))

예상 결과:

--- 데이터 준비 완료 (처음 3개 행) ---
   Survived  Pclass                                               Name     Sex   Age  SibSp  Parch            Ticket     Fare Cabin Embarked
0         0       3                            Braund, Mr. Owen Harris    male  22.0      1      0         A/5 21171   7.2500   NaN        S
1         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  38.0      1      0          PC 17599  71.2833   C85        C
2         1       3                             Heikkinen, Miss. Laina  female  26.0      0      0  STON/O2. 3101282   7.9250   NaN        S
(실제 데이터 로드 결과에 따라 다를 수 있음)

6.2 성별/객실 등급별 생존자 (countplot)

countplot을 사용하여 범주형 변수에 따른 데이터 개수를 시각화합니다. 여기서는 성별 또는 객실 등급에 따른 생존자/사망자 수를 비교합니다.

예시 코드:

plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
sns.countplot(data=df, x='Sex', hue='Survived', palette={0: 'salmon', 1: 'lightgreen'})
plt.title('Survival Count by Sex (Seaborn)')

plt.subplot(1, 2, 2)
sns.countplot(data=df, x='Pclass', hue='Survived', palette={0: 'grey', 1: 'skyblue'})
plt.title('Survival Count by Pclass (Seaborn)')

plt.tight_layout()
plt.show()

예상 결과:

(두 개의 서브플롯이 나타납니다.
첫 번째는 성별(male, female)에 따라 생존자(연두색)와 사망자(연어색) 수를 막대로 표시합니다.
두 번째는 객실 등급(1, 2, 3)에 따라 생존자(하늘색)와 사망자(회색) 수를 막대로 표시합니다. 각 플롯에는 제목이 포함됩니다.)

6.3 생존 여부에 따른 연령 분포 (histplot/kdeplot)

생존 여부에 따라 승객들의 연령 분포를 히스토그램이나 KDE 플롯으로 비교합니다.

예시 코드:

plt.figure(figsize=(14, 6))

plt.subplot(1, 2, 1)
sns.histplot(data=df, x='Age', hue='Survived', multiple='layer', kde=True, palette={0:'orangered', 1:'dodgerblue'})
# multiple='layer': 겹쳐서 그림, kde=True: KDE 곡선 추가
plt.title('Age Distribution by Survival (Histogram with KDE)')

plt.subplot(1, 2, 2)
sns.kdeplot(data=df, x='Age', hue='Survived', fill=True, alpha=0.5, palette={0:'red', 1:'blue'})
plt.title('Age Distribution by Survival (KDE Plot)')

plt.tight_layout()
plt.show()

예상 결과:

(두 개의 서브플롯이 나타납니다.
첫 번째는 생존 여부(Survived: 0 또는 1)에 따라 승객 연령(Age) 분포를 히스토그램으로 보여줍니다. 각 그룹의 히스토그램은 겹쳐서 그려지고, KDE 곡선이 함께 표시됩니다 (사망자는 주황색 계열, 생존자는 파란색 계열).
두 번째는 동일한 데이터를 KDE 플롯으로만 보여주며, 각 그룹의 부드러운 분포 곡선이 그려지고 내부가 반투명하게 채워집니다 (사망자는 빨간색, 생존자는 파란색). 각 플롯에는 제목이 포함됩니다.)

6.4 객실 등급 및 성별에 따른 연령 분포 (boxplot)

객실 등급(Pclass)과 성별(Sex)에 따라 승객들의 연령(Age) 분포를 박스 플롯으로 비교합니다.

예시 코드:

plt.figure(figsize=(10, 7))
sns.boxplot(data=df, x='Pclass', y='Age', hue='Sex', palette={'male': 'lightblue', 'female': 'pink'})
plt.title('Age Distribution by Pclass and Sex (Box Plot)')
plt.show()

예상 결과:

(x축에 객실 등급(1, 2, 3)이 표시되고, 각 등급 내에서 성별(male, female)에 따라 연령 분포를 나타내는 박스 플롯이 그려집니다.
남성은 하늘색, 여성은 분홍색으로 구분됩니다. 제목이 포함됩니다.)

6.5 생존 여부에 따른 요금 분포 (violinplot)

생존 여부(Survived)에 따라 승객들이 지불한 요금(Fare)의 분포를 바이올린 플롯으로 시각화합니다.

예시 코드:

plt.figure(figsize=(8, 6))
sns.violinplot(data=df, x='Survived', y='Fare', hue='Survived', palette={0: 'salmon', 1: 'lightgreen'}, legend=False)
# Fare의 범위가 매우 넓으므로 y축 로그 스케일 또는 범위 제한 고려 가능
plt.yscale('log') # y축을 로그 스케일로 변경 (분포를 더 잘 보기 위함)
# 또는 plt.ylim(0, 300) 등으로 범위 제한
plt.title('Fare Distribution by Survival (Violin Plot - Log Scale)')
plt.xticks([0,1], ['Died', 'Survived'])
plt.show()

예상 결과:

(x축에 생존 여부(Died, Survived)가 표시되고, 각 그룹에 대한 요금(Fare) 분포를 바이올린 플롯으로 보여줍니다.
사망자는 연어색, 생존자는 연두색으로 구분됩니다. 요금의 분포 범위가 넓어 y축은 로그 스케일로 표시될 수 있습니다. 제목과 x축 레이블이 포함됩니다.)

6.6 특성 간 상관관계 (heatmap)

데이터셋의 수치형 특성들 간의 상관관계를 히트맵으로 시각화합니다.

예시 코드:

# 수치형 데이터만 선택하여 상관관계 계산
# 'Sex'와 'Embarked'는 이미 숫자로 변환되었거나, pd.get_dummies 등을 사용해야 함
# 여기서는 간단히 주요 수치형 특성만 선택
numerical_df = df[['Survived', 'Pclass', 'Age', 'SibSp', 'Parch', 'Fare']]
correlation_matrix = numerical_df.corr()

plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f", linewidths=.5)
# annot=True: 각 셀에 값 표시, cmap: 색상 맵, fmt: 값 형식
plt.title('Correlation Matrix of Titanic Features (Seaborn Heatmap)')
plt.show()

예상 결과:

(선택된 수치형 특성들 간의 상관계수 행렬이 히트맵으로 표시됩니다.
각 셀은 두 특성 간의 상관계수 값을 나타내며, 값의 크기에 따라 색상(coolwarm 팔레트)이 다르게 표시됩니다.
각 셀에는 상관계수 값이 소수점 둘째 자리까지 숫자로 표시됩니다. 제목이 포함됩니다.)

6.7 주요 특성 간 관계 (pairplot)

선택된 몇 가지 주요 특성들 간의 쌍별 관계를 한 번에 시각화합니다. 생존 여부에 따라 색상을 구분할 수 있습니다.

예시 코드:

# pairplot을 위한 주요 특성 선택 (너무 많으면 오래 걸림)
pairplot_df = df[['Survived', 'Pclass', 'Age', 'Fare', 'Sex']]

# Sex를 숫자로 변환 (pairplot은 수치형 또는 명확한 범주형을 선호)
# 간단히 male=0, female=1로 변환하거나, hue로 직접 사용 가능
# pairplot_df['Sex_numeric'] = pairplot_df['Sex'].map({'male':0, 'female':1})

plt.figure() # pairplot 자체가 figure-level 함수이므로 plt.figure()가 필수는 아님
sns.pairplot(pairplot_df, hue='Survived', palette={0: 'red', 1: 'blue'},
             markers=["o", "s"], corner=True, diag_kind='kde')
# diag_kind='kde': 대각선에 KDE 플롯
# corner=True: 하단 삼각형만 그림
plt.suptitle('Pair Plot of Key Titanic Features by Survival', y=1.02)
plt.show()

예상 결과:

(선택된 특성들('Survived', 'Pclass', 'Age', 'Fare', 'Sex') 간의 모든 쌍별 관계를 보여주는 그리드가 나타납니다.
대각선에는 각 특성의 단일 변수 분포(KDE)가 생존 여부에 따라 다른 색으로 표시되고,
비대각선에는 두 특성 간의 산점도가 생존 여부에 따라 다른 색상(사망자는 빨간색, 생존자는 파란색)과 마커로 표시됩니다.
corner=True로 설정되어 그리드의 하단 삼각형 부분만 표시됩니다. 전체 제목이 포함됩니다.)

제 7 장: 맺음말

Seaborn은 Matplotlib을 기반으로 하여 통계적 데이터 시각화를 더욱 쉽고 아름답게 만들어주는 강력한 라이브러리입니다. 이 문서를 통해 Seaborn의 다양한 플롯 기능과 사용자 정의 방법을 익히셨기를 바랍니다.

Seaborn은 데이터 분석 과정에서 탐색적 데이터 분석(EDA)을 수행하고, 분석 결과를 효과적으로 전달하는 데 매우 유용합니다. 더 많은 예제와 상세한 API 정보는 Seaborn 공식 API 문서튜토리얼을 참고하시기 바랍니다.