R cpp: лучший способ изменить некоторые столбцы фрейма данных с помощью Rcpp. - PullRequest
0 голосов
/ 06 августа 2020

Обычно мне приходится работать с большими пространственными данными, поэтому ожидается высокая скорость и эффективность памяти. Предположим, я хочу изменить некоторые числовые c столбцы фрейма данных с помощью самоопределяемой функции в R cpp, меня смущает механизм ссылок и копирования в C ++ и R cpp. Не могли бы вы помочь мне с помощью трех приведенных ниже минимальных примеров кода ответить на следующие вопросы:

  1. Является ли updateDF3 лучшей функцией для выполнения такой задачи с максимальной скоростью и минимальным объемом памяти? Эта функция изменена на основе аналогичного вопроса здесь , но я не понимаю предупреждение, данное автором: «С этим подходом связаны проблемы. Ваш исходный фрейм данных и тот, который вы создали, имеют одинаковые векторы. и может случиться так плохое ". Если я использую эту функцию только для такой подфункции как updateDF3 и вызываю ее из R, безопасно ли это?

  2. Почему разница в производительности updateDF1 и updateDF2 незначительна? В чем разница между передачей параметра со ссылкой (&) или без нее?

  3. Является ли функция закодированной в пуле, и есть другой способ, например 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

1 Ответ

0 голосов
/ 07 августа 2020

Согласно предложению @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
...