第六章 流程控制和循环

第六章 流程控制和循环

6.1流程控制

我们一般不满足于仅仅逐行地执行代码,而是希望更好地控制它们的执行流程。这就意味 着只有在某些条件满足后你会才执行一些代码。

6.1.1if和else

最简单的流程控制逻辑是使用 if,if 接受一个逻辑值(更准确地说是一个长度为 1 的逻辑向量)作为参数,且当该值为 TRUE 时才会执行下一条语句:

if(逻辑表达式){
	逻辑体
}else{
	逻辑体
}

注意:if 的条件中不允许缺失值,这样做会抛出一个错误

为了使代码更清晰,一些编程风格指南建议:即使条件执行语句只有一个,也要使用大括号。但else 必须与 if 语句的右大括号紧接在同一行。如果你把它挪到下一行,将出现错误:

如果你的条件中可能会出现缺失值,先用 is.na 来判断它:

> if(is.na(NA)) message("The value is missing!")
The value is missing!

可以反复使用 if 和 else 来定义多个条件。if 和 else 仍然是两个独立的词——还有一个 ifelse 函数。

6.1.2矢量化的if

ifelse(condition, statement1, statement2)

若condition为TRUE,则执行第一个语句;若condition为FALSE,则执行第二个语句。(相当于python的三目运算符)

> ifelse(rbinom(10, 1, 0.5), "Head", "Tail")
 [1] "Tail" "Head" "Head" "Tail" "Head" "Tail" "Tail"
 [8] "Tail" "Head" "Head"

> (yn <- rep.int(c(TRUE, FALSE), 6))
 [1]  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE
 [9]  TRUE FALSE  TRUE FALSE
> ifelse(yn, 1:3, -1:-12)
 [1]   1  -2   3  -4   2  -6   1  -8   3 -10   2 -12

> yn[c(3, 6, 9, 12)] <- NA 
> ifelse(yn, 1:3, -1:-12)
 [1]   1  -2  NA  -4   2  NA   1  -8  NA -10   2  NA

6.1.3多分支

switch(expression,list)

expression为表达式,list为列表,可以用有名定义。如果表达式返回值在1到length(list)之间,则返回列表相应位置的值,否则返回“NULL”值。当list是有名定义,表达式等于变量名时,返回变量名对应的值,否则返回“NULL”值。

> switch(2, mean(1:10), 1:5, 1:10) 
[1] 1 2 3 4 5
> switch("fruit", fruit = "apple", vegetable = "broccoli", meat = "beef")
[1] "apple"

如果找不到任何匹配的名字,那么 switch 将(隐式地)返回 NULL:

> (greek <- switch(
     "delta",
     alpha = 1, 
     beta = sqrt(4),  
     gamma = {  
         a <- sin(pi / 3)
         4 * a ^ 2 
         }
     )
 )
NULL

对于这种情况,你可以在没有其他选择的情况下提供一个没有命名的参数:

> (greek <- switch(
     "delta",
     alpha = 1, 
     beta = sqrt(4),  
     gamma = {  
         a <- sin(pi / 3)
         4 * a ^ 2 
         },
     "NO RESULT"
     )
 )
[1] "NO RESULT"

switch 的第一个参数也可以是一个整数。在这种情况下,其余的参数不需要名字——如果 第一个参数结果为 1,那么将返回第二个参数的结果;如果第一个参数的结果为 2,则返 回第三个参数的结果,以此类推:

> switch(
     3,  
     "first",  
     "second",   
     "third",  
     "fourth" 
     )
[1] "third

6.2循环

6.2.1repeat循环

R 中最容易掌握的循环是repeat。它所做的事情就是反复地执行代码,直到告诉它停为止。

repeat expr或者repeat {if(cond){break}}
> repeat {   
     message("Happy Groundhog Day!")   
     action <- sample(   
         c(       
             "Learn French",     
             "Make an ice statue",    
             "Rob a bank",    
             "Win heart of Andie McDowell" 
         ),    
         1  
     )  
     message("action = ", action) 
    if(action == "Win heart of Andie McDowell") break
     }

#Happy Groundhog Day!
#action = Win heart of Andie McDowell

有时候,我们想做的不是退出整个循环,而是跳过当前的迭代,开始 next 下一次迭代而已:

> repeat {   
     message("Happy Groundhog Day!")   
     action <- sample(   
         c(       
             "Learn French",     
             "Make an ice statue",    
             "Rob a bank",    
             "Win heart of Andie McDowell" 
         ),    
         1  
     )
     if(action == "Rob a bank"){  
         message("Quietly skipping to the next iteration")  
         next 
         }   
     message("action = ", action) 
     if(action == "Win heart of Andie McDowell") break
     }
#Happy Groundhog Day!
#Quietly skipping to the next iteration
#Happy Groundhog Day!
#action = Make an ice statue
#Happy Groundhog Day!
#action = Win heart of Andie McDowell

6.2.2while循环

while 循环是先进行检查再(可能)执行代码。因为检查发生在开始时,所以循环体有可能不会被执行

while (cond) {expr}

cond为谈判条件,expr为一个或一组表达式。while循环重复执行语句expr,直到条件cond不为真为止。

> x <- c(1, 1)
> i <- 3
> while (i <= 10) {  
     x[i] <- x[i - 1] + x[i - 2]  
     i <- i + 1
     }
> x
 [1]  1  1  2  3  5  8 13 21 34 55

6.2.3for循环

for 循环将接受一个迭代器变量 和一个向量参数。在每个循环中,迭代器变量会从向量中取得一个值。

for (name in expr1) {expr2}
> for(i in 1:5) message("i = ", i)
i = 1
i = 2
i = 3
i = 4
i = 5

6.3高级循环

6.3.1replication

replicate函数能调用表达式数次。大多数情况下与rep函数基本相等,只有当使用随机数时才会出现不同。 rep 函数每次都将重复相同的随机 数,而 replicate 每次的结果都不相同。

> replicate(5, runif(1))
[1] 0.4349063 0.5684572 0.9457541 0.5491513 0.6882297

6.3.2apply系列

在某些情况下,保持矢量化意味着控制代码的方式不太自然。此时,apply 系列的函数能更自然地让你进行 “伪矢量化”。

函数名称 使用对象 返回结果
apply 矩阵、数组或者数据框 向量、数组或列表
lapply 列表、数据框或者向量 列表
sapply 列表、数据框或者向量 向量、数组或列表
tapply 不规则列阵 阵列
mapply 多个列表或者向量参数 列表

6.3.2.1apply函数

apply(x, MARGIN, FUN, …) 把FUN函数运用到x数据的第MARGIN维度上

> x <- matrix(1:20, ncol = 4)
> apply(x, 1, mean)  # 计算各行的均值
[1]  8.5  9.5 10.5 11.5 12.5
> apply(x, 2, mean)  # 计算各列的均值
[1]  3  8 13 18

6.3.2.2lapply函数

lapply(x, FUN, …) 把函数FUN运用到列表的每一个元素

> x <- list(a = 1:5, b = exp(0:3))
> lapply(x, mean) # 对列表x的每一个元素计算均值
$a
[1] 3

$b
[1] 7.798219

6.3.2.3tapply函数

tapply(x, INDEX, FUN=NULL, …, simplify=TRUE) 把FUN函数根据INDEX索引应用到x数据

> height <- c(174, 165, 180, 171, 160)
> sex <- c("F", "F", "M", "F", "M")
> tapply(height, sex, mean)  # 计算不同sex对应的height的均值
  F   M 
170 170 

6.3.2.4sapply函数

sapply(x, FUN, …, simplify=TRUE, USE.NAMES=TRUE) 是lapply函数更加友好的版本,可以使用simplify参数来调整输出的数据格式

> list <- list(c("a", "b", "c"), c("A", "B", "C"))
> sapply(list, paste, 1:3, simplify = TRUE)
     [,1]  [,2] 
[1,] "a 1" "A 1"
[2,] "b 2" "B 2"
[3,] "c 3" "C 3"

6.3.2.5vapply函数

vapply(x, FUN, FUN.VALUE, …, USE.NAMES=TRUE) 类似于sapply,但是返回值只能按照预先制定的方式输出

> vapply(prime_factors, length, numeric(1))
  two three  four  five   six seven eight  nine   ten 
    1     1     2     1     2     1     3     2     2 

6.3.2.6mapply函数

mapply(FUN, …, MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)运用于多变量情况

> mapply(rep, times = 1:3, MoreArgs = list(x = 1:2))
[[1]]
[1] 1 2

[[2]]
[1] 1 2 1 2

[[3]]
[1] 1 2 1 2 1 2

6.4plyr包

6.4.1llply函数

llply 的输入参数是列表,它将函数应用于每个元素上, 并返回一个列表,这使它成为 lapply 的一个替代函数:

> library(plyr) 
> llply(prime_factors, unique)
$two
[1] 2

$three
[1] 3

$four
[1] 2

$five
[1] 5

$six
[1] 2 3

$seven
[1] 7

$eight
[1] 2

$nine
[1] 3

$ten
[1] 2 5

6.4.2laply函数

laply 以一个列表作为参数,并返回一个数组,这酷似 sapply。如果输入参数为空,它也 会智能地返回一个空的逻辑向量(与返回空列表的 sapply 不一样):

> laply(prime_factors, length)
[1] 1 1 2 1 2 1 3 2 2

6.4.3raply函数

raply 能取代 replicate(不是 rapply!),还有 rlply 和 rdply 函数能分别返回列表或数据 框,还有一个 r_ply 函数能丢弃结果(在绘图时有用):

> raply(5, runif(1)) # 数组输出
[1] 0.5664283 0.9234548 0.5400023 0.9055282 0.4335837
> rlply(5, runif(1)) # 列表给出
[[1]]
[1] 0.6625848

[[2]]
[1] 0.2813876

[[3]]
[1] 0.5165715

[[4]]
[1] 0.9951116

[[5]]
[1] 0.9677127

> rdply(5, runif(1)) # 数据框输出
  .n        V1
1  1 0.5281355
2  2 0.1613995
3  3 0.8198804
4  4 0.7920243
5  5 0.1821630
> r_ply(5, runif(1)) # 丢弃输出

6.4.4ddply函数

ddply函数的输入和输出都是数据框,它可以替换 tapply 函 数 。其优点是易于同时计算多个列。

> (frogger_scores <- data.frame( 
     player = rep(c("Tom", "Dick", "Harry"), times = c(2, 5, 3)),  
     score = round(rlnorm(10, 8), -1) 
     ))
> 
> ddply(   
     frogger_scores, 
     .(player), 
     colwise(mean) # 除了 player 之外,对每个列调用 mean 函数 
     )
  player score
1   Dick  5760
2  Harry  3800
3    Tom  4400
posted @ 2023-08-13 08:59  昵称已经被使用  阅读(33)  评论(0)    收藏  举报