使用非连续向量作为循环的输入

3

我在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值的向量中选择正确的数值,但我更希望在运行循环后直接得到一个合适的向量。
谢谢!
4个回答

7
请记住,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)))

然后认识到如果在sapply函数调用中给rnorm命名第一个参数,那么rnorm可以替代匿名函数function(n) ...rnorm的第一个参数被命名为n,因此有些混淆;但是我们强制让ns的元素匹配第二个参数mean。例如,在第一次sapply中,我们会评估rnorm(ns[[1]], n=1)。R首先通过名称匹配参数,因此n=1rnorm的第一个参数匹配,然后通过剩余参数的位置进行匹配,因此未命名的参数ns[[1]]匹配下一个可用的参数mean

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

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

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

7

@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生成一系列1、2、3……的数字,其长度与ns相同,
  • 使用n作为索引来选择正确的值,而不是直接使用n的值。
在这个示例中,调用rnorm() lenght(ns)次是浪费的,但有些情况下做这样的事情确实是有意义的,并且通过循环变量进行索引而不是使用循环变量本身是一个方便的方法。

2

有几种方法可以即时创建向量。以下是几个选项:

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。

1
你提到避免在R中使用循环,但仅仅建议使用sapply()并不够具体。你的两个例子在R中都形成了循环,只是恰好sapply()在编译代码中处理了更多的循环。但是,在循环体占主导计算时间的情况下,sapply()使用编译循环代码的优势基本可以忽略不计,并且这两种解决方案的执行时间非常相似。R中对循环的厌恶常常源于S-Plus时代或者编写糟糕的R代码。 - Gavin Simpson
@GavinSimpson - 感谢您的澄清。我认为在许多情况下执行时间可能会相似。我认为sapply()的主要优点是它促进了更好的编程风格。 - Victor K.
我认为,实际上对编写for循环的厌恶常常会导致人们编写复杂的*apply代码/函数,而使用for()循环会更自然、更容易理解,也更省力。 - Gavin Simpson

1

编程时遇到了类似的问题,所以我想提供一个更清晰的循环版本。个人建议使用以下结构:

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 ##

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接