Rcpp 创建具有可变列数的 DataFrame

Posted

技术标签:

【中文标题】Rcpp 创建具有可变列数的 DataFrame【英文标题】:Rcpp Create DataFrame with Variable Number of Columns 【发布时间】:2015-08-17 21:29:32 【问题描述】:

我对使用 Rcpp 创建具有可变列数的数据框很感兴趣。我的意思是,只有在运行时才能知道列数。一些列将是标准列,但其他列将重复 n 次,其中 n 是我在特定运行中考虑的特征数。

我知道我可以按如下方式创建数据框:

IntegerVector i1(3); i1[0]=4;i1[1]=2134;i1[2]=3453;
IntegerVector i2(3); i2[0]=4123;i2[1]=343;i2[2]=99123;
DataFrame df = DataFrame::create(Named("V1")=i1,Named("V2")=i2);

但在这种情况下,假设列数为 2。

为了简化对我需要的解释,假设我想传递一个 SEXP 变量,指定要在变量部分创建的列数。比如:

RcppExport SEXP myFunc(SEXP n, SEXP <other stuff>)
IntegerVector i1(3); <compute i1>
IntegerVector i2(3); <compute i2>
for(int i=0;i<n;i++)compute vi
DataFrame df = DataFrame::create(Named("Num")=i1,Named("ID")=i2,...,other columns v1 to vn);

其中 n 作为参数传递。 R 中的最终数据框看起来像

Num ID V1 ... Vn
  1  2  5     'aasda'
  ...

(实际上,列名不会采用“Vx”的形式,但它们会在运行时知道。)换句话说,我不能使用静态列表

Named()=...

因为数字会改变。

我尝试跳过构造函数的“Named()”部分,然后在最后命名列,但结果是垃圾。

这个可以吗?

【问题讨论】:

你会反对传递一个包含两列数据框的列表和一个 n 元素列表吗?然后你可以在 R 中使用as.data.frame 将它们绑定到一个数据框中。列表的第二个元素(即向量的 n 元素列表)可以创建为向量的向量(即std::vector&lt;std::vector &lt;double&gt; &gt;)并使用Rcpp::wrap 将其转换为 R 向量列表。 很好的解决方案@Bridgeburners。 as.data.frame 转换没有真正的开销(data.frame 毕竟是一个花哨的list),这应该可以工作。 @xbot:记住Rcpp::Lists 和 Rcpp::DataFrames 有 20 个元素的限制 我对这次交流有点困惑。我认为DataFrame 与类-'data.frame' 的对象不同。 【参考方案1】:

如果我正确理解您的问题,似乎最容易利用将List 作为参数的DataFrame 构造函数(因为可以直接指定List 的大小),并通过.attr("names")CharacterVector 设置列的名称:


#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::DataFrame myFunc(int n, Rcpp::List lst, 
                       Rcpp::CharacterVector Names = Rcpp::CharacterVector::create()) 

  Rcpp::List tmp(n + 2);
  tmp[0] = Rcpp::IntegerVector(3);
  tmp[1] = Rcpp::IntegerVector(3);

  Rcpp::CharacterVector lnames = Names.size() < lst.size() ?
    lst.attr("names") : Names;
  Rcpp::CharacterVector names(n + 2);
  names[0] = "Num";
  names[1] = "ID";

  for (std::size_t i = 0; i < n; i++) 
    // tmp[i + 2] = do_something(lst[i]);
    tmp[i + 2] = lst[i];
    if (std::string(lnames[i]).compare("") != 0) 
      names[i + 2] = lnames[i];
     else 
      names[i + 2] = "V" + std::to_string(i);
    
  
  Rcpp::DataFrame result(tmp);
  result.attr("names") = names;
  return result;


还有一些额外的东西可以让Names 向量成为可选的——例如如果您只使用命名列表,则可以省略第三个参数。


lst1 <- list(1L:3L, 1:3 + .25, letters[1:3])
##
> myFunc(length(lst1), lst1, c("V1", "V2", "V3"))
#  Num ID V1   V2 V3
#1   0  0  1 1.25  a
#2   0  0  2 2.25  b
#3   0  0  3 3.25  c

lst2 <- list(
  Column1 = 1L:3L,
  Column2 = 1:3 + .25,
  Column3 = letters[1:3],
  Column4 = LETTERS[1:3])
##
> myFunc(length(lst2), lst2)
#  Num ID Column1 Column2 Column3 Column4
#1   0  0       1    1.25       a       A
#2   0  0       2    2.25       b       B
#3   0  0       3    3.25       c       C

请注意 @hrbrmstr 所指出的 DataFrame 构造函数的 this signature 的 20 长度限制。

【讨论】:

谢谢。这看起来像我所追求的。 @nrussell 你的意思是说我们不能在 Rcpp 中为数据框对象使用超过 20 列吗? @Prometheus 你可以使用更多。只是从 List 中创建 DataFrame 的函数只有 20 个。一个有 1 列,一个有 2 列,依此类推。 C++ 和 R 都可以使用超过 20 列,只是转换受到限制。看看我的答案,让 C++ 告诉 R 处理转换。这适用于任意数量的列。【参考方案2】:

这是一个老问题,但我认为更多的人正在为此苦苦挣扎,就像我一样。从这里的其他答案开始,我得到了一个不受 DataFrame 构造函数的 20 列限制的解决方案:

// [[Rcpp::plugins(cpp11)]]
#include <Rcpp.h>
#include <string>
#include <iostream>

using namespace Rcpp;

// [[Rcpp::export]]
List variableColumnList(int numColumns=30) 
    List retval;
    for (int i=0; i<numColumns; i++) 
        std::ostringstream colName;
        colName << "V" << i+1;
        retval.push_back( IntegerVector::create(100*i, 100*i + 1),colName.str());
    
    return retval;


// [[Rcpp::export]]
DataFrame variableColumnListAsDF(int numColumns=30) 
    Function asDF("as.data.frame");

    return asDF(variableColumnList(numColumns));


// [[Rcpp::export]]
DataFrame variableColumnListAsTibble(int numColumns=30) 
    Function asTibble("tbl_df");

    return asTibble(variableColumnList(numColumns));

因此,首先通过将列推送到空的List 上来构建 C++ List。 (我在这里动态生成值和列名。)然后,要么将其作为 R list 返回,要么使用两个辅助函数之一将它们转换为 data.frametbl_df。可以从 R 中执行后者,但我发现这个更干净。

【讨论】:

以上是关于Rcpp 创建具有可变列数的 DataFrame的主要内容,如果未能解决你的问题,请参考以下文章

T-SQL 查询将数据插入到具有可变列数的表中

具有动态列数的 QML TableView

SwiftUI:如何创建具有相同行数和列数的 LazyGrid?

我们如何在 Spark 中使用 Dataframes(由 structtype 方法创建)合并具有不同列数的 2 个表?

如何在工作表中动态创建具有列数的数组,以删除多列中的重复项

具有每行动态列数的 Android GridLayout