![9d5c2356fbd0d37286a1cd443ef02913.png](https://i-blog.csdnimg.cn/blog_migrate/314073c345204543eb5dc5785647fde7.jpeg)
教材: ggplot2 数据分析与图形艺术
qplot()的局限性在于它只能使用一个数据集和一组图形属性映射, 解决这个问题的办法就是使用图层。每个图层可以有自己的数据集和图形属性映射, 附加的数据元素可以通过图层添加到图形中。
一个图层通常由五部分构成:
数据
一组图形属性的映射
几何对象
统计变换
位置调整
1. 创建绘图对象
ggplot() 有两个主要的参数: 数据 和 图形属性的映射。 这两个参数将被设定为绘图的默认参数,只有在新添加的图层里设定了新参数时,默认值才会被修改。
参数数据指定绘图所用的默认数据集,必须是数据框
参数映射的设定方法与前面讲过的qplot()非常相似, 只需要将图形属性和变量名放到函数aes() 的括号里面即可。
p <- ggplot(diamonds, aes(carat, price, colour = cut ))
这个图形对象在加上图层之前无法显示,因此你现在什么也看不见。
2. 图层
最简易的图层莫过于只设定一个几何对象
p <- p + layer(geom = "point")
layer(geom, geomparams, stat, stat_params, data, mapping, postion)
下面给出了一个更复杂的代码。 该代码可以生成一个组距为2,铁青色的直方图:
p <- ggplot(diamonds, aes(x = carat))
p <- p + layer(gnome = "bar",
geom_params = list(fill = "steelblue"),
stat = "bin",
stat_params = list(binwidth = 2))
p
该图层的参数设定的非常细致但是过于繁琐,我们可以用快捷函数来简化上面的代码。因为每一个几何对象对应着一个默认的统计变换和位置参数,而每一个统计变换都对应着一个默认的几何对象参数,所以对于一个图层我们只需要设定stat或gemo参数即可。下面的代码将生成与上述代码完全相同的图层
geom_histogram(bdnwidth = 2, fill = "steelblue")
所有这类快捷函数都有相同的形式——以geom或者stat_开头:
geom_XXX(mapping, data, ..., stat, position)
stat_XXX(mapping, data, ..., geom, position)
它们的参数定义了图层的各种组件:
mapping:一组图形属性映射,通过aes()函数来设定
data:一个数据集,它会修改默认的数据集。大部分情况下该参数会被省略掉,默认数据集将被调用。
...: geom 或 stat的参数,例如直方图的组距或者loess光滑曲线的带宽。我们也可以用图形属性作为参数,这样该属性就被设定为一个固定的值,而不是被映射给数据集中的一个变量
注意,参数data和mapping在ggplot()函数和图层函数中的位置是相反的,因为我们在图形对象中一般先设定数据集,而在图层函数中大多是设定图形属性而不是数据集。建议尽量写清楚参数名而不要依赖位置。
图层可以被添加到用ggplot或qplot创建的图形之上。实际上qplot的绘图原理就是先创建图形对象然后再添加图层。下面这个例子展示了这两种绘图方法的等价性。
ggplot(msleep, aes(sleep_rem/sleep_total, awake)) + geom_point()
##等价于
qplot(sleep_rem/sleep_total, awake, data = msleep)
##也可以给qplot添加图层
qplot(sleep_rem/sleep_total, awake, data = msleep) + geom_smooth()
##等价于
qplot(sleep_rem/sleep_total, awake, data = msleep, geom = c("point", "smooth"))
##或
ggplot(msleep, aes(sleep/rem/sleep_total, awake)) + geom_point() + geom_smooth()
图层是普通的R对象,所以可以存储到变量里去,这有利于代码避繁就简。
3. 图形属性映射
aes() 函数用来将数据变量映射到图形中,从而使变量成为可以被感知的图形属性。aes()函数里有一系列的图形属性参数:
aes(x = weight, y = height, colour = age)
这和qplot的用法一样。注意最好不要使用指定数据集以外的变量,因为这样无法将绘图所用的数据都封装到一个对象里。
3.1 图和图层
默认的图形属性可以在图形对象初始化时设定,或者过后用+修改
p <- ggplot(mtcars, aes(x = mpg, y = wt))
p + geom_point()
p + geom_point(aes(colour = factor(cyl))
p + geom_point(aes(y = disp))
一个图层设定的图形属性映射只对该图层起作用。因此,除非你修改默认的标度,否则坐标标签以及图例标题都会根据图形对象的默认设置生成。
3.2 设定和映射
p <- ggplot(mtcars, aes(mpg, wt))
p + geom_point(colour = "darkblue")
这里将点的颜色设定为深蓝,这和下面例子有很大区别
p + geom_point(aes(colour = "darkblue"))
这里将colour映射到“dark blue”颜色,实际上是先创建了一个只含有“darkblue”字符的变量,然后将colour映射到这个变量。因为这个新变量的值是离散型的,所以默认的颜色标度将用色轮上等间距的颜色,并且此处新变量只有一个值,因此这个颜色就是桃红色。
![eb52ab14a73353964924e58a476ce7fc.png](https://i-blog.csdnimg.cn/blog_migrate/ed35c8d19d75bd6652d1f79b674d7759.jpeg)
![6c322ec1fd24b1270cac09598a390d04.png](https://i-blog.csdnimg.cn/blog_migrate/e990e295087ceebe57cdb50687a07423.jpeg)
3.3 分组
可以将group映射到一个在不同组有不同取值的变量,当现有的单个变量不能够正确的分组,而两个变量的组合可以正确分组时,可以使用interaction()函数
数据:nlme包里的一个数据集Oxboys
library(nlme)
p <- ggplot(Oxboys, aes(age, height, group = Subject)) + geom_line()
![46a7d10875237423917f566207b10afd.png](https://i-blog.csdnimg.cn/blog_migrate/73b3042eca051edeaf0e87398647a27d.jpeg)
![b0018e47bb6dd33efd13692af14c82dd.png](https://i-blog.csdnimg.cn/blog_migrate/f185380a94b0a354150febf712de5ff1.jpeg)
图二则是典型的没有正确分组的错误图片,效果等同于group = 1
如果要为所有男孩添加一个光滑的回归线条:
p + geom_smooth(aes(group=1), method = "lm", size = 3, se = F)
![6c41f444c2c3a549cea42a50e7ba0b59.png](https://i-blog.csdnimg.cn/blog_migrate/2e34fb0a846d5ac862aad1ffcc91141a.jpeg)
注意,如果不是group=1而是group=Subject 会为没有男孩都添加一条
3.4 匹配图形属性和图形对象
想象一个更复杂的系统,其中的线段平稳地从一种图形属性变换到另一种图形属性。这种方式对连续性变量很有效,如大小和颜色,但并不适用于渐变线条的类型。如果想得到这种效果可以用线性插值法来做:
xgrid <- with(df, seq(min(x), max(x), length = 50))
interp <- data.frame(
x = xgrid,
y = approx(df$x, df$y, xout = xgrid)$y,
colour = approx(df$x, df$colour, xout = xgrid)$y)
qplot(x, y, data = df, colour = colour size = I(5)) + geom_line(data = interp, size = 2)
3.5 几何对象
几何图形对象,简称为geom,它执行着图层时间渲染,控制着生成的图像类型。每个几何对象都有一组它能识别的图形属性和依序绘图所需要的值。
3.6 统计变换
ggplot(diamonds, aes(carat)) + geom_histogram(aes(y = ..density..), binwidth = 0.1)
生成变量名字必须要用..围起来。这样可以防止原数据集中的变量和生成变量重名时造成的混淆,并且在以后处理代码时,可以很清晰得分辨哪些变量是由统计变换生成。
3.7 位置调整
stack 将图形元素堆叠
dodge 避免重叠,并排放置
fill 堆叠图形元素并将高度标化为1
identity 不做任何调整
jitter 给点添加扰动避免重合
3.8 整合
d <- ggplot(diamonds, aes(carat)) + xlim(0, 3)
d + stat_bin(aes(ymax = ..count..), binwidth = 0.1, geom = "area")
d + stat_bin(aes(size = ..density..), binwidth = 0.1, geom = "point", position = "identity")
![5518f3760b38a8b8ca4a25bf8c333202.png](https://i-blog.csdnimg.cn/blog_migrate/63f6a11e851833920c1effaf1f96af29.jpeg)
![63b975cab8a073cb8eb838c228ecc8a9.png](https://i-blog.csdnimg.cn/blog_migrate/56521b6c240407b88db5f5487ea687ee.jpeg)
3.8.2 显示已计算过的统计量
如果你有已经汇总过的数据,并且想直接用它,而不进行其他的统计变换,可以使用stat_identity(), 然后将合适的变量映射到相应的图形属性中。
3.8.3 改变图形属性和数据集
require(nlme, quiet = TRUE, warn.conflicts = FALSE)
model <- lme(height ~ age, data = Oxboys, random = ~1 + age | Subject)
oplot <- ggplot(Oxboys, aes(age,height, group = Subject)) + geom_line()
age_grid <- seq(-1, 1, length =10)
subjects <- unique(Oxboys$Subject)
preds <- expand.grid(age = age_grid, Subject = subjects)
preds$height <- predict(model, preds)
oplot + geom_line(data = preds, colour = "#3366FF", size = 0.4)
Oxboys$fitted <- predict(model)
Oxboys$resid <- with(Oxboys, fitted - height)
oplot %+% Oxboys + aes(y = resid) + geom_smooth(aes(group=1)) ##%+%更新数据
![b07d056fbd208a6143deec3692d2b9a3.png](https://i-blog.csdnimg.cn/blog_migrate/5cbe447e583e3b16d338499d29315c79.jpeg)
从图形可以看到残差并不是随机分布的,因此所建立的模型有缺陷。我们向模型中添加一个二次项, 再次计算拟合值和残差并重新绘制残差图。这次没有明显的证据表明模型拟合的不好
model2 <- update(model, height~age + I(age^2))
Oxboys$fitted2 <- predict(model2)
Oxboys$resid2 <- with(Oxboys, fitted2-height)
oplot %+% Oxboys + aes(y = resid2) + geom_smooth(aes(group=1))
值得注意的是,我们对图像的修改是非常容易的。我们更新了数据并且重新作了两次图却再也没有运行过oplot,这正是ggplot2图层功能所秉承的理念: 使反复拟合和评估模型变得轻松而自然。