优化R中大数据文件的循环,可能使用Rcpp

时间:2017-10-11 20:33:16

标签: r performance loops rcpp

我在R中有一个循环,它很慢(但有效)。目前,这个计算在我的笔记本电脑上大约需要3分钟,我认为可以改进。最后,我将根据此代码的结果循环遍历运行计算的许多数据文件,并且我希望尽可能使当前代码更快。

基本上,对于每个日期,对于11个不同的X值,循环抓住最后的X年'降雨量值(Y),找到线性反向加权(Z),使最老的降雨量值加权最小,将雨(Y)和权重(Z)乘以得到矢量A,然后取A的总和为最后的结果。这是在数千个日期完成的。

然而,我无法想到或找到任何方法在R中使这个更快,所以我试图在Rcpp中重写它,我对它的知识有限。我的Rcpp代码没有完全复制R代码,因为结果矩阵与它应该是不同的(错误)(out1 vs out2;我知道out1是正确的)。看起来Rcpp代码更快,但我只能使用几列来测试它,因为如果我尝试运行所有11列(i <= 10),它会开始崩溃(RStudio中的致命错误)。

我正在寻找有关如何改进R代码和/或更正Rcpp代码以提供正确结果而不会在此过程中崩溃的反馈。

(虽然我在下面发布的代码并未显示,但数据会以[作为数据框]的方式加载到R中,以便在显示的代码之外进行一些计算。此处显示的具体计算仅使用数据帧的第2列。)

数据文件位于:https://drive.google.com/file/d/0Bw_Ca37oxVmJekFBR2t4eDdKeGM/view?usp=sharing

尝试R

library(readxl)

library(readxl)
library(Rcpp)
file = data.frame(read_excel("lake.xlsx", trim_ws=T)
col_types=c("date","numeric","numeric","date",rep("numeric",4),"text")))
file[,1] = as.Date(file[,1], "%Y/%m/%d", tz="UTC")
file[,4] = as.Date(file[,4], "%Y/%m/%d", tz="UTC")

rainSUM = function(df){
rainsum = data.frame("6m"=as.numeric(), "1yr"=as.numeric(), "2yr"=as.numeric(), "3yr"=as.numeric(), "4yr"=as.numeric(), "5yr"=as.numeric(), "6yr"=as.numeric(), "7yr"=as.numeric(), "8yr"=as.numeric(), "9yr"=as.numeric(), "10yr"=as.numeric()) # create dataframe for storing the sum of weighted last d values

  Tdays <- length(df[,1])

  for(i in 1:11) {           # loop through the lags
    if (i==1) {
      d <- 183               # 6 month lag only has 183 days,
    } else {
      d <- (i-1)*366         # the rest have 366 days times the number of years
    }
    w <- 0:(d-1)/d

    for(k in 1:Tdays) {      # loop through rows of rain dataframe (k = row)
      if(d>k){               # get number of rain values needed for the lag
        rainsum[k,i] <- sum(df[1:k,2] * w[(d-k+1):d])                                
      } else{
        rainsum[k,i] <- sum(df[(k-d+1):k,2] * w)                          
      }
    }
  }
  return(rainsum)
}

out1 <- rainSUM(file)

尝试使用Rcpp

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]

NumericVector myseq(int first, int last) {  // simulate R's X:Y sequence (step of 1)
  NumericVector y(0);
  for (int i=first; i<=last; ++i)
    y.push_back(i);
  return(y);
}

// [[Rcpp::export]]   
NumericVector splicer(NumericVector vec, int first, int last) {  // splicer
  NumericVector y(0);
  for (int i=first; i<=last; ++i)
    y.push_back(vec[i]);
  return(y);
}

// [[Rcpp::export]]              
NumericVector weighty(int d) {             // calculate inverse linear weight according to the number of days in lag 
  NumericVector a = myseq(1,d);            // sequence 1:d; length d
  NumericVector b = (a-1)/a;               // inverse linear
  return(b);                               // return vector                              
} 

// [[Rcpp::export]]              
NumericMatrix rainsumCPP(DataFrame df, int raincol) {
  NumericVector q(0);
  NumericMatrix rainsum(df.nrows(), 11);       // matrix with number of row days as data file and 11 columns 
  NumericVector p = df( raincol-1 );           // grab rain values (remember C++ first index is 0)
  for(int i = 0; i <= 10; i++) {                // loop through 11 columns (C++ index starts at 0!)
    if (i==0) {
      int d = 183;                             // 366*years lag days 
      NumericVector w = weighty(d);            // get weights for this lag series
      for(int k = 0; k < df.nrows(); k++) {    // loop through days (rows)
        if(d>k){                               // if not enough lag days for row, use what's available
          NumericVector m = splicer(p, 0, k);  // subset rain values according to the day being considered
          NumericVector u = splicer(w, (d-k), (d-1));   // same for weight
          m = m*u;                              // multiply rain values by weights
          rainsum(k,i) = sum(m);               // add the sum of the weighted rain to the rainsum matrix
        } else{
          NumericVector m = splicer(p, k-d+1, k);
          m = m*w;
          rainsum(k,i) = sum(m);
        }
      }
    }
    else {
      int d = i*366;                           // 183 lag days if column 0
      NumericVector w = weighty(d);            // get weights for this lag series
      for(int k = 0; k < df.nrows(); k++) {    // loop through days (rows)
        if(d>k){                               // if not enough lag days for row, use what's available
          NumericVector m = splicer(p, 0, k);  // subset rain values according to the day being considered
          NumericVector u = splicer(w, (d-k), (d-1));   // same for weight
          m = m*u;                             // multiply rain values by weights
          rainsum(k,i) = sum(m);               // add the sum of the weighted rain to the rainsum matrix
        } else{
          NumericVector m = splicer(p, k-d+1, k);
          m = m*w;
          rainsum(k,i) = sum(m);
        }
      } 
    }
  }
  return(rainsum);
}


/*** R
out2 = rainsumCPP(file, raincol) # raincol currently = 2
  */

1 个答案:

答案 0 :(得分:2)

恭喜!您有index out of bounds (OOB)错误导致undefined behavior (UB)!您可以通过将矢量访问器从[]更改为()以及将矩阵访问器从()更改为.at()来检测此情况。

切换到这些访问器会产生:

Error in rainsumCPP(file, 2) : 
  Index out of bounds: [index=182; extent=182].

表示索引超出范围,因为索引必须始终在0到1之间,小于范围(例如,向量的长度-1)。

初步一瞥表明此问题主要是由于未将基于索引的索引正确映射到基于零的索引。

在使用myseq()splicer()weighty()函数时,他们 匹配 R 等效项投入。可以使用all.equal(R_result, Rcpp_Result)来检查。这种不匹配分为两部分:1。myseqsplicer的界限以及2. weighty内完成的反转。

因此,通过使用以下被修改的函数,您应该在获得正确结果的基础上做好准备。

// [[Rcpp::export]]
NumericVector myseq(int first, int last) {  // simulate R's X:Y sequence (step of 1)
  int vec_len = abs(last - first);

  NumericVector y = no_init(vec_len);

  int count = 0;
  for (int i = first; i < last; ++i) {
    y(count) = count;
    count++;
  }

  return y;
}

// [[Rcpp::export]]   
NumericVector splicer(NumericVector vec, int first, int last) {  // splicer

  int vec_len = abs(last - first);

  NumericVector y = no_init(vec_len);

  int count = 0;
  for (int i = first; i < last; ++i) {
    y(count) = vec(i);

    count++;
  }

  return y;
}

// [[Rcpp::export]]              
NumericVector weighty(int d) {       // calculate inverse linear weight according to the number of days in lag 
  NumericVector a = myseq(0, d - 1); // (fixed) sequence 1:d; length d
  NumericVector b = a / d;           // (fixed) inverse linear
  return(b);                         // return vector                              
}

从那里开始,您可能需要修改rainsumCpp,因为没有给出 R 等价物的输出。