【上一篇:81.关于Base R中的for循环】
【下一篇:83.关于purrr包中的函数与apply函数的运行原理说明-十分详细】
上篇讲了:基本的for循环包含三个部分,特点是输入长度已知,输出长度已知,基于索引进行循环对列进行操作,且输出是一个新的对象---一个新向量,这样的for循环并不能概括我们的需要。所以上述四个特点有一个发生变化,我们又该如何做呢?
修改已经存在的对象
求数据框中的每一列,对列元素进行0-1标准化:
df <- tibble(
a = rnorm(10),
b = rnorm(10),
c = rnorm(10),
d = rnorm(10)
)
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
# 改一列
df$a <- rescale01(df$a)
# 循环改所有列
for (i in seq_along(df)) {
df[[i]] <- rescale01(df[[i]])
}
上述例子中输出已经存在,即等同于df。看例子就可以理解了。这类情况一般发生在List和data frame中,记住取子集用工具" [[ ",而不是" [ "。
Looping模式
有三种基本方法来循环一个vector:1)根据索引:最常用的方法," x[[indice]] "获取对应的元素;2)根据元素:在只关心side-effect(绘图或保存一个文件)时很有用,因为很难有效保存输出。3)根据name:用" x[[name]] "获取元素,在绘图标题或文件名中使用名称,这是非常有用的。
不必记忆何时使用,当遇到问题的时候你会自觉选择合适的方法。
未知长度的输出
means <- c(0, 1, 2)
output <- double()
for (i in seq_along(means)) {
n <- sample(100, 1)
output <- c(output, rnorm(n, means[[i]]))
}
str(output)
如上例,当输出长度不定时,使用c()连接每次循环的输出。这样做的缺点是:每次迭代,R必须拷贝上次迭代的结果中的所有数据,In technical terms you get “quadratic” (O(n2)) behaviour which means that a loop with three times as many elements would take nine (32) times as long to run,所以不是非常有效。
解决方法是:当遇到输出长度不定的循环时,尝试将结果保存到一个List或者更复杂的结构中,待循环结束再合并结果。
例如:串联字符串,除了用paste(old_string,new_string,sep="")循环paste之外,还可以将每次取到的new_string保存到一个list中,最后用paste(unlist(list),collopse=T)得到结果。还比如,循环多个数据框,想把这些数据框合并,除了每次迭代时使用rbind()函数之外,还可将每次取到的数据框保存到一个list中,最后用dplyr::bind_rows(output)得到结果。
未知的sequence长度
即你不知道输入序列要循环多少次,此时可以用while循环实现:
i <- 1
while (i <= length(x)) {
# body
i <- i + 1
}
while循环也比for循环更通用,因为你可以将任何for循环重写为while循环,但你不能将每个while循环重写为for循环。while循环在simulation中用得比较多。
【上一篇:81.关于Base R中的for循环】
【下一篇:83.关于purrr包中的函数与apply函数的运行原理说明-十分详细】
网友评论