R- 组合图(折线+条形图)绘制

2021-02-22 12:04:23 浏览数 (2)

就是下面这张图,在途中用条形图展示了不同季节样本浮游动物的组成情况,同时使用带误差棒的折线图来表示浮游动物生物量的变化,相当于在一幅图中同时展示了群落的相对丰度和绝对丰度

对于这种图我一直都是使用R的base函数来完成的,代码其事并不复杂涉及到的无非就是plot、barplot、axis、text、mtext、par等几个基本的函数。

使用base函数进行画图有一个缺点,就是一旦更换数据之后,图像中各种元素所处的位置会发生一些变化,也就是说不同的数据使用同样的代码进行绘图,需要根据出图来调整一些参数的具体数值

其实我更喜欢分享这种绘图代码,虽然比ggplot2的代码用起来要费事一些,但是可以强迫大家去学习代码中每一个参数的具体含义,通过修改参数的数值也能够理解代码如何调整,通过几个图像的学习,你就会发现自己画一个图也不是什么难事,我想这才是分享应该有的意义。

绘图过程

接下来介绍绘图的过程,既然是同时展示相对丰度和绝对丰度,那就必然需要两个绘图数据文件。

第一个文件是个组样本中不同物种相对丰度的平均值,另一个文件为个组样品中总生物量的平均值及其标准偏差。

请在公众号后台回复“Season”获取绘图示例文件。

首先要导入绘图数据。

代码语言:javascript复制
dr <- read.table("R.Season.txt",header = TRUE,row.names = 1,sep = "t")
dt <- read.table("Season.txt",header = TRUE,row.names = 1,sep = "t")

将相对丰度文件转换为矩阵形式,并定义一个色卡用于颜色填充。

代码语言:javascript复制
dr <- as.matrix(dr)
Palette <- c("#B2182B","#E69F00","#56B4E9","#009E73","#F0E442","#0072B2","#D55E00","#CC79A7","#CC6666","#9999CC","#66CC99")

对相对丰度文件进行处理,选取丰度排名前十的物种,并将剩余物种合并为Others。

代码语言:javascript复制
sum <- apply(dr,1,sum)
dr <- cbind(dr,sum)
dr <- as.data.frame(dr)
dr <- dr[order(dr[,"sum"],decreasing = T),]
dr <- subset(dr, select = -sum)
dr <- dr[1:10,]
dr <- t(dr)
sum <- apply(dr,1,sum) 
Others <- 1-sum
dr <- cbind(dr,Others)
dr <- t(dr)

这里的逻辑很简单,首先计算每一个物种在所有样本中相对丰度的总和,之后按照其数值高低对数据进行重排,保留丰度排名前十的物种数据,之后计算这些物种在各样本中的丰度总和,进而求出Others对应的数值。

接下来进行绘图,首先定义图像输出形式和绘图区域范围。

代码语言:javascript复制
png(filename = "Season.zoo.png",width = 12000,height = 9000,res = 600)
par(mar=c(6.3,8,4,20))
par(xpd = TRUE)

png表示绘图完成后以png格式输出图像,height和width代表图像输出的大小,注意该数值如果进行修改,会导致图像中各元素的位置发生变化,需要根据出图效果进行调整。

在par中使用mar定义绘图区域,4个数值分别对应下、左、上、右4个方向的绘图边界,这里由于要在右侧放置图里,因此右侧数值较大。

xpd = TRUE表示可以将图像绘制在绘图区之外,也就是定义的边界位置也能显示图像。

接下来绘制条形图。

代码语言:javascript复制
bar1 <- barplot(dr,names.arg = c(rep("",3)),space = 0.1,col = Palette,
                border = "white",xlim = c(0,5),axes = F,ylim = c(0,1))
axis(side = 2,at = c(0,0.2,0.4,0.6,0.8,1),labels = FALSE,line = -1.55,lwd = 5,lwd.ticks = 5)
text(x=-0.14,y = c(0,0.2,0.4,0.6,0.8,1),labels = c("0%","20%","40%","60%","80%","100%"),
     font = 2,cex = 2.5,adj = c(1,0.5))

使用barplot绘制条形图,这里要注意应用axes = F将坐标轴去除,并使用names.arg将横坐标的标签定义为空,注意有几组其对应的数字就设置为几,xlim的范围从0至样本组数目 2。

使用axis在左侧添加相对丰度对应的坐标轴,side = 2代表左侧。

使用text添加其对应的标签,注意这里的line用于调整坐标轴的位置,如果左侧空间不够,需要调整上一步par内mar中的第二个数值。

条形图绘制完成之后绘制折线图。

代码语言:javascript复制
par(xpd = TRUE)
par(new = T)
par(mar = c(6.3,8,4,20))
plot(bar1,y=dt$Total,type = "b",col = "black",axes = F,xlim = c(0,5),xlab = "",ylab = "",
     ylim = c(-1000,3000),pch = 19,bg = "black",cex = 5,lwd = 7)

注意这里因为是一副新的图像,所以一定要添加par(new = T),不然条形图就被覆盖了,这里绘图区域要与上一步条形图的绘图区保持一致。

使用plot添加折线图,type定义为b表示折线 点,axes同样设置为F去除坐标轴,xlim保持与条形图一致,xlab和ylab均设置为空,ylim根据具体的数据进行调整。

使用axis和text添加横坐标轴及其对应标签。

代码语言:javascript复制
axis(side = 1,at = bar1,line = 1,labels = FALSE,lwd = 5,lwd.ticks = 5)
text(x = bar1,y = -1350,labels = colnames(dr),cex = 3.2, font = 2)

side = 1为下方,at = bar1表示按照条形图的横坐标匹配坐标轴间隔,labels = FALSE表示不显示标签。

使用text添加横坐标标签时,要注意y的数值,这个需要根据上一步折线图中ylim的范围进行调整。

使用axis在右侧添加总生物量对应的纵坐标,side = 4表示右侧,at根据折线图ylim的范围进行调整,line调整坐标轴与图像的距离。

代码语言:javascript复制
axis(side = 4,at = c(-1000,0,1000,2000,3000),line = -23,las = 2,cex.axis = 2.5,lwd = 5,lwd.ticks = 5,font = 2)
mtext("Relative abundance",side = 2,line = 5,font = 2,cex = 4)
mtext("Total zooplankton density (ind./L)",side = 4,line = -15,font = 2,cex = 4)

接下来使用mtext添加两个纵坐标轴对应的labe文字,注意根据出图情况调整line的数值,以保证文字处于合理的位置。

使用arrows函数以箭头的形式添加折线图的误差棒。

代码语言:javascript复制
arrows(x0 = bar1,y0 = dt$Total,x1 = bar1,y1 = dt$Total dt$Sd,col = "black",angle = 90,length = 0.5,lwd = 7)
arrows(x0 = bar1,y0 = dt$Total,x1 = bar1,y1 = dt$Total-dt$Sd,col = "black",angle = 90,length = 0.5,lwd = 7)

angle = 90表示箭头的两边都与中间的线成90度夹角,也就形成了误差棒应有的效果。

最后添加图例。

代码语言:javascript复制
par(xpd=TRUE)
par(new = T)
plot(0:1, 0:1, type="n", xlab="",ylab="", axes=FALSE)
legend(0.85,1.0,legend = rownames(dr),fill = Palette,bty = "n",ncol = 1,cex = 2.5,text.font = 4)
legend(0.87,0.52,legend = c("Total zooplankton"),pch = 19,col = "black",bty = "n",cex = 2.5,pt.cex = 3.3,text.font = 2,text.width = 0.2,x.intersp = 1.35,lty = 1,lwd = 5)
dev.off()

添加图例之前,同样要使用par(new = T)定义一个新的绘图空间,之后使用plot函数添加一个完全空白的图像。

这里需要添加两个图里,分别对应条形图和折线图。

要注意调整两个图里添加的位置,以及折线图腿中文字与图形元素的间距,是的两个图里看起来像是一个。

最后使用dev.off()关闭绘图区域并保存图像。

⚠️使用该代码绘制自己数据的图像前,一定要做到能看懂代码,知道根据需求调整什么参数!!

0 人点赞