Data-Science-For-Beginners 第 10 课:用 R 与 ggplot2 可视化数据分布(直方图与密度图实战)
【免费下载链接】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 课程《Visualizing Distributions》的 R 语言教学版本,讲解如何使用 R 的ggplot2包对明尼苏达州鸟类数据集(data/birds.csv)进行分布分析:从散点图快速概览,到直方图(含bins参数调优、子集过滤、二维直方图),再到针对文本型数据的直方图变换,以及更平滑的密度图(geom_density与adjust参数)。学完本文,你将能够用纯 R 代码复现整套分布可视化工作流,并把同一套方法论迁移到任意数值型与分类型数据集上。
课程背景与数据集说明
在上一课中,你已经通过散点图与异常值(outlier)分析了解过这份明尼苏达州鸟类数据集,包括不同鸟类类别在最大体长上的差异。本课换一个视角:不再盯住单个变量与类别的对应关系,而是观察数据沿着坐标轴是如何组织与聚集的,也就是数据的“分布”(distribution)。
课程使用同一份数据文件 data/birds.csv,该 CSV 首行带 UTF-8 BOM 头(Name,ScientificName,...),因此 R 版本在读取时必须显式声明fileEncoding="UTF-8-BOM",否则第一列列名会带有不可见字符。数据集的列结构如下:
| 列名 | 含义 |
|---|---|
Name/ScientificName | 鸟类常用名 / 学名 |
Category/Order/Family/Genus | 分类学层级 |
ConservationStatus | 保护状态(IUCN 缩写) |
MinLength/MaxLength | 最小 / 最大体长(cm) |
MinBodyMass/MaxBodyMass | 最小 / 最大体重(g) |
MinWingspan/MaxWingspan | 最小 / 最大翼展(cm) |
在 R 控制台(或 RStudio)中按以下方式导入ggplot2、读取数据并去除异常值(过滤掉翼展超过 500 的记录,沿用上一课的做法):
library(ggplot2) birds <- read.csv("../../data/birds.csv", fileEncoding="UTF-8-BOM") birds_filtered <- subset(birds, MaxWingspan < 500) head(birds_filtered)过滤后前几行数据大致如下(以丹麦语教学版表格为例,字段含义与英文原版一致):
| Navn(名称) | Kategori(类别) | Orden(目) | Familie(科) | Slægt(属) | Bevaringsstatus | MinLængde | MaxLængde | MinKropsmasse | MaxKropsmasse | MinVingefang | MaxVingefang | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Sortbuget fløjlsand | Ænder/Gæs/Vandfugle | Anseriformes | Anatidae | Dendrocygna | LC | 47 | 56 | 652 | 1020 | 76 | 94 |
| 1 | Rødbrun fløjlsand | Ænder/Gæs/Vandfugle | Anseriformes | Anatidae | Dendrocygna | LC | 45 | 53 | 712 | 1050 | 85 | 93 |
| 2 | Snegås | Ænder/Gæs/Vandfugle | Anseriformes | Anatidae | Anser | LC | 64 | 79 | 2050 | 4050 | 135 | 165 |
| 3 | Ross' gås | Anser rossii | Anseriformes | Anatidae | Anser | LC | 57.3 | 64 | 1066 | 1567 | 113 | 116 |
| 4 | Stor hvidkindet gås | Anser albifrons | Anseriformes | Anatidae | Anser | LC | 64 | 81 | 1930 | 3310 | 130 | 165 |
用散点图快速概览分布
在进入正式分布图表之前,可以先沿用上一课的散点图对“体长 × 目”的关系做快速侦察。R 版课程使用geom_point()+coord_flip()把目名称翻转到横轴,便于阅读:
ggplot(data=birds_filtered, aes(x=Order, y=MaxLength, group=1)) + geom_point() + ggtitle("Max Length per order") + coord_flip()这张图能给出每个鸟目体长分布的大致范围,但它并不是展示“真实分布”的最优方式——点与点之间没有聚合信息,肉眼难以判断哪个区间的样本更密集。处理分布问题的标准工具是直方图(Histogram)。
直方图:数值型数据的分布利器
ggplot2对直方图提供了非常成熟的实现:geom_histogram()会把连续数值轴切分成若干小区间(bin),用柱子的高低反映每个区间内的样本频数,形状上类似柱状图,但表达的是“数据在轴上如何聚集”。
直方图只接受数值型数据。第一个例子针对MaxBodyMass(最大体重)绘制整份数据集的分布,bins=10表示把数据切成 10 个区间:
ggplot(data = birds_filtered, aes(x = MaxBodyMass)) + geom_histogram(bins=10) + ylab('Frequency')可以看到:数据集中 400+ 只鸟的最大体重绝大多数落在 2000 以下,呈现明显的右偏(长尾)形态——这正是生物体重数据常见的分布特征。右尾由少数大体重鸟类(如天鹅、猛禽)拉长。
调整 bins 参数提升分辨率
把bins从 10 提高到 30,直方图会展示更细粒度的信息:
ggplot(data = birds_filtered, aes(x = MaxBodyMass)) + geom_histogram(bins=30) + ylab('Frequency')参数说明:
bins决定区间数量。值越小柱子越粗、分布越平滑但信息越少;值越大柱子越细、细节越丰富但也更容易暴露噪声。可以把它理解成数据聚合的“分辨率”旋钮。
通过子集过滤消除左偏
上面的直方图整体向左偏(长尾在右侧)。想要得到一个不那么偏斜、更“居中”的分布,可以在绘图前先用subset()圈定数据范围——例如只保留最大体重介于 1 与 60 之间的鸟类,再以 30 个 bin 绘图:
birds_filtered_1 <- subset(birds_filtered, MaxBodyMass > 1 & MaxBodyMass < 60) ggplot(data = birds_filtered_1, aes(x = MaxBodyMass)) + geom_histogram(bins=30) + ylab('Frequency')✅ 动手试试:换用其他过滤条件(如MinLength、MaxWingspan)和数据范围;如果想看数据的完整分布形态,也可以去掉体重过滤条件,只保留bins设置。
二维直方图:同时观察两个分布的收敛
直方图同样支持二维扩展:geom_bin2d()把平面切成网格,用颜色深浅表示每个格子里样本的密度,scale_fill_continuous()可以指定配色方案(示例使用viridis,一种对色觉障碍友好的渐变色):
ggplot(data=birds_filtered_1, aes(x=MaxBodyMass, y=MaxLength)) + geom_bin2d() + scale_fill_continuous(type = "viridis")结果显示体重与体长沿一条预期中的正向轴存在相关关系,并且在某个区间存在一个特别强的收敛点(颜色最亮的方格)。这种“亮点”往往提示数据的聚集中心,是探索两个变量联合分布时非常直观的起点。
文本型数据的分布:保护状态变换实战
直方图天然适合数值型数据;当需要按文本类别观察分布时,必须先做数据变换。本数据集的ConservationStatus列记录了鸟类的保护状态,缩写来自 IUCN Red List Categories(国际自然保护联盟红色名录):
CR:极度濒危(Critically Endangered)EN:濒危(Endangered)EX:灭绝(Extinct)LC:无危(Least Concern)NT:近危(Near Threatened)VU:易危(Vulnerable)
这些是文本值,直接传给geom_histogram会失败。R 版课程的思路是:先把各状态重编码为有序数值标签(x1~x6),再以MinWingspan为横轴、以重编码后的状态为填充色,绘制重叠直方图。position = "identity"让不同状态的柱子互相叠放而不堆叠,alpha = 0.4提供半透明效果以便观察重叠区域:
birds_filtered_1$ConservationStatus[birds_filtered_1$ConservationStatus == 'EX'] <- 'x1' birds_filtered_1$ConservationStatus[birds_filtered_1$ConservationStatus == 'CR'] <- 'x2' birds_filtered_1$ConservationStatus[birds_filtered_1$ConservationStatus == 'EN'] <- 'x3' birds_filtered_1$ConservationStatus[birds_filtered_1$ConservationStatus == 'NT'] <- 'x4' birds_filtered_1$ConservationStatus[birds_filtered_1$ConservationStatus == 'VU'] <- 'x5' birds_filtered_1$ConservationStatus[birds_filtered_1$ConservationStatus == 'LC'] <- 'x6' ggplot(data=birds_filtered_1, aes(x = MinWingspan, fill = ConservationStatus)) + geom_histogram(position = "identity", alpha = 0.4, bins = 20) + scale_fill_manual(name="Conservation Status", values=c("red","green","blue","pink"), labels=c("Endangered","Near Threatened","Vulnerable","Least Concern"))参数说明:
scale_fill_manual()的name设置图例标题,values指定每个级别的填充色,labels覆盖图例显示文本。注意示例代码中values只有 4 个颜色、labels也只对应 4 个状态,而实际数据里仍存在 6 个级别(其中部分级别在过滤后的子集中没有样本或样本过少),这是 R 教学示例中常见的数据情况,不影响理解机制。
从图中看,最小翼展与保护状态之间并没有表现出明显的相关性。你可以用同样的方法测试数据集里的其他字段(如MaxLength、MaxBodyMass),也可以更换过滤条件,观察是否能找到更显著的关联。
密度图:平滑的分布曲线
细心的读者会发现,直方图是“阶梯状”的(柱子之间有明确的边界),曲线无法平滑地连成弧线。当需要展示更光滑的分布形态时,应改用密度图(density plot)。
最小翼展的密度图只需一行几何对象:
ggplot(data = birds_filtered_1, aes(x = MinWingspan)) + geom_density()这张图与之前MinWingspan的直方图形状一致,只是曲线化、平滑化了——它本质上是用核密度估计(KDE)把离散频数拟合成连续概率密度曲线。
之前那张锯齿感很强的MaxBodyMass直方图,同样可以平滑化:
ggplot(data = birds_filtered_1, aes(x = MaxBodyMass)) + geom_density()adjust 参数:控制平滑程度
如果觉得曲线“过于平滑”而丢失了细节,可以修改adjust参数。adjust是带宽的缩放因子:值小于 1 会让带宽变窄、曲线更贴近原始数据(更粗糙),值大于 1 会让曲线更平滑。例如adjust = 1/5得到一条光滑但保留了较多起伏的曲线:
ggplot(data = birds_filtered_1, aes(x = MaxBodyMass)) + geom_density(adjust = 1/5)✅ 建议进一步阅读geom_density()的全部可用参数(如kernel、bw、alpha、fill),并动手实验不同取值对曲线形态的影响。
按类别分组的密度图
密度图非常擅长“以极少量代码产出解释力极强的图”。例如只需三行代码,就能按鸟目(Order)分别绘制最大体重的密度曲线,fill = Order自动分组上色,alpha = 0.5控制透明度让重叠区域可见:
ggplot(data=birds_filtered_1, aes(x = MaxBodyMass, fill = Order)) + geom_density(alpha=0.5)此时各鸟目的体重分布曲线一目了然——哪些目集中在低体重区间、哪些目明显更“重”,以及不同目之间的分布重叠程度,都能直观读出。这正是密度图相对直方图的独特优势:多组比较时更不易杂乱、更易解读。
课程延伸:挑战、自学与作业
- 挑战(🚀 Challenge):直方图是比基础散点图、柱状图、折线图更高级的图表类型。请上网搜索优秀的直方图应用案例,思考它们被用在哪些领域、能说明什么问题。这类图表在生物学、经济学、质量控制、图像处理等需要刻画“单变量分布”的场景中非常普遍。
- 自学(Review & Self Study):本课围绕
ggplot2展开,接下来可以研究geom_density_2d()——官方将其描述为“一个或多个维度上的连续概率密度曲线”。它可以把密度估计扩展到二维平面(等高线形式),与二维直方图形成互补。 - 作业(Assignment):本课配套作业(丹麦语版见 translations/da/3-Data-Visualization/R/10-visualization-distributions/assignment.md,英文版见 3-Data-Visualization/R/10-visualization-distributions/assignment.md)要求:从 Kaggle 等平台另选一份数据集,编写一个带完整注释的 R 脚本,围绕该数据集讲一个故事,并至少使用 5 个直方图来支撑你的发现(优秀标准还包括注明数据来源)。
与本仓库其他资源的关系
本课是 Data-Science-For-Beginners 课程第三模块“数据可视化”的一部分,同一主题在仓库中还有两个并行版本可供对照学习:
- Python 版:3-Data-Visualization/10-visualization-distributions/README.md 使用 Pandas + Matplotlib(
plot(kind='hist'))实现相同分析,并在密度图部分引入 Seaborn 的kdeplot()(对应 R 版的geom_density(),平滑参数bw_adjust对应 R 版的adjust); - R 版英文原稿:3-Data-Visualization/R/10-visualization-distributions/README.md 即本文所讲解文档的原始版本。
R 版课程目录下还提供了与本文每张示意图对应的 PNG 源图(3-Data-Visualization/R/10-visualization-distributions/images/),以及丹麦语译文使用的 webp 版本(translated_images/da/),便于对照图表细节。整个课程模块还配套了在线测验应用(见 quiz-app/)与手绘速记图(见 sketchnotes/10-Visualizing-Distributions.png),可以作为课前课后复习的补充材料。
至此,你已经掌握了从散点概览、直方图调参、子集过滤、二维直方图,到文本数据变换与密度图平滑的完整分布分析工具箱——下一步,就把这套流程用到你自己的数据上吧。
【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考