r - Rcpp:这是使用 Rcpp 修改数据框某些列的最佳方法
问题描述
通常我必须处理大空间数据,并且需要高速和内存效率。假设我想用 Rcpp 中的自定义函数修改数据帧的一些数值列,我对 C++ 和 Rcpp 的引用和复制机制感到困惑。使用下面的三个最小示例代码,请您帮我澄清以下问题:
updateDF3 是执行此类任务所需的最高速度和最低内存的最佳功能吗?此功能是从此处的一个类似问题修改而来的,但我不明白作者给出的警告,“这种方法存在问题。您的原始数据框和您创建的数据框共享相同的向量,因此可能会发生不好的事情。” 如果我将此函数仅用于作为 updateDF3 的子函数并从 R 调用,它安全吗?
为什么 updateDF1 和 updateDF2 的性能差异不显着?使用或不使用引用 (&) 传递参数有什么区别?
函数编码是否池化,还有另一种方式,比如DataFrame out=clone(df), tmpstr=asstd::string(colnames[v])?
提前致谢。
#include <Rcpp.h>
#include <iostream>
using namespace Rcpp;
using namespace std;
// [[Rcpp::export]]
bool contains(CharacterVector x, std::string y) {
return std::find(x.begin(), x.end(), y)!=x.end();
}
// [[Rcpp::export]]
DataFrame updateDF1(DataFrame df, Nullable<Rcpp::CharacterVector> vars=R_NilValue) {
DataFrame out=clone(df);
string tmpstr;
NumericVector tmpv;
if(vars.isNotNull()){
CharacterVector selvars(vars);
for(int v=0;v<selvars.size();v++){
tmpstr=as<std::string>(selvars[v]);
tmpv=df[tmpstr];
tmpv=tmpv+1.0;
out[tmpstr]=tmpv;
}
}
return out;
}
// [[Rcpp::export]]
DataFrame updateDF2(DataFrame& df, Nullable<Rcpp::CharacterVector> vars=R_NilValue) {
DataFrame out=clone(df);
string tmpstr;
NumericVector tmpv;
if(vars.isNotNull()){
CharacterVector selvars(vars);
for(int v=0;v<selvars.size();v++){
tmpstr=as<std::string>(selvars[v]);
tmpv=df[tmpstr];
tmpv=tmpv+1.0;
out[tmpstr]=tmpv;
}
}
return out;
}
// [[Rcpp::export]]
List updateDF3(DataFrame& df, Nullable<Rcpp::CharacterVector> vars=R_NilValue) {
List out(df.size());
CharacterVector colnames=df.attr("names");
string tmpstr;
NumericVector tmpv;
for(int v=0;v<df.size();v++){
if(vars.isNotNull()){
CharacterVector selvars(vars);
tmpstr=as<std::string>(colnames[v]);
if(contains(selvars,tmpstr)){
tmpv=df[tmpstr];
tmpv=tmpv+1.0;
out[v]=tmpv;
}else{
out[v]=df[tmpstr];
}
}else{
out[v]=df[tmpstr];
}
}
out.attr("class") = df.attr("class") ;
out.attr("row.names") = df.attr("row.names") ;
out.attr("names") = df.attr("names") ;
return out;
}
/*** R
df=as.data.frame(matrix(1:120000000,nrow=10000000))
names(df)=paste("band",1:ncol(df),sep="_")
df=cbind(x="charcol",df)
microbenchmark::microbenchmark(
x1<<-updateDF1(df,vars=names(df)[-1]),
x2<<-updateDF2(df,vars=names(df)[-1]),
x3<<-updateDF3(df,vars=names(df)[-1]),
times=10
)
identical(x1,x2)
identical(x1,x3)
*/
##performance
#Unit: milliseconds
# expr min lq mean median
# x1 <<- updateDF1(df, vars = names(df)[-1]) 587.6023 604.9242 711.8981 651.1242
# x2 <<- updateDF2(df, vars = names(df)[-1]) 581.7129 641.2876 882.9999 766.9354
# x3 <<- updateDF3(df, vars = names(df)[-1]) 406.1824 417.5892 542.2559 420.8485
解决方案
根据@Roland的建议,通过修改updateDF2使用参考方法的最佳方式,代码如下:
// [[Rcpp::export]]
DataFrame updateDF(DataFrame& df, Nullable<Rcpp::CharacterVector> vars=R_NilValue) {
string tmpstr;
NumericVector tmpv;
if(vars.isNotNull()){
CharacterVector selvars(vars);
for(int v=0;v<selvars.size();v++){
tmpstr=selvars[v];
tmpv=df[tmpstr];
tmpv=tmpv+1.0;
df[tmpstr]=tmpv;
}
}
return df;
}
具有以下性能:
Unit: milliseconds
expr min lq mean median
x1 <<- updateDF1(df, vars = names(df)[-1]) 573.8246 728.4211 990.8680 951.3108
x2 <<- updateDF2(df, vars = names(df)[-1]) 595.7339 694.0645 935.4226 941.7450
x3 <<- updateDF3(df, vars = names(df)[-1]) 197.7855 206.4767 377.4378 225.0290
x4 <<- updateDF(df, vars = names(df)[-1]) 148.5119 149.7321 247.1329 152.3744
推荐阅读
- javascript - 在 Bootstrap 中触发模态隐藏时是否可以停止模态隐藏?
- javascript - 错误:类型“void”不可分配给类型“ReactNode”
- java - 一张地图
转 JSONString - javascript - 关于没有虚假陈述的条件(三元)的书呆子问题
- php - 需要根据匹配类从字符串中获取 div
- node.js - 是否可以在没有 ObjectId 属性的情况下在另一个猫鼬模式中引用猫鼬模式
- python - 如何使用不同的下拉菜单更新绘图破折号
- ruby-on-rails - Rspec - 比较两个 json 值
- java - JAVA:用空格而不是输入信号开始新输入
- c# - C# 没有返回 IDataReader