2.ggplot2——入门(二)
2.6绘制几何图形
您可能会猜测,通过替换geom_point()其他的 geom 函数,您会得到不同类型的图形。这是一个很好的猜测!在以下部分中,您将了解 ggplot2 中提供的一些其他重要图形类型。这不是一个详尽的列表,但包含最常用的绘图类型。你想了解跟多individual-geoms和collective-geoms。
- geom_smooth() 拟合数据的平滑曲线并显示平滑曲线及其标准误差。
- geom_boxplot()生成箱线图来总结一组点的分布。
- geom_histogram()和geom_freqpoly()显示连续变量的分布。
- geom_bar()`显示了分类变量的分布。
- geom_path()和geom_line()在数据点之间画线。折线图从左到右行进行的连线,而路径图可以朝任何方向行进。折线图通常用于探索事物如何随时间变化。
2.6.1 在图形中添加平滑曲线
如果您的散点图包含大量噪声,则很难看到主要模式。在这种情况下,geom_smooth()向图中添加平滑线很有用:
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth()
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
image.png
这将用平滑曲线覆盖散点图,包括以灰色显示的各点置信区间的形式评估不确定性。如果您对置信区间不感兴趣,请使用geom_smooth(se = FALSE)
将其关闭。
method
是[geom_smooth()](https://ggplot2.tidyverse.org/reference/geom_smooth.html)
的一个重要参数,它允许您选择使用哪种类型的模型来拟合平滑曲线:
-
method = "loess"
,n 较小时的默认值,使用平滑的局部回归(如?loess中所述)。线条的平滑程度由span
参数控制,范围从 0(很不平滑)到 1(非常平滑)。
image imageggplot(mpg, aes(displ, hwy)) + geom_point() + geom_smooth(span = 0.2) #> `geom_smooth()` using method = 'loess' and formula 'y ~ x' ggplot(mpg, aes(displ, hwy)) + geom_point() + geom_smooth(span = 1) #> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
Loess 不适用于大型数据集(内存消耗是O(n2),因此在n超过1000是下使用另一种平滑算法。
-
method = "gam"
可以调用mgcv 包提供的广义可加模型。您需要先加载 mgcv,然后使用类似formula = y ~ s(x)
或y ~ s(x, bs = "cs")
(对于大数据)的公式 。这就是 ggplot2 在超过 1000 个数据时使用。library(mgcv) ggplot(mpg, aes(displ, hwy)) + geom_point() + geom_smooth(method = "gam", formula = y ~ s(x))
-
method = "lm"
拟合线性模型,默认进行线性拟合。
imageggplot(mpg, aes(displ, hwy)) + geom_point() + geom_smooth(method = "lm") #> `geom_smooth()` using formula 'y ~ x'
-
method = "rlm"
工作原理类似于lm(),但使用了强大的拟合算法,因此异常值不会对拟合产生太大影响。它是MASS 包的一部分,所以记得先加载它。
2.6.2 箱线图和扰动点图
当一组数据包含分类变量和多个连续变量时,您可能需要了解连续变量的值如何随分类变量而变化。假设我们想了解具有相同动力传动系统的汽车的耗油量如何变化。我们可以从这样的散点图开始:
ggplot(mpg, aes(drv, hwy)) +
geom_point()
image
因为drv
和hwy
的取值很少,所以有很多过度绘制。许多点绘制在同一位置,很难看到分布。有三种有用的技术可以解决这个问题:
-
扰动点图,geom_jitter(),为数据添加了一些随机噪声,这有助于避免过度重复。
-
箱线图,geom_boxplot()用若干统计量概括数据分布情况。
-
小提琴图,geom_violin()显示了分布“密度”,突出数据分布密集的区域。
这些如下图所示:
ggplot(mpg, aes(drv, hwy)) + geom_jitter()
ggplot(mpg, aes(drv, hwy)) + geom_boxplot()
ggplot(mpg, aes(drv, hwy)) + geom_violin()
image
image
image
每种方法都有其优点和缺点。箱线图仅用五个数字对分布进行概括,而扰动点图显示了每个点,但仅适用于相对较小的数据集。小提琴图提供了最丰富的信息,但依赖于密度估计的计算,这可能难以解释。
对于扰动点图,geom_jitter()和geom_point()提供了图形属性作为控制:size
,colour
,和shape
。对于geom_boxplot()和geom_violin(),您可以通过colour
或fill
控制轮廓和内部颜色。
2.6.3 直方图和频率多边形
直方图和频数多边图显示单个数值变量的分布。与箱线图相比,它们提供了更多关于单个组分布的信息,但占用内存更大。
ggplot(mpg, aes(hwy)) + geom_histogram()
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
ggplot(mpg, aes(hwy)) + geom_freqpoly()
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
image
image
直方图和频数多边图的工作方式相同:它们对数据进行分箱,然后计算每个分箱中的观察次数。唯一的区别是:直方图使用条形显示,频率多边形使用线条显示。
您可以使用binwidth
参数控制箱的宽度(如果您不想要均匀间隔的箱,您可以使用该breaks
参数)。分箱宽度是非常重要的。默认值只是将您的数据分成 30 个箱,这可能不是最佳选择。您应该尝试多个 bin 宽度,并且您可能会发现需要多个 bin 宽度来展示数据。
ggplot(mpg, aes(hwy)) +
geom_freqpoly(binwidth = 2.5)
ggplot(mpg, aes(hwy)) +
geom_freqpoly(binwidth = 1)
image
image
频数多边形的替代方法是密度图,geom_density()。我不喜欢密度图,因为它们更难解释,因为底层计算更复杂。密度图的前提是要求基础分布是连续的、无界的和平滑的。
要比较不同子组的分布,您可以将分类变量 (geom_histogram()) 或 (geom_freqpoly())映射到填充颜色。使用频数多边形比较分布更容易,因为基础感知任务更容易。您也可以使用分面:这会使比较更困难一些,但更容易查看每个组的分布。
ggplot(mpg, aes(displ, colour = drv)) +
geom_freqpoly(binwidth = 0.5)
ggplot(mpg, aes(displ, fill = drv)) +
geom_histogram(binwidth = 0.5) +
facet_wrap(~drv, ncol = 1)
image
image
2.6.4 条形图
离散变量情形下,条形图与直方图类似,使用geom_bar():
ggplot(mpg, aes(manufacturer)) +
geom_bar()
image
(修复标签链接中学习如何修复标签)。
条形图可能会令人困惑,因为把两种截然不同的图像通常都称为条形图。上面的表格是未经过处理的数据,每个观察值对应每个条的高度。另一种形式的条形图用于预先处理的数据。例如,您有三种具有平均效果的药物:
drugs <- data.frame(
drug = c("a", "b", "c"),
effect = c(4.2, 9.7, 6.1)
)
要显示此类数据,您需要修改geom_bar()默认设置( 不让stat 对数据进行分类和计数)。但是,使用geom_point()会更好,因为点比条占用的空间更少,并且不需要 y 轴包含 0。
ggplot(drugs, aes(drug, effect)) + geom_bar(stat = "identity")
ggplot(drugs, aes(drug, effect)) + geom_point()
image
image
2.6.5 时间序列中的折线图和路径图
折线图和路径图通常用于时间序列数据。折线图从左到右连接点,而路径图按照它们在数据集中出现的顺序连接它们(换句话说,折线图是按 x 值排序的数据的路径图)。折线图通常在 x 轴上有时间,显示单个变量如何随时间变化。路径图显示两个变量如何随时间同时变化,时间以观测值的连接方式进行编码。
由于mpg
数据集中的年份变量只有两个值,我们将使用economics
数据集显示一些时间序列图,其中包含过去 40 年测量的美国经济数据。下图显示了随时间变化的两个失业图,均使用geom_line(). 第一个显示失业率,而第二个显示失业周数的中位数。我们已经可以看到这两个变量的一些差异,特别是在最后一个高峰期,失业率低于之前的高峰期,但失业的持续时间很长。
ggplot(economics, aes(date, unemploy / pop)) +
geom_line()
ggplot(economics, aes(date, uempmed)) +
geom_line()
image
image
为了更详细地研究这种关系,我们想在同一个图上绘制两个时间序列。我们可以绘制失业率与失业持续时间的散点图,但是我们无法再看到随时间的演变。解决方案是将时间上相邻的点与线段连接起来,形成路径图。
下面我们绘制失业率与失业时间的关系,并将个人观察结果与一条路径结合起来。由于有许多线交叉,在第一个图中不容易看出时间流动的方向。在第二个图中,我们为点着色,以便更容易看到时间的方向。
ggplot(economics, aes(unemploy / pop, uempmed)) +
geom_path() +
geom_point()
year <- function(x) as.POSIXlt(x)$year + 1900
ggplot(economics, aes(unemploy / pop, uempmed)) +
geom_path(colour = "grey50") +
geom_point(aes(colour = year(date)))
image
image
我们可以看到失业率和失业时间高度相关,但近年来失业时长相对于失业率一直在增加。
对于纵向数据,您通常希望在每个图上显示多个时间序列,每个序列代表一个人。为此,您需要将group
图形属性映射到一个变量,该变量编码每个观察的组成员资格。
2.7 修改坐标轴
后续将学习所有可用的选项,但有两个有用的最常见选项。xlab()和ylab()修改 x 和 y 轴标签:
ggplot(mpg, aes(cty, hwy)) +
geom_point(alpha = 1 / 3)
ggplot(mpg, aes(cty, hwy)) +
geom_point(alpha = 1 / 3) +
xlab("city driving (mpg)") +
ylab("highway driving (mpg)")
# Remove the axis labels with NULL
ggplot(mpg, aes(cty, hwy)) +
geom_point(alpha = 1 / 3) +
xlab(NULL) +
ylab(NULL)
image
image
image
ggplot(mpg, aes(drv, hwy)) +
geom_jitter(width = 0.25)
ggplot(mpg, aes(drv, hwy)) +
geom_jitter(width = 0.25) +
xlim("f", "r") +
ylim(20, 30)
#> Warning: Removed 139 rows containing missing values (geom_point).
# For continuous scales, use NA to set only one limit
ggplot(mpg, aes(drv, hwy)) +
geom_jitter(width = 0.25, na.rm = TRUE) +
ylim(NA, 30)
image
image
image
更改坐标区范围,区间之外的值被设置为NA
。您可以使用na.rm = TRUE
抑制相关警告,但要小心。如果您的绘图计算汇总统计量(例如,样本均值),则此转换NA
发生在计算汇总统计量之前,并且在某些情况下可能会导致不希望出现的结果。
2.8 输出
大多数情况下,您创建一个绘图对象并立即绘制它,但您也可以将绘图保存到变量中并对其进行操作:
p <- ggplot(mpg, aes(displ, hwy, colour = factor(cyl))) +
geom_point()
一旦你有了一个绘图对象,你可以用它做一些事情:
-
显示在屏幕上print()。这在交互式运行时会自动发生,但在循环或函数中,您需要手动输入print()完成。
imageprint(p)
-
使用ggsave()将其保存到磁盘。
# Save png to disk ggsave("plot.png", p, width = 5, height = 5)
-
用summary() 查看图像结构。
summary(p) #> data: manufacturer, model, displ, year, cyl, trans, drv, cty, hwy, fl, #> class [234x11] #> mapping: x = ~displ, y = ~hwy, colour = ~factor(cyl) #> faceting: <ggproto object: Class FacetNull, Facet, gg> #> compute_layout: function #> draw_back: function #> draw_front: function #> draw_labels: function #> draw_panels: function #> finish_data: function #> init_scales: function #> map_data: function #> params: list #> setup_data: function #> setup_params: function #> shrink: TRUE #> train_scales: function #> vars: function #> super: <ggproto object: Class FacetNull, Facet, gg> #> ----------------------------------- #> geom_point: na.rm = FALSE #> stat_identity: na.rm = FALSE #> position_identity
-
使用saveRDS(), 将它的缓存副本保存到磁盘。 这将保存绘图对象的完整副本,因此您可以轻松地使用readRDS()访问.
saveRDS(p, "plot.rds") q <- readRDS("plot.rds")