每天学习一点R:15.barplot条形图之双坐标轴
前几天学习了R语言绘图相关基本参数,接下来主要是以几个实例来解释各个绘图参数在实际绘图中如何使用,今天先以一个条形图的绘制来进行展开,例图是参考一篇关于抗生素抗性基因文章中的一张图,之后对其进行了改进,以双坐标轴的方式在同一张图中展示不同类型抗生素抗性基因和可移动基因单元的检出数目。
本文会详细介绍绘图过程中如何进行条形图的绘制并通过修改参数对图像进行调整,本文旨在通过绘图实例讲解各个参数的使用方法,希望大家能够活学活用。
特别要注意的是,本文内容中涉及的具体参数数值是根据绘图数据和生成图像的大小设置的,在自己绘制图像时需要根据自己的数据进行调整,切勿直接套用。
例图展示
原本文章中的图像如下所示,途中通过两个相邻的条形展示了不同样品中抗生素抗性基因和可移动基因单元的丰度,并且由于抗生素抗性基因可以根据对应的抗生素进行分类,因而使用堆叠的色块展示不同类型抗生素抗性基因的丰度。
我在绘图的过程中,对上述图像进行了改进,以堆叠的色块展示不同样品中不同类型抗生素抗性基因的检出数目,以“点+垂线”的方式表示可移动基因单元的检出数目,并分别应用两个纵坐标轴进行标识,同时在图像中增加了一些用于表示分组的文本,最终的图像就是下方这样。
先给出绘图的完整代码:
args.mges.number <- read.table("ARGs_MGEs_number.txt",header = TRUE,sep = "\t",row.names = 1)
args.mges.number <- as.matrix(args.mges.number)
library(RColorBrewer)
tiff(filename = "ARGs.MGEs.number.tiff",width = 12000,height = 9000,res = 600,compression = "lzw")
par(mar=c(6.3,5,4,10))
bar <- barplot(args.mges.number[1:8,],names.arg = c(rep("",13)),col = brewer.pal(8,"Set3"),border = "black",xlim = c(0,15),axes = F,ylim = c(0,40))
axis(side = 2,at = seq(0,40,5),las = 2,line = -1.55,cex.axis = 2.2,lwd = 3,lwd.ticks = 3,font = 2)
par(xpd = TRUE)
par(new = T)
par(mar = c(4,5,4,10))
plot(x = bar,y = args.mges.number[9,],type = "p",col = "black",axes = F,xlim = c(0,15),xlab = "",ylab = "",ylim = c(0,7),pch = 19,bg = "black",cex = 4)
par(xpd = TRUE)
par(new = T)
par(mar = c(4,5,4,10))
plot(x = bar,y = args.mges.number[9,],type = "h",col = "black",axes = F,xlim = c(0,15),xlab = "",ylab = "",ylim = c(0,7),bty = "o",lwd =5)
axis(side = 1, at = bar,line = -1,labels = FALSE,cex.axis = 2.2,lwd = 3,lwd.ticks = 3,font = 2)
text(x = bar, y = -0.24,adj = c(1,0.8),labels = colnames(args.mges.number),cex = 2.4, font = 2,srt = 45)
axis(side = 4,at = c(0:7),line = 4,las = 2,cex.axis = 3,lwd = 3,lwd.ticks = 3,font = 2)
mtext("The detected number of ARGs in all samples",side = 2,line = 3,font = 2,cex = 3)
mtext("The detected number of MGEs in all samples",side = 4,line = 9,font = 2,cex = 3)
par(xpd=TRUE)
par(new = T)
plot(0:1, 0:1, type="n", xlab="",ylab="", axes=FALSE)
legend(0,1.12,legend = rownames(args.mges.number[1:8,]),fill = brewer.pal(9,"Set3"),bty = "n",ncol = 4,text.width = 0.2,cex = 2.5,text.font = 2)
legend(0.01,1.033,legend = "MGEs",pch = 19,col = "black",bty = "n",cex = 2.5,pt.cex = 3.3,text.font = 2,text.width = 0.2,x.intersp = 1.35)
segments(0.095,0.72,0.315,0.72,col = "black",lwd = 5)
segments(0.335,0.6,0.555,0.6,col = "black",lwd = 5)
segments(0.575,0.47,0.795,0.47,col = "black",lwd = 5)
segments(0.815,0.43,1.035,0.43,col = "black",lwd = 5)
text(x = 0.205,y = 0.74,labels = "Group A",font = 2,cex = 2.5)
text(x = 0.445,y = 0.62,labels = "Group B",font = 2,cex = 2.5)
text(x = 0.685,y = 0.49,labels = "Group C",font = 2,cex = 2.5)
text(x = 0.925,y = 0.45,labels = "Group D",font = 2,cex = 2.5)
arrows(x0 = 0.1525,y0 = 0.85,x1 = 0.09,y1 = 0.85,lwd = 4,col = "black")
text(x = 0.21,y = 0.85,labels = "Control",font = 2,cex = 2.5)
dev.off()
绘图所需的数据就是普通的数据框形式。
barplot
该图像使用barplot()函数进行条形图的绘制,首先介绍一下barplot()函数的基本信息。
barplot(height, width = 1, space = NULL,
names.arg = NULL, legend.text = NULL,
beside = FALSE, horiz = FALSE, density = NULL,
angle = 45, col = NULL, border = par("fg"),
main = NULL, sub = NULL, xlab = NULL, ylab = NULL,
xlim = NULL, ylim = NULL, xpd = TRUE, log = "",
axes = TRUE, axisnames = TRUE,
cex.axis = par("cex.axis"), cex.names = par("cex.axis"),
inside = TRUE, plot = TRUE, axis.lty = 0, offset = 0,
add = FALSE, args.legend = NULL, ...)
各参数意义:
height为进行绘图的数据;
width定义bar的宽度;
space定义不同bar之间的间隔;
names.arg定义每一个bar下方的名称,如果为NULL则默认为数据中的列名;
legend.text定义legend中的名称,默认为数据中的行名;
beside定义绘图方式,FALSE为堆叠图,TRUE为并列图;
horiz定义绘图方式,FALSE为纵向绘图,TRUE为横向绘图;
density为底纹的密度;
angle为底纹的倾斜角度;
col为定义条形填充颜色;
border定义条形边框颜色,NA为去除边框;
main和sub定义标题和副标题;
xlab和ylab定义x和y轴的名称;
xlim和ylim定义x和y轴的界限;
xpd定义是否允许bar超过图像的范围;
log定义数据是否需要log转换;
axes定义是否需要显示垂直的或水平的轴;
axisnames是否显示轴的名字;
cex.axis和cex.names定义轴标签和bar表现的大小;
inside当space为0时,相邻bar之间是否用线间隔;
plot是否绘图;
axis.lty定义轴的线型;
add定义bar是否是添加进一个已有的图像中;
args.legend应用list函数通过legend命令的参数调整图例。
绘图过程详解
首先载入绘图数据和绘图所需要的包。
#导入绘图数据,将第一行和第一列设置为行名和列明
args.mges.number <- read.table("ARGs_MGEs_number.txt",header = TRUE,sep = "\t",row.names = 1)
#将绘图数据转换为矩阵形式
args.mges.number <- as.matrix(args.mges.number)
#载入所需要的颜色包
library(RColorBrewer)
条形图绘制
#定义图像输出路径和大小
tiff(filename = "ARGs.MGEs.number.tiff",width = 12000,
height = 9000,res = 600,compression = "lzw")
#定义绘图区边界
par(mar=c(6.3,5,4,10))
#绘制条形图
bar <- barplot(args.mges.number[1:8,],
names.arg = c(rep("",13)),
col = brewer.pal(8,"Set3"),
border = "black",xlim = c(0,15),
axes = F,ylim = c(0,40))
这里绘图边界的大小要根据实际需要进行调整,主要是要满足后续点线图与条形图的匹配,以及为坐标轴和图例留出足够的空间。
条形图只使用[1:8,]选择前8行进行绘图,第9行是可移动基因单元,用于绘制“点+垂线”图。
names.arg将x轴坐标设置为空,axes = F将坐标轴隐藏,这样才能通过后续的自主添加调整坐标轴及标签的位置,自带的坐标轴位置固定并且各项参数不好调整。
xilm和ylim设置x和y轴的范围,x轴设置为样品数目+2,y轴根据绘图数据调整,我在绘图前已经规划好将图例放在上方,因而y轴的范围进行了适当的扩大。
将条形图赋值为bar,后续会持续用到。
添加条形纵坐标
axis(side = 2,at = seq(0,40,5),
las = 2,line = -1.55,cex.axis = 2.2,
lwd = 3,lwd.ticks = 3,font = 2)
side = 2表示在左侧,at确定坐标轴间隔,las = 2表示标签与间隔线垂直,font = 2对标签进行加粗。
line调整轴与图像的间隔,cex.axis调整轴标签的大小,lwd调整轴的线宽,lwd.ticks调整轴中间隔线的长度,通过这几个参数的调整,使坐标轴的展示效果达到自己满意的要求。
“点+垂线”图的绘制
#使得新的图像可以在绘图区以外显示
par(xpd = TRUE)
#新建一个绘图区用于添加散点图
par(new = T)
#定义新绘图区的边界
par(mar = c(4,5,4,10))
#进行散点图的绘制
plot(x = bar,y = args.mges.number[9,],
type = "p",col = "black",axes = F,
xlim = c(0,15),xlab = "",ylab = "",
ylim = c(0,7),pch = 19,bg = "black",cex = 4)
首先通过par(xpd = TRUE)命令使得接下来绘制的图像可以在条形图绘图边界部分显示,防止后续图像显示不全。
接下来要新建用于绘制“点+垂线”图的绘图区。
使用plot()函数先绘制散点图,x轴坐标选择上一步绘制的条形图bar,即可自动匹配横坐标位置,纵坐标使用[]选择第9行数据绘制可移动基因单元的检出数目。
同样应用axes = F隐藏坐标轴,将x和y轴标签设置为空,选择type = “p”绘制散点图,点的形状选择19为实心圆形,颜色调控为黑色。
其它参数根据图像的展示情况调整直至满意。
之后再使用plot()函数绘制垂线图。
par(xpd = TRUE)
par(new = T)
par(mar = c(4,5,4,10))
plot(x = bar,y = args.mges.number[9,],
type = "h",col = "black",axes = F,
xlim = c(0,15),xlab = "",ylab = "",
ylim = c(0,7),bty = "o",lwd =5)
同样也要新建一个绘图区域,绘图区域的参数要与散点图一致。
注意该图像y轴的范围,要稍微大一些,使得“点”与条形最上端相对和谐,便于后续添加分组信息。
特别需要注意的是,散点图和垂线图的绘图区边界实际上是由垂线图决定的,最终要通过调整会图边界保证垂线和点位于条形的正中,并且垂线的末端与条形的最下端在同一条水平线上。
垂线图的基本参数也要与散点图保持一致,使用type = “h”绘制垂线图,lwd调整垂线的线宽。
添加其它坐标轴
首先添加横坐标信息。
axis(side = 1, at = bar,line = -1,
labels = FALSE,cex.axis = 2.2,lwd = 3,
lwd.ticks = 3,font = 2)
text(x = bar, y = -0.24,adj = c(1,0.8),
labels = colnames(args.mges.number),
cex = 2.4, font = 2,srt = 45)
side = 1规定坐标轴在下方,at = bar以坐标轴间隔与图像的位置,labels = FALSE隐藏轴的标签,line调整轴与图像的间距。
cex.axis、lwd、lwd.ticks和font的参数与之前绘制的条形图纵坐标轴保持一致。
之后使用text()函数添加x轴标签,这样做是为了能够方便地对标签进行调整,例如改变其对其方式、大小、位置、旋转角度等。
接下来添加“点+垂线”图对应的纵坐标轴。
axis(side = 4,at = c(0:7),line = 4,
las = 2,cex.axis = 3,lwd = 3,
lwd.ticks = 3,font = 2
相关参数与第一条纵坐标轴一致,除了应用side = 4将其放置在图像右侧,并且根据会图数据改变其标签间隔。
最后使用mtext()命令添加两个坐标轴对应的labels。
mtext("The detected number of ARGs in all samples",
side = 2,line = 3,font = 2,cex = 3)
mtext("The detected number of MGEs in all samples",
side = 4,line = 9,font = 2,cex = 3)
添加图例
首先要新建一个绘图区,并且建立一个空的图用来辅助图例的添加。
par(xpd=TRUE)
par(new = T)
plot(0:1, 0:1, type="n", xlab="",ylab="", axes=FALSE)
之后要分两次添加图例,第一次添加条形图的图例,第二次添加“点+垂线”图的图例。
legend(0,1.12,legend = rownames(args.mges.number[1:8,]),
fill = brewer.pal(9,"Set3"),bty = "n",ncol = 4,
text.width = 0.2,cex = 2.5,text.font = 2)
在条形图的图例添加时,要注意预留第二个图例的位置。
使用ncol调整图例的列数,text.width调整不同列见图例的宽度以达到对图例文字的完整展示。
其它参数根据出图效果进行调整。
接下来添加“点+垂线”图的图例。
legend(0.01,1.033,legend = "MGEs",pch = 19,
col = "black",bty = "n",cex = 2.5,
pt.cex = 3.3,text.font = 2,
text.width = 0.2,x.intersp = 1.35
通过xy坐标调整图例的位置,cex、pt.cex调整点和文字的大小,x.intersp调整点和文字的间距,使得该图例看起来与之前插入的条形图图例为同一个图例。
指示文字的插入
最后我们分别通过线段、箭头和文字的插入来指示分组信息。
segments(0.095,0.72,0.315,0.72,col = "black",lwd = 5)
segments(0.335,0.6,0.555,0.6,col = "black",lwd = 5)
segments(0.575,0.47,0.795,0.47,col = "black",lwd = 5)
segments(0.815,0.43,1.035,0.43,col = "black",lwd = 5)
text(x = 0.205,y = 0.74,labels = "Group A",font = 2,cex = 2.5)
text(x = 0.445,y = 0.62,labels = "Group B",font = 2,cex = 2.5)
text(x = 0.685,y = 0.49,labels = "Group C",font = 2,cex = 2.5)
text(x = 0.925,y = 0.45,labels = "Group D",font = 2,cex = 2.5)
对于包含多个样品的样品组,使用segments()在其条形上方插入线段以表示线段下方的样品属于同一组,之后使用text()函数在线段上加入分组名称。
arrows(x0 = 0.1525,y0 = 0.85,x1 = 0.09,
y1 = 0.85,lwd = 4,col = "black")
text(x = 0.21,y = 0.85,labels = "Control",
font = 2,cex = 2.5)
dev.off()
#dev.off()用于结束绘图,并按照绘图最开始的命令将图像输出
对于只有一个样本的分组,可以使用arrows()在其附近添加指示箭头,之后在箭头末端应用text()函数添加分组名称。