[关闭]
@K1999 2016-07-04T10:28:45.000000Z 字数 5144 阅读 2729

数据可视化ggplot2系列——图层语法和图形组合

R 数据可视化


1. 图层的几何和统计类型

1.1 几何/统计类型设置函数

在ggplot2中,每种几何类型都有对应的(默认)统计类型,反之亦然。两者不分家,所以得放在一起来说。
几何类型的设置函数全部为geom_xxx形式
统计类型设置函数全部为stat_xxx的形式

  1. library(ggplot2)
  2. ls("package:ggplot2", pattern="^geom_.+")
  3. ls("package:ggplot2", pattern="^stat_.+")

下面看一下几何函数geom_point和统计函数stat_identity的参数:

  1. geom_point(mapping = NULL,
  2. data = NULL,
  3. stat = "identity",
  4. position = "identity",
  5. na.rm = FALSE,
  6. ...)
  7. stat_identity(mapping = NULL,
  8. data = NULL,
  9. geom = "point",
  10. position = "identity",
  11. width = NULL,
  12. height = NULL,
  13. ...)

有4个参数是一样的:映射(mapping)、数据(data)、位置(position)和点点点(Dot-dot-dot:…)。
H.W.特别强调了mapping和data参数的先后顺序在几何/统计类型设定函数和ggplot函数中的差别:ggplot函数先设定数据,再设定映射;而几何/统计类型函数则相反,因为确定作图或统计之前一般都已经有数据,只需指定映射即可。如果不写参数名,它们的用法是这样的:

  1. ggplot(数据, 映射)
  2. geom_xxx(映射, 数据)
  3. stat_xxx(映射, 数据)

"点点点"参数是R语言非常特殊的一个数据类型,用在函数的参数中表示任意参数,在这里表示传递给图层的任意参数如color, shape, alpha等。
取ggplot2的diamonds数据集的一部分数据:

  1. set.seed(100)
  2. d.sub <- diamonds[sample(nrow(diamonds), 500), ]

前面我们一直用geom_point来做散点图,其实完全可以用stat_identity来做,得到的图形是完全相同的:

  1. theme_set(theme_bw())
  2. p <- ggplot(d.sub, aes(x=carat, y=price))
  3. p + stat_identity()
  4. p + geom_point()

查看图层组成可以看到两者都有geom_point、stat_identity和position_identity:

  1. > (p + stat_identity())$layers
  2. [[1]]
  3. geom_point: na.rm = FALSE
  4. stat_identity: na.rm = FALSE
  5. position_identity
  6. > (p + geom_point())$layers
  7. [[1]]
  8. geom_point: na.rm = FALSE
  9. stat_identity: na.rm = FALSE
  10. position_identity

1.2 图层对象

geom_xxx和stat_xxx可以指定数据,映射、几何类型和统计类型,一般来说,有这些东西我们就可以作图了。但实际情况是这些函数不可以直接出图,因为它不是完整的ggplot对象:

  1. p <- geom_point(mapping=aes(x=carat, y=price), data=d.sub)
  2. class(p)
  3. [1] "LayerInstance" "Layer" "ggproto"
  4. p
  5. mapping: x = carat, y = price
  6. geom_point: na.rm = FALSE
  7. stat_identity: na.rm = FALSE
  8. position_identity

图层只是存储类型为environment的R语言对象,它只有建立在ggplot结构的基础上才会成为图形,在这里哪怕是一个空的ggplot对象框架都很有用。这好比仓库里的帐篷,如果你找不到地方把它们支起来,这些东西顶多是一堆货物:

  1. ggplot() + p

前面说过多个图层的相加是有顺序的,图层和ggplot对象的加法也是有顺序的,如果把ggplot对象加到图层上就没有意义。这种规则同样适用于映射和ggplot的相加:

  1. p + ggplot()
  2. NULL
  3. class(aes(x=carat, y=price) + ggplot(d.sub))
  4. [1] "NULL"
  5. class(ggplot(d.sub) + aes(x=carat, y=price))
  6. [1] "gg" "ggplot"

2. 图层的位置调整参数

这和位置映射没有关系,当前版ggplot2只有5种:

dodge:“避让”方式,即往旁边闪,如柱形图的并排方式就是这种。
fill:填充方式, 先把数据归一化,再填充到绘图区的顶部。
identity:原地不动,不调整位置
jitter:随机抖一抖,让本来重叠的露出点头来
stack:叠罗汉

  1. p <- ggplot(d.sub, aes(x=cut, y=price, fill=color))
  2. p + geom_bar(stat="summary", fun.y="mean", position="stack")
  3. p + geom_bar(stat="summary", fun.y="mean", position="fill")
  4. p + geom_bar(stat="summary", fun.y="mean", position="dodge")
  5. p + geom_bar(stat="summary", fun.y="mean", position="jitter")

上面柱形图的抖动很没道理,但散点图里面就有效果了:

  1. p + geom_point(position="identity")
  2. p + geom_point(position="jitter")

所以怎么调整还得看图形的需要。
再看看x轴数据连续的图形:

  1. p <- ggplot(d.sub, aes(x=price, fill=cut, color=cut))
  2. p + stat_density(position="stack")
  3. p + stat_density(position="fill")
  4. p + stat_density(position="identity")
  5. p + stat_density(position="identity", fill="transparent")

看起来很奇妙,改变一个参数就得到不同的图形。

3. 图层组合

图层的组合不是连续使用几个几何或统计类型函数那么简单。ggplot函数可以设置数据和映射,每个图层设置函数(geom_xxx和stat_xxx) 也都可以设置数据和映射,这虽然给组合图制作带来很大便利,但也可能产生一些混乱。如果不同图层设置的数据和映射不同,将会产生什么后果?得了解规则。

3.1 简单组合

不同的图层使用同一套数据,只是几何类型或统计类型有差别。这是最简单也是最常用的,用ggplot函数设置好数据和映射,把几个图层加起来即可:

  1. datax <- data.frame(x=1:10, y=rnorm(10)+1:10)
  2. p <- ggplot(datax, aes(x=x, y=y))
  3. p + geom_point() + geom_line()
  4. p + geom_point() + geom_smooth(method="lm")

ggplot2的图层设置函数对映射的数据类型是有较严格要求的,比如geom_point和geom_line函数要求x映射的数据类型为数值向量,而 geom_bar函数要使用因子型数据。如果数据类型不符合映射要求就得做类型转换,在组合图形时还得注意图层的先后顺序:

  1. > p <- ggplot(datax, aes(x=factor(x), y=y)) + xlab("x")
  2. > p + geom_bar(stat="identity", fill="gray") + geom_line(aes(group=1), size=2) + geom_point(color="red")
  3. > p + geom_bar(stat="identity", fill="gray") + geom_smooth(aes(group=1), method="lm", se=FALSE, size=2)

上面第一个图除了花哨一点外没有任何科学意义,如果放在论文中会被骂得狗血喷头:一套数据重复作图还都是一个意思,是不是脑子有病?但这里只是说明作图方法。作图过程应先设置x映射为因子型(分类坐标轴),用于作柱形图,因为它要求x映射是因子型数据,不能使用连续型数值坐标轴。x映射为因子的数据作散点图的调整步骤相对简单,设置group映射即可。

3.2 不同映射的组合

映射反映的是数据变量。多数情况下一个图形中使用的是同一个数据集,只是变量不同。通常情况下x,y轴至少有一个是相同的,可以用不同图层叠加不同的数据变量:

  1. > p <- ggplot(d.sub, aes(x=carat)) + ylab("depth (blue) / table (red)")
  2. > p + geom_point(aes(y=depth), color="blue") + geom_point(aes(y=table), color="red")

但是为什么要这么做呢?预先处理一下数据再作图会更好,图标都已经帮你设好了:

  1. library(reshape2)
  2. datax <- melt(d.sub, id.vars="carat", measure.vars=c("depth", "table"))
  3. ggplot(datax, aes(x=carat, y=value, color=variable)) + geom_point()

3.3 不同类型数据的组合

如果在geom_xxx函数中改变数据会怎么样呢?不同类型的数据一般不会有完全相同的变量,否则就不是“不同类型”了,所以映射也会相应做修改。下面把钻石数据diamonds和汽车数据mtcars这两个风牛马不相及的数据放在一起看看。
(首先声明:下面的方法只是演示,图形没有任何科学意义。科学图形应该能让观众直观地了解数据,而不是让明白者糊涂让糊涂者脑残。有不少人喜欢用双坐标作混合数据图,个人认为那是很愚昧的做法。)
diamonds数据我们在前面已经了解过了,先看看R datasets包里面的mtcars数据:

  1. library(datasets)
  2. head(mtcars, 5)

好,开始玩点玄乎的:

  1. p <- ggplot(data=d.sub, aes(x=carat, y=price, color=cut))
  2. layer1 <- geom_point(aes(x=carb, y=mpg), mtcars, color="black")
  3. (p1 <- p + layer1)

图中数据点是正确的,但坐标轴标题却对不上号。看看ggplot对象的数据、映射和图层:

  1. head(p1$data, 4)
  2. p1$mapping
  3. p1$layers

数据和映射都还是ggplot原来设置的样子,layer2图层设置的都没有存储到ggplot图形列表对象的data和mapping元素中,而是放在了图层中,但图层中设定的数据不知道跑哪里。
如果再增加一个图层,把坐标轴标题标清楚:

  1. layer2 <- geom_point(aes(y=depth))
  2. (p1 <- p1 + layer2 + xlab("carb(black) / carat") + ylab("mpg(black) / depth"))

很有意思。layer2重新指定了y映射,但没碰原来ggplot对象设置的x和color映射,从获得的图形来看y数据改变了,x和color还是原ggplot对象的设置。查看一下映射和图层:

  1. p1$mapping
  2. p1$layers

可以这么理解:ggplot2图层作图时依次从ggplot对象和图层中获取数据/映射,如果两者映射有重叠,后者将替换前者,但只是在该图层中进行替换而不影响ggplot对象。
如果ggplot对象的映射比图层的映射多,而图层又使用了不同的数据,这是什么情况?看看:

  1. p + geom_point(aes(x=carb, y=mpg), mtcars)
  2. ## Error: arguments imply differing number of rows: 32, 0

由于图层继承了ggplot对象的color映射,但又找不到数据,所以没法作图。解决办法是把原有的映射用NULL取代,或者设为常量(非映射):

  1. p + geom_point(aes(x=carb, y=mpg, color=NULL), mtcars) p + geom_point(aes(x=carb, y=mpg), mtcars, color="red")
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注