ggplot2笔记7:定位(分面和坐标系)
ggplot2绘图基础系列:
Positioning
7.1 简介
本章主题是图形的定位。
定位由四个部分组成,前两个在前期讲过,而后两个部分在这章中会详细说到:
位置调整:调整每个图层中出现重叠的对象的位置
位置标度:控制数据到图形的映射
分面:在一个页面上自动摆放多个图形。将数据分为多个子集,然后将每个子集依次绘制到不同面板上
坐标系:控制两个独立的位置标度形成一个二维坐标系,如笛卡尔坐标系
7.2 分面
每个小图都代表不同的数据子集。分面可以快速地分析数据各子集模式的异同。
本节讨论怎样较好微调分面,特别是和位置标度相关的方法。
facet_null()
:默认,无分面,单一图形。
face_wrap()
:封装型,本质上是1维,为节省空间封装成二维
face_grid()
:网格型,生成一个二维的面板由两个独立的部分组成,,面板的行和列可以通过变量来定义
facet_wrap()
使用这个参数设置一长串面板二维排列,更加节省空间
可以通过 ncol
, nrow
, as.table
, dir
等参数设置排列。
如下例:
library(ggplot2)
## 从mpg数据集中取一个子集mpg2
mpg2 <- subset(mpg, cyl != 5 & drv %in% c("4", "f") & class != "2seater")
base <- ggplot(mpg2, aes(displ, hwy)) +
geom_blank() +
xlab(NULL) +
ylab(NULL)
base
## 添加分面,设置ncol或nrow(二选一即可)
base + facet_wrap(~class, ncol = 3)
## 设置as.table
base + facet_wrap(~class, ncol = 3, as.table = FALSE)
as.table
决定各个分面的排列顺序。
## 设置dir,水平或垂直排列
base + facet_wrap(~class, nrow = 3, dir = "v")
facet_grid()
网格分面,在二维网格中展示图形。
需要设定哪些变量作为分面绘图的行,哪些变量作为列,中间用 ~
短波浪线做标记。
如下例:
mpg2 <- subset(mpg, cyl != 5 & drv %in% c("4", "f") & class != "2seater")
base <- ggplot(mpg2, aes(displ, hwy)) +
geom_blank() +
xlab(NULL) +
ylab(NULL)
## 一行多列
base + facet_grid(. ~ cyl)
## 一列多行
base + facet_grid(drv ~ .)
## 多行多列
base + facet_grid(drv ~ cyl)
依次生成下列三种:
控制标度
• scales="fixed"
: x 和 y的标度在所有分面中都相同(默认) • scales="free_x"
: x 的标度可变,y 固定 • scales="free_y"
: y 的标度可变,x 固定 • scales="free"
: x 和 y 的标度都可变
如下例:
p <- ggplot(mpg2, aes(cty, hwy)) +
geom_abline() +
geom_jitter(width = 0.1, height = 0.1)
## 默认各个分面标度一致
p + facet_wrap(~cyl)
## 设置分面标度可变
p + facet_wrap(~cyl, scales = "free")
依次生成下图:
固定标度可以让我们在相同的基准上对子集进行比较
自由标度可以帮助我们看到更多细节
在变量y单位、数量级不同时,单独固定x轴,不限制y轴也很常用。如下例,经济数据集:
> economics_long
# A tibble: 2,870 x 4
date variable value value01
<date> <fct> <dbl> <dbl>
1 1967-07-01 pce 507 0
2 1967-08-01 pce 510 0.000266
3 1967-09-01 pce 516 0.000764
4 1967-10-01 pce 513 0.000472
5 1967-11-01 pce 518 0.000918
6 1967-12-01 pce 526 0.00158
7 1968-01-01 pce 532 0.00207
8 1968-02-01 pce 534 0.00230
9 1968-03-01 pce 545 0.00322
10 1968-04-01 pce 545 0.00319
# ... with 2,860 more rows
按照不同的 variable
变量来分面:
ggplot(economics_long, aes(date, value)) +
geom_line() +
facet_wrap(~variable, scales = "free_y", ncol = 1)
注意,使用 facet_grid()
时有个限制,因为网格分面中,每列共用一个x轴,每行共用一个y轴。所以同列的分面中必须有相同的x标度,同行的分面必须有相同的y标度。
在 facet_grid()
中,有个参数叫 space
,当 space="free"
时,每行的高度和该行的标度范围一致,这对分类标度很有用,如下例:
其中, reorder()
使得模型(model)和生产商(manufacturer)按城市油耗英里数(cty)重新排序。
mpg2 <- subset(mpg, cyl != 5 & drv %in% c("4", "f") & class != "2seater")
mpg2$model <- reorder(mpg2$model, mpg2$cty)
mpg2$manufacturer <- reorder(mpg2$manufacturer, -mpg2$cty)
ggplot(mpg2, aes(cty, model)) +
geom_point() +
facet_grid(manufacturer ~ ., scales = "free", space = "free") +
theme(strip.text.y = element_text(angle = 0))
下图为各种不同类型的小汽车的城市油耗英里数的点数:
缺失分面变量
下面的例子中,我们设置了俩数据框,df1和df2,其中df1中有分面变量gender,而df2里没有这个变量。这是我们可以通过增加df2的图层(并且把它放在前面,要不就盖住df1的数据点了),让df2中的数据出现在每个分面中。
df1 <- data.frame(x = 1:3, y = 1:3, gender = c("f", "f", "m"))
df2 <- data.frame(x = 2, y = 2)
ggplot(df1, aes(x, y)) +
## 先写df2的数据,设置个颜色和大小加以区分
geom_point(data = df2, colour = "red", size = 5) +
## 这里默认的就是主函数中的映射
geom_point() +
facet_wrap(~gender)
分组和分面(Grouping vs. Facetting)
通过调整颜色、形状等图形属性可以来分组,分面则是另一种分组方法。这是两种绘图技巧,而根据子集相对位置的不同,两种技巧各有优缺。
在组间重叠严重时,分面:每个组都在单独的面板中,相隔比较远,组间无重叠。
比较组间细微差别时,使用图形属性
如下例,创建一些随机数组(x, y),然后我们先用 z
颜色分组:
df <- data.frame(
x = rnorm(120, c(0, 2, 4)),
y = rnorm(120, c(1, 2, 1)),
z = letters[1:3]
)
ggplot(df, aes(x, y)) +
geom_point(aes(colour = z))
如果我们用 z
分面:
ggplot(df, aes(x, y)) +
geom_point() +
facet_wrap(~z)
下面这个例子在一幅图中既分面,还算出了每组的平均值,又标明了颜色。
首先要先加载两个没用过的包(因为用到了其中的函数),我在网上看了两个小教程,大概了解一下他们是干啥的:
magrittr包:R语言高效的管道操作magrittr(http://blog.fens.me/r-magrittr/)
dplyr包:R语言数据处理利器——dplyr简介(http://www.cnblogs.com/big-face/p/4863001.html)
安装后加载
library(magrittr)
library(dplyr)
library(ggplot2)
创建随机数的数据框 df
,
df <- data.frame(
x = rnorm(120, c(0, 2, 4)),
y = rnorm(120, c(1, 2, 1)),
z = letters[1:3]
)
创建新的数据框 df_sum
,这里用到一个管道操作符 %>%
,
摘抄一段相关说明:magrittr包被定义为一个高效的管道操作工具包,通过管道的连接方式,让数据或表达式的传递更高效,使用操作符
%>%
,可以直接把数据传递给下一个函数调用或表达式。magrittr包的主要目标有2个,第一是减少代码开发时间,提高代码的可读性和维护性;第二是让你的代码更短,再短,短短短…
group_by()
用于对数据集按照给定变量分组,返回分组后的数据集。对返回后的数据集使用以上介绍的函数时,会自动的对分组数据操作。
df_sum <- df %>%
group_by(z) %>%
summarise(x = mean(x), y = mean(y)) %>%
rename(z2 = z)
生成均值数据集:
> df_sum
# A tibble: 3 x 3
z2 x y
<fct> <dbl> <dbl>
1 a 0.0875 0.934
2 b 1.91 1.99
3 c 4.05 0.993
然后创建散点分面图,第一个图层是基本散点图,第二个是通过数据处理后生成的均值数据集,并设置颜色等图形属性,最后按照 z
分面:
ggplot(df, aes(x, y)) +
geom_point() +
geom_point(data = df_sum, aes(colour = z2), size = 4) +
facet_wrap(~z)
生成图片:
或者还有一种方法,就是把分面和颜色结合起来:
还是上面的随机数数据集df,用 dplyr
数据包中的 select()
参数,选择子数据集, -z
是指除去 z
。
df <- data.frame(
x = rnorm(120, c(0, 2, 4)),
y = rnorm(120, c(1, 2, 1)),
z = letters[1:3]
)
df2 <- dplyr::select(df, -z)
ggplot(df, aes(x, y)) +
geom_point(data = df2, colour = "grey70") +
geom_point(aes(colour = z)) +
facet_wrap(~z)
出图:
连续变量
连续变量也可以用来分面,我们需要先给变量规定区间,转变成离散型:
cut_interval(x,n)
:把数据分割成n和相同长度的部分cut_width(x,width)
:按宽度分cut_number(x,n=10)
:将数据划分为n个相同数目点的部分,每个分面上点数相同
(x代表数据集)
如下例,设置三种分面方法:
library(ggplot2)
mpg2 <- subset(mpg, cyl != 5 & drv %in% c("4", "f") & class != "2seater")
# Bins of width 1
mpg2$disp_w <- cut_width(mpg2$displ, 1)
# Six bins of equal length
mpg2$disp_i <- cut_interval(mpg2$displ, 6)
# Six bins containing equal numbers of points
mpg2$disp_n <- cut_number(mpg2$displ, 6)
plot <- ggplot(mpg2, aes(cty, hwy)) +
geom_point() +
labs(x = NULL, y = NULL)
plot + facet_wrap(~disp_w, nrow = 1)
plot + facet_wrap(~disp_i, nrow = 1)
plot + facet_wrap(~disp_n, nrow = 1)
7.3 坐标系(Coordinate Systems)
坐标系是将两种位置标度结合在一起的二维定位系统
主要有俩功能:
将两个位置图形属性(位置1、位置2;例如x, y)结合起来在图形中形成二维方位系统
配合方面:绘制坐标轴和面板背景。标度控制坐标轴的数值,映射到图形上的位置,然后通过坐标系将他们绘制出来。
具体说来,可以分为线性坐标系和非线性坐标系
线性坐标系:
coord_cartesian()
:即默认笛卡尔坐标系coord_flip()
:x轴y轴互换后的笛卡尔坐标系coord_fixed()
:具有固定长宽比的笛卡尔坐标系
非线性坐标系:
coord_map()
/coord_quickmap()
: 地图投影coord_polar()
: 极坐标coord_trans()
: 变换后的笛卡尔坐标系
7.4 线性坐标系
详细看看三种线性坐标系函数怎么使用
设定坐标轴取值范围
coord_cartesian()
函数有两个重要的参数 xlim
和 ylim
。用来设置x、y轴的取值范围。
有没有一种似曾相识的感觉?标度中的范围参数(参见笔记8,标度6.5)和他相似度90%以上。他不是也可以控制图形外观吗?
两者的不同之处在于范围参数的工作原理:
当设定标度范围时,超出范围的参数就被删除了
设定笛卡尔坐标系范围时,我们使用的仍是所有的数据,只不过只显示了其中的一小部分图形区域的数据而已。
base <- ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth()
# Full dataset
base
# Scaling to 5--7 throws away data outside that range
base + scale_x_continuous(limits = c(5, 7))
base + coord_cartesian(xlim = c(5, 7))
翻转坐标轴
如果你对y轴取值条件状态下的x值感兴趣,那我们可以通过使用 coord_filp()
调换坐标轴
举个栗子
ggplot(mpg, aes(displ, cty)) +
geom_point() +
geom_smooth()
# Exchanging cty and displ rotates the plot 90 degrees, but the smooth is fit to the rotated data.
ggplot(mpg, aes(cty, displ)) +
geom_point() +
geom_smooth()
# coord_flip() fits the smooth to the original data, and then rotates the output
ggplot(mpg, aes(displ, cty)) +
geom_point() +
geom_smooth() +
coord_flip()
固定两轴比例
coord_fixed()
设置具有固定长宽比的笛卡尔坐标系。
7.5 非线性坐标系
与线性坐标不同,非线性坐标可以改变几何形状。例如,在极坐标中,矩形可能会变成弧形。在地图投影中,两点之间的最短路径不一定是直线。
下面的代码显示了在几个不同的坐标系中,如何渲染线条和矩形。
coord_polar()
是极坐标函数
rect <- data.frame(x = 50, y = 50)
line <- data.frame(x = c(1, 200), y = c(100, 1))
base <- ggplot(mapping = aes(x, y)) +
geom_tile(data = rect, aes(width = 50, height = 50)) +
geom_line(data = line) +
xlab(NULL) + ylab(NULL)
base
base + coord_polar("x")
base + coord_polar("y")
除了以上三种,还有如下三种,依次是翻转、变换和固定坐标系:
base + coord_flip()
base + coord_trans(y = "log10")
base + coord_fixed()
最后我们说说变换(transformation),坐标系变换分两步,
首先,几何形状的参数变化只根据定位而变,和维度无关,例如矩形,只能先定位四个角再变换位置,将矩形转化为多边形。
其次,将每个位置转化到新的坐标系中。点无所谓,但对于线和多边形而言,就困难得多,因为新的坐标系中,直线不一定就是直线了。所以我们假定坐标系之间的变换是连续的,把直线看做无数段极端的直线,而这些直线变换后已然是直线,这个过程就是无限切割,然后再组合,即munching。
举个简单地栗子:
首先来看两点线:
df <- data.frame(r = c(0, 1), theta = c(0, 3 / 2 * pi))
ggplot(df, aes(r, theta)) +
geom_line() +
geom_point(size = 2, colour = "red")
第二步就是把这条直线平均分割出15个节点:
df <- data.frame(r = c(0, 1), theta = c(0, 3 / 2 * pi))
## 首先定义一个函数,产生多少个节点
interp <- function(rng, n) {
seq(rng[1], rng[2], length = n)
}
## 设定一个新的数据集
munched <- data.frame(
r = interp(df$r, 15),
theta = interp(df$theta, 15)
)
ggplot(munched, aes(r, theta)) +
geom_line() +
geom_point(size = 2, colour = "red")
第三步就是转换后拼接
df <- data.frame(r = c(0, 1), theta = c(0, 3 / 2 * pi))
interp <- function(rng, n) {
seq(rng[1], rng[2], length = n)
}
munched <- data.frame(
r = interp(df$r, 15),
theta = interp(df$theta, 15)
)
transformed <- transform(munched,
x = r * sin(theta),
y = r * cos(theta)
)
ggplot(transformed, aes(x, y)) +
geom_path() +
geom_point(size = 2, colour = "red") +
coord_fixed()
参考资料:
Hadley Wickham(2016). ggplot2. Springer International Publishing. doi: 10.1007/978-3-319-24277-4
《R语言应用系列丛书·ggplot2:数据分析与图形艺术》
-------------------我是求关注的分界线--------------
更多R语言、可视化作图ggplot2包学习笔记请关注微信公众号:
猜你喜欢
10000+:肠道细菌 人体上的生命 宝宝与猫狗 梅毒狂想曲 提DNA发Nature 实验分析谁对结果影响大 Cell微生物专刊
文献阅读 热心肠 SemanticScholar Geenmedical
16S功能预测 PICRUSt FAPROTAX Bugbase Tax4Fun
写在后面
为鼓励读者交流、快速解决科研困难,我们建立了“宏基因组”专业讨论群,目前己有国内外150+ PI,1500+ 一线科研人员加入。参与讨论,获得专业解答,欢迎分享此文至朋友圈,并扫码加主编好友带你入群,务必备注“姓名-单位-研究方向-职称/年级”。技术问题寻求帮助,首先阅读《如何优雅的提问》学习解决问题思路,仍末解决群内讨论,问题不私聊,帮助同行。
学习16S扩增子、宏基因组科研思路和分析实战,关注“宏基因组”