Data-Science-For-Beginners 第10课实战:用直方图与密度曲线可视化数据分布(Matplotlib + Seaborn 详解)
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
本文基于 Data-Science-For-Beginners 仓库 10-visualization-distributions 一课展开:以密歇根州鸟类数据集(birds.csv)为例,系统讲解如何用 Matplotlib 直方图刻画数值型分布、用分组叠加直方图刻画分类变量分布、再用 Seaborn 的 KDE 密度曲线做平滑可视化。读完本文,你将能独立完成从bins调参、数据过滤去偏到kdeplot分组密度绘图的完整分布分析流程,并理解每个关键参数的作用。
1. 课程背景:birds 数据集
本课承接第 9 课(用散点图发现 Minnesota 鸟类数据中的异常值、对比不同目鸟类的最大体长)。分布(distribution)回答的是"数据沿某个坐标轴如何组织"的问题,例如:400 多种鸟的最大翼展或最大体重的整体分布是什么样的?
课程使用仓库根目录下的 birds.csv,共 443 条鸟类记录、13 个字段:名称、学名、类别(Category)、目(Order)、科(Family)、属(Genus)、保护状态(ConservationStatus)以及体长/体重/翼展的上下限(MinLength、MaxLength、MinBodyMass、MaxBodyMass、MinWingspan、MaxWingspan)。在课程文件夹下的 notebook.ipynb 中导入 Pandas 与 Matplotlib 并加载数据(代码从课程文件夹运行,故数据路径为../../data/birds.csv):
import pandas as pd import matplotlib.pyplot as plt birds = pd.read_csv('../../data/birds.csv') birds.head()前 5 行如下(与原课程文档一致):
| Name | ScientificName | Category | Order | Family | Genus | ConservationStatus | MinLength | MaxLength | MinBodyMass | MaxBodyMass | MinWingspan | MaxWingspan | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Black-bellied whistling-duck | Dendrocygna autumnalis | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Dendrocygna | LC | 47 | 56 | 652 | 1020 | 76 | 94 |
| 1 | Fulvous whistling-duck | Dendrocygna bicolor | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Dendrocygna | LC | 45 | 53 | 712 | 1050 | 85 | 93 |
| 2 | Snow goose | Anser caerulescens | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Anser | LC | 64 | 79 | 2050 | 4050 | 135 | 165 |
| 3 | Ross's goose | Anser rossii | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Anser | LC | 57.3 | 64 | 1066 | 1567 | 113 | 116 |
| 4 | Greater white-fronted goose | Anser albifrons | Ducks/Geese/Waterfowl | Anseriformes | Anatidae | Anser | LC | 64 | 81 | 1930 | 3310 | 130 | 165 |
2. 快速窥探分布:散点图的局限
课程先给出一个快速概览手段——散点图:
birds.plot(kind='scatter', x='MaxLength', y='Order', figsize=(12,8)) plt.title('Max Length per Order') plt.ylabel('Order') plt.xlabel('Max Length') plt.show()它能给出"各目鸟类最大体长的整体布局",但并不是展示真实分布的最优方式——点的密集程度依赖肉眼估算。展示真实分布的任务通常由直方图(Histogram)完成。
3. 直方图:bins 参数、过滤与 2D 直方图
直方图类似条形图,但通过柱子的起伏(rise and fall)呈现分布形态;它要求数值型数据。构建方式是把数据数组划分成若干更小的"箱"(bins),展示各区间内取值数量。
3.1 基础直方图与 bins 调参
展示整个数据集的MaxBodyMass分布:
birds['MaxBodyMass'].plot(kind='hist', bins=10, figsize=(12,12)) plt.show()可以看到,数据集中 400 多种鸟的 MaxBodyMass 大多落在 2000 以下的区间(左侧偏态明显,由大型雁形目物种把整体范围拉高所致)。
把bins提高到 30,可以获得更细粒度的分布:
birds['MaxBodyMass'].plot(kind='hist', bins=30, figsize=(12,12)) plt.show()bins是直方图最核心的参数:过小会掩盖分布细节(如 10 箱时只能看到"大部分集中在低端"),过大会引入噪声。课程建议尝试其他过滤器与数据点;若要查看完整分布,可去掉['MaxBodyMass']的列过滤。
3.2 用过滤消除偏态
上面的图偏态(skewed)明显,是因为少数超重型物种撑大了横轴范围。只筛选体重在 1g 到 60g 之间的小鸟、并使用 40 个 bins,可以得到一个"不那么偏态"的图:
filteredBirds = birds[(birds['MaxBodyMass'] > 1) & (birds['MaxBodyMass'] < 60)] filteredBirds['MaxBodyMass'].plot(kind='hist', bins=40, figsize=(12,12)) plt.show()这里filteredBirds是一个用布尔掩码过滤出的子 DataFrame,后续文本数据与 KDE 部分都复用它。
3.3 2D 直方图:观察两个分布的关系
Matplotlib 还提供内置的 2D 直方图来比较两个变量的联合分布,并用更亮的颜色标示"汇聚点"(convergence):
x = filteredBirds['MaxBodyMass'] y = filteredBirds['MaxLength'] fig, ax = plt.subplots(tight_layout=True) hist = ax.hist2d(x, y)从图中可以看出:体重与体长之间存在沿预期方向的相关性,并且有一个特别强的汇聚点——这正是用颜色密度代替散点堆叠的价值所在。
4. 文本(分类)数据的分布:保护状态 × 最小翼展
直方图默认面向数值数据,那么"按文本维度看分布"怎么办?birds 数据集中每个物种带有 IUCN 红色名录(IUCN Red List Categories)风格的保护状态缩写:
| 缩写 | 含义 |
|---|---|
| CR | Critically Endangered(极危) |
| EN | Endangered(濒危) |
| EX | Extinct(灭绝) |
| LC | Least Concern(无危) |
| NT | Near Threatened(近危) |
| VU | Vulnerable(易危) |
课程把过滤后的filteredBirds按 6 种保护状态分别取出MinWingspan,用 6 条不同颜色、半透明(alpha=0.5)、相同分箱(bins=20)的直方图叠加,形成带标签的分类分布对比:
x1 = filteredBirds.loc[filteredBirds.ConservationStatus=='EX', 'MinWingspan'] x2 = filteredBirds.loc[filteredBirds.ConservationStatus=='CR', 'MinWingspan'] x3 = filteredBirds.loc[filteredBirds.ConservationStatus=='EN', 'MinWingspan'] x4 = filteredBirds.loc[filteredBirds.ConservationStatus=='NT', 'MinWingspan'] x5 = filteredBirds.loc[filteredBirds.ConservationStatus=='VU', 'MinWingspan'] x6 = filteredBirds.loc[filteredBirds.ConservationStatus=='LC', 'MinWingspan'] kwargs = dict(alpha=0.5, bins=20) plt.hist(x1, **kwargs, color='red', label='Extinct') plt.hist(x2, **kwargs, color='orange', label='Critically Endangered') plt.hist(x3, **kwargs, color='yellow', label='Endangered') plt.hist(x4, **kwargs, color='green', label='Near Threatened') plt.hist(x5, **kwargs, color='blue', label='Vulnerable') plt.hist(x6, **kwargs, color='gray', label='Least Concern') plt.gca().set(title='Conservation Status', ylabel='Min Wingspan') plt.legend()这里的技术要点:用.loc[条件, 列]对分类字段做子集提取;用kwargs字典统一传递alpha(透明度,使叠加时彼此可见)与bins;最后用plt.legend()显示标签。
课程结论:最小翼展与保护状态之间似乎没有明显的相关性,并鼓励读者用同样方法测试数据集的其他字段、尝试不同过滤器,看能否找到真正的相关性。
5. 密度图(KDE):用 Seaborn 画平滑分布
5.1 从直方图到密度曲线
前面看到的直方图都是"阶梯状"的,不会形成平滑弧线。要展示更平滑的密度图,可以引入 Seaborn:
import seaborn as sns sns.kdeplot(filteredBirds['MinWingspan']) plt.show()曲线形态与前面最小翼展直方图对应,只是更平滑。课程引用了 Seaborn 官方文档对 KDE 的定位:相对于直方图,KDE 能产生更不杂乱、更易于解读的图(尤其是绘制多个分布时),但当底层分布有界或不平滑时,KDE 也可能引入失真——"与直方图一样,表现质量也依赖于好的平滑参数选择"。换句话说,离群值仍然会让你的图"表现糟糕"。
5.2 bw_adjust:控制平滑程度
把第 3 节那张"锯齿状"的MaxBodyMass直方图用 KDE 重画:
sns.kdeplot(filteredBirds['MaxBodyMass']) plt.show()如果想要"平滑但不过度平滑"的曲线,调整bw_adjust参数(带宽调整因子,取值越小带宽越窄、曲线越贴近原始波动):
sns.kdeplot(filteredBirds['MaxBodyMass'], bw_adjust=0.2) plt.show()课程要求读者阅读该类型图的全部可用参数并动手实验。
5.3 分组密度:按鸟类目展示体重密度
只需几行代码,就能展示按鸟类目(Order)分组的最大体重密度,填充色块 + 各自归一化,适合对比多个子群体的形状:
sns.kdeplot( data=filteredBirds, x="MaxBodyMass", hue="Order", fill=True, common_norm=False, palette="crest", alpha=.5, linewidth=0, )关键参数含义:hue指定分组字段;fill=True填充曲线下区域;common_norm=False表示各组独立归一化(比较"形状"而非"总量");palette/alpha/linewidth分别控制配色板、透明度与轮廓线宽。
5.4 双变量多密度叠加
还可以在同一张图里映射多个变量的密度。例如把体长(MinLength × MaxLength)与保护状态叠加:
sns.kdeplot(data=filteredBirds, x="MinLength", y="MaxLength", hue="ConservationStatus")课程由此引出进一步思考:按体长聚类的 "Vulnerable"(易危)鸟群聚集是否有生物学意义?这正是一个从图表走向研究问题的入口。
6. 仓库实现佐证:solution notebook 中的真实运行证据
以上全部代码在仓库内都有可直接运行的完整实现:solution/notebook.ipynb 依次保存了"基础直方图 → 调整 bins → 过滤后新直方图 → MaxBodyMass×MaxLength 的 2D 直方图 → 保护状态叠加直方图 → MinWingspan KDE → MaxBodyMass KDE → bw_adjust 实验 → MinLength×MaxLength 双变量 KDE(hue=ConservationStatus)"全部单元格的执行输出,可与本文各节逐一对应复现。
从该 notebook 的运行记录还能得到两条实操层面的事实(均来自其保存的 stderr/警告输出,可推断为当时的运行环境特性):
- 在较早的 Matplotlib/Seaborn 组合下执行
kdeplot会触发FutureWarning: Support for multi-dimensional indexing (e.g. obj[:, None]) is deprecated...,属于版本过渡期告警,不影响出图; - 双变量 KDE(MinLength×MaxLength)运行时会提示
UserWarning: Dataset has 0 variance; skipping density estimate.——说明某个(小样本)分组在某个方向上方差为 0 而被跳过。可以推断:分组 KDE 对样本量极少的类别并不稳健,这也是课程建议"研究 VU 聚集是否有意义"背后的方法论提醒。
另请注意:课程 README 中 2D 直方图小节还演示了from matplotlib import colors/PercentFormatter的可选导入(见 solution notebook 对应单元格),用于自定义 2D 直方图的颜色标尺。
7. 关键参数速查
| 参数 | 所属函数 | 作用与取值说明 |
|---|---|---|
bins | plot(kind='hist')/plt.hist | 分箱数量;本课用了 10、30、40、20,越大粒度越细但越易引入噪声 |
figsize | plot | 画布尺寸(英寸),本课统一使用 (12,12) 或默认 |
alpha | plt.hist | 柱条透明度(0~1);叠加多个直方图时 0.5 可保证层次可见 |
label/plt.legend() | plt.hist | 图例文字与显示,用于区分分类分组 |
color | plt.hist | 每条直方图的颜色,本课按 6 种保护状态一一指定 |
bw_adjust | sns.kdeplot | 带宽调整因子(正数);越小越"欠平滑",越大越"过平滑" |
hue | sns.kdeplot | 按指定列分组绘制多条密度曲线 |
fill/common_norm | sns.kdeplot | 是否填充;是否共享归一化基准(False 便于比较形状) |
palette/linewidth | sns.kdeplot | 分组配色板;曲线轮廓线宽(0 即无轮廓) |
8. 作业、挑战与 R 语言版本
- 课后作业:assignment.md 要求换一个数据集(例如来自 Kaggle 的数据源)自建 notebook 讲一个"故事",且必须使用直方图;评分标准(Rubric)为:优秀 = 有完整注释(含数据来源)且至少使用 5 个直方图发现数据事实;及格 = 注释不完整或有 bug;待改进 = 无注释且含 bug。
- 课堂挑战:在网络上搜索直方图的良好使用案例,思考它们如何用直方图展示分布、应用于哪些领域。
- 课后自修:阅读 Seaborn
kdeplot文档,理解"一个或多个维度上的连续概率密度曲线"这一概念。 - R 语言版本:仓库同时提供等价的 R/ggplot2 实现,见 R 版课程,其中用
geom_histogram(bins=10)与coord_flip()完成同样的分布可视化流程,便于与 Python 版本对照学习。
9. 小结
本课在 Data-Science-For-Beginners 的"3-Data-Visualization"模块中承担"从量(quantity)走向分布(distribution)"的转折:
- 散点图能给分布一个粗略轮廓,但刻画真实分布应使用直方图;
bins、布尔掩码过滤是控制直方图粒度与偏态的两个基本杠杆;hist2d用颜色密度揭示双变量联合分布的汇聚点;- 分类(文本)字段通过
.loc逐类提取 + 半透明叠加直方图,即可在一张图中对比多组分布; - Seaborn
kdeplot以平滑曲线替代阶梯,bw_adjust控制平滑强度,hue/fill/common_norm组合支持分组密度对比,直至双变量多密度叠加——为第 11 课"比例可视化"与后续生命周期分析打好分布直觉基础。
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考