分配 n 个 NumericMatrix 的 Rcpp 列表

Posted

技术标签:

【中文标题】分配 n 个 NumericMatrix 的 Rcpp 列表【英文标题】:Allocate Rcpp List of n NumericMatrix 【发布时间】:2020-01-13 18:05:35 【问题描述】:

有没有办法分配一个长度为 n 的 Rcpp List,其中 List 的每个元素都将填充一个 NumericMatrix,但每个 NumericMatrix 的大小可以改变?

我有一个使用 std::list 和 push_back() 的想法,但是列表的大小可能非常大,我想避免在从函数。

下面的 R 代码给出了我希望做什么的想法:

myvec = function(n) 
  x = vector("list", n)
  for (i in seq_len(n)) 
    nc = sample(1:3, 1)
    nr = sample(1:3, 1)
    x[[i]] = matrix(rbinom(nc * nr, size = 1, prob = 0.5),
                    nrow = nr, ncol = nc)
  
  x

这可能会导致:

> myvec(2)
[[1]]
     [,1]
[1,]    0
[2,]    1

[[2]]
     [,1] [,2] [,3]
[1,]    0    1    0
[2,]    0    1    1

更新:基于@Dirk 和@Ralf 的cmets,我创建了基于Rcpp::List 和std::list 的函数,最后有一个换行符。速度比较似乎并不偏爱一个版本,但也许我没有意识到效率低下。

src = '
#include <Rcpp.h>
// [[Rcpp::export]]
Rcpp::List myvec(int n) 
  Rcpp::RNGScope rngScope;
  Rcpp::List x(n);
  // Rcpp::IntegerVector choices = 1, 2 ,3;
  Rcpp::IntegerVector choices = Rcpp::seq_len(50);
  for (int i = 0; i < n; ++i) 
    int nc = Rcpp::sample(choices, 1).at(0);
    int nr = Rcpp::sample(choices, 1).at(0);
    Rcpp::NumericVector entries = Rcpp::rbinom(nc * nr, 1, 0.5);
    x(i) = Rcpp::NumericMatrix(nc, nr, entries.begin());
  
  return x;


// [[Rcpp::export]]
Rcpp::List myvec2(int n) 
  Rcpp::RNGScope scope;
  std::list< Rcpp::NumericMatrix > x;
  // Rcpp::IntegerVector choices = 1, 2 ,3;
  Rcpp::IntegerVector choices = Rcpp::seq_len(50);
  for (int i = 0; i < n; ++i) 
    int nc = Rcpp::sample(choices, 1).at(0);
    int nr = Rcpp::sample(choices, 1).at(0);
    Rcpp::NumericVector entries = Rcpp::rbinom(nc * nr, 1, 0.5);
    x.push_back( Rcpp::NumericMatrix(nc, nr, entries.begin()));
  
  return Rcpp::wrap(x);

'
sourceCpp(code = src)

在我的计算机上产生的基准是:

> library(microbenchmark)
> rcpp_list = function() 
+   set.seed(10);myvec(105)
+ 
> std_list = function() 
+   set.seed(10);myvec2(105)
+ 
> microbenchmark(rcpp_list(), std_list(), times = 1000)
Unit: milliseconds
        expr    min      lq     mean  median      uq
 rcpp_list() 1.8901 1.92535 2.205286 1.96640 2.22380
  std_list() 1.9164 1.95570 2.224941 2.00555 2.32315
    max neval cld
 7.1569  1000   a
 7.1194  1000   a

【问题讨论】:

【参考方案1】:

Rcpp 对象是 R 对象的基本问题支配了我的 R 的内存管理,其中调整大小是昂贵的:完整副本。

因此,当我有与您类似的任务,但大小可能会改变或未知时,我经常使用不同的数据结构——STL 为我们提供了很多——并且只在return 步骤转换为 R(cpp)在末尾。

这里的细节是魔鬼(一如既往)。简介,实验,...

编辑:从狭义上讲,“我们可以返回具有不同大小的 NumericMatrix 对象列表吗”,答案是当然可以,因为这就是 List对象做。您也可以插入其他类型。

【讨论】:

德克,感谢您的及时回复。当您说“仅在最后的 return 步骤转换为 R(cpp)”时,我假设您的意思是使用 Rcpp::wrap 函数?【参考方案2】:

正如 Dirk 所说,当然可以创建一个包含不同大小矩阵的列表。为了让它更具体一点,这里是你的 R 函数的翻译:

#include <Rcpp.h>
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::export]]
Rcpp::List myvec(int n) 
    Rcpp::List x(n);
    Rcpp::IntegerVector choices = 1, 2 ,3;
    for (int i = 0; i < n; ++i) 
        int nc = Rcpp::sample(choices, 1).at(0);
        int nr = Rcpp::sample(choices, 1).at(0);
        Rcpp::NumericVector entries = Rcpp::rbinom(nc * nr, 1, 0.5);
        x(i) = Rcpp::NumericMatrix(nc, nr, entries.begin());
    
    return x;


/***R
myvec(2)
*/

与 R 代码的主要区别在于显式命名的向量 choicesentries,它们仅隐含在 R 代码中。

【讨论】:

感谢 Ralf,您在代码中包含 \\[[Rcpp::plugins(cpp11)]] 是否有特定原因?它似乎在没有它的情况下运行,但也许有一个我不知道的好处。 @dr_jfrench choices的初始化使用C++11。但是,通常不需要这样做,因为现在许多系统默认使用 C++11。 这是有道理的。我什至没有注意到这是指定向量的另一种方法。这比 IntegerVector choices = IntegerVector::create(1, 2, 3); 之类的标准(?) Rcpp 方法更简单 @dr_jfrench,看到这篇文章***.com/a/2236233/4408538。它被称为列表初始化。这里有更多信息en.cppreference.com/w/cpp/language/list_initialization

以上是关于分配 n 个 NumericMatrix 的 Rcpp 列表的主要内容,如果未能解决你的问题,请参考以下文章

如何将 std::vector<std::vector<double>> 转换为 Rcpp::Dataframe 或 Rcpp::NumericMatrix

将字符缓冲区移植到 Rcpp

rank 和 unrank 组合将 k 个球分配到 n 个不同容量的 bin 中

员工分配工作(深度优先)

将M个客服随机分配给N个客户

网络流24题分配问题