《R for Data Science (2e)》免费中文翻译 (第9章) --- Layers(2)
写在前面
本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github,欢迎大家参与贡献,详细信息见:
Books-zh-cn 项目介绍:
Books-zh-cn:开源免费的中文书籍社区
r4ds-zh-cn Github 地址:
https://github.com/Books-zh-cn/r4ds-zh-cn
r4ds-zh-cn 网站地址:
https://books-zh-cn.github.io/r4ds-zh-cn/
目录
-
9.5 统计转换
-
9.6 位置调整
-
9.7 坐标系统
-
9.8 图形的分层语法
-
9.9 总结
9.5 统计转换
考虑使用 geom_bar()
或 geom_col()
绘制的基本条形图(bar chart)。 下图显示了 diamonds
数据集中的钻石(diamonds)总数,按 cut
分组。diamonds
数据集位于 ggplot2 包中,包含 ~54,000 颗钻石的信息,包括每颗钻石的 price
, carat
, color
, clarity
, and cut
。 该图表显示,高质量 cuts 的钻石数量多于低质量 cuts 的钻石。
ggplot(diamonds, aes(x = cut)) + geom_bar()
在 x-axis,图表显示 cut
,这是 diamonds
的一个变量。 在 y-axis,它显示 count,但 count 不是 diamonds
中的变量! count 从何而来? 许多图表,例如 scatterplots,都会绘制数据集的原始值。 其他图表,例如 bar charts,会计算要绘制的新值:
-
Bar charts, histograms, and frequency polygons 对数据进行分 bin,然后绘制 bin counts,即每个 bin 中的点数。
-
Smoothers 将模型拟合到您的数据,然后根据模型绘制预测。
-
Boxplots 计算分布的 five-number summary,然后将该 summary 显示为特殊格式的 box。
用于计算图形新值的算法称为 stat,是统计变换(statistical transformation)的缩写。Figure 9.2
显示了此过程如何与 geom_bar()
配合使用。
Figure 9.2: 创建条形图时,我们首先从原始数据开始,然后将其聚合以计算每个条形中的观察值数量,最后映射这些计算变量以绘制美观的图。
您可以通过检查 stat
参数的默认值来了解 geom 使用哪个 stat。 例如,?geom_bar
显示 stat
的默认值为 "count",这意味着 geom_bar()
使用 stat_count()
。stat_count()
与 geom_bar()
记录在同一页面上。 如果向下滚动,名为 "Computed variables" 的部分会解释它计算两个新变量:count
和 prop
。
每个 geom 都有一个默认的 stat;每个 stat 都有一个默认的 geom。 这意味着您通常可以使用 geoms,而不必担心底层的统计转换。 但是,您可能需要显式使用 stat 的三个原因如下:
-
您可能想要覆盖默认 stat。 在下面的代码中,我们将
geom_bar()
的 stat 从 count (默认)更改为 identity。 这让我们可以将条形的高度映射到 y 变量的原始值。diamonds |>count(cut) |>ggplot(aes(x = cut, y = n)) +geom_bar(stat = "identity")
-
您可能想要覆盖从转换变量到美学的默认映射。 例如,您可能想要显示比例条形图,而不是计数:
ggplot(diamonds, aes(x = cut, y = after_stat(prop), group = 1)) + geom_bar()
要查找可由 stat 计算的可能变量,请在
geom_bar()
的帮助中查找标题为 "computed variables" 的部分。 -
您可能希望更加关注代码中的统计转换。 例如,您可以使用
stat_summary()
,它总结每个唯一 x 值的 y 值,以引起人们对您正在计算的 summary 的注意:ggplot(diamonds) + stat_summary(aes(x = cut, y = depth),fun.min = min,fun.max = max,fun = median)
ggplot2 提供了 20 多种 stats 供您使用。 每个 stat 都是一个函数,因此您可以通过通常的方式获得帮助,例如 ?stat_bin
。
9.5.1 练习
-
与
stat_summary()
关联的默认 geom 是什么? 如何重写前面的绘图以使用 geom 函数而不是 stat 函数? -
geom_col()
的作用是什么? 它与geom_bar()
有什么不同? -
大多数 geoms 和 stats 都是成对出现的,几乎总是一起使用。 列出所有配对的列表。 他们有什么共同点? (提示:通读文档。)
-
stat_smooth()
计算哪些变量? 什么参数控制其行为? -
在我们的比例条形图中,我们需要设置
group = 1
。 为什么呢?换 句话说,这两张图有什么问题呢?ggplot(diamonds, aes(x = cut, y = after_stat(prop))) + geom_bar() ggplot(diamonds, aes(x = cut, fill = color, y = after_stat(prop))) + geom_bar()
9.6 位置调整
条形图还有一个神奇之处。 您可以使用 color
aesthetic 或更有用的 fill
aesthetic 来为条形图着色:
# Left
ggplot(mpg, aes(x = drv, color = drv)) + geom_bar()# Right
ggplot(mpg, aes(x = drv, fill = drv)) + geom_bar()
请注意,如果将 fill aesthetic 映射到另一个变量(例如 class
),会发生什么:条形图会自动堆叠(stacked)。 每个彩色矩形代表 drv
和 class
的组合。
ggplot(mpg, aes(x = drv, fill = class)) + geom_bar()
使用 position
参数指定的位置调整(position adjustment)自动执行堆叠。 如果您不需要堆叠条形图,可以使用其他三个选项之一:"identity"
, "dodge"
or "fill"
。
-
position = "identity"
将把每个对象准确地放置在图表上下文中的位置。 这对于条形图来说不是很有用,因为它会导致重叠。 要看到重叠,我们需要通过将alpha
设置为较小的值来使条形稍微透明,或者通过设置fill = NA
使条形完全透明。# Left ggplot(mpg, aes(x = drv, fill = class)) + geom_bar(alpha = 1/5, position = "identity")# Right ggplot(mpg, aes(x = drv, color = class)) + geom_bar(fill = NA, position = "identity")
identity position 调整对于 2d geoms 更有用,例如 points,它是默认值。
-
position = "fill"
的工作方式类似于堆叠(stacking),但使每组堆叠的条形高度相同。 这使得比较不同组之间的比例变得更加容易。 -
position = "dodge"
将重叠的对象直接放在一起。 这使得比较各个值变得更容易。# Left ggplot(mpg, aes(x = drv, fill = class)) + geom_bar(position = "fill")# Right ggplot(mpg, aes(x = drv, fill = class)) + geom_bar(position = "dodge")
还有另一种类型的调整对于条形图没有用,但对于散点图非常有用。 回想一下我们的第一个散点图。 您是否注意到该图仅显示 126 个点,即使数据集中有 234 个观测值?
hwy
和 displ
的基础值是四舍五入的,因此这些点出现在网格上,并且许多点彼此重叠。 此问题称为过度绘图(overplotting)。这 种安排使得很难看到数据的分布。 数据点是否均匀分布在整个图表中,或者是否存在包含 109 个值的 hwy
和 displ
的一种特殊组合?
您可以通过将位置调整设置为 "jitter" 来避免这种网格化。position = "jitter"
为每个点添加少量随机噪声。 这会将点分散开,因为没有两个点可能接收到相同数量的随机噪声。
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(position = "jitter")
添加随机性似乎是改善绘图的一种奇怪方法,但虽然它会使您的图表在小尺度上不太准确,但它会使您的图表在大尺度上更具启发性。 因为这是一个非常有用的操作,所以 ggplot2 附带了 geom_point(position = "jitter")
的简写形式:geom_jitter()
。
要了解有关 position adjustment 的更多信息,请查找与每个调整相关的帮助页面:?position_dodge
, ?position_fill
, ?position_identity
, ?position_jitter
, and ?position_stack
。
9.6.1 练习
-
下面的绘图有什么问题吗? 你可以如何改进它?
ggplot(mpg, aes(x = cty, y = hwy)) + geom_point()
-
两个图之间有什么区别(如果有的话)?为 什么?
ggplot(mpg, aes(x = displ, y = hwy)) +geom_point() ggplot(mpg, aes(x = displ, y = hwy)) +geom_point(position = "identity")
-
geom_jitter()
的哪些参数控制抖动量? -
将
geom_jitter()
与geom_count()
进行比较和对比。 -
geom_boxplot()
的默认位置调整是多少? 创建mpg
数据集的可视化来演示它。
9.7 坐标系统
坐标系统(Coordinate systems)可能是 ggplot2 中最复杂的部分。 默认坐标系是笛卡尔坐标系,其中 x 和 y 位置独立作用以确定每个点的位置。 还有另外两个坐标系偶尔会有帮助。
-
coord_quickmap()
正确设置地理地图的纵横比。 如果您使用 ggplot2 绘制空间数据,这一点非常重要。 本书没有足够的篇幅来讨论地图,但您可以在 ggplot2: Elegant graphics for data analysis 的Maps chapter
了解更多信息。nz <- map_data("nz")ggplot(nz, aes(x = long, y = lat, group = group)) +geom_polygon(fill = "white", color = "black")ggplot(nz, aes(x = long, y = lat, group = group)) +geom_polygon(fill = "white", color = "black") +coord_quickmap()
-
coord_polar()
使用极坐标。 极坐标揭示了 bar chart 和 Coxcomb chart 之间的有趣联系。bar <- ggplot(data = diamonds) + geom_bar(mapping = aes(x = clarity, fill = clarity), show.legend = FALSE,width = 1) + theme(aspect.ratio = 1)bar + coord_flip() bar + coord_polar()
9.7.1 练习
-
使用
coord_polar()
将 stacked bar chart 转换为 pie chart。 -
coord_quickmap()
和coord_map()
有什么区别? -
下图告诉您什么关于城市和高速公路 mpg 之间的关系? 为什么
coord_fixed()
很重要?geom_abline()
的作用是什么?ggplot(data = mpg, mapping = aes(x = cty, y = hwy)) +geom_point() + geom_abline() +coord_fixed()
9.8 图形的分层语法
我们可以通过添加 position adjustments, stats, coordinate systems, and faceting 来扩展您在 Section 1.3
中学到的图形模板:
ggplot(data = <DATA>) + <GEOM_FUNCTION>(mapping = aes(<MAPPINGS>),stat = <STAT>, position = <POSITION>) +<COORDINATE_FUNCTION> +<FACET_FUNCTION>
我们的新模板采用七个参数,即模板中出现的括号内的单词。 在实践中,您很少需要提供所有七个参数来制作图表,因为 ggplot2 将为除 data、mappings、 、geom 函数之外的所有内容提供有用的默认值。
模板中的七个参数组成了图形语法,这是一个用于构建绘图的正式系统。 图形语法基于这样的见解:您可以将任何绘图独特地描述为 a dataset、a geom、a set of mappings、a stat、a position adjustment、a coordinate system、a faceting scheme、a theme 的组合。
要了解其工作原理,请考虑如何从头开始构建基本图:您可以从 dataset 开始,然后将其转换为您想要显示的信息(with a stat)。 接下来,您可以选择一个 geometric object 来表示转换数据中的每个观测值。 然后,您可以使用 geoms 的 aesthetic properties 来表示数据中的变量。 您可以将每个变量的值 map 到 aesthetic 水平。 这些步骤如 Figure 9.3
所示。 然后,您可以选择一个 coordinate system 来放置 geoms,使用对象的位置(其本身就是一种美学属性)来显示 x 和 y 变量的值。
Figure 9.3: 从原始数据到频率表再到条形图的步骤,其中条形的高度代表频率。
此时,您将拥有一个完整的图形,但您可以进一步调整坐标系内 geoms 的位置(a position adjustment)或将图形拆分为子图(faceting)。 您还可以通过添加一个或多个附加图层来扩展绘图,其中每个附加图层都使用 a dataset, a geom, a set of mappings, a stat, and a position adjustment。
您可以使用此方法来构建您想象的任何绘图。 换句话说,您可以使用本章中学到的代码模板来构建数十万个独特的绘图。
如果您想了解更多关于 ggplot2 的理论基础,您可能会喜欢阅读 "The Layered Grammar of Graphics",这是一篇详细描述 ggplot2 理论的科学论文: https://vita.had.co.nz/papers/layered-grammar.pdf
。
9.9 总结
在本章中,您学习了图形的分层语法,从 aesthetics 和 geometries 开始构建简单的绘图,将绘图 facets 成子集,了解如何计算 geoms 的 statistics,在 geoms 可能发生变化时控制位置细节以避免重叠的 position adjustments。 coordinate systems 允许您从根本上改变 x
和 y
的含义。 我们还没有触及的一层是 theme,我们将在 Section 11.5
中介绍它。
ggplot2 cheatsheet (which you can find at https://posit.co/resources/cheatsheets
) 和 ggplot2 package website (https://ggplot2.tidyverse.org
) 是了解完整 ggplot2 功能的两个非常有用的资源。
从本章中你应该学到的一个重要教训是,当你觉得需要 ggplot2 未提供的 geom 时,最好看看其他人是否已经通过创建一个 ggplot2 扩展包来解决你的问题,该扩展包提供了那个 geom。
--------------- 本章结束 ---------------
本期翻译贡献:
-
@TigerZ生信宝库
注:本文已开启快捷转载,欢迎大家转载,只需标明文章出处即可。