使用R语言purrr::map实现两个data.frame行级遍历计算
问题描述
我有两个存储公式计算用值的data.frame,需要将df1的每一行和df2的每一行组合进行计算。已经用嵌套for循环实现了预期结果,但尝试用purrr::map实现时出现报错:
map2(df1,df2,function(.x,.y) {(eval(str2lang(.x$h))*eval(str2lang(.y$limit))*eval(str2lang(.x$ED)))/eval(str2lang(.y$zone))}) #Error in `map2()`: #! Can't recycle `.x` (size 4) to match `.y` (size 3). #Run `rlang::last_error()` to see where the error occurred.
尝试嵌套map调用也无法正确获取列值,求如何用purrr::map实现该需求?
原始数据与for循环代码
library(tibble) df1 <- tibble(Type=c("atype","btype","ctype"), h=c("1","2","3"), w=c("4","5","6"), ED=c("101","102","103")) df2 <- tibble(Item=c("123-htc","but-456","xtc","newID"), limit=c("rnorm(1)","12","13","14"), zone=c("30","40","30","11")) # 注:数据框中的值存为字符串,这样可以支持调用随机数函数 resItm <- list() resTyp <- list() for (i in 1:length(df1$Type)) { h <- NULL w <- NULL ED <- NULL limit <- NULL zone <- NULL for (j in 1:length(df2$Item)) { Typ<-df1[i,] Itm<-df2[j,] h<-eval(str2lang(Typ$h)) w<-eval(str2lang(Typ$w)) ED<-eval(str2lang(Typ$ED)) limit<-eval(str2lang(Itm$limit)) zone<-eval(str2lang(Itm$zone)) res1 <- (h * limit * ED) / (zone) resItm[[j]] <- list(Item=Itm$Item, Result=res1) } resTyp[[i]] <- list(Type=Typ$Type, Res_Item = resItm) } str(resTyp)
解决方案
问题根源
你用map2报错是因为它默认按列同时遍历两个data.frame,但你需要的是df1每行和df2每行的全组合计算,所以得用嵌套map来实现行对行的遍历。
实现代码
library(purrr) library(tibble) # 先把两个数据框拆成按行的列表,方便逐行遍历 df1_rows <- split(df1, seq(nrow(df1))) df2_rows <- split(df2, seq(nrow(df2))) # 嵌套map实现全组合计算 resTyp_map <- map(df1_rows, function(row1) { # 内层遍历所有df2行,计算当前df1行和每个df2行的结果 resItm <- map(df2_rows, function(row2) { h <- eval(str2lang(row1$h)) ED <- eval(str2lang(row1$ED)) limit <- eval(str2lang(row2$limit)) zone <- eval(str2lang(row2$zone)) result <- (h * limit * ED) / zone list(Item = row2$Item, Result = result) }) # 整理当前df1行的结果结构 list(Type = row1$Type, Res_Item = resItm) }) # 查看结果结构,和原for循环输出完全一致 str(resTyp_map)
简化写法
如果想让代码更简洁,可以用purrr的匿名函数语法:
resTyp_map <- map(df1_rows, ~{ resItm <- map(df2_rows, ~{ result <- (eval(str2lang(.x$h)) * eval(str2lang(.y$limit)) * eval(str2lang(.x$ED))) / eval(str2lang(.y$zone)) list(Item = .y$Item, Result = result) }, .y = .x) list(Type = .x$Type, Res_Item = resItm) })
内容的提问来源于stack exchange,提问作者mando_R
相关产品推荐
相关产品推荐

