【R语言】循环遍历机制 :for、purrr::map、apply、repeat、while、迭代器等
前言
解决一些循环遍历的问题,实操可以在前文“【R语言】csv + excel批量文件的读取、导出处理”查看。
准备
依旧是准备测试文件
代码
梳理了R语言中一些常见的遍历循环的操作,总共六类。
part_1: for循环
介绍: for 循环结构可以遍历向量或者列表中的每个元素, 循环必有三要素: 输出–>序列–>循环体
模板:
for (var in list) { # 表达式 expression
action(var)
}
举例:
如:
output <- vector("double", ncol(df)) # 1. 输出 output <- vector("double", length(x))
for ( i in seq_along(df) ) { # 2. 序列 seq_along() ≈ length() 唯一优势:能区别0;
output[[i]] <- median(df[[i]]) # 3. 循环体
}
output
PS:
在开始循环前, 你必须为输出结果分配足够的空间.这对循环效率非常重要.
如果在每次迭代中都使用 c() 来保存循环的结果,那么 for 循环的速度就会特别慢.
创建给定长度的空向量的一般方法是使用 vector() 函数
该函数有两个参数:向量类型(“logical”、“integer”、“double”、“character” 等)和向量的长度.
实例:
举例:
sum_100 = 0 # 1.输出
for(i in seq(from=1,to=100,by=1)){ # 2.序列
sum_100 = sum_100 + i # 3.循环体
}
sum_100
part_2: purrr::map
介绍:purrr 包 提供了大量的类似map的函数,可以服务于减少循环、处理嵌套数据、多模型等应用需求,属于R语言中比较常见的编程函数包。
主要内容:
library("purrr")
purrr::map() 依次应用一元函数到一个序列的每个元素上, 用于输出列表, 基本等同base::lapply() /plyr::lapply()
purrr::map2() 依次应用二元函数到两个序列的每对元素上 map2(x,y,function = x+y)
m <- list(1,2,3)
n <- list(4,5,6)
map2(m,n, `+`) # 列表的四维运算, +-*/ ;
purrr::pmap() 应用多元函数到多个序列的每组元素上,可以实现对数据框逐行迭代
map 系列默认返回列表型,可根据想要的返回类型添加后缀:int, _dbl, _lgl, _chr, _df
purrr::map_lgl(data, function) 用于输出逻辑型向量;
purrr::map_int(data, function) 用于输出整型向量;
purrr::map_dbl(data, function) 用于输出双精度型向量;
purrr::map_chr(data, function) 用于输出字符型向量;
可以接着对返回的数据框df做行/列合并:purrr::map_dfr, purrr::map_dfc
purrr::map_dfr(.x, .f, .id = "id_col") # 将函数.f依次应用到序列.x的每个元素返回数据框,再bind_rows按行合并为一个数据框,.id可用来增加新的列描述来源
purrr::map_dfc(.x, .f)
purrr::map_dfr(set_names(filenames),read_xlsx,.id = "来源") # id 为来源字段名称
purrr::map_dfr(set_names(files),~read_xlsx(.x,sheet = 1),.id = "来源")
PS:
与 for 循环相比,映射函数的重点在于需要执行的操作(即 mean()、median() 和 sd()),而不是在所有元素中循环所需的跟踪记录以及保存结果;
如果不理解,加上使用管道, 可以让两者差距更加明显:
df <- list(a = rnorm(10),
b = rnorm(10),
c = rnorm(10)
)
library("magrittr") # 管道函数
map_dbl(df, mean) <=> df %>% map_dbl(mean)
# > a b c d #> 0.2026 -0.2068 0.1275 -0.0917
map_dbl(df, median) <=> df %>% map_dbl(median)
# > a b c d
# > 0.237 -0.218 0.254 -0.133
map_dbl(df, sd) <=> df %>% map_dbl(sd)
# > a b c d
# > 0.796 0.759 1.164 1.062
part_3: ?pply系列
介绍:
?pply是一个系列函数,存在于base/plyr两个包中
基本函数格式: ?pply(data, 参数(非必要) ,function() )
主要类别:
apply() 应用矩阵/数组
lapply() 应用列表/向量
sapply() 应用列表/向量
tapply()
mapply()
详细介绍:
列举了几个函数的详细介绍和使用场景、差异。
(1). apply(X, MARGIN=1/2, FUN)
· 参数说明
· x: 一个数组或者矩阵
· MARGIN=1 : 操作基于行 MARGIN=2: 操作基于列 MARGIN=c(1,2) : 对行和列都进行操作;
· FUN: 使用哪种操作,内置的函数有mean、medium、sum、min、max,用户自定义函数;
tmp <- matrix(C<-(1:10),nrow=5, ncol=6)
tmp <- apply(tmp, 2, sum)
tmp
(2). lapply()函数
# 多一个l 代表 输出格式为list,其他和apply无差;
movies <- c("SPYDERMAN","BATMAN","VERTIGO","CHINATOWN")
class(movies) # 查看数据类型
movies_lower <- lapply(movies, tolower)
str(movies_lower) # 输出list形式,故一般用units() 整合
movies_lower <- unlist(lapply(movies,tolower))
(3). sapply()函数
# 与lapply 无大差异,返回向量;
# 多个参数
dt <- cars[1:5,]
t <- c("red","red","red","blue","bile")
dt <- data.frame(dt,t)
lapply(dt, min) #如果数框中出现非数字结构变量,需指定变量;
sapply(dt, min)
sapply(dt, min, simplify <- F) <- lapply(dt, min)
sapply(dt, min, simplify <- T)
(4). tapply(X, INDEX, FUN = NULL)
# X: 一个对象,一般都是向量
# INDEX: 一个包含分类因子的列表(list)
# FUN: 对X里面每个元素进行操作的函数
t =c("red","red","red","blue","bile")
dt = data.frame(dt,t)
dt %>% group_by(t) %>% summarise(sum(speed)) # 与tapply同样结果 ,更好用 ps:dplyr包函数;
(5). mapply(FUN,X ...)
用的比较少,和上面没区别,只是参数位置换了
part_4: repeat结构
介绍: repeat 比较容易理解,有些python的感觉,会用循环break的语句。
本质是简单的重复同一个表达式:repeat expression
如果要跳出循环,可以使用break命令,若要跳至循环中的下一轮迭代,需要使用next命令;
如果在循环中不包括break命令,R代码将会是一个无限循环.
模板:
repeat {
expression
if(condition){
break
}
}
举例:
# 求1-100的和
i <- 1
sum_100 <- 0
repeat{ sum_100 = sum_100 + i
i = i + 1
if(i > 100){
print(sum_100)
break
}
}
part_5: while结构
介绍: while和repeat有些类似,在某个条件为真时,重复某一特定的表达式,直到条件结束。
模板:
while(condition) { # 条件
action(variable) # 表达式
}
举例:
i <- 1
sum_100 <- 0
while(i<=100){
sum_100 <- sum_100 + i; # 分号不能忘
i <- i + 1
}
print(sum_100)
part_6: 迭代器foreach
介绍: 该部分属于循环的拓展了,C或者java等现代编程语言中,都会有foreach等迭代器. R本身并没有提供这样的机制,只能通过R语言添加包来实现;
详情:
(1)迭代器:从另外一个对象中返回元素的抽象对象.使用迭代器可以使代码具有更好的可读性同时易于并行执行.
添加R语言扩展包iterators可以实现迭代器功能.
迭代器可以返回向量、数组、数据框或者其他对象的元素,当然也可以返回函数.
模板:
iter(obj,checkFunc=function(...) TRUE,recycle=FALSE,...)
# obj 指定对象
# checkFunc 指定一个过滤迭代器返回值的函数
# recyle 指定当对象元素迭代完成之后是否对迭代进行重置
举例:
iter_one <- iter(1:10,checkFunc=function(x) x%%2==0,recycle=F)
nextElem(iter_one) #nextElem()函数用来查看下一个迭代项,这个函数会隐式地调用checkFunc,如果下一个值符合checkFunc,则返回该值,否则将迭代下一个值,直至找到一个符合checkFunc的值或者将所有值都迭代完毕
(2)foreach循环: 通过R语言扩展包foreach实现,foreach能够循环遍历某个对象(向量、矩阵、数据框或者迭代器)中的多个元素,针对各个元素执行表达式,并返回结果.
模板:
foreach(..., .combine, .init, .final=NULL, .inorder=TRUE,
.multicombine=FALSE,
.maxcombine=if (.multicombine) 100 else 2,
.errorhandling=c('stop', 'remove', 'pass'),
.packages=NULL, .export=NULL, .noexport=NULL,
.verbose=FALSE)
真正执行foreach循环,需要使用%do%或者%dopar%运算符
i_square <- foreach(i=1:5) %do% i^2
%do%运算符顺序执行表达式,而%dopar%运算符可以用来并行执行表达式.
总结
以上是梳理总结的一些关于R语言循环遍历机制的内容,希望能帮到大家, 如有错误,欢迎指正。
原创不易,转载请注意出处:
https://blog.csdn.net/weixin_41613094/article/details/128250752