【问题标题】:How to rewrite sapply as a for loop如何将 sapply 重写为 for 循环
【发布时间】:2012-07-23 03:01:42
【问题描述】:

我想用 for 循环替换 sapply 的行为。 (如果有兴趣,请进一步了解原因。)

这是我的代码的 sapply 版本的简化:

slow_function=function(n) c(n*n, 0, (-n)^n, -1, +1)
quick_analysis=function(res) res[1]+res[3]

results=sapply(1:8,function(n){
  res=slow_function(n)
  a=quick_analysis(res)
  b=table(sign(res))
  list(
    a=a,
    up=b['1'],
    down=b['-1'],
    level=b['0']
    )
  })

这给了我:

      [,1] [,2] [,3] [,4] [,5]  [,6]  [,7]    [,8]    
a     0    8    -18  272  -3100 46692 -823494 16777280
up    2    3    2    3    2     3     2       3       
down  2    1    2    1    2     1     2       1       
level 1    1    1    1    1     1     1       1  

(很好,虽然我实际上希望它转置,行的值为 n,列的值为 a, up, down, level。但是,没问题,我知道该怎么做。)

当我把它变成一个for循环时:

results=vector()
for(n in 1:8){
  res=slow_function(n)
  a=quick_analysis(res)
  b=table(sign(res))
  results[n]=list(
    a=a,
    up=b['1'],
    down=b['-1'],
    level=b['0']
    )
  }

然后我收到 8 条警告消息,例如:

1: In results[n] = list(a = a, up = b["1"], down = b["-1"], level = b["0"]) :
  number of items to replace is not a multiple of replacement length

而结果却大不相同:

[[1]]
[1] 0

[[2]]
[1] 8

[[3]]
[1] -18

[[4]]
[1] 272

[[5]]
[1] -3100

[[6]]
[1] 46692

[[7]]
[1] -823494

[[8]]
[1] 16777280

我有点明白发生了什么。我不知道的是获得我想要的结果的魔法咒语!我尝试将结果初始化为 matrix()list(),但输出相同。

ASIDE:为什么要使用 for 循环?我实际上想在 sapply 循环的每次传递中进行两次计算。换句话说,只对slow_function 进行 8 次调用,但返回 16 行结果。如果 sapply 允许,我的代码将类似于:

results=sapply(1:8,function(n){
  res=slow_function(n)
  a=quick_analysis(res)
  b=table(sign(res))
  list(
    a=a,
    up=b['1'],
    down=b['-1'],
    level=b['0']
    )
  res=-res  #Modify res
  a=quick_analysis(res)
  b=table(sign(res))
  list(
    a=a,
    up=b['1'],
    down=b['-1'],
    level=b['0']
    )
  })

所需的输出:(抱歉格式已关闭,我必须手动制作)

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12]  [,13]   [,14] [,15]   [,16]   
a     0     0    8     -8   -18  18    272  -272 -3100 3100  46692  -46692 -823494 823494 16777280 -16777280
up    2     2    3     1    2    2     3     1    2     2     3      1      2       2      3        1
down  2     2    1     3    2    2     1     3    2     2     1      3      2       2      1        3
level 1     1    1     1    1    1     1     1    1     1     1      1      1       1      1        1  

【问题讨论】:

  • 我不完全按照你在做什么,但你想要的可以通过 apply 家庭来完成。如果您提供了一些数据和预期结果,我认为您会得到比说我想使用 for 循环执行此操作更好的答案。
  • 永远记住列表中[[[ 之间的区别。你想要后者。但我同意 Tyler 的观点,我怀疑这是否真的是你想要的方向。
  • @TylerRinker,joran 感谢您的回复。我的问题包含重现所有内容的所有数据(尽管我承认它现在看起来相当抽象)。 ASIDE 部分显示了我想要做的事情;我刚刚更新了我的问题以显示预期的输出(即 8 次 sapply 循环的 16 个输出)。
  • @joran 看过巴蒂斯特的回答后,我现在明白你指的是什么了。谢谢!

标签: r for-loop sapply


【解决方案1】:

这是一个关于如何在没有数据的情况下完成此任务并且知道您的目标是什么:

slow_function=function(n) c(n*n, 0, (-n)^n, -1, +1)
quick_analysis=function(res) res[1]+res[3]

results=lapply(1:8,function(i){
  res=slow_function(i)
  FUN <- function(res.in) {
      a=quick_analysis(res.in)
      b=table(sign(res.in))
      data.frame(
       a=a,
       up=b['1'],
       down=b['-1'],
       level=b['0']
       )
  }
  data.frame(id=c("p", "n"), it=i, rbind(FUN(res), FUN(-res)))
})

DAT <- do.call(rbind, results)
with(DAT, DAT[order(id, it), ]) #maybe the order you're after

编辑:

这会给你你想要的东西(索引和 id 从来没有必要我这样做是因为我不知道你想要的输出;你可以删除你认为合适的主题):

rownames(DAT) <- NULL
t(DAT[, -c(1:2)])

【讨论】:

  • 谢谢泰勒。那个 DAT 是我所追求的输出。重新编辑,实际上我要去的地方是rownames(DAT)=paste(DAT$id,DAT$it,sep='')
  • 但我仍然不明白如何使用 for 循环来做到这一点:如何在 for 循环中建立一个向量/输出列表,就像 sapply/lapply 一样?
【解决方案2】:

sapply 默认情况下简化结果,当它们都具有相同的长度时。因此,您需要手动组合 for 循环返回的各种子列表,

results2 = list()
for (n in 1:8){
  res=slow_function(n)
  a=quick_analysis(res)
  b=table(sign(res))
  results2[[n]] = list(
    a=a,
    up=b['1'],
    down=b['-1'],
    level=b['0']
  )

}

do.call(cbind, results2)

【讨论】:

  • 谢谢!我已将此标记为正确答案,因为它回答了我的实际问题。但是在我有空的时候,我无法弄清楚如何将生成的对象转换为 data.frame,而且我发现 data.frame 更容易使用;所以在我的真实世界代码中,我使用的是更接近 Tyler 的答案。
猜你喜欢
  • 2020-05-03
  • 2014-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-27
  • 1970-01-01
  • 1970-01-01
  • 2013-07-24
相关资源
最近更新 更多