【问题标题】:Do I need to reduce for-loop in R and how?我是否需要减少 R 中的 for 循环以及如何减少?
【发布时间】:2021-06-12 11:58:33
【问题描述】:

我是 R 编码的大一新生,但我听说 R 中的循环比 Python 或 C 等其他语言慢得多。那么在 R 中编码时我需要减少循环吗?

具体来说,在这个模拟代码中,我该如何提高我糟糕的编码能力?

library(moments)
n <- c(5:20)
m <- c(1:10000)
skew <- c()
kurt <- c()
for(num in n){
  beta1 <- c()
  beta2 <- c()
  for(i in m){
    set.seed(num * 10000 + i)
    x <- rnorm(num, mean = 0, sd = 1)
    beta1 <- c(beta1, skewness(x))
    beta2 <- c(beta2, kurtosis(x) - 3)
  }
  skew <- c(skew, quantile(beta1, probs = c(0, 0.01, 0.1, 0.2, 0.5, 0.8, 0.9, 0.99, 1)))
  kurt <- c(kurt, quantile(beta2, probs = c(0, 0.01, 0.1, 0.2, 0.5, 0.8, 0.9, 0.99, 1)))
}

【问题讨论】:

  • 在每个样本量中,您想要一个偏斜和库尔特分位数列表,即 16 个这样的列表?
  • For 循环在 R 中还不错,但它们可能会被误用。在循环中“增长”一个对象是一个坏习惯,并且会减慢你的代码。你在你的内部循环中增长 beta1beta2 - 它们每次迭代都会变长。分配一个适当长度的空向量,然后填充孔,效率更高。免费的R Inferno 是关于这个主题的优秀读物。

标签: r for-loop statistics simulation montecarlo


【解决方案1】:

在 R 中不使用for 循环的一个主要优点是利用了它的向量化。因此,虽然在 Python 或 C 等语言中,您可以为向量的每个元素编写向量计算,但在 R 中,您可以方便地一次对整个向量的计算进行编码(参见下面的编辑),还可以通过实际使用快速的底层 C 来减少计算时间, Fortran等函数。

我会将您想要对单个样本大小进行的所有计算放入函数 statFUN 中,并将其放入 lapply 以循环遍历样本大小向量 n

对于分位数,我们可以使用applymatrixStats::rowQuantiles,我建议这样做,因为它更快。

set.seed() 在运行lapply 之前应该只需要一次,所有results 都可以用那一个种子重现。

n <- 5:20  ## different sample sizes
m <- 1e4   ## number of replications in each iteration
probs <- c(0, 0.01, 0.1, 0.2, 0.5, 0.8, 0.9, 0.99, 1)

library(moments)
library(matrixStats)

statFUN <- function(i, num) {
  r <- replicate(i, {
    x <- rnorm(num, mean=0, sd=1)
    c(kurt=kurtosis(x) - 3, skew=skewness(x))
  })
  # t(apply(r, 1, quantile, probs=probs))  ## using base R
  rowQuantiles(r, probs=probs)  ## using matrixStats
}

set.seed(42)
res <- lapply(n, statFUN, m)

结果

result 是每个样本大小的峰度分位数和偏度分位数的列表。

res
# [[1]]
#               0%          1%         10%         20%
# kurt -0.04710729 -0.04658709 -0.04190536 -0.03670343
# skew -0.03045563 -0.02969417 -0.02284104 -0.01522645
#              50%          80%           90%         99%
# kurt -0.03388803 -0.006250622  1.068998e-03 0.007656657
# skew -0.01028591 -0.006132523 -5.883157e-05 0.005407491
#             100%
# kurt 0.008388619
# skew 0.006014860
# 
# [[2]]
#               0%          1%         10%         20%
# kurt -0.09089922 -0.08859363 -0.06784329 -0.04478737
# skew -0.03252828 -0.03165837 -0.02382918 -0.01513009
#               50%          80%        90%        99%
# kurt -0.023634727 -0.005277533 0.01038904 0.02448896
# skew  0.003433589  0.017711708 0.01947178 0.02105585
#            100%
# kurt 0.02605562
# skew 0.02123186
#
# [...]

在哪里

length(res)
# [1] 16

编辑

这里有一个小例子来更好地说明 R 中向量化的实际含义。虽然在大多数编程语言中,两个向量的相加是按元素编码的,但在 R 中,向量的相加可以直接编码(即在向量化的方式)。

a <- 1:9
b <- rev(a)

## element wise addition of vectors a and b
s1 <- c()
for (i in seq(a)) {
  s1[i] <- a[i] + b[i]
}
s1
# [1] 10 10 10 10 10 10 10 10 10

## direct addition of vectors a and b (i.e. vectorized)
s2 <- a + b
s2
# [1] 10 10 10 10 10 10 10 10 10

我们可以查看*apply 系列,而不是for 循环。但是,大多数情况下仍然隐藏着 for 循环。 (要查看功能代码类型,例如 lapply 不带括号或其他任何内容。)

您可能想阅读例如那些很棒的问答:

注意:向量化其实只是R的语言特性。所谓的“向量化函数”内部经常使用C、Fortran等代码,在其中你仍然可以找到for循环结尾,但是用一种更快的语言。例如,当我们使用 sum() 时,会调用 source code of summary.c

【讨论】:

  • 非常感谢,但是我怎样才能学会使用矢量化编码呢?我需要先思考还是直接写矢量化代码?
  • @JamesYu 我已经改写了我的答案,以便更清楚,并添加了一个关于矢量化的小例子。这会有帮助吗?
  • 你分享的链接对我帮助很大!
  • @JamesYu 很高兴知道,玩得开心!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 2011-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多