【问题标题】:Using non-sequential vector as input for a loop使用非序列向量作为循环的输入
【发布时间】:2013-01-29 21:12:18
【问题描述】:

我在使用 R 中的循环函数时遇到了一些问题,在这个网站上找不到这个问题的答案。我想使用数字向量作为 R 中循环的输入。

例如:

ns <- c(10, 20, 40, 80, 160)

for (n in ns) {
 ni[n] <- round(rnorm(1, mean = n, sd = 1))
}

这段代码的结果是一个具有 155 倍 NA 的向量,该向量中有五个正确值。但是,我想摆脱所有这些 NA 并获得一个只有五个正确值的向量。我知道如何从 155 NA 的向量中选择正确的值,但我更喜欢在运行循环后直接获取正确的向量。

提前谢谢你!

【问题讨论】:

    标签: r loops vector


    【解决方案1】:

    请记住,R 中的许多函数都是矢量化的

    > rnorm(length(ns), mean=ns)
    [1]   9.905652  19.721717  40.462751  78.982971 160.770257
    

    (在您的问题中,ni[n] 创建的向量与 n 的最大值一样长,即 160 个元素)。

    有趣的是,这是从@VictorK 提供的sapply 解决方案演变而来的。

    sapply(ns, function(n) round(rnorm(1, mean = n, sd = 1)))
    

    分解出round 并删除默认参数sd = 1,所以

    round(sapply(ns, function(n) rnorm(1, mean = n)))
    

    然后认识到rnorm 可以替换匿名函数function(n) ...,如果我们将其命名为sapply 调用中的第一个参数。 rnorm 的第一个参数命名为n,所以事情有点混乱;但我们强制ns 的元素匹配第二个参数mean。例如,我们第一次通过 sapply 评估 rnorm(ns[[1]], n=1)。 R 首先按名称匹配参数,因此 n=1 匹配 rnorm 的第一个参数,然后根据其余参数中的位置匹配,因此未命名参数 ns[[1]] 匹配下一个可用参数 mean)

    round(sapply(ns, rnorm, n = 1))
    

    然后也许我们会看到完全矢量化的解决方案

    round(rnorm(n = length(ns), mean = ns))
    

    【讨论】:

      【解决方案2】:

      @Martin Morgan 已针对您给出的特定示例向您展示了如何正确执行此操作。但是,让我们假设您想要使用未矢量化的函数,或者您想要按照实际示例的方式执行其他操作。

      这样做的一种方法是迭代ns 的元素的索引,而不是元素本身。考虑

      ns <- c(10, 20, 40, 80, 160)
      ni <- numeric(length = length(ns)) ## pre-allocate storage
      
      for (n in seq_along(ns)) {
        ni[n] <- round(rnorm(1, mean = ns[n], sd = 1))
      }
      
      > ni
      [1]  12  21  40  80 160
      

      主要区别是

      • 使用seq_along() 让R 生成一个与ns 一样长的1、2、3、...序列,并且
      • 使用n 作为ns 的索引来选择正确的值,而不是使用n 本身的值。

      对于这个例子,调用rnorm()lenght(ns) 次是浪费,但在某些情况下,这样做确实有意义,并且通过循环变量而不是使用循环变量本身进行索引是一种方便的方法。

      【讨论】:

        【解决方案3】:

        有几种方法可以动态创建矢量。这里有几个选项:

        1) 使用循环(但请参阅下一个解决方案,因为您应该尽量避免 R 中的循环):

        ns <- c(10, 20, 40, 80, 160)
        
        ni <- numeric(length(ns)) # pre-allocate the resulting vector
        for (i in 1:length(ns)) {
          ni[i] <- round(rnorm(1, mean = ns[i], sd = 1))
        }
        

        2) 使用 apply 系列函数:

        sapply(ns, function(n) round(rnorm(1, mean = n, sd = 1)))
        

        第二个是惯用的R。

        【讨论】:

        • 您指出要避免 R 中的循环,但建议 sapply() 更好是不够具体的。您的两个示例都在 R 中形成循环,恰好sapply()for 相比,在编译代码中执行了更多的循环,但是循环的主体是主要的计算时间量,即@987654326 @ 使用已编译的循环代码在很大程度上无关紧要,两种解决方案的执行时间都非常相似。对 R 中的循环的厌恶通常是 S-Plus 时代遗留下来的结果,或者是由于编写了糟糕的 R 代码。
        • @GavinSimpson - 感谢您的澄清。我同意在许多情况下执行时间可能相似。我认为sapply() 的主要优势在于它促进了更好的编码风格。
        • 我实际上认为,如果for() 循环会更自然、更容易理解且更省力,人们通常会编写复杂的*apply 代码/函数,而不是写for 循环。
        【解决方案4】:

        在编码时偶然发现了一个类似的问题..所以我想我会给我的版本一个更干净的循环。我个人会使用以下构造:

        ns <- c(10, 20, 40, 80, 160)
        ni <- numeric(length = length(ns))  # keeping this as per Victor's earlier post#
        
        y=0  #adding starting counter#
         for (i in ns) {  # ns is already defined with its numeric sequence  on line 1 of the  
                          # code #
        
           y=y+1 # counter used in the ni vector
        
          ni[[y]] <- meanX(i)  # preferable to create a custom function but not mandatory
         }
        
        ni  # will produce only the 5 outputs of the rnorm function
            # 11  21  41  81 161
        
        
        ## Custom function  ##
        meanX <-function(meanX) {round(rnorm(1, mean = meanX, sd = 1))
        
          return(round(rnorm(1, mean = meanX, sd = 1)))
        
          }
        ##  end of Custom function ##
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-08-18
          • 2016-02-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多