5.ggplot2——统计汇总
5 统计汇总
5.1 揭示不确定性
如果您的数据中存在的不确定性的信息,无论是来自模型还是来自分布假设,那么展示这些信息非常重要。ggplot2中有四个基本几何对象可用于这项任务,具体取决于 x 值是离散型的还是连续型的,以及您想要展示区间内的中间值还是仅展示区间:
- 离散型x变量,仅展示区间: geom_errorbar(),geom_linerange()
- 离散型x变量,展示区间和中间值: geom_crossbar(),geom_pointrange()
- 连续型x变量,仅展示区间: geom_ribbon()
- 连续型x变量,展示区间和中间值: geom_smooth(stat = "identity")
这些几何对象均假设我们对给定的x 条件时 y 的分布感兴趣,并使用图形属性ymin
和ymax
确定 y 值的范围。如果您想要相反的情况,请参阅coord-flip。
y <- c(18, 11, 16)
df <- data.frame(x = 1:3, y = y, se = c(1.2, 0.5, 1.0))
base <- ggplot(df, aes(x, y, ymin = y - se, ymax = y + se))
base + geom_crossbar()
base + geom_pointrange()
base + geom_smooth(stat = "identity")
image
image
image
base + geom_errorbar()
base + geom_linerange()
base + geom_ribbon()
image
image
image
因为有很多不同的方法来计算标准误差,所以计算方法取决于你。对于简单的情况,ggplot2提供了部分数据摘要的函数,不然你就得自己动手了。R for Data Science ( https://r4ds.had.co.nz ) 包含关于使用更复杂的模型。
5.2 加权数据
当您整合好数据后,其中数据集中的每一行代表多个观察值时,您需要某种方式来考虑权重变量。我们将使用内置midwest
数据集(在 2000 年美国人口普查中在中西部各州收集的一些数据)。数据主要包括比例型数据(例如,白人百分比、贫困线以下百分比、大学学历百分比)和每个县的一些信息(面积、总人口、人口密度)。
我们可以通过以下几个方面来加权:
- 什么都不用,直接看县的数量。
- 总人口,与原始的绝对数配合使用。
- 面积,用于研究边缘效应。(这对
midwest
数据集用处不大,但当我们研究耕地面积比例等变量时它会起到不错的效果。)
权重变量的选择会极大影响我们在图中看到的内容以及我们将得出的结论。有两个图形属性可用于调整权重。首先,对于像线和点这样的简单几何图形,使用图形属性size来改变点的大小:
# Unweighted
ggplot(midwest, aes(percwhite, percbelowpoverty)) +
geom_point()
# Weight by population
ggplot(midwest, aes(percwhite, percbelowpoverty)) +
geom_point(aes(size = poptotal / 1e6)) +
scale_size_area("Population\n(millions)", breaks = c(0.5, 1, 2, 4))
image
image
对于涉及一些统计转换的更复杂的情况,我们通过修改weight
图形属性来体现权重。这些权重将传递给统计汇总函数。在任何有意义的情况下都支持权重:平滑器、分类回归、箱线图、直方图和密度图。我们不能直接看到这个权重变量,也不会产生图例,但是会改变统计汇总的结果。以下代码显示了加权人口密度如何影响白人百分比与贫困线以下百分比之间的关系。
# Unweighted
ggplot(midwest, aes(percwhite, percbelowpoverty)) +
geom_point() +
geom_smooth(method = lm, size = 1)
#> `geom_smooth()` using formula 'y ~ x'
# Weighted by population
ggplot(midwest, aes(percwhite, percbelowpoverty)) +
geom_point(aes(size = poptotal / 1e6)) +
geom_smooth(aes(weight = poptotal), method = lm, size = 1) +
scale_size_area(guide = "none")
#> `geom_smooth()` using formula 'y ~ x'
image
image
当我们按总人口对直方图或密度图进行加权时,我们从查看县数的分布变为查看人数的分布。以下代码显示了这对贫困线以下百分比直方图的影响:
ggplot(midwest, aes(percbelowpoverty)) +
geom_histogram(binwidth = 1) +
ylab("Counties")
ggplot(midwest, aes(percbelowpoverty)) +
geom_histogram(aes(weight = poptotal), binwidth = 1) +
ylab("Population (1000s)")
image
image
5.3 钻石数据
为了演示大型数据集的分析工具,我们将使用内置diamonds
数据集,其中包含约 54,000 颗钻石的价格和质量信息:
diamonds
#> # A tibble: 53,940 × 10
#> carat cut color clarity depth table price x y z
#> <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43
#> 2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31
#> 3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31
#> 4 0.29 Premium I VS2 62.4 58 334 4.2 4.23 2.63
#> 5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75
#> 6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48
#> # … with 53,934 more rows
数据包含钻石质量的四个 “C”:克拉、切工、颜色和净度;以及五个物理测量值:深度、砖面宽度、x、y 和 z,如图。
如何测量变量 x、y、z、表和深度。数据集没有得到很好的清理,因此除了展示了有关钻石的有趣的性质外,还显示了一些数据质量问题。
5.4 展示数据分布
有许多几何图形可用于展示数据分布,具体取决于分布的维度,是连续分布还是离散分布,以及您对条件分布还是联合分布感兴趣。
对于一维连续分布,最重要的几何对象是直方图,geom_histogram():
ggplot(diamonds, aes(depth)) +
geom_histogram()
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
ggplot(diamonds, aes(depth)) +
geom_histogram(binwidth = 0.1) +
xlim(55, 70)
#> Warning: Removed 45 rows containing non-finite values (stat_bin).
#> Warning: Removed 2 rows containing missing values (geom_bar).
image
image
为了更好的展示视图,需要不断的测试组距,通过更改binwidth
、指定数量bins
或指定breaks
的切分位置。不要过分依赖默认参数来获得分布展示的视图。对 x 轴放大 :xlim(55, 70)
,并选择较小的 bin 宽度binwidth = 0.1
,可以展示更多的信息。
展示时不要忘记在标题中包含有关重要参数(如 bin 宽度)的信息。
如果要比较组之间的分布,您有几个选择:
- 绘制多个小直方图,
facet_wrap(~ var)
。 - 绘制频数多边形并以颜色作为分类,geom_freqpoly()。
- 绘制“条件密度图”,
geom_histogram(position = "fill")
。
频数多边形和条件密度图如下所示。条件密度图用于position_fill()堆叠每个 bin,将其缩放到相同的高度。此图在感知上具有挑战性,因为您需要比较条形高度,而不是位置,但您可以看到最明显的模式。
ggplot(diamonds, aes(depth)) +
geom_freqpoly(aes(colour = cut), binwidth = 0.1, na.rm = TRUE) +
xlim(58, 68) +
theme(legend.position = "none")
ggplot(diamonds, aes(depth)) +
geom_histogram(aes(fill = cut), binwidth = 0.1, position = "fill",
na.rm = TRUE) +
xlim(58, 68) +
theme(legend.position = "none")
image
image
直方图和频数多边形几何对象都使用相同的底层统计变换:stat = "bin"
. 此统计数据产生两个输出变量:count
和density
。默认情况下,count 映射到 y 位置,因为它最易于解释。密度是计数除以总计数乘以 bin 宽度,当您要比较分布的形状而不是整体大小时非常有用。
基于 bin 的可视化的替代方法是密度估计。geom_density()在每个数据点放置一点正态分布并将所有曲线相加。它理论性质比较理想,但更难与数据联系起来。当您知道基础密度是平滑、连续和无界的时,请使用密度图。您可以使用adjust
参数使密度或多或少变得平滑。
ggplot(diamonds, aes(depth)) +
geom_density(na.rm = TRUE) +
xlim(58, 68) +
theme(legend.position = "none")
ggplot(diamonds, aes(depth, fill = cut, colour = cut)) +
geom_density(alpha = 0.2, na.rm = TRUE) +
xlim(58, 68) +
theme(legend.position = "none")
image
image
请注意,每个密度估计的面积都标准化为 1,因此您会丢失有关每个组的相对大小的信息。
直方图、频数多边形和密度曲线显示分布的详细视图。但是,有时您想比较许多分布,牺牲一些数据质量而换取数量是很有用的。这里有三个选项:
-
geom_boxplot():箱线图显示了五个汇总统计数据以及一个“异常值”。它显示的信息比直方图少得多,但占用的空间也少得多。
您可以将箱线图与分类变量 x 和连续变量 x 结合使用。对于连续的 x,您还需要设置分组来定义 x 变量如何分解为 bin。可以使用函数cut_width():
image imageggplot(diamonds, aes(clarity, depth)) + geom_boxplot() ggplot(diamonds, aes(carat, depth)) + geom_boxplot(aes(group = cut_width(carat, 0.1))) + xlim(NA, 2.05) #> Warning: Removed 997 rows containing missing values (stat_boxplot).
-
geom_violin():小提琴图是密度曲线图的精简版。底层计算是相同的,但结果以与箱线图类似的方式显示:
image imageggplot(diamonds, aes(clarity, depth)) + geom_violin() ggplot(diamonds, aes(carat, depth)) + geom_violin(aes(group = cut_width(carat, 0.1))) + xlim(NA, 2.05) #> Warning: Removed 997 rows containing non-finite values (stat_ydensity).
-
geom_dotplot():为每个观察值绘制一个点,仔细调整以避免重叠并显示分布。它对于较小的数据集很有用。
5.5 处理遮盖绘图
散点图是研究两个连续变量之间关系的非常重要的工具。但是,当数据很大时,点通常会出现重叠现象,从而掩盖了真实的关系。在极端情况下,您将只能看到数据的范围,从图形中得出的任何结论都值得怀疑。这个问题被称为overplotting。
根据数据的大小和过度绘制的严重程度,有多种方法可以处理它。第一组技术涉及调整图形属性。这些对于较小的数据集往往最有效:
-
有时可以通过使点变小或使用空心字形来缓解非常少量的过度绘制。以下代码显示了从二元正态分布采样的 2000 个点的一些选项。
image image imagedf <- data.frame(x = rnorm(2000), y = rnorm(2000)) norm <- ggplot(df, aes(x, y)) + xlab(NULL) + ylab(NULL) norm + geom_point() norm + geom_point(shape = 1) # Hollow circles norm + geom_point(shape = ".") # Pixel sized
-
对于具有更多重叠的较大数据集,您可以混合使用 alpha(透明度)使点透明。如果您指定
alpha
为比率,则分母会给出必须过度绘制以提供纯色的点数。可用最小透明度为1/500,若选取的alpha值过小,则各点将完全透明。
image image imagenorm + geom_point(alpha = 1 / 3) norm + geom_point(alpha = 1 / 5) norm + geom_point(alpha = 1 / 10)
-
如果数据中存在一些离散性,您可以增加抖动点geom_jitter()以减轻重叠。这在结合透明度时特别有用。默认情况下,添加的抖动量是数据分辨率的 40%,这在相邻区域之间留下了小间隙。您可以使用
width
和height
参数修改默认值 。
我们可以将过度绘制视为二维密度估计问题,这会产生另外两种方法:
-
将全部数据点分箱并统计每个箱中点的数量,用geom_bin2d()可视化计数(直方图的2D推广)。将图形分成许多小方块会产生分散注意力的视觉假象。建议改用六边形,这是在hexbin包中使用 geom_hex()实现的。
下面的代码进行比较正方形和六边形箱,可以使用
bins
和binwidth
参数控制箱的数量和大小。
image imagenorm + geom_bin2d() norm + geom_bin2d(bins = 10)
image imagenorm + geom_hex() norm + geom_hex(bins = 10)
-
使用二维估计密度stat_density2d(),然后使用statistical-summaries中显示三维曲面。
-
如果您对给定 x 的 y 的条件分布感兴趣,那么distribution](https://ggplot2-book.org/getting-started.html#distribution)的技术也将很有用。
处理过度绘图的另一种方法是添加数据摘要,以帮助引导眼睛观察数据分布的真实形状。例如,您可以使用geom_smooth()或使用以下摘要之一添加显示数据中心的平滑线。
5.6 统计汇总
geom_histogram()和geom_bin2d()使用类似的几何对象geom_bar()和geom_raster(),结合新的统计变换stat_bin()和stat_bin2d()。统计变换stat_bin()与stat_bin2d()将数据分组并计算每个组中观测值的数量。但是如果我们想要一个除 count 之外的摘要呢?到目前为止,我们只使用了与每个几何对象的默认统计转换。现在我们将探索如何使用stat_summary_bin()和stat_summary_2d()来计算不同的摘要。
让我们从几个钻石数据的例子开始。第一个示例展示了我们如何计算每个 bin 中的钻石数量;第二个显示了我们如何计算平均价格。
ggplot(diamonds, aes(color)) +
geom_bar()
ggplot(diamonds, aes(color, price)) +
geom_bar(stat = "summary_bin", fun = mean)
image
image
ggplot(diamonds, aes(table, depth)) +
geom_bin2d(binwidth = 1, na.rm = TRUE) +
xlim(50, 70) +
ylim(50, 70)
ggplot(diamonds, aes(table, depth, z = price)) +
geom_raster(binwidth = 1, stat = "summary_2d", fun = mean,
na.rm = TRUE) +
xlim(50, 70) +
ylim(50, 70)
#> Warning: Raster pixels are placed at uneven horizontal intervals and will be
#> shifted. Consider using geom_tile() instead.
#> Warning: Raster pixels are placed at uneven vertical intervals and will be
#> shifted. Consider using geom_tile() instead.
image
image
如果要这两种统计变换的更多信息,可参考stat_summary_bin()和stat_summary_2d()。您可以控制 bin 的大小和摘要函数。stat_summary_bin()支持y
,ymin
和ymax
图形属性。有关更多详细信息,请参阅文档统计变换中了解有关 geom 和 stats 结合使用。
这些汇总函数均有其局限性,但对于快速解决问题通常很有用。如果您发现它们受到限制,则需要自己进行总结(想了解更多请参阅 R for Data Science https://r4ds.had.co.nz)
5.7 曲面图
到目前为止,我们已经考虑了两类几何对象:
-
简单几何对象,其中数据框中的行与几何对象的物理元素之间存在一对一的对应关系
-
群组几何对象,其中在原始数据和结果之间引入了统计摘要
现在我们将考虑需要对三维曲面图进行可视化的情况。ggplot2 包不支持真正的 3d 表面,但它支持许多用于在 2d 中总结 3d 表面的常用工具:等高线图、着色瓦片和气泡图。这些都类似,仅在用于第三维的美学上有所不同。以下是等高线图的示例:
ggplot(faithfuld, aes(eruptions, waiting)) +
geom_contour(aes(z = density, colour = ..level..))
image
上面所使用的..level..
可能比较难以理解,因为faithfuld
数据下没有所谓的可变..level..
。在这种情况下,..
符号指的是内部计算的变量(参见generated-variables)。要显示与热图相同的密度,您可以使用geom_raster():
ggplot(faithfuld, aes(eruptions, waiting)) +
geom_raster(aes(fill = density))
image
# Bubble plots work better with fewer observations
small <- faithfuld[seq(1, nrow(faithfuld), by = 10), ]
ggplot(small, aes(eruptions, waiting)) +
geom_point(aes(size = density), alpha = 1/3) +
scale_size_area()
image
对于交互式 3d 绘图,包括真正的 3d曲面图,请参阅 3d曲面图 。