zl程序教程

您现在的位置是:首页 >  其他

当前栏目

17. R编程(三:运算符、控制语句、基本函数)

2023-04-18 14:58:33 时间

部分内容参见:生信技能树 课程

1. 运算符

关系运算符

关系正确返回TRUE 否则FALSE== 相等 != 不相等>大于< 小于 ps:字母多的字符串比少的大

★= 大于等于 <= 小于等于”

也可以进行向量的比较,向量中数据一一比较,若条件符合则返回TRUE

逻辑运算符

ps:通过tail(),head() 获取数据中的某个信息。

linkedin <- c(16, 9, 13, 5, 2, 17, 14)
last <- tail(linkedin, 2)
# 获得向量中最后两个元素
start <- head(linkedin, 2)
# 获得向量中开始的两个元素

| 表示 或,只有一个条件成立就返回真&表示 与,必须所有条件均满足才返回真

2. if 条件语句

if 基本句

只要if 随后的条件句返回为TRUE,则其后的语句则会被执行。

i = -1
if (i<0) print('up')
if (i>0) print('up')

> i = -1
> if (i<0) print('up')
[1] "up"
> if (i>0) print('up')

if 完全表达(多条件)

if/else if/else 句 需要注意的是,无论是if 还是else if,其随后的条件句只能返回一个逻辑值(不可以是逻辑值向量)。

Warning message:
In if (x > 0) { :
  the condition has length > 1 and only the first element will be used

当满足 if,执行if,当不满足if,看是否满足else if,以此类推... 最后都不符合的条件,执行else 语句。

if (condition1) {
  expr1
} else if (condition2) {
  expr2
} else if (condition3) {
  expr3
} else {
  expr4
}

tips:else 和 else if 和if 的末尾花括号在同一个位置。

ifelse 语句

ifelse 包含三个参数。条件句(返回逻辑值),yes(逻辑值TRUE返回结果),no(逻辑值FALSE 返回结果)。

> x=rnorm(10)
> x
 [1]  1.19962285 -0.47281555 -0.55231510 -0.30625073
 [5]  0.29614962  1.22719717  1.55893876  0.02546322
 [9]  0.07590546 -0.16473795
> y=ifelse(x>0,"+","-") # x大于0返回+,小于0返回-
> y
 [1] "+" "-" "-" "-" "+" "+" "+" "+" "+" "-"

而由此也可见ifelse 中的条件句,其结果可以是一个逻辑值的向量。而借助ifelse这一功能,我们便可以把复杂的字符串向量通过函数转换为对应的逻辑值向量(按照自定义的筛选),再转换为自定义的分类。

x <- "The birch canoe slid on the smooth planks"
x2 <- str_split(x, ' ')[[1]] %>%
  str_to_lower()
ifelse(str_detect(x2, 't'), 'tumor','normal')

[1] "tumor"  "normal" "normal" "normal" "normal" "tumor" 
[7] "tumor"  "normal"

3. switch 条件句

switch 用于条件句的选择执行。

switch(EXPR, ...)

... 表示各种与 EXPR 可能输出值的绑定语句。当... 中的某个丛句与EXPR值相等时,便会输出丛句对应的值。因此switch 常用于for 循环的遍历打印。

switch(EXPR = aa, 
              #EXPR = "aa", 
              aa=c(3.4,1),
              bb=matrix(1:4,2,2),
              cc=matrix(c(T,T,F,T,F,F),3,2),
              dd="string here",
              ee=matrix(c("red","green","blue","yellow")))

如果EXPR 的值与aa,bb 等不相等,则按照EXPR 顺序选择(比如aa 等于5,则返回第五个ee 对应的值)。如果均不满足,则不反回任何内容。

4. while 循环

只要condition 为TRUE,则永远执行expr 中的语句。

while (condition) {
  expr
}

break 终止循环

break 直接跳出while 语句。

# Initialize the speed variable
speed <- 88

while (speed > 30) {
  print(paste("Your speed is", speed))
  
  # Break the while loop when speed exceeds 80
  if (speed > 80 ) {
  break
  }
  
  if (speed > 48) {
    print("Slow down big time!")
    speed <- speed - 11
  } else {
    print("Slow down!")
    speed <- speed - 6
  }
}

5. repeat 循环

#注意:必须有break
i=0L
s=0L
i
repeat{
 i = i + 1
 s = s + i
 print(c(i,s))
 if(i==50) break
}

6. for 循环

for 循环的两种表示

# loop version 1
primes <- c(2, 3, 5, 7, 11, 13)
for (p in primes) {
  print(p)
}
# loop version 2
primes <- c(2, 3, 5, 7, 11, 13)
for (i in 1:length(primes)) {
  print(primes[[i]])
}

ps:for循环中对向量取值,建议使用 [[]] ,即 [[i]] 替换 [i] 。虽然后者也没有什么问题。

对列表循环

对于list 类型变量来说使用for 循环打印元素需要注意

for (i in 1:length(nyc)){
  print(nyc[[i]])
}
# 需要写[[]]
# 单个[] 会打印list 中各个类型变量
#打印结果
"""
[1] 8405837
[1] "Manhattan"     "Bronx"         "Brooklyn"      "Queens"       
[5] "Staten Island"
[1] FALSE
"""
for (i in 1:length(nyc)){
  print(nyc[i])
}
# 打印结果
'''
$pop
[1] 8405837

$boroughs
[1] "Manhattan"     "Bronx"         "Brooklyn"      "Queens"       
[5] "Staten Island"

$capital
[1] FALSE
'''

嵌套循环

利用for循环遍历打印矩阵中元素

# The tic-tac-toe matrix ttt has already been defined for you

# define the double for loop
for (i in 1:nrow(ttt)) {
  for (j in 1:ncol(ttt)) {
    print(paste("On row", i , "and column", j, "the board contains", ttt[i, j]))
  }
}

ps: paste 函数用于打印字符串与变量组合在一起。

break 和 next

在循环语句中可以通过break 与next 语句跳出循环。不过二者存在区别。next:跳过循环语句中的剩余内容,直接跳到下一次循环开始。进行中的循环结束,迭代继续。break:直接跳出循环。停止迭代,结束循环。

x <- c(5,6,0,3)
s = 0
for (i in 1:length(x)){
  s=s+x[[i]]
  #if(i == 3) next
  #if (i == 3) break
  print(c(i,x[[i]],1/i,s))
}

保存循环结果

使用print,我们可以将循环的相关变量打印出来。但可不可以保存它们?我们可以创建一个空列表,让每次循环输出的值作为一个元素添加到列表中。使用 do.call 语句我们可以对列表进行 cbind ,将其拼接在一起。

x <- c(5,6,0,3)
s = 0
result = list()
for(i in 1:length(x)){
  s=s+x[i]
  result[[i]] = c(i,x[i],1/i,s)
}
do.call(cbind,result)

     [,1] [,2]       [,3]  [,4]
[1,]    1  2.0  3.0000000  4.00
[2,]    5  6.0  0.0000000  3.00
[3,]    1  0.5  0.3333333  0.25
[4,]    5 11.0 11.0000000 14.00

7. 函数

形参与实参

image.png

使用内置函数

使用函数可以按照顺序或名称调用。如sd(x, na.rm = FALSE) 通过位置

sd(values, TRUE)

通过名称

sd(x = values, na.rm = TRUE)

好用的tips:args()可以不用像help()一样阅读大量内容,获得function的表达式。help()?function_name 可以获取函数使用记载的详细文件。如

> args(strsplit)
function (x, split, fixed = FALSE, perl = FALSE, useBytes = FALSE) 
NULL

定义一个函数

范例

my_fun <- function(arg1, arg2) {
  body
}

如定义一个计算绝对值加和的函数

sum_abs <- function(a, b){
  abs(a) + abs(b)
}
# 调用函数
# sum_abs(-3, 2)
# 返回5

也可以定义不需要任何输入值的函数,直接调用 在function中不设定参数

hello <- function(){
  print("Hi there!")
  TRUE
}
hello()

★function 中定义的变量为局部变量,因此只能在函数内调用,在外部调用会显示无目标值。”

★变量被函数调用后发生的变化只会发生在返回值上,而变量本身数值不变。即通过某个函数计算某变量,该变量本身数值并不会改变。”

使用函数

当一个代码需要重复使用三次及以上,就该考虑使用函数或者循环。

test=iris
g <- function(i){
  plot(test[,i],col=test[,ncol(test)])
}

g(1)
g(2)
g(3)

练习题

6

#练习6-3
rm(list=ls())

library(stringr)
library(tidyverse)
#1.使用循环,查看"a",TRUE和3的数据类型
# 坑:不同类型的数据只有列表可以存放。
for (i in list("a",TRUE,3)){
  print(i)
  print(class(i))
}
  
#2.生成10个随机数,根据这10个随机数生成一个新向量,>中位数的值对应"A",<中位数的值对应"B"。
t1 <- rnorm(10)
t2 <- ifelse(t1>median(t1), "A", "B")
#3.根据上一练习题中的tmp2生成一个新向量,含有e的值对应"A",不含有e的值对应"B"
tmp = "Bioinformatics is a new subject of genetic data collection,analysis and dissemination to the research community."
tmp2 = tmp %>% 
  str_replace(","," ") %>%
  str_remove("[.]") %>% 
  str_split(" ")
tmp2 = tmp2[[1]]
tmp3 <- ifelse(str_detect(tmp2, "e"),"A", "B")
#4.加载deg.Rdata,根据a、b两列的值,按照以下条件生成向量x:
#a<1 且b<0.05,则x对应的值为down;
#a>1 且b<0.05,则x对应的值为up;
#其他情况,x对应的值为no
load("deg.Rdata")
x1 <- (deg$a<1)&(deg$b<0.05)
x2 <- (deg$a>1)&(deg$b<0.05)
x <- ifelse(x2, "up", ifelse(x1, "down", "no"))
# 5.统计x的重复值个数
table(x)
sum(table(x))
# 6.将x添加到deg数据框中,成为新的一列
deg <- mutate(deg, x)
load("deg.Rdata")
deg <- cbind(deg,x)
load("deg.Rdata")
deg$x <- x

#练习6-4 :
#1.使用循环,对iris的1到4列分别画点图(plot)
rm(list=ls())
str(iris)

for (i in 1:4){
  plot(iris[,i], col = iris[,5])
}

#2.生成一个随机数(rnorm)组成的10行6列的矩阵,
#列名为sample1,sample2….sample6,行名为gene1,gene2…gene10,
#分组为sample1、2、3属于A组,sample4、5、6属于B组。
#用循环对每个基因画ggplot2箱线图。
x <- matrix(rnorm(60),nrow=10)
View(x)
colnames(x) <- c(paste0("sample",1:6))
rownames(x) <- c(paste0("gene",1:10))
t_x <- t(x)
group <- rep(c('A','B'), each = 3)
t_x <- data.frame(t_x)
t_x_group <- mutate(t_x, group)

# 画图
# 发现ggplot 并不能像基础包直接循环绘图,查询后尝试先用list存储,
# 参考:https://blog.csdn.net/weixin_33963594/article/details/89616215
# 再拼图
library(ggplot2)
library(patchwork)

list_plot = list()
for (i in 1:10){
  list_plot[[i]] <-  ggplot(data=t_x_group,
                            aes_string(x='group', y=colnames(t_x_group)[i])) +
    geom_boxplot(aes(color = group))
  
}

wrap_plots(list_plot, nrow = 2,guides = "collect")

dev.off()


# 彩蛋提示,发现还可以用长窄的gather 数据框结合分面完成。
x <- matrix(rnorm(60),nrow=10)
View(x)
colnames(x) <- c(paste0("sample",1:6))
rownames(x) <- c(paste0("gene",1:10))
t_x <- t(x)
group <- rep(c('A','B'), each = 3)
t_x_group <- cbind(t_x, group)
t_x_group <- data.frame(t_x_group)
x_gather <- gather(data = t_x_group, key=gene, value=exp, - group)
x_gather$exp <- as.numeric(x_gather$exp)

# 作图
library(ggplot2)

ggplot(data=x_gather) + 
  geom_boxplot(aes(x=group, y=exp, color=group)) +
  facet_wrap(~gene, ncol = 5) 
# 发现gene10 会排在gene1后面。未来可以进一步解决。