逐行迭代,例如 apply with purrr
Posted
技术标签:
【中文标题】逐行迭代,例如 apply with purrr【英文标题】:Row-wise iteration like apply with purrr 【发布时间】:2018-04-04 14:19:17 【问题描述】:如何使用 purrr::map 实现逐行迭代?
以下是我如何使用标准的逐行应用来做到这一点。
df <- data.frame(a = 1:10, b = 11:20, c = 21:30)
lst_result <- apply(df, 1, function(x)
var1 <- (x[['a']] + x[['b']])
var2 <- x[['c']]/2
return(data.frame(var1 = var1, var2 = var2))
)
但是,这不是太优雅,我宁愿用 purrr 来做。也可能(或可能不会)更快。
【问题讨论】:
【参考方案1】:您可以使用pmap
进行逐行迭代。这些列用作您正在使用的任何函数的参数。在您的示例中,您将有一个三参数函数。
例如,这里是pmap
使用匿名函数来完成您正在做的工作。列按照它们在数据集中的顺序传递给函数。
pmap(df, function(a, b, c)
data.frame(var1 = a + b,
var2 = c/2)
)
您可以使用 purrr 波浪号“速记”来表示匿名函数,方法是按顺序引用前面带有两个点的数字的列。
pmap(df, ~data.frame(var1 = ..1 + ..2,
var2 = ..3/2) )
如果您想将这些特定结果作为 data.frame 而不是列表,您可以使用pmap_dfr
。
【讨论】:
在第一个示例中,如果 df 有 100 列并且我只想操作第 90 列,我该怎么办?我知道我可以通过索引号来引用它,但我想通过名称来引用它。 @matsuo_basho 如果您只想使用单个列,其他工具可能更合适(例如,dplyr::mutate
)。但是,pmap
的文档指出,您始终可以使用...
来“吸收输入 [the] 列表中未使用的组件”。所以如果感兴趣的列被命名为“c”,像pmap(df, function(c, ...) data.frame(var1 = c/2) )
这样的东西就可以了。
...
是做什么用的?
@AlvaroMorales 它包含所有其余的列名,因此您无需引用pmap()
中的每个列名。 map 系列函数的文档Examples
部分中有一个示例,您可能会发现它很有用!【参考方案2】:
请注意,您在示例中仅使用矢量化操作,因此您可以很好地做到:
df %>% dplyr::transmute(var1 = a+b,var2 = c/2)
(或在基础 R 中:transform(df,var1 = a+b,var2 = c/2)[4:5]
)
如果您使用非向量化函数,例如中位数,您可以使用@aosmith 的答案中的pmap
,或使用dplyr::rowwise
。
rowwise
速度较慢,包维护人员建议改用map
系列,但在某些情况下,它可以说比pmap
更容易看。当速度不是问题时,我个人仍然使用它:
library(dplyr)
df %>% transmute(var3 = pmap(.,~median(c(..1,..2,..3))))
df %>% rowwise %>% transmute(var3 = median(c(a,b,c)))
(返回严格的未命名列表输出:res %>% split(seq(nrow(.))) %>% unname
)
【讨论】:
【参考方案3】:您可以随时对您“喜欢”的函数进行封装。
rmap <- function (.x, .f, ...)
if(is.null(dim(.x))) stop("dim(X) must have a positive length")
.x <- t(.x) %>% as.data.frame(.,stringsAsFactors=F)
purrr::map(.x=.x,.f=.f,...)
应用新功能rmap
(rowwisemap)
rmap(df1,~
var1 <- (.x[[1]] + .x[[2]])
var2 <- .x[[3]]/2
return(data.frame(var1 = var1, var2 = var2))
)
附加信息:(从上到下评估)
df1 <- data.frame(a=1:3,b=1:3,c=1:3)
m <- matrix(1:9,ncol=3)
apply(df1,1,sum)
rmap(df1,sum)
apply(m,1,sum)
rmap(m,sum)
apply(1:10,1,sum) # intentionally throws an error
rmap(1:10,sum) # intentionally throws an error
【讨论】:
【参考方案4】:您可以结合使用pmap
和...
,这对我来说是最好的解决方案,因为我不需要指定参数。
df <- data.frame(a = 1:10, b = 11:20, c = 21:30)
lst_result <- df %>%
pmap(function(...)
x <- tibble(...)
return(tibble(var1 = x$a + x$b, var2 = x$c/2))
)
【讨论】:
以上是关于逐行迭代,例如 apply with purrr的主要内容,如果未能解决你的问题,请参考以下文章
R语言问题剖析20篇-R语言泛函式编程purrr实现优雅循环迭代