【问题标题】:Replace loop with one of the functions of the "apply" family用“应用”系列的功能之一替换循环
【发布时间】:2018-06-23 07:06:07
【问题描述】:

我在列表中有 data.frames,通常,当我想使数据居中时,我使用循环(如下例所示)。我想使用“应用”系列的一些功能,但我不知道如何编写代码。

我的数据示例:

env <- list (data.frame(a=c(-1.08, -1.07, -1.07),
                        b=c( 4.61,  4.59,  4.59),
                        c=c( 3.46,  3.56,  3.52)),
             data.frame(a=c( 3.93,  3.94,  3.92),
                        b=c(-6.69, -6.72, -6.68),
                        c=c( 3.04,  3.08,  3.03)))

我将用来使它们居中的值:

d <- c(a=10.20, b=-10.91, c=11.89)

我常用的循环类型:

for(i in 1:length(env)) {
    env[[i]][, 1] <- env[[i]][, 1] - d[1]
    env[[i]][, 2] <- env[[i]][, 2] - d[2]
    env[[i]][, 3] <- env[[i]][, 3] - d[3]
}

有没有办法使用“apply”系列的函数来做我在上面循环中做的同样的事情?

【问题讨论】:

  • 很好的可重现示例。顺便提一句。您正在使用的循环可以通过在列上使用内部循环来缩短:for (j in 1:length(env[[i]])) {env[[i]][, j] &lt;- env[[i]][, j] - d[j]}

标签: r loops apply


【解决方案1】:

您也可以使用map 函数来完成相同的操作。具体来说,您可以使用map() 循环遍历列表env,然后使用map2() 循环(同时)遍历d 和各个数据帧env[[1]]env[[2]]j-k 是数据集中的地方。

library('purrr')
map(env, function(i){
  map2(i, d, function(j,k){
    j-k
  })
})

屈服,

[[1]]
[[1]]$a [1] -11.28 -11.27 -11.27
[[1]]$b [1] 15.52 15.50 15.50
[[1]]$c [1] -8.43 -8.33 -8.37

[[2]] 
[[2]]$a [1] -6.27 -6.26 -6.28
[[2]]$b [1] 4.22 4.19 4.23
[[2]]$c [1] -8.85 -8.81 -8.86

【讨论】:

    【解决方案2】:

    非常感谢您提供快速而有趣的答案。

    我在 microbenchmark::microbenchmark 函数中运行了您发布的所有解决方案。

    对于生成矩阵列表的解决方案,我添加了(仅使用我目前对 R 的了解)一个额外的行来将它们转换为数据框列表。

    env1 <- env
    env2 <- env
    env3 <- env
    env4 <- env
    env5 <- env
    env6 <- env
    env7 <- env
    
    ## install.packages
    library("microbenchmark")
    microbenchmark(
    ## 1; the original.
    for(i in 1:length(env1)) {
        env1[[i]][, 1] <- env1[[i]][, 1] - d[1]
        env1[[i]][, 2] <- env1[[i]][, 2] - d[2]
        env1[[i]][, 3] <- env1[[i]][, 3] - d[3]}
    ,
    
    ## 2
    for(i in 1:length(env2)) {
        for (j in 1:length(env2[[i]])) {
            env2[[i]][, j] <- env2[[i]][, j] - d[j]
        }
    }
    ,
    
    ## 3
    {env3 <- lapply(env3, function(i) t(t(i) - d))
    env3 <- lapply(env3, function(i) as.data.frame(i))}
    ,
    
    ## 4
    {env4 <- lapply(env4, scale, center=d, scale=FALSE)
    env4 <- lapply(env4, function(i) as.data.frame(i))}
    ,
    
    ## 5
    {nrows <- 3
    env5 <- lapply(env5, function(x) x - matrix(rep(d, nrows), nrow = 
    nrows, byrow = TRUE))}
    ,
    
    ## 6
    env6 <- lapply(env6, sweep, 2, d, "-")
    ,
    
    ## 7
    {env7 <- lapply(lapply(lapply(env7, t), "-", d), t)
    env7 <- lapply(env7, function(i) as.data.frame(i))}
    )
    
    ## install.packages("compare")
    library("compare")
    identical(env1, env2)
    identical(env1, env3)
    identical(env1, env4)
    identical(env1, env5)
    identical(env1, env6)
    identical(env1, env7)
    

    如您所见,所有的行都产生相同的对象。

    执行“microbenchmark”函数5次后,上面代码中##7的解决方案更快,虽然##3的解决方案稍微慢了一点。

    我会详细研究您提出的每个解决方案,再次感谢您!

    作为感谢,请欣赏这首我非常喜欢的歌曲! https://www.youtube.com/watch?v=QnguI5OrfZ4

    您好!

    【讨论】:

      【解决方案3】:

      我的PoGibas' answer (+1):

      lapply(lapply(lapply(env, t), "-", d), t)
      

      它做同样的事情:

      • 转置data.frame 对象
      • 使用回收规则减去d
      • 将它们移回原来的位置
      • 它返回 matrix 对象,这不是 OP 想要的。

      我认为随着它更彻底地使用矢量化,它最终会更快一些。但事实并非如此。

      microbenchmark(
        f1 = lapply(env, function(i) t(t(i) - d)),
        f2 = lapply(lapply(lapply(env, t), "-", d), t), times = 1E5L)
      #Unit: microseconds
      # expr     min      lq     mean  median      uq        max neval cld
      #   f1  99.838 103.104 114.8280 104.970 108.702 106230.106 1e+05  a 
      #   f2 103.570 107.303 118.9683 110.102 113.834   7765.414 1e+05   b
      

      【讨论】:

        【解决方案4】:

        1) 扫描 使用sweep 生成数据帧列表:

        lapply(env, sweep, 2, d, "-")
        

        给予:

        [[1]]
               a     b     c
        1 -11.28 15.52 -8.43
        2 -11.27 15.50 -8.33
        3 -11.27 15.50 -8.37
        
        [[2]]
              a    b     c
        1 -6.27 4.22 -8.85
        2 -6.26 4.19 -8.81
        3 -6.28 4.23 -8.86
        

        另请参阅How to divide each row of a matrix by elements of a vector in R,了解与sweep 等价或几乎等价的众多表达式。

        2) 缩放或像这样使用scale;但是,它给出的是数字矩阵列表而不是数据框列表:

        lapply(env, scale, d, FALSE)
        

        给予:

        [[1]]
                  a     b     c
        [1,] -11.28 15.52 -8.43
        [2,] -11.27 15.50 -8.33
        [3,] -11.27 15.50 -8.37
        attr(,"scaled:center")
             a      b      c 
         10.20 -10.91  11.89 
        
        [[2]]
                 a    b     c
        [1,] -6.27 4.22 -8.85
        [2,] -6.26 4.19 -8.81
        [3,] -6.28 4.23 -8.86
        attr(,"scaled:center")
             a      b      c 
         10.20 -10.91  11.89 
        

        【讨论】:

          【解决方案5】:

          这是一个使用lapply 的hack'ey 解决方案

          nrows <- 3
          lapply(env, function(x) x - matrix(rep(d, nrows), nrow = nrows, byrow = TRUE))
          

          【讨论】:

          • ps。 @PoGibas 解决方案要快得多。
          • 但是该解决方案没有给出问题中的代码产生的数据框列表。相反,它给出了一个矩阵列表。此处的解决方案确实提供了数据框列表,如问题所示。
          【解决方案6】:

          您可以在这里简化两件事:循环遍历列表元素并分别减去d 中的每个值。

          要替换 for 循环,您可以使用 lapply(“l”,因为我们正在遍历列表)。

          # Run function for every element i in list env
          lapply(env, function(i))
          

          为了简化减法,您可以:

          1. 转置数据帧t(i)
          2. 执行减法t(i) - d
          3. 转回t(t(i) - d)

          所以最终的代码是:

          lapply(env, function(i) t(t(i) - d))
          

          【讨论】:

          • 假设另一种写法是使用scalelapply(env, scale, center=d, scale=FALSE)(尽管这两个都返回矩阵而不是操作中的 data.frames)
          猜你喜欢
          • 1970-01-01
          • 2017-04-08
          • 1970-01-01
          • 1970-01-01
          • 2015-08-08
          • 2011-10-01
          • 2021-11-09
          • 1970-01-01
          • 2020-05-07
          相关资源
          最近更新 更多