逐行迭代,例如 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 %&gt;% split(seq(nrow(.))) %&gt;% 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,...)

应用新功能rmaprowwisema​​p

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实现优雅循环迭代

使用等效的purrr ::: map迭代data.table

如何简化这个 python 迭代?

Plpgsql - 多次迭代记录集

R(purrr)展平命名列表列表以列出并保留名称

如何在目标c中逐行解析JSON文件