한 변수의 분포를 보는 도구는 세 가지 관점입니다: 빈도 막대(histplot), 매끄러운 밀도 곡선(kdeplot), 누적 비율 계단(ecdfplot). 여기에 개별 관측 위치를 축에 표시하는 rugplot을 곁들입니다.
figure-level 함수 displot이 위 셋을 kind="hist"|"kde"|"ecdf"로 통합하며, col/row로 격자를 만듭니다.
가장 큰 함정은 히스토그램의 bin 편향입니다: bin 개수·경계를 어떻게 잡느냐에 따라 같은 데이터가 단봉으로도, 다봉으로도 보입니다.
import seaborn as sns
sns.set_theme()
penguins = sns.load_dataset("penguins")histplot — 빈도/밀도 막대sns.histplot(data=penguins, x="flipper_length_mm", bins=30)
sns.histplot(data=penguins, x="flipper_length_mm", binwidth=5) # 폭으로 지정
sns.histplot(data=penguins, x="flipper_length_mm", stat="density")
sns.histplot(data=penguins, x="flipper_length_mm", hue="species",
multiple="stack") # "layer"|"dodge"|"stack"|"fill"
sns.histplot(data=penguins, x="flipper_length_mm", kde=True) # 밀도 곡선 겹치기
sns.histplot(data=penguins, x="bill_length_mm", y="flipper_length_mm") # 2D 히트맵 히스토그램그림에서 보이는 것: flipper_length_mm 히스토그램은 약 190mm와 215mm 부근에 두 개의 봉우리가 있는 이봉(bimodal) 형태로 나타납니다. hue="species"로 나누면 이 두 봉우리가 사실상 Gentoo와 나머지 두 종의 차이임이 드러납니다.
💡 이럴 때: 그룹별 분포를 비교하는데 표본 수가 다르면
multiple="stack"보다stat="density"+common_norm=False가 형태 비교에 유리합니다. 절대 빈도가 아니라 모양을 보고 싶기 때문입니다.
같은 데이터라도 bin 설정에 따라 인상이 달라집니다.
for b in [5, 15, 50]:
sns.displot(data=penguins, x="flipper_length_mm", bins=b)bin이 너무 적으면: 봉우리가 뭉개져 단봉처럼 보입니다(과평활).
bin이 너무 많으면: 표본 잡음이 튀어 가짜 다봉이 생깁니다(과적합).
bin 경계 위치만 옮겨도 특정 값이 어느 칸에 들어가느냐가 바뀌어 모양이 흔들립니다. 대응:
binwidth로 폭을 도메인 기준(예: 5mm)으로 고정하면 재현·비교가 쉽습니다.
여러 bin 설정을 같이 보거나, bin에 덜 민감한 ecdfplot으로 교차 확인합니다.
KDE는 bin 문제는 없지만 대신 대역폭(bw) 선택 문제가 있습니다(3절).
💡 이럴 때: "이 분포가 정말 이봉인가?"가 의심되면
ecdfplot을 봅니다. 누적 곡선의 기울기가 완만해지는 구간이 곧 밀도가 낮은 골이므로, bin 선택과 무관하게 봉우리 수를 가늠할 수 있습니다.
kdeplot — 커널 밀도 추정각 관측점에 작은 커널(보통 가우시안)을 놓고 합산해 매끄러운 밀도 곡선을 만듭니다.
sns.kdeplot(data=penguins, x="flipper_length_mm")
sns.kdeplot(data=penguins, x="flipper_length_mm", bw_adjust=0.5) # 대역폭 축소 → 더 뾰족
sns.kdeplot(data=penguins, x="flipper_length_mm", hue="species", fill=True, alpha=0.4)
sns.kdeplot(data=penguins, x="flipper_length_mm", hue="species",
multiple="stack")
sns.kdeplot(data=penguins, x="bill_length_mm", y="flipper_length_mm") # 2D 등고선그림에서 보이는 것: bw_adjust=0.5면 곡선이 울퉁불퉁해지며 국소 봉우리가 여럿 생기고, 기본값이나 bw_adjust=2면 완만한 이봉 곡선으로 정리됩니다.
주의:
clip=(0, None)을 씁니다.💡 이럴 때: KDE의
bw_adjust는 히스토그램의bins와 같은 역할(평활 정도 통제)입니다. 하나의 정답 값은 없으므로, 결론이 대역폭에 따라 뒤집히면 "데이터가 그 판단을 지지하지 않는다"는 신호로 읽습니다.
ecdfplot — 경험적 누적분포관측값을 정렬해 "이 값 이하가 전체의 몇 %인가"를 계단 함수로 그립니다.
sns.ecdfplot(data=penguins, x="flipper_length_mm")
sns.ecdfplot(data=penguins, x="flipper_length_mm", hue="species")
sns.ecdfplot(data=penguins, x="flipper_length_mm", stat="count") # 비율 대신 개수장점:
💡 이럴 때: A/B 두 그룹의 지표를 비교할 때
ecdfplot(hue=...)을 씁니다. 두 계단이 교차하지 않고 한쪽이 계속 앞서면 "분포 전체가 이동"한 것이고, 교차하면 "일부 구간에서만 차이"입니다.
rugplot — 개별 관측 위치ax = sns.kdeplot(data=penguins, x="flipper_length_mm")
sns.rugplot(data=penguins, x="flipper_length_mm", ax=ax, height=0.05)축을 따라 관측마다 짧은 눈금을 그립니다. KDE/히스토그램에 겹쳐 "곡선이 실제 데이터 밀집도를 반영하는지" 확인하는 용도입니다. 표본이 많으면 눈금이 뭉쳐 의미가 줄어듭니다.
displot — figure-level 통합sns.displot(data=penguins, x="flipper_length_mm", kind="hist", col="species")
sns.displot(data=penguins, x="flipper_length_mm", kind="kde", hue="sex", col="species")
sns.displot(data=penguins, x="flipper_length_mm", kind="ecdf", hue="species")
sns.displot(data=penguins, x="bill_length_mm", y="flipper_length_mm", col="species") # 2Dkind로 histplot/kdeplot/ecdfplot을 고릅니다. 각 축 함수의 인자(bins, bw_adjust 등)를 그대로 전달합니다.col/row로 종별·성별 격자를 자동 생성합니다.ax=는 받지 못합니다(1장 참고). 서브플롯 합성이 필요하면 축 함수를 직접 씁니다.
그림에서 보이는 것: col="species"로 나눈 세 패널에서 Adelie·Chinstrap은 190mm 부근 단봉, Gentoo는 215mm 부근 단봉으로, 전체에서 보이던 이봉이 "종 간 차이"였음이 분명해집니다.💡 이럴 때: 분포가 카테고리(부서·지역·분기)에 따라 어떻게 달라지는지가 관심사면
displot(..., col="카테고리")가 가장 빠릅니다. 한 축에 다 겹쳐서hue로만 보면 4~5개 그룹부터 판독이 어렵습니다.
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.