🌸 저번 시간에 배운 NumPy · Pandas · Matplotlib · Seaborn을 모두 활용하는 종합 실습이에요!
붓꽃(Iris) 데이터셋이란?
데이터 분석 입문에서 가장 많이 쓰이는 유명한 데이터셋이에요.
붓꽃 3종류 — Setosa, Versicolor, Virginica — 각 50개, 총 150개의 꽃 측정 데이터가 담겨 있어요.
| 컬럼명 | 한국어 | 설명 |
|---|---|---|
sepal_length | 꽃받침 길이 | 단위: cm |
sepal_width | 꽃받침 너비 | 단위: cm |
petal_length | 꽃잎 길이 | 단위: cm |
petal_width | 꽃잎 너비 | 단위: cm |
species | 품종 | setosa / versicolor / virginica |
💡 Seaborn에 내장되어 있어서
sns.load_dataset("iris")로 바로 불러올 수 있어요!
STEP 1. 데이터 불러오기 & 탐색
목표: 데이터가 어떻게 생겼는지 파악해요.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 데이터 불러오기
df = sns.load_dataset("iris")
# 기본 탐색
print("=== 처음 5행 ===")
print(df.head())
print("\n=== 데이터 크기 ===")
print(df.shape) # (150, 5)
print("\n=== 열 정보 ===")
print(df.info())
print("\n=== 통계 요약 ===")
print(df.describe())
print("\n=== 품종별 개수 ===")
print(df["species"].value_counts())
실행 결과 예시
=== 처음 5행 ===
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
...
=== 품종별 개수 ===
species
setosa 50
versicolor 50
virginica 50
❓ 생각해보기: 150개 행, 5개 열이 맞게 출력됐나요?
df.describe()에서 평균과 최댓값을 확인해보세요.
STEP 2. NumPy로 통계 계산하기
목표: Pandas 없이 NumPy만으로 직접 통계를 계산해봐요.
import numpy as np
import seaborn as sns
df = sns.load_dataset("iris")
# 꽃잎 길이(petal_length) 배열로 추출
petal_lengths = df["petal_length"].to_numpy()
print("=== 꽃잎 길이 통계 ===")
print(f"평균: {np.mean(petal_lengths):.2f} cm")
print(f"표준편차: {np.std(petal_lengths):.2f} cm")
print(f"최솟값: {np.min(petal_lengths):.2f} cm")
print(f"최댓값: {np.max(petal_lengths):.2f} cm")
print(f"중앙값: {np.median(petal_lengths):.2f} cm")
# 품종별로 나눠서 계산
for species in df["species"].unique():
subset = df[df["species"] == species]["petal_length"].to_numpy()
print(f"\n[{species}] 꽃잎 길이 평균: {np.mean(subset):.2f} cm")
실행 결과 예시
=== 꽃잎 길이 통계 ===
평균: 3.76 cm
표준편차: 1.77 cm
최솟값: 1.00 cm
최댓값: 6.90 cm
중앙값: 4.35 cm
[setosa] 꽃잎 길이 평균: 1.46 cm
[versicolor] 꽃잎 길이 평균: 4.26 cm
[virginica] 꽃잎 길이 평균: 5.55 cm
❓ 생각해보기: 품종별 평균 차이가 꽤 크네요! 이걸 그래프로 그리면 어떻게 보일까요?
STEP 3. Pandas로 품종별 집계
목표: groupby를 활용해서 품종별 특성을 비교해요.
import seaborn as sns
df = sns.load_dataset("iris")
# 품종별 평균 (모든 수치 컬럼)
print("=== 품종별 평균 ===")
print(df.groupby("species").mean().round(2))
# 품종별 꽃잎 길이 최대·최솟값
print("\n=== 품종별 꽃잎 길이 범위 ===")
print(df.groupby("species")["petal_length"].agg(["min", "max", "mean"]).round(2))
# 꽃잎 길이가 5cm 이상인 행만 필터링
big_petal = df[df["petal_length"] >= 5.0]
print(f"\n꽃잎 길이 5cm 이상: {len(big_petal)}개")
print(big_petal["species"].value_counts())
실행 결과 예시
=== 품종별 평균 ===
sepal_length sepal_width petal_length petal_width
species
setosa 5.01 3.43 1.46 0.25
versicolor 5.94 2.77 4.26 1.33
virginica 6.59 2.97 5.55 2.03
꽃잎 길이 5cm 이상: 42개
species
virginica 36
versicolor 6
❓ 생각해보기:
agg(["min", "max", "mean"])처럼 집계 함수를 여러 개 한 번에 적용할 수 있어요. 다른 컬럼도 해보세요!
STEP 4. Matplotlib으로 기본 시각화
목표: 막대 그래프와 산점도로 데이터를 직접 그려봐요.
import matplotlib.pyplot as plt
import seaborn as sns
df = sns.load_dataset("iris")
# 한글 폰트 설정 (필요한 경우)
plt.rcParams["axes.unicode_minus"] = False
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 왼쪽: 품종별 평균 꽃잎 길이 막대 그래프
species_mean = df.groupby("species")["petal_length"].mean()
colors = ["#FF9AA2", "#C7CEEA", "#B5EAD7"]
axes[0].bar(species_mean.index, species_mean.values, color=colors)
axes[0].set_title("품종별 평균 꽃잎 길이", fontsize=14)
axes[0].set_xlabel("품종")
axes[0].set_ylabel("꽃잎 길이 (cm)")
axes[0].set_ylim(0, 7)
for i, (name, val) in enumerate(species_mean.items()):
axes[0].text(i, val + 0.1, f"{val:.2f}", ha="center", fontsize=11)
# 오른쪽: 꽃받침 길이 vs 꽃잎 길이 산점도
color_map = {"setosa": "#FF9AA2", "versicolor": "#C7CEEA", "virginica": "#B5EAD7"}
for species, group in df.groupby("species"):
axes[1].scatter(
group["sepal_length"],
group["petal_length"],
label=species,
color=color_map[species],
edgecolors="gray",
alpha=0.7
)
axes[1].set_title("꽃받침 길이 vs 꽃잎 길이", fontsize=14)
axes[1].set_xlabel("꽃받침 길이 (cm)")
axes[1].set_ylabel("꽃잎 길이 (cm)")
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
💡
fig, axes = plt.subplots(1, 2)— 그래프를 1행 2열로 나란히 그릴 수 있어요!
tight_layout()은 그래프 간격을 자동으로 조정해줘요. ❓ 생각해보기: 산점도에서 setosa는 확실히 분리되는데, versicolor와 virginica는 좀 섞이죠? 이게 무슨 의미일까요?
STEP 5. Seaborn으로 고급 시각화
목표: Seaborn의 특화 차트로 분포와 상관관계를 한눈에 파악해요.
import seaborn as sns
import matplotlib.pyplot as plt
df = sns.load_dataset("iris")
fig, axes = plt.subplots(2, 2, figsize=(13, 10))
fig.suptitle("🌸 Iris 데이터셋 종합 분석", fontsize=16, fontweight="bold")
# (1) 박스플롯 — 품종별 꽃잎 길이 분포
sns.boxplot(
data=df, x="species", y="petal_length",
palette=["#FF9AA2", "#C7CEEA", "#B5EAD7"],
ax=axes[0, 0]
)
axes[0, 0].set_title("품종별 꽃잎 길이 분포 (박스플롯)")
axes[0, 0].set_xlabel("품종")
axes[0, 0].set_ylabel("꽃잎 길이 (cm)")
# (2) 바이올린 플롯 — 박스플롯보다 분포 모양을 더 잘 보여줌
sns.violinplot(
data=df, x="species", y="sepal_width",
palette=["#FF9AA2", "#C7CEEA", "#B5EAD7"],
ax=axes[0, 1]
)
axes[0, 1].set_title("품종별 꽃받침 너비 분포 (바이올린)")
axes[0, 1].set_xlabel("품종")
axes[0, 1].set_ylabel("꽃받침 너비 (cm)")
# (3) 히트맵 — 수치 컬럼 간 상관관계
numeric_df = df.drop(columns=["species"])
corr = numeric_df.corr()
sns.heatmap(
corr, annot=True, fmt=".2f",
cmap="coolwarm", center=0,
ax=axes[1, 0]
)
axes[1, 0].set_title("컬럼 간 상관관계 히트맵")
# (4) 산점도 + 회귀선 — 꽃잎 길이와 너비의 관계
sns.scatterplot(
data=df, x="petal_length", y="petal_width",
hue="species",
palette=["#FF6B8A", "#7B9FD4", "#5DBD8A"],
ax=axes[1, 1]
)
axes[1, 1].set_title("꽃잎 길이 vs 꽃잎 너비")
axes[1, 1].set_xlabel("꽃잎 길이 (cm)")
axes[1, 1].set_ylabel("꽃잎 너비 (cm)")
plt.tight_layout()
plt.show()
💡 히트맵 읽는 법
- 값이 1에 가까울수록 → 두 값이 함께 커지는 경향 (양의 상관관계)
- 값이 -1에 가까울수록 → 한 값이 커지면 다른 값은 작아지는 경향 (음의 상관관계)
- 값이 0에 가까울수록 → 두 값 사이에 관계가 거의 없음 ❓ 생각해보기: 히트맵에서
petal_length와petal_width의 상관계수가 높게 나오죠? 실제 산점도 (4번 그래프)와 비교해보세요. 일치하나요?
STEP 6. 도전 과제 🔥
아래 문제를 직접 코드로 풀어보세요!
📌 도전 1 — 꽃받침 넓이 계산하기
꽃받침의 넓이를 sepal_length × sepal_width로 근사해서 새 컬럼 sepal_area를 만들어보세요.
# 여기에 코드를 작성해보세요
df["sepal_area"] = ___ # sepal_length × sepal_width
print(df[["species", "sepal_area"]].groupby("species").mean().round(2))
📌 도전 2 — 히스토그램 그리기
꽃잎 길이(petal_length)의 전체 분포를 히스토그램으로 그려보세요.
import matplotlib.pyplot as plt
import seaborn as sns
df = sns.load_dataset("iris")
# 힌트: sns.histplot() 또는 plt.hist() 사용
# bins=20 으로 막대 개수를 설정해보세요
📌 도전 3 (심화) — pairplot 그리기
Seaborn의 pairplot은 모든 수치 컬럼 쌍을 한 번에 산점도로 그려줘요. 한 줄로 그려보세요!
import seaborn as sns
df = sns.load_dataset("iris")
# 한 줄로 완성해보세요!
sns.pairplot(___, hue=___)
오늘 배운 것 정리
| 단계 | 라이브러리 | 한 일 |
|---|---|---|
| STEP 1 | Pandas | 데이터 불러오기 & 기본 탐색 |
| STEP 2 | NumPy | 품종별 통계 직접 계산 |
| STEP 3 | Pandas | groupby로 집계 & 필터링 |
| STEP 4 | Matplotlib | 막대 그래프, 산점도 직접 그리기 |
| STEP 5 | Seaborn | 박스플롯, 히트맵, 바이올린 플롯 |
| STEP 6 | 전체 | 도전 과제 |
4가지 라이브러리를 모두 써서 하나의 데이터셋을 처음부터 끝까지 분석했어요. 데이터 분석의 기본 흐름인 불러오기 → 탐색 → 통계 → 시각화 를 직접 경험한 거예요!
