如何快速创建具有重复元素的大型向量?

How to create large vectors with repeated elements fast?

提问人:matehorvath 提问时间:10/29/2023 最后编辑:jay.sfmatehorvath 更新时间:11/2/2023 访问量:69

问:

有一个向量,我想通过根据序列获取其元素来制作一个新向量:

set.seed(0)

n <- 1000
ncval1 <- as.integer(n)
ncval2 <- ncval1:1L
ncval3 <- sequence(ncval2, from = 1L, by = 1L)
x <- as.double(runif(n))

y <- x[ncval3]

这大约需要 2.2 毫秒。也许可以通过采用重复元素的属性来加快速度。

R 矢量 分析 RCPP

评论

0赞 Mark 10/29/2023
你问的是 1 吗?更快地创建向量, 2.创建任何具有重复元素的向量,速度快于 2.2 毫秒,或其他东西
0赞 matehorvath 10/29/2023
问题是关于通过该序列从“x”中创建一个新的向量“y”,并且原始向量“x”的长度是自定义的。因此,目标将是一个广义的 slution。

答:

4赞 jay.sf 10/29/2023 #1

您可以使用 .Rcpp

Rcpp::sourceCpp(code='
  #include <Rcpp.h>
  // [[Rcpp::export]]
  Rcpp::NumericVector foo(int n) {
    // draw from standard normal
    Rcpp::NumericVector r(n);
    r = Rcpp::runif(n);
    // length of result
    int l = 0;
    for (int i = 0; i <= n; i++) {
      l = l + i;
    }
    // subset and concatenate
    Rcpp::NumericVector a(l);
    int p = 0;
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n - i; j++) {
        a[p] = r[j];
        p = p + 1;
      }
    }
    return a;
  }
')

n = 10 的用法

> set.seed(0)
> foo(10)
 [1] 0.8966972 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 0.8983897
 [8] 0.9446753 0.6607978 0.6291140 0.8966972 0.2655087 0.3721239 0.5728534
[15] 0.9082078 0.2016819 0.8983897 0.9446753 0.6607978 0.8966972 0.2655087
[22] 0.3721239 0.5728534 0.9082078 0.2016819 0.8983897 0.9446753 0.8966972
[29] 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 0.8983897 0.8966972
[36] 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 0.8966972 0.2655087
[43] 0.3721239 0.5728534 0.9082078 0.8966972 0.2655087 0.3721239 0.5728534
[50] 0.8966972 0.2655087 0.3721239 0.8966972 0.2655087 0.8966972

基准

n <- 1e3
microbenchmark::microbenchmark(
  OP={
    set.seed(0)
    ncval1 <- as.integer(n)
    ncval2 <- ncval1:1L
    ncval3 <- sequence(ncval2, from = 1L, by = 1L)
    x <- as.double(runif(n))
    x[ncval3]
  },
  foo={set.seed(0); foo(n)}, 
  check='identical'
)

$ Rscript --vanilla foo.R
Unit: milliseconds
 expr      min       lq     mean   median       uq      max neval cld
   OP 2.109090 2.199845 3.119882 2.294714 4.213308 7.297789   100  a 
  foo 1.055756 1.190470 1.983916 1.318557 2.741124 6.850159   100   b

根据中位数,只需要 57% 的时间。foo()

更新

对于给定的向量,这简化为:x

Rcpp::sourceCpp(code='
  #include <Rcpp.h>
  // [[Rcpp::export]]
  Rcpp::NumericVector foo2(Rcpp::NumericVector x) {
    // length of vector
    int n = x.size();
    // length of result
    int l = 0;
    for (int i = 0; i <= n; i++) {
      l = l + i;
    }
    // subset and concatenate
    Rcpp::NumericVector a(l);
    int p = 0;
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n - i; j++) {
        a[p] = x[j];
        p = p + 1;
      }
    }
    return a;
  }
')

用法

> set.seed(0)
> x <- runif(10)
> foo2(x)
 [1] 0.8966972 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 0.8983897
 [8] 0.9446753 0.6607978 0.6291140 0.8966972 0.2655087 0.3721239 0.5728534
[15] 0.9082078 0.2016819 0.8983897 0.9446753 0.6607978 0.8966972 0.2655087
[22] 0.3721239 0.5728534 0.9082078 0.2016819 0.8983897 0.9446753 0.8966972
[29] 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 0.8983897 0.8966972
[36] 0.2655087 0.3721239 0.5728534 0.9082078 0.2016819 0.8966972 0.2655087
[43] 0.3721239 0.5728534 0.9082078 0.8966972 0.2655087 0.3721239 0.5728534
[50] 0.8966972 0.2655087 0.3721239 0.8966972 0.2655087 0.8966972

评论

0赞 matehorvath 11/2/2023
谢谢你的回答。如何修改此 Rccp 代码,以便它可以与给定给函数的自定义向量一起使用?比如:foo(x, n)。
0赞 jay.sf 11/2/2023
@matehorvath 请参阅更新的答案。
0赞 matehorvath 11/3/2023
效果很好,谢谢。此外,在使用 Rccp 时,它是 for 循环的最快方法还是可以进一步优化?
0赞 jay.sf 11/3/2023
@matehorvath 我不相信,C++是低级的,你需要这些循环。即使 Rcpp 有一些句法糖,也可能不会有显着的速度改进。