☰
Data-Science-For-Beginners 实战:用 R 与 ggplot2 可视化数据分布(直方图与密度图)
2026/9/29 22:31:13 网站建设 项目流程

Data-Science-For-Beginners 实战:用 R 与 ggplot2 可视化数据分布(直方图与密度图)

【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners

分布(Distribution)是数据分析中最基础也最有信息量的视角之一。本篇文章基于 Data-Science-For-Beginners 课程第 10 课《Visualizing Distributions》的 R 版本实现(见 3-Data-Visualization/R/10-visualization-distributions/README.md),以明尼苏达州鸟类数据集为对象,系统讲解如何用 R 的ggplot2包从散点图过渡到直方图、二维直方图与密度图,从而回答"数据沿某个轴如何组织"这一核心问题。读完本文,你将掌握bins、alpha、position、adjust等关键参数的实际作用,并能在数值型与文本型数据上独立完成分布分析与可视化。

课程背景与数据集准备

本课是课程第三部分"数据可视化"中继"可视化数量"(3-Data-Visualization/R/09-visualization-quantities/README.md)之后的进阶章节。上一课中,我们通过绘制翼展散点图发现数据集中存在录入错误(如翼展超过 2000 厘米的"异常记录"),并据此清理了数据。本课在此基础上,将目光转向数据的分布形态——即数据沿某一坐标轴的排列方式,例如明尼苏达鸟类在"最大翼展"或"最大体重"维度上的整体分布。

加载数据与清除离群值

数据集存放在仓库根目录的 data/birds.csv 中,共包含 442 条鸟类记录,13 个字段:Name、ScientificName、Category、Order、Family、Genus、ConservationStatus、MinLength、MaxLength、MinBodyMass、MaxBodyMass、MinWingspan、MaxWingspan。注意该 CSV 文件带有 UTF-8 BOM 头,因此read.csv()必须显式指定fileEncoding="UTF-8-BOM",否则第一列列名(Name)前会残留不可见字符,导致后续按列名操作失败。

library(ggplot2) birds <- read.csv("../../data/birds.csv", fileEncoding="UTF-8-BOM") # 沿用上一课的方法,剔除翼展大于 500 的离群值 birds_filtered <- subset(birds, MaxWingspan < 500) head(birds_filtered)

过滤后的前几行数据示例:

NameScientificNameCategoryOrderFamilyGenusConservationStatusMinLengthMaxLengthMinBodyMassMaxBodyMassMinWingspanMaxWingspan
0Black-bellied whistling-duckDendrocygna autumnalisDucks/Geese/WaterfowlAnseriformesAnatidaeDendrocygnaLC475665210207694
1Fulvous whistling-duckDendrocygna bicolorDucks/Geese/WaterfowlAnseriformesAnatidaeDendrocygnaLC455371210508593
2Snow gooseAnser caerulescensDucks/Geese/WaterfowlAnseriformesAnatidaeAnserLC647920504050135165
3Ross's gooseAnser rossiiDucks/Geese/WaterfowlAnseriformesAnatidaeAnserLC57.36410661567113116
4Greater white-fronted gooseAnser albifronsDucks/Geese/WaterfowlAnseriformesAnatidaeAnserLC648119303310130165

在ggplot2的图形语法框架中,一张图可概括为Plot = Data + Aesthetics + Geometry:Data指数据集,Aesthetics指定要研究的变量(x、y 轴),Geometry决定图的类型(线图、柱图、散点图等)。本课用到的几何对象全部围绕"分布"这一主题。

先用散点图观察整体分布

在对分布做正式分析前,可以沿用上一课的散点图快速获得全局印象。下面代码按鸟类Order(目)分组,展示每只鸟的最大体长:

ggplot(data=birds_filtered, aes(x=Order, y=MaxLength, group=1)) + geom_point() + ggtitle("Max Length per order") + coord_flip()

coord_flip()将坐标系翻转,使大量类别沿纵轴排列、避免标签拥挤。该图能给出"各目鸟类体长大致落在什么范围"的整体印象,但它只是点的堆叠,无法精确表达数值的聚集程度与频次——这正是直方图要解决的问题。

直方图:数值型数据的分布利器

直方图在形式上类似柱状图,但横轴是连续数值的区间(bin),柱子的高低代表落入该区间的数据频次,从而直观呈现"众数在哪、数据是否偏斜、是否存在多峰"。ggplot2中直接使用geom_histogram()即可。

bins 参数控制分组粒度

先对清理后的全量数据绘制MaxBodyMass(最大体重)的直方图,设置 10 个箱:

ggplot(data = birds_filtered, aes(x = MaxBodyMass)) + geom_histogram(bins=10) + ylab('Frequency')

从图中可以立即读出:数据集中的 400 余只鸟,最大体重绝大多数落在 2000 以下,只有极少数大型鸟类(如天鹅、鹭类)拥有更高的体重。这说明数据呈明显的右偏(正偏)分布。

将bins提高到 30,直方图以更细的粒度展示同一批数据:

ggplot(data = birds_filtered, aes(x = MaxBodyMass)) + geom_histogram(bins=30) + ylab('Frequency')

对比两张图可以看到:bins直接决定区间划分数量,箱数越多粒度越细,越能呈现局部的起伏细节;但箱数过多也会放大噪声。实践中应结合数据量与业务语义反复尝试,这与 Matplotlib 版课程(见 3-Data-Visualization/10-visualization-distributions/README.md)中通过bins参数调节粒度的思路完全一致。

数据过滤:修正偏斜分布

左偏(右侧长尾)的直方图会让主体区间被压缩得难以阅读。一个常用策略是先按业务范围过滤数据,再绘图。例如只保留体重介于 1 与 60 之间的鸟类,并维持 30 个箱:

birds_filtered_1 <- subset(birds_filtered, MaxBodyMass > 1 & MaxBodyMass < 60) ggplot(data = birds_filtered_1, aes(x = MaxBodyMass)) + geom_histogram(bins=30) + ylab('Frequency')

过滤后右偏程度显著降低,主体分布(例如集中在 20~40 区间附近的频峰)变得清晰可读。你可以继续尝试其他过滤条件与变量(如MinLength、MaxWingspan),也可以移除过滤条件直接查看全量数据,体会过滤操作对分布形态观察的影响。

二维直方图:比较两个分布的关联

直方图不仅限于单变量。geom_bin2d()将平面按 x、y 两个维度切分为网格,并用颜色深浅(频次高低)表达两个变量的联合分布。下面比较MaxBodyMass与MaxLength的关系,并使用 viridis 色带增强明暗对比:

ggplot(data=birds_filtered_1, aes(x=MaxBodyMass, y=MaxLength)) + geom_bin2d() + scale_fill_continuous(type = "viridis")

图中在预期的对角方向上呈现一条明显的聚集带,并在某一点出现特别强的汇聚(亮色网格),说明体重与体长两个指标之间存在可预期的正相关。scale_fill_continuous()用于调整频次到颜色的映射,viridis色带对色觉障碍者友好,是 ggplot2 生态中的推荐默认方案之一。

文本数据的分布:保护状态的变换与叠加直方图

直方图默认面向数值数据。当需要按文本类别(如保护状态)查看数值分布时,需要先做一次"类别编码"变换。鸟类数据集中的ConservationStatus字段采用 IUCN 红色名录的缩写:

  • CR:极危(Critically Endangered)
  • EN:濒危(Endangered)
  • EX:灭绝(Extinct)
  • LC:无危(Least Concern)
  • NT:近危(Near Threatened)
  • VU:易危(Vulnerable)

为了在直方图的图例中按固定顺序展示这些类别,可以将缩写重编码为带顺序的标签:

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'

接着用fill美学将保护状态映射到颜色,绘制MinWingspan(最小翼展)的叠加直方图。关键参数包括:

  • position = "identity":直方图不堆叠,各状态直接在原点叠加,便于对比形状;
  • alpha = 0.4:设置透明度,避免相互遮挡导致无法阅读;
  • scale_fill_manual():手动指定颜色与图例标签。
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 Threathened","Vulnerable","Least Concern"))

从结果看,各保护状态的翼展分布曲线高度重合,说明"最小翼展"与"保护状态"之间没有明显的相关关系——这也是一种有价值的结论:并非每个变量都与目标标签相关。你可以用同样的方法测试数据集中的其他字段(如MaxLength、MaxBodyMass),并尝试不同的过滤条件,看看能否找到更具区分度的变量。

密度图:平滑的分布曲线

直方图的柱状边缘是"阶梯状"的,无法体现分布的连续变化。密度图(geom_density())通过核密度估计(KDE)输出一条平滑的连续曲线,曲线越高表示该取值附近的数据越密集。

基础密度图

对MinWingspan绘制密度图:

ggplot(data = birds_filtered_1, aes(x = MinWingspan)) + geom_density()

对比此前的叠加直方图可以发现,密度图保留了同一分布的主要峰形,但曲线更加平滑、更易解释。同理,前面第二张直方图中"锯齿状"的MaxBodyMass曲线,也可以用密度图重新绘制以获得平滑版本:

ggplot(data = birds_filtered_1, aes(x = MaxBodyMass)) + geom_density()

adjust 参数控制平滑程度

KDE 的平滑程度由带宽(bandwidth)决定。geom_density()中的adjust参数是带宽的缩放系数:大于 1 更平滑,小于 1 保留更多细节。将adjust设为 1/5 即可获得"平滑但不失细节"的曲线:

ggplot(data = birds_filtered_1, aes(x = MaxBodyMass)) + geom_density(adjust = 1/5)

需要留意的是,与直方图依赖bins类似,密度图的形态也依赖带宽选择——带宽过小会过度拟合噪声(包括离群值),带宽过大则会抹平真实结构。本课数据已经过上一课的离群值清理,因此密度估计较为稳健。

分组密度:按 Order 对比

密度图在"多分布并排对比"场景下极具表现力。下面将Order映射到fill,叠加展示各目鸟类的最大体重密度,并用alpha=0.5保证重叠区域可读:

ggplot(data=birds_filtered_1, aes(x = MaxBodyMass, fill = Order)) + geom_density(alpha=0.5)

几行代码即可完成"多个分布的重叠比较",这正是密度图相比直方图的优势所在。

深入练习与延伸阅读

本课还给出了几条深化方向,值得在仓库之外进一步研究:

  • 探索直方图的应用场景:直方图是比散点图、柱状图、线图更"进阶"的图形类型。可以调研直方图在质量控制、图像直方图均衡、金融收益分布分析等领域的典型用法,理解它适合回答哪类问题。
  • 学习geom_density_2d():本课自研学习部分建议读者研究geom_density_2d()——"在一维或多维上的连续概率密度曲线"。它是二维密度估计的可视化实现,能够在一张图上同时呈现两个变量的联合密度等高线,适合进一步验证本课中MaxBodyMass与MaxLength的聚集关系。
  • 完成课后作业:课程配套作业要求脱离鸟类数据集,另选一个真实数据集(例如 Kaggle 上的开放数据),编写一个带注释的 R 脚本"讲一个数据故事",并且至少使用 5 张直方图来支撑结论,具体要求与评分标准见 3-Data-Visualization/R/10-visualization-distributions/assignment.md(评分维度包括注释完整度、脚本是否有 bug、是否覆盖至少 5 张直方图)。

小结

本课完整演示了从"散点图初探"到"直方图量化"再到"密度图平滑"的分布分析路径:geom_histogram()用bins控制粒度、用过滤修正偏斜;geom_bin2d()揭示双变量联合分布的聚集点;面对文本类别,先重编码再用position="identity"与alpha叠加对比;最后geom_density()与adjust提供连续平滑的分布曲线,并可用fill实现多组对比。掌握这套方法论后,你可以对任何数值型数据集快速回答"数据长什么样、集中在哪里、如何偏斜、哪些变量相关"等关键问题——这正是探索性数据分析(EDA)的核心能力。

【免费下载链接】Data-Science-For-Beginners10 Weeks, 20 Lessons, Data Science for All!项目地址: https://gitcode.com/GitHub_Trending/da/Data-Science-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询