Rcpp - 从矩阵/数据帧列表中提取行

时间:2016-03-14 19:23:10

标签: c++ r rcpp

作为this question的后续行动,我决定沿着Rcpp的路线走下R中的复杂语法。我认为这将提供更好的可读性(并且可能也更快)。

假设我有一个data.frame列表(我可以通过as轻松转换为矩阵)。鉴于之前的answe -r -s,这似乎是最佳方法。

# input data
my_list <- vector("list", length= 10)
set.seed(65L)
for (i in 1:10) {
  my_list[[i]] <- data.frame(matrix(rnorm(10000),ncol=10))
  # alternatively 
  # my_list[[i]] <- matrix(rnorm(10000),ncol=10)
}

从矩阵中提取行的适当方法是什么?目标是创建一个列表,每个列表元素包含每个原始列表data.frames的nr行的列表。我尝试了几种不同的语法并不断出错:

#include <Rcpp.h>
using namespace Rcpp;
using namespace std:

List foo(const List& my_list, const int& n_geo) {
  int n_list = my_list.size();
  std::vector<std::vector<double> > list2(n_geo);

  // needed code....

  return wrap(list2);
}

选项

for (int i = 0; i < n_list; i++) {
  for (int nr = 0; nr < n_geo; nr++) {
    list2[nr][i] = my_list[i].row(nr);
    // or list2[nr].push_back(my_list[i].row(nr));
    // or list2[nr].push_back(as<double>(my_list[i].row(nr)));
    // or list2[nr].push_back(as<double>(my_list[i](nr, _)));
  }
}

// or:
NumericMatrix a = my_list[1] 
... 
NumericMatrix j = my_list[10]

for (int nr = 0; nr < n_geo; nr++) {
  list2[nr][1] = // as above
}

这些都不适合我。我究竟做错了什么?以下是我从上面的语法选择中收到的错误。

  

错误:没有匹配函数来调用'as(Rcpp :: Matrix&lt; 14&gt; :: Row)'

  

错误:在分配中无法将'Rcpp :: Matrix&lt; 14&gt; :: Row {aka Rcpp :: MatrixRow&lt; 14&gt;}'转换为'double'

1 个答案:

答案 0 :(得分:3)

这是一种方法:

#include <Rcpp.h>

// x[[nx]][ny,]  ->  y[[ny]][[nx]]

// [[Rcpp::export]]
Rcpp::List Transform(Rcpp::List x) {
    R_xlen_t nx = x.size(), ny = Rcpp::as<Rcpp::NumericMatrix>(x[0]).nrow();
    Rcpp::List y(ny);

    for (R_xlen_t iy = 0; iy < ny; iy++) {
        Rcpp::List tmp(nx);
        for (R_xlen_t ix = 0; ix < nx; ix++) {
            Rcpp::NumericMatrix mtmp = Rcpp::as<Rcpp::NumericMatrix>(x[ix]);
            tmp[ix] = mtmp.row(iy);
        }
        y[iy] = tmp;
    }

    return y;
}

/*** R

L1 <- lapply(1:10, function(x) {
    matrix(rnorm(20), ncol = 5)
})

L2 <- lapply(1:nrow(L1[[1]]), function(x) {
    lapply(L1, function(y) unlist(y[x,]))
})

all.equal(L2, Transform(L1))
#[1] TRUE

microbenchmark::microbenchmark(
    "R" = lapply(1:nrow(L1[[1]]), function(x) {
        lapply(L1, function(y) unlist(y[x,]))
    }),
    "Cpp" = Transform(L1),
    times = 200L)

#Unit: microseconds
#expr    min      lq      mean  median       uq      max neval
#  R 254.660 316.627 383.92739 347.547 392.7705 1909.097   200
#Cpp  18.314  26.007  71.58795  30.230  38.8650  945.167   200

*/

我不确定这会如何扩展;我认为这只是一种固有的低效转型。根据我在源代码顶部的注释,看起来你只是在做一种坐标交换 - 输入列表的ny元素的nx行成为{{输出列表的nx元素的第1个元素:

ny

为了解决您遇到的错误,x[[nx]][ny,] -> y[[ny]][[nx]] 是一个通用对象 - 技术上是Rcpp::List - 所以当您尝试执行此操作时,例如

Rcpp::Vector<VECSXP>

编译器不知道my_list[i].row(nr) my_list[i]。因此,您必须使用NumericMatrix

进行显式转换
Rcpp::as<>

我只是在示例数据中使用了Rcpp::NumericMatrix mtmp = Rcpp::as<Rcpp::NumericMatrix>(x[ix]); tmp[ix] = mtmp.row(iy); 元素来简化操作。在实践中,你可能最好直接在R中强制matrixdata.frame个对象,而不是试图在C ++中这样做;它会更加简单,而且最有可能的是,强制只是调用底层的C代码,所以除了尝试这样做之外没有什么可以获得的。

我还应该指出,如果您使用matrix同类型,则可以使用Rcpp::List来提高性能。这样您就可以跳过上面完成的Rcpp::ListOf<type>次转化:

Rcpp::as<type>