【问题标题】:Avoiding nested loops but iterate over 2 values using (l)apply?避免嵌套循环但使用 (l)apply 迭代 2 个值?
【发布时间】:2019-11-30 17:25:48
【问题描述】:

我想更好地在 R 中编写优雅的代码,并试图避免编写嵌套循环,但无法找到 (l)apply 解决我的问题的方法。

我有一组配对文件,每个文件都有两个与之关联的变量 - 一个名称和一个数字。文件名很长,所以我想生成一个文件名向量,然后可以由我自己的自定义下游函数访问,以将它们读入数据框、绘图等。

例如,文件如下所示:

 5_simulationA.k  5_simulationA.b
10_simulationA.k 10_simulationA.b
 5_simulationB.k  5_simulationB.b
10_simulationB.k 10_simualtionB.b

“.k”和“.b”文件是一对配对文件,必须放在一起以进行下游处理。

我可以通过编写一个看起来像这样的嵌套循环来读取这些文件,

K_files = c()
B_files = c()

for (i in c(A,B,C)){ # iterate over letter variable
    for (n in c(5,10,15)){ #iterate over numbers of the files
        k_filename = paste(n, "_simulation", i, ".k")
        b_filename = paste(n, "_simulation", i, ".b")
        K_files = c(K_files, k_filename)
        B_files = c(B_files, b_filename)
    }
}

这当然是非常丑陋和不像 R 的。我很想找到一种方法来使用非常强大的 apply 或 lapply 语句,或者任何人可能拥有的任何其他优雅的解决方案。谢谢!

【问题讨论】:

  • 您要查找的函数是 mapply()map2 来自 purrr 包(purrr 只是让 apply 系列更加一致)。
  • 请注意c(A,B,C) 应该是c("A", "B", "C")
  • Map(function(a,b) { a*b }, 1:3, 4:6)Mapmapply 的特例)等价于lapply(list(c(1,4), c(2,5), c(3,6)), function(z) { z[1]*z[2] })Map 的一个优点是它需要 1 个或更多 个参数,因此可以根据需要轻松扩展到 许多 个参数。 (在这种情况下,我个人发现它比 lapply 等价物更容易阅读和排除故障。如果您好奇,总是使用 Map 代替 lapply 是很可行的。我不知道是否性能明显不同...)
  • mapply() 解决了这个问题!很容易实现,谢谢! :)
  • 简单的测试表明lapply 更快,但对该争论的判断应考虑可读性、可维护性和数据结构。

标签: r loops apply


【解决方案1】:

基础R函数outer就是针对这类问题的。

L <- c("A", "B", "C")
N <- c(5, 10, 15)

f <- function(i, n, e) paste0(n, "_simulation", i, e)
sapply(c(".k", ".b"), function(.e) outer(L, N, f, e = .e))
#     .k                 .b                
# [1,] "5_simulationA.k"  "5_simulationA.b" 
# [2,] "5_simulationB.k"  "5_simulationB.b" 
# [3,] "5_simulationC.k"  "5_simulationC.b" 
# [4,] "10_simulationA.k" "10_simulationA.b"
# [5,] "10_simulationB.k" "10_simulationB.b"
# [6,] "10_simulationC.k" "10_simulationC.b"
# [7,] "15_simulationA.k" "15_simulationA.b"
# [8,] "15_simulationB.k" "15_simulationB.b"
# [9,] "15_simulationC.k" "15_simulationC.b"

【讨论】:

    【解决方案2】:

    从 OP 的示例输出文件名来看,我们似乎想要 ni 的所有组合。 expand.grid 返回 ns 和 is 的所有组合的数据框。然后我们可以使用apply 循环遍历它的行来生成文件名:

    i <- c("A", "B", "C")
    n <- c(5, 10, 15)
    combi <- expand.grid(n = n, i = i)
    
    invisible(apply(combi, 1, function(x){
      k_filename = paste0(x[1], "_simulation", x[2], ".k")
      b_filename = paste0(x[1], "_simulation", x[2], ".b")
      print(k_filename)
      print(b_filename)
    }))
    

    注意到我使用invisible 来抑制apply 的输出,因为我们只对副作用(读/写文件)感兴趣。或者,我们可以使用来自purrrpwalk,它将相同expand.grid 数据帧的每一列作为输入,并静默创建文件名:

    library(dplyr)
    library(purrr)
    combi %>%
      pwalk(~ {
        k_filename = paste0(.x, "_simulation", .y, ".k")
        b_filename = paste0(.x, "_simulation", .y, ".b")
        print(k_filename)
        print(b_filename)
      })
    

    输出:

    [1] "5_simulationA.k"
    [1] "5_simulationA.b"
    [1] "10_simulationA.k"
    [1] "10_simulationA.b"
    [1] "15_simulationA.k"
    [1] "15_simulationA.b"
    [1] "5_simulationB.k"
    [1] "5_simulationB.b"
    [1] "10_simulationB.k"
    [1] "10_simulationB.b"
    [1] "15_simulationB.k"
    [1] "15_simulationB.b"
    [1] "5_simulationC.k"
    [1] "5_simulationC.b"
    [1] "10_simulationC.k"
    [1] "10_simulationC.b"
    [1] "15_simulationC.k"
    [1] "15_simulationC.b"
    

    【讨论】:

    • 我猜效率更高一点:y &lt;- c("A", "B", "C"); x &lt;- c(5, 10, 15); z &lt;- c("k", "b"); f &lt;- function(x, y, z) sprintf(fmt = "%d_simulation%s.%s", x, y, z); do.call(f, expand.grid(x = x, y = y, z = z))
    【解决方案3】:
    library(tidyverse)
    Type = c("A", "B", "C")
    Index = c(5, 10, 15)
    
    crossing(Type, Index) %>% 
    mutate(k_filename = map2_chr(Index, Type, ~paste(.x, "_simulation", .y, ".k", sep="")), 
           b_filename = map2_chr(Index, Type, ~paste(.x, "_simulation", .y, ".b", sep=""))) -> names 
    

    之后,您可以使用pull 访问k_filenameb_filename

    K_files <- names %>% pull(k_filename)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-27
      相关资源
      最近更新 更多