第六章 流程控制和循环
第六章 流程控制和循环
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
浙公网安备 33010602011771号