【问题标题】:Rcpp function to select (and to return) a sub-dataframe用于选择(并返回)子数据帧的 Rcpp 函数
【发布时间】:2015-11-12 07:49:40
【问题描述】:

是否可以编写一个 C++ 函数,获取一个 R 数据帧作为输入,然后修改数据帧(在我们的例子中取一个子集)并返回新的数据帧(在这个问题中,返回一个子数据帧)? 我下面的代码可能会让我的问题更清楚:

代码

# Suppose I have the data frame below created in R:
myDF = data.frame(id = rep(c(1,2), each = 5), alph = letters[1:10], mess = rnorm(10))

# Suppose I want to write a C++ function that gets id as inout and returns 
# a sub-dataframe corresponding to that id (**If it's possible to return 
# DataFrame in C++**)

# Auxiliary function --> helps get a sub vector:
arma::vec myVecSubset(arma::vec vecMain, arma::vec IDVec, int ID){
  arma::uvec AuxVec = find(IDVec == ID);
  arma::vec rslt = arma::vec(AuxVec.size());
  for (int i = 0; i < AuxVec.size(); i++){
    rslt[i] = vecMain[AuxVec[i]];
  }
  return rslt;
}

# Here is my C++ function:
Rcpp::DataFrame myVecSubset(Rcpp::DataFrame myDF, int ID){
  arma::vec id = Rcpp::as<arma::vec>(myDF["id"]);
  arma::vec alph = Rcpp::as<arma::vec>(myDF["alpha"]);
  arma::vec mess = Rcpp::as<arma::vec>(myDF["mess"]);

  // here I take a sub-vector:
  arma::vec id_sub = myVecSubset(id, id, int ID);
  arma::vec alph_sub = myVecSubset(alph, id, int ID);
  arma::vec mess_sub = myVecSubset(mess, id, int ID);

  // here is the CHALLENGE: How to combine these vectors into a new data frame???
  ???
}

总结起来,其实主要有两个问题: 1) 有没有更好的方法在 C++ 中获取上面的子数据帧? (希望我能简单地说 myDF[myDF$id == ID,]!!!)

2) 无论如何我可以将 id_sub、alpha_sub 和 mess_sub 组合成一个 R 数据帧并返回它?

非常感谢您的帮助。

【问题讨论】:

    标签: r rcpp


    【解决方案1】:

    这是一个完整的测试文件。它不需要您的提取器功能,只需重新组装子集——但为此它需要最新的 Rcpp,因为目前在 GitHub 上,Kevin 恰好在子集索引上添加了一些工作,这正是我们在这里需要的:

    #include <Rcpp.h>
    
    /*** R
    ##  Suppose I have the data frame below created in R:
    ##  NB: stringsAsFactors set to FALSE
    ##  NB: setting seed as well
    set.seed(42)
    myDF <- data.frame(id = rep(c(1,2), each = 5), 
                       alph = letters[1:10], 
                       mess = rnorm(10), 
                       stringsAsFactor=FALSE)
    */
    
    // [[Rcpp::export]]
    Rcpp::DataFrame extract(Rcpp::DataFrame D, Rcpp::IntegerVector idx) {
    
      Rcpp::IntegerVector     id = D["id"];
      Rcpp::CharacterVector alph = D["alph"];
      Rcpp::NumericVector   mess = D["mess"];
    
      return Rcpp::DataFrame::create(Rcpp::Named("id")    = id[idx],
                                     Rcpp::Named("alpha") = alph[idx],
                                     Rcpp::Named("mess")  = mess[idx]);
    }
    
    /*** R
    extract(myDF, c(2,4,6,8))
    */
    

    使用该文件,我们得到了预期的结果:

    R> library(Rcpp)
    R> sourceCpp("/tmp/sepher.cpp")
    
    R> ##  Suppose I have the data frame below created in R:
    R> ##  NB: stringsAsFactors set to FALSE
    R> ##  NB: setting seed as well
    R> set.seed(42)
    
    R> myDF <- data.frame(id = rep(c(1,2), each = 5), 
    +                    alph = letters[1:10], 
    +                    mess = rnorm(10), 
    +               .... [TRUNCATED] 
    
    R> extract(myDF, c(2,4,6,8))
      id alpha     mess
    1  1     c 0.363128
    2  1     e 0.404268
    3  2     g 1.511522
    4  2     i 2.018424
    R>
    R> packageDescription("Rcpp")$Version   ## unreleased version
    [1] "0.11.1.1"
    R> 
    

    几周前我只需要类似的东西(但不涉及字符向量)并使用带有elem() 函数的犰狳,使用unsigned int 向量作为索引。

    【讨论】:

    • 事实上,子集操作进入了最新的 CRAN 版本:)
    • 我实际上已经检查过了——我在这里发布的文件不会像在 CRAN 上那样使用 0.11.1 构建。
    • 啊,你是对的,有一些调整没有进入最近的版本。我的错——目前,应该使用最新的 GitHub 版本来避免这些问题。
    • 不用担心。我们将达到 0.11.2,这将是一个很好的增强。
    • 感谢@DirkEddelbuettel。很有帮助。
    【解决方案2】:

    要补充 Romain 的答案,您可以尝试通过 Rcpp 调用 [ 运算符。如果我们了解df[x, ] 是如何评估的(即,它实际上是对"[.data.frame"(df, x, R_MissingArg) 的调用,这很容易做到:

    #include <Rcpp.h>
    using namespace Rcpp;
    
    Function subset("[.data.frame");
    
    // [[Rcpp::export]]
    DataFrame subset_test(DataFrame x, IntegerVector y) {
      return subset(x, y, R_MissingArg);
    }
    
    /*** R
    df <- data.frame(x=1:3, y=letters[1:3])
    subset_test(df, c(1L, 2L))
    */
    

    给我

    > df <- data.frame(x=1:3, y=letters[1:3])
    > subset_test(df, c(1L, 2L))
      x y
    1 1 a
    2 2 b
    

    在 Rcpp 中对 R 的回调通常会较慢,但取决于瓶颈的程度,它对您来说仍然足够快。

    但要小心,因为此函数将对整数向量使用基于 1 的子集,而不是基于 0 的子集。

    【讨论】:

    • 感谢@KevinUshey,非常有帮助。是的,当我们在 R 和 C++ 之间来回移动时,索引部分会使事情变得有点复杂。感谢您的帮助。
    【解决方案3】:

    你不需要RcppRcppArmadillo,你可以使用R 的subset 或者dplyr::filter。这可能比您的代码更有效,您的代码必须将数据从数据帧深度复制到犰狳向量中,创建新的犰狳向量,然后将它们复制回 R 向量,以便您可以构建数据帧。这会产生大量浪费。另一个浪费的来源是你find 3 倍于相同的东西

    无论如何,要回答您的问题,只需使用DataFrame::create

    DataFrame::create( _["id"] = id_sub, _["alpha"] = alph_dub, _["mess"] = mess_sub ) ;
    

    另外,请注意,在您的代码中,alpha 将是一个因素,因此arma::vec alph = Rcpp::as&lt;arma::vec&gt;(myDF["alpha"]); 不太可能做您想做的事。

    【讨论】:

      猜你喜欢
      • 2017-12-05
      • 2017-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多