Rcpp 创建具有可变列数的 DataFrame
Rcpp Create DataFrame with Variable Number of Columns
我对使用 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()" 部分,然后在最后命名列,但结果是垃圾。
这可以做到吗?
如果我正确理解你的问题,那么利用 DataFrame
构造函数似乎是最简单的,该构造函数以 List
作为参数(因为 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
请注意 DataFrame
构造函数的 this signature 的 20 长度限制,正如@hrbrmstr 所指出的那样。
这是一个老问题,但我认为更多的人正在为此苦苦挣扎,就像我一样。从这里的其他答案开始,我得出了一个不受 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
。 (我在这里动态生成值和列名。)然后,return 作为 R list
,或者使用两个辅助函数之一将它们转换为 data.frame
或 tbl_df
。后者可以从 R 中完成,但我发现它更干净。
我对使用 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()" 部分,然后在最后命名列,但结果是垃圾。
这可以做到吗?
如果我正确理解你的问题,那么利用 DataFrame
构造函数似乎是最简单的,该构造函数以 List
作为参数(因为 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
请注意 DataFrame
构造函数的 this signature 的 20 长度限制,正如@hrbrmstr 所指出的那样。
这是一个老问题,但我认为更多的人正在为此苦苦挣扎,就像我一样。从这里的其他答案开始,我得出了一个不受 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
。 (我在这里动态生成值和列名。)然后,return 作为 R list
,或者使用两个辅助函数之一将它们转换为 data.frame
或 tbl_df
。后者可以从 R 中完成,但我发现它更干净。