Data_10기

데이터 전처리 & 시각화 - Matplotlib, Seaborn, Altair

senu 2025. 11. 19. 21:36

오늘은 Python에서 사용되는 세 가지 시각화 도구인 Matplotlib, Seaborn, Altair를 중점으로 배워보았다. 처음에는 단순 막대그래프에서 시작했지만, 점점 라인그래프, 파이차트, 피라미드차트, 이중축 그래프 등 다채로운 시각화를 해보면서 데이터를 눈으로 이해하는 법에 대해 배워볼 수 있었다.


오늘도 마찬가지로 먼저 분석에 필요한 라이브러리와 서로 다른 csv 파일 여러개를 불러온 뒤, 실습에 들어갔다.

#pip install matplotlib

import pandas as pd 
import numpy as np
import time
from PIL import Image

import altair as alt
import seaborn as sns
import matplotlib.pyplot as plt 

# seaborn 팔레트 설정
palette = sns.color_palette("pastel")

import warnings
# 오류 경고 무시하기
warnings.filterwarnings(action='ignore')


# csv 파일 불러오기
df = pd.read_csv("product_details.csv")
df2 = pd.read_csv("customer_details.csv")
df3 = pd.read_csv("E-commerece sales data 2024.csv")

 

Matplotlib(pyplot)는 가장 기본적인 시각화 도구이고,

Seaborn은 Matplotlib 기반의 "예쁜 스타일 라이브러리,

Altair는 인터랙티브 즉, 움직이는 차트를 제작할 수 있는 도구이다.


1️⃣ 데이터 시각화 - Matplotlib

1. 기본 그래프

성별에 따른 유저 수를 빠르게 비교하기 위해 막대그래프를 그려보았다.

# 데이터 전처리 (결측치 제거)
df2 = df2.dropna()

# 그래프 그리기
df2.groupby('Gender')['Customer ID'].count().plot.bar(color=['pink','green'])
  • groupby로  Gender별 고객 수 집계
  • plot.bar( )로 막대그래프 생성
  • color로 막대 색상 직접 지정

 

2. 라인 그래프 그리기

카테코리별 사용자 수를 선형 흐름으로 파악하기 위해 라인 그래프를 그려보았다.

d1 = df2.groupby('Category')['Customer ID'].count().reset_index()

# 그래프 그리기
dplot1 = plt.figure(figsize = (10, 3))

x = d1['Category']
y = d1['Customer ID']

plt.plot(x, y, color='purple', marker='o', alpha=0.5, linewidth=10)
plt.title("Group by category - user count")
plt.xlabel("Category")
plt.ylable("User count")
  • figsize : 그래프 크기
  • marker : 데이터 포인트 표시 (* → ★ / ^ → ▲ / ...)
  • alpha : 투명도
  • linewidth : 선 두께

 

3. 막대그래프 + 피벗 형태

카테고리별, 성별 유저 수를 한눈에 비교하기 위해 다음 형태의 그래프를 그려보았다.

d2 = df2.groupby(['Category','Gender'])['Customer ID'].count().unstack(1)

# 그래프 그리기
dplot8 = d2.plot(kind='bar',color=['#2F2FB1','#9b59b6'])
plt.title("bar plot1")
plt.xlabel("category")
plt.ylabel("usercnt")

 

unstack( )을 이용해 Gender를 컬럼으로 펼친 후 그래프를 그렸다.

 

4.누적 막대그래프

# 그래프 그리기
dplot9 = d2.plot(kind='bar', stacked=True, color=['#F4D13B','#9b59b6'])
plt.title("bar plot2")
plt.xlabel("category")
plt.ylabel("usercnt")

 

stacked = True 옵션만 넣으면 바로 누적형 그래프가 된다.

 

5. 파이차트

파이차트는 전체 비중을 표현할 때 유용하다.

piedf = df2.groupby('Gender')['Customer ID'].count().reset_index()

# 그래프 그리기
dplot7= plt.figure(figsize=(7,7))
plt.pie(
    x=piedf['Customer ID'], 
    labels=piedf['Gender'],
    autopct='%1.1f',
    colors=['red','#9b59b6'],
    startangle=90
)
plt.legend(piedf['Gender'])
plt.title("pie plot", loc="center", pad=10, fontsize=10, fontweight="bold")
plt.show()
  • autopct : 퍼센트 표기
  • startangle : 시작 각도 조절

 

6. 산점도

변수 간 관계(나이 vs 평균 구매금액)를 직관적으로 확인해보기 위해 산점도를 그려보았다.

d3 = df2.groupby('Age')['Purchase Amount (USD)'].mean().reset_index()

# 그래프 그리기
plt.scatter(d3['Age'],d3['Purchase Amount (USD)'], c="purple")
plt.title("Scatter graph", loc="center", pad=10, fontsize=10, fontweight="bold")

 

7. 이중축 그래프

두 개의 서로 다른 scale을 가진 데이터를 동시에 비교하고 싶을 때 이중축 그래프를 그려볼 수 있다.

plt.style.use('default')
plt.rcParams['figure.figsize'] = (4, 3)
plt.rcParams['font.size'] = 12

x = np.arange(2020, 2027)
y1 = np.array([1, 3, 7, 5, 9, 7, 14])
y2 = np.array([1, 3, 5, 7, 9, 11, 13])

 

두 축(twinx)을 활용한 코드는 다음과 같다:

fig, ax1 = plt.subplots()

ax1.plot(x, y1, color='green', linewidth=5, alpha=0.7, label='Price')   
ax1.set_ylim(0, 30)
ax1.set_ylabel('Price ($)')
ax1.set_xlabel('Year')

ax2 = ax1.twinx()
ax2.bar(x, y2, color='purple', label='Demand', alpha=0.7, width=0.7)
ax2.set_ylim(0, 18)
ax2.set_ylabel(r'Demand ($\times10^6$)')

 

두 그래프가 겹칠 때 z-order로 어떤 그래프가 먼저 나올지 즉, 앞/뒤 순서 조정을 할 수 있다.

ax1.set_zorder(ax2.get_zorder() + 20)
ax1.patch.set_visible(False)

 

8. 피라미드 그래프

인구통계에서 자주 볼 수 있는 형태인 피라미드 그래프를 활용해 남녀 인구를 나이대별로 파악할 수 있는 그래프를 그려보았다.

 

1) 나이대 구간 만들기

# 나이대 구간 나누기
bins2 = [10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80]
df2["bin"] = pd.cut(df2["Age"], bins = bins2)

# 보기 쉽게 구간 정리
df2["age"] = df2["bin"].apply(lambda x: str(x.left) + " - " + str(x.right))

 

2) 구간별 성별 인구 수 계산 후 대칭 구조 만들기

df7 = df2.groupby(['age','Gender'])['Customer ID'].count().reset_index()
df7 = pd.pivot_table(df7, index='age', columns='Gender', values='Customer ID').reset_index()

 

3) 피벗으로 가로 형태로 펼치기

df7 = pd.pivot_table(df7, index='age',
                     columns='Gender',
                     values='Customer ID').reset_index()

 

4) 좌우 대칭 구간 생성

df7["Female_Left"] = 0
df7["Female_Width"] = df7["Female"]
df7["Male_Left"] = -df7["Male"]
df7["Male_Width"] = df7["Male"]
  • Female_Left = 0 : 오른쪽만 표시
  • Male_Left = - (남성유저수) : 왼쪽으로 음수 방향 표시

 

5) 막대 그리기

dplot6 = plt.figure(figsize=(7,5))

plt.barh(y=df7["age"], width=df7["Female_Width"],
         color="#F4D13B", label="Female")

plt.barh(y=df7["age"], width=df7["Male_Width"],
         left=df7["Male_Left"],
         color="#9b59b6", label="Male")


plt.xlim(-300,270)
plt.ylim(-2,12)
plt.text(-200, 11.5, "Male", fontsize=10)
plt.text(160, 11.5, "Female", fontsize=10, fontweight="bold")


# 막대 위에 숫자 작성하기
for idx in range(len(df7)):
    # 남성 데이터 텍스트로 추가 
    plt.text(x=df7["Male_Left"][idx]-0.5, 
             y=idx,
             s="{}".format(int(df7["Male"][idx])),
             ha="right", va="center",
             fontsize=8, color="#9b59b6")

    # 여성 데이터 텍스트로 추가 
    plt.text(x=df7["Female_Width"][idx]+0.5,
             y=idx, 
             s="{:.0f}".format(df7["Female"][idx]),
             ha="left", va="center",
             fontsize=8, color="#F4D13B")
             
             
# 최종 타이틀
plt.title("Pyramid plot", loc="center", pad=15, fontsize=15, fontweight="bold")
  • harh( ) → 가로 방향 막대그래프
  • x축 좌우 넓이 (-300 ~ 270) 설정
  • y축 나이대 개수에 맞게 설정
  • ha/right, ha/left : 숫자 정렬 방향

 

9. 여러개의 그래프 그리기

Matplotlib에서는 subplots( ) 기능을 사용하면 여러 개의 그래프를 하나의 캔버스 (figure) 위에 배치할 수 있다. 데이터를 비교하거나 시각적으로 묶어서 보여줄 때 유용하게 사용할 수 있다.

 

1) 캔버스와 축(ax) 만들기

fig,ax=plt.subplots(2,2)
  • 2,2 → 2행 2열로 총 4개의 그래프 자리 생성
  • 반환값 ax는 2*2 형태의 배열처럼 구성됨
    • ax[0,0] → 첫 번째 그래프
    • ax[0,1] → 두 번째 그래프
    • ax[1,0] → 세 번째 그래프
    • ax[1,1] → 네 번째 그래프

2) 각 위치에 서로 다른 그래프 그리기

ax[0,0].plot(
    np.linspace(0,100,20),
    np.linspace(0,100,20)**2,
    marker='o', markersize=2, markeredgecolor='y'
)

ax[0,1].hist(np.random.randn(500), bins=30, color='purple', alpha=0.5)

ax[1,0].bar([1,3,5,7],np.array([1,2,3,4]),color='r')

ax[1,1].boxplot(np.random.randn(500,5))

# 그래프 사이 여백 조정하기
fig.subplots_adjust(hspace=0.5, wspace=0.3)

# 각 그래프 제목 추가
ax[0,0].set_title("line plot", fontsize=9)
ax[0,1].set_title("hist plot", fontsize=9)
ax[1,0].set_title("bar plot", fontsize=9)
ax[1,1].set_title("box plot", fontsize=9)

# 그래프 호출
plt.show()


2️⃣ 데이터시각화 - Seaborn

Seaborn은 Matplotlib 기반의 고급 시각화 라이브러리다. 스타일이 더 예쁘고, 범례·색상·통계 기반 그래프가 쉽게 구현되는 것이 특징이다.

 

1. 바 그래프 그리기

df33 = df2.groupby('Gender')['Customer ID'].count().reset_index()

# 그래프 그리기
p = ["#F4D13B","red"]
sns.set_palette(p)

plot1 = sns.barplot(data=df33, x="Gender", y="Customer ID")
plot1.bar_label(plot1.containers[0], labels=df33['Customer ID'], fontsize=7, color='green')
plot1.set_title("User - bar chart")

 

bar_label을 통해 막대 위에 숫자를 넣어줄 수 있다.

 

 

2. Bar Chart - 날짜 데이터 기반

# Timestamp 데이터 필터링
mask2 = (df3['Time stamp']>' ')
df8 = df3[mask2]

# 문자열 날짜를 datetime으로 변환해서 "YYYY-MM" 만 추출
df8['Date'] = pd.to_datetime(
    df8['Time stamp'], format='%d/%m/%Y %H:%M'
).dt.strftime('%Y-%m')

# 월별 유저수 집계
df9 = df8.groupby('Date')['user id'].count().reset_index()
df9.rename(columns={'user id':'user count'}, inplace=True)

# 그래프 그리기
plt.figure(figsize=(15, 8))
dplot1 = sns.barplot(x="Date", y="user count", data=df9, palette='rainbow')
dplot1.set_xticklabels(dplot1.get_xticklabels(), rotation=270)

for p in dplot1.patches:
    height = p.get_height()
    dplot1.text(
        x=p.get_x()+(p.get_width()/2),
        y=height-50,
        s='{:.0f}'.format(height),
        ha='center'
    )

 

3. Count Plot

plt.figure(figsize=(6, 5))
sns.countplot(x='Category', hue='Gender', data=df2, palette='cubehelix')
  •  countplot 은 y값을 자동으로 갯수(count)로 계산해줌
  • hue 로 성별을 나누면 범례도 자동 생성