R语言入门系列之三:R脚本

2022-05-05 11:20:18 浏览数 (1)

写在前面:

在前面两篇文章R语言入门系列之一R语言入门系列之二中,我分别介绍了R语言中的对象与结构、数据的输入输出及可视化。基于前面的基础,今天我介绍一下R语言中基础的程序结构,来帮助我们完成更复杂的数据处理任务。此外,如果你有大批量数据处理、可视化任务,需要着重学习R脚本在命令行的调用方式以及命令行参数的使用方法。

写好的R语言程序一般保存为R脚本,这样在以后完成相似数据处理任务时可以方便地直接调用。在linux系统命令行,我们可以使用“Rscript”命令来调用运行写好的程序,并添加一些必须的命令行参数;在Windows系统的Rstudio中,可以使用source()函数来调用写好的R脚本。

1重复循环

R中的循环主要有for和while结构。for循环重复执行一个语句,直到value值不再包含在向量vector中为止,for结构的基本语法如下所示:

代码语言:javascript复制
for (value in vector) {
    statements
}

例如我们想要将群落数据小数值转换成百分值,如下所示:

代码语言:javascript复制
data=read.table("phylum.txt", header=TRUE)
rownames(data)=data[, 1]
data=data[, -1]
for (i in 1:nrow(data)) {
  for (j in 1:ncol(data)) {
    data[i, j]=data[i, j]*100
  }
}

while结构重复执行一个语句,直到条件语句不为TRUE为止,while结构基本语法如下:

代码语言:javascript复制
while (condition) {
   statements
}

例如上面for的程序可以改写为:

代码语言:javascript复制
i=1
while (i <= nrow(data)) {
  j=1
  while (j <= ncol(data)) {
    data[i, j]=data[i, j]*100
    j=j 1
  }
  i=i 1
}

虽然while可以执行循环,但是本质上是一个不断判断过程,i=i 1实现了程序的连续运行,与for还是有很大区别,因此判断条件必须要完善。

2条件执行

在条件执行也即选择结构中,语句只有在满足一定条件时才会执行,主要有if-else、ifelse、switch三种。if结构基本语法如下所示:

代码语言:javascript复制
if(condition) {
   statements
}
#或者
if(condition) {
   statement1
} else {
   statement2
}

ifelse结构是if-else结构的紧凑版本,如下所示:

代码语言:javascript复制
ifelse(condition, statement1, statement2)

举例如下:

代码语言:javascript复制
a=5
ifelse (a>=3, a<-TRUE, a<-FALSE)

注意,这时候在括号内部必须使用赋值符号!

switch根据一个表达式的值选择语句执行,如下所示:

代码语言:javascript复制
switch(expression, case1, case2, case3....)

举例如下:

代码语言:javascript复制
feelings=c("sad", "afraid")
for (i in feelings) {
  print(
    switch(i,
           happy="I'm glad you are happy",
           afraid="There's nothing to fear",
           sad="Cheer up",
           angry="Calm down now")
  )
}

repeat-if-break函数是常用的一个循环判断符合结构,基本语法如下所示:

代码语言:javascript复制
repeat { 
   commands 
   if(condition){
        break
   }
}

repeat会重复执行一个命令直到if判断条件符合然后使用break终止,举例如下:

代码语言:javascript复制
a=c("Hello", "loop")
i=1
repeat{
  print(a)
  i=i 1
  if(i==5){
    break
  }
}

3高级函数

aggregate()函数

对于向量和矩阵,我们可以方便的使用循环等来进行统计计算,然而对含有因子的数据框,aggregate()函数就会大显威力,其使用语法如下:

代码语言:javascript复制
aggregate(object, by, FUN, formula...)

其中by是制定进行统计的类别列表,一般为因子变量,FUN为统计函数,可以随意选择。下面我们以R内置数据mtcars为例展示其使用方法:

代码语言:javascript复制
attach(mtcars)
aggregate(mpg, by=list(cyl), FUN=mean) #根据cyl进行分组计算mpg均值

结果如下所示:

其结果以数据框的形式储存。

apply函数家族

apply函数家族主要成员如下:

代码语言:javascript复制
apply   对数组行或者列使用函数  apply(X, MARGIN, FUN, ...)
lapply  对列表或者向量使用函数  lapply(X, FUN, ...)
sapply  对列表或者向量使用函数  sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
vapply  对列表或者向量使用函数  vapply(X, FUN, FUN.VALUE, ..., USE.NAMES = TRUE)
tapply  对不规则矩阵使用函数    tapply(X, INDEX, FUN = NULL, ..., simplify = TRUE)
eapply  对环境中的值使用函数    eapply(env, FUN, ..., all.names = FALSE, USE.NAMES = TRUE)
mapply  对多个列表或者向量参数使用函数  mapply(FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
rapply  运用函数递归产生列表    rapply(object, f, classes = "ANY", deflt = NULL,how = c("unlist", "replace", "list"), ...)

apply()通过对数组或者矩阵的一个维度使用函数生成值得列表或者数组、向量:

代码语言:javascript复制
apply(X, MARGIN, FUN, ...)

其中X数组,包括矩阵,MARGIN:1表示矩阵行,2表示矩阵列,也可以是c(1,2),举例如下:

最终以向量或矩阵返回结果。

lapply()通过对x的每一个元素运用函数,生成一个与元素个数相同的值列表:

代码语言:javascript复制
lapply(X, FUN, ...)

X表示一个列表对象,其余对象将被通过as.list强制转换为list,举例如下:

sapply()是lapply函数的包装版。sapply(x, f, simplify=FALSE, USE.NAMES=FALSE)返回的值与lapply(x, f)是一致的:

代码语言:javascript复制
sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)

X表示一个向量或者列表对象,其余对象将被通过as.list强制转换为list,simplify为逻辑值或者字符串,指明结果是否应该被简化为向量、矩阵或者高维数组,默认值TRUE。如果simplify="array",结果将返回一个数组。举例如下:

4自定义函数

用户可以根据需求自定义函数,R函数是通过使用关键字function来创建。R函数的定义基本语法如下:

代码语言:javascript复制
function_name=function(arg_1, arg_2, ...) {
    Function body
    return(object)
}
# function_name:这是函数的实际名称。它被存入R环境作为一个对象使用此名称。
# arg:参数是一个占位符。当调用一个函数,传递一个值到参数。参数是可选的,也就是说,一个函数可以含有任何参数。此外参数可以有默认值。
# Function body:函数体包含定义函数是使用来做什么的语句集合。
# return:一个函数的返回值是在函数体中评估计算最后一个表达式的值。

下面我们自己编写一个计算OTU平均相对丰度并排序、筛选高丰度OTU的函数,如下所示:

代码语言:javascript复制
otu_select=function(otu, number=nrow(otu), abund=0, mean=TRUE) {
  csum=numeric(ncol(otu))
  for (i in 1:ncol(otu)) {
  csum[i]= sum(otu[, i])
  }
  for (i in 1:nrow(otu)) {
    for (j in 1:ncol(otu)) {
      otu[i, j]=otu[i, j]/csum[j]
    }
  }
  ave_abund=numeric(nrow(otu))
  for (i in 1:nrow(otu)) {
    ave_abund[i]=mean(otu[i, ])
  }
  newotu=cbind(otu, ave_abund)
  newotu=newotu[order(newotu[, ncol(newotu)], decreasing=TRUE),]
  newotu=newotu[1:number, ]
  newotu=newotu[newotu[, ncol(newotu)]>=abund,]
  ifelse (mean==TRUE, abundotu<-newotu, abundotu<-newotu[, -ncol(newotu)])
  return(abundotu)
}

上面自定义函数中提供了两种方法筛选高丰度OTU,一个是根据丰度数值进行筛选,另一种是根据OTU数目,返回结果可以包含均值也可以不包含。我们可以直接在R中运行上面程序然后使用这个函数,也可以保存为R脚本然后使用source()函数调用。例如将上面自定义函数保存为otu_select.R并放到当前R工作路径,调用方法如下所示:

代码语言:javascript复制
source("otu_select.R")
data=read.table("sample.subsample.otu_table.txt", header=TRUE)
rownames(data)=data[, 1]
data=as.matrix(data[, -1])
newdata=otu_select(data, abund=0.01, mean=FALSE)

运行结果如下所示:

5命令行参数

当在Linux系统命令行运行R脚本时,可以使用commandArgs()设置命令行参数来增强脚本的适用性,我们可以通过下面脚本来查看R语言命令行参数设置规则:

代码语言:javascript复制
Args <- commandArgs()
cat("Args[1]=", Args[1],"n")
cat("Args[2]=", Args[1],"n")
cat("Args[3]=", Args[3],"n")
cat("Args[4]=", Args[4],"n")
cat("Args[5]=", Args[5],"n")
cat("Args[6]=", Args[6],"n")
cat("Args[7]=", Args[7],"n")

将上述程序保存为Args.R,在Linux系统命令行运行结果如下所示:

可以发现,前五个为R内置参数,用户输入参数从第6个开始,R脚本中的命令行参数的使用示例如下所示:

如果想忽略R内置参数,则可以如下设置:

代码语言:javascript复制
Args <- commandArgs(TRUE)

这样,Args[1]即为用户输入的第一个位置参数。

0 人点赞