Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
行和的Rcpp等价物 我正在寻找C++中的R函数ROWSUM的快速替换方法。p>_C++_R_Eigen_Rcpp_Armadillo - Fatal编程技术网

行和的Rcpp等价物 我正在寻找C++中的R函数ROWSUM的快速替换方法。p>

行和的Rcpp等价物 我正在寻找C++中的R函数ROWSUM的快速替换方法。p>,c++,r,eigen,rcpp,armadillo,C++,R,Eigen,Rcpp,Armadillo,目的是根据分组向量b得到向量a中元素的和。例如: > a [1] 2 2 2 2 2 2 2 2 2 2 > b [1] 1 1 1 1 1 2 2 2 2 2 > rowsum(a,b) [,1] 1 10 2 10 在Rcpp中编写一个简单的for循环非常慢,但可能我的代码效率很低 我还尝试在Rcpp中调用函数rowsum,但是,rowsum不是很快 不是答案,但可能有助于确定问题。看起来最坏的情况是将许多短组相加,这似乎与向量的大小成线性比例 &

目的是根据分组向量b得到向量a中元素的和。例如:

> a
 [1] 2 2 2 2 2 2 2 2 2 2    
> b
 [1] 1 1 1 1 1 2 2 2 2 2
> rowsum(a,b)
  [,1]
1   10
2   10
在Rcpp中编写一个简单的for循环非常慢,但可能我的代码效率很低


我还尝试在Rcpp中调用函数rowsum,但是,rowsum不是很快

不是答案,但可能有助于确定问题。看起来最坏的情况是将许多短组相加,这似乎与向量的大小成线性比例

> n = 100000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f))
   user  system elapsed 
  0.228   0.000   0.229 
> n = 1000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f)) 
   user  system elapsed 
  1.468   0.040   1.514 
> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f))
   user  system elapsed 
 17.369   0.748  18.166 
似乎有两条捷径可走,避免重新订购

> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f, reorder=FALSE))
   user  system elapsed 
 16.501   0.476  17.025 
避免对性格的内在强迫

> n = 10000000; x = runif(n); f = as.character(sample(n/2, n, TRUE)); 
> system.time(rowsum(x, f, reorder=FALSE))
   user  system elapsed 
  8.652   0.268   8.949 
然后是可能涉及的基本操作——计算分组因子的唯一值以预分配结果向量并进行求和

> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time({ t = tabulate(f); sum(x) })
   user  system elapsed 
  0.640   0.000   0.643 
因此,是的,似乎有相当大的空间可以实现更快的单用途实现。这对于data.table来说似乎很自然,在C中实现也不太困难。这里有一个混合的解决方案,使用R进行制表,使用“经典”C接口进行求和

library(inline)

rowsum1.1 <- function(x, f) {
    t <- tabulate(f)
    crowsum1(x, f, t)
}

crowsum1 = cfunction(c(x_in="numeric", f_in="integer", t_in = "integer"), "
    SEXP res_out;
    double *x = REAL(x_in), *res;
    int len = Rf_length(x_in), *f = INTEGER(f_in);

    res_out = PROTECT(Rf_allocVector(REALSXP, Rf_length(t_in)));
    res = REAL(res_out);
    memset(res, 0, Rf_length(t_in) * sizeof(double));
    for (int i = 0; i < len; ++i)
        res[f[i] - 1] += x[i];
    UNPROTECT(1);
    return res_out;
")

因此,对于所有这些工作,我们只快了2倍,更不一般——x必须是数字,f必须是整数;没有NA值。是的,存在效率低下的问题,例如,分配没有计数的空间级别,尽管这避免了对名称的字符向量进行昂贵的强制。

不是答案,但可能有助于确定问题的框架。看起来最坏的情况是将许多短组相加,这似乎与向量的大小成线性比例

> n = 100000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f))
   user  system elapsed 
  0.228   0.000   0.229 
> n = 1000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f)) 
   user  system elapsed 
  1.468   0.040   1.514 
> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f))
   user  system elapsed 
 17.369   0.748  18.166 
似乎有两条捷径可走,避免重新订购

> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f, reorder=FALSE))
   user  system elapsed 
 16.501   0.476  17.025 
避免对性格的内在强迫

> n = 10000000; x = runif(n); f = as.character(sample(n/2, n, TRUE)); 
> system.time(rowsum(x, f, reorder=FALSE))
   user  system elapsed 
  8.652   0.268   8.949 
然后是可能涉及的基本操作——计算分组因子的唯一值以预分配结果向量并进行求和

> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time({ t = tabulate(f); sum(x) })
   user  system elapsed 
  0.640   0.000   0.643 
因此,是的,似乎有相当大的空间可以实现更快的单用途实现。这对于data.table来说似乎很自然,在C中实现也不太困难。这里有一个混合的解决方案,使用R进行制表,使用“经典”C接口进行求和

library(inline)

rowsum1.1 <- function(x, f) {
    t <- tabulate(f)
    crowsum1(x, f, t)
}

crowsum1 = cfunction(c(x_in="numeric", f_in="integer", t_in = "integer"), "
    SEXP res_out;
    double *x = REAL(x_in), *res;
    int len = Rf_length(x_in), *f = INTEGER(f_in);

    res_out = PROTECT(Rf_allocVector(REALSXP, Rf_length(t_in)));
    res = REAL(res_out);
    memset(res, 0, Rf_length(t_in) * sizeof(double));
    for (int i = 0; i < len; ++i)
        res[f[i] - 1] += x[i];
    UNPROTECT(1);
    return res_out;
")

因此,对于所有这些工作,我们只快了2倍,更不一般——x必须是数字,f必须是整数;没有NA值。是的,存在效率低下的问题,例如,分配没有计数的空间级别,尽管这避免了对名称的字符向量进行昂贵的强制。

这是我第一次使用包时使用Rcpp进行此操作的尝试,请指出我的效率低下:

library(inline)
library(Rcpp)

rowsum_helper = cxxfunction(signature(x = "numeric", y = "integer"), '
  NumericVector var(x);
  IntegerVector factor(y);

  std::vector<double> sum(*std::max_element(factor.begin(), factor.end()) + 1,
                          std::numeric_limits<double>::quiet_NaN());
  for (int i = 0, size = var.size(); i < size; ++i) {
    if (sum[factor[i]] != sum[factor[i]]) sum[factor[i]] = var[i];
    else sum[factor[i]] += var[i];
  }

  return NumericVector(sum.begin(), sum.end());
', plugin = "Rcpp")

rowsum_fast = function(x, y) {
  res = rowsum_helper(x, y)
  elements = which(!is.nan(res))
  list(elements - 1, res[elements])
}

还注意到,与表中的许多减速相比,在R代码中发生了,因此如果将该移动移到C++,则应该看到更多的改进:

system.time(rowsum_helper(x,f))
#    user  system elapsed 
#   0.210   0.018   0.228
这是一个可以处理几乎所有y类型的泛化,但速度会慢一点。实际上,我更喜欢在Rcpp中这样做,但不知道如何处理任意的R类型:

rowsum_fast = function(x, y) {
  if (is.numeric(y)) {
    y.min = min(y)
    y = y - y.min
    res = rowsum_helper(x, y)
  } else {
    y = as.factor(y)
    res = rowsum_helper(x, as.numeric(y))
  }

  elements = which(!is.nan(res))

  if (is.factor(y)) {
    list(levels(y)[elements-1], res[elements])
  } else {
    list(elements - 1 + y.min, res[elements])
  }
}

这是我第一次尝试使用Rcpp来实现这一点,请务必指出我的低效之处:

library(inline)
library(Rcpp)

rowsum_helper = cxxfunction(signature(x = "numeric", y = "integer"), '
  NumericVector var(x);
  IntegerVector factor(y);

  std::vector<double> sum(*std::max_element(factor.begin(), factor.end()) + 1,
                          std::numeric_limits<double>::quiet_NaN());
  for (int i = 0, size = var.size(); i < size; ++i) {
    if (sum[factor[i]] != sum[factor[i]]) sum[factor[i]] = var[i];
    else sum[factor[i]] += var[i];
  }

  return NumericVector(sum.begin(), sum.end());
', plugin = "Rcpp")

rowsum_fast = function(x, y) {
  res = rowsum_helper(x, y)
  elements = which(!is.nan(res))
  list(elements - 1, res[elements])
}

还注意到,与表中的许多减速相比,在R代码中发生了,因此如果将该移动移到C++,则应该看到更多的改进:

system.time(rowsum_helper(x,f))
#    user  system elapsed 
#   0.210   0.018   0.228
这是一个可以处理几乎所有y类型的泛化,但速度会慢一点。实际上,我更喜欢在Rcpp中这样做,但不知道如何处理任意的R类型:

rowsum_fast = function(x, y) {
  if (is.numeric(y)) {
    y.min = min(y)
    y = y - y.min
    res = rowsum_helper(x, y)
  } else {
    y = as.factor(y)
    res = rowsum_helper(x, as.numeric(y))
  }

  elements = which(!is.nan(res))

  if (is.factor(y)) {
    list(levels(y)[elements-1], res[elements])
  } else {
    list(elements - 1 + y.min, res[elements])
  }
}

在@Ben删除的一条评论和“答案”中,f是有序的,并且是递增的

n = 1e7; x = runif(n);
f <- cumsum(c(1L, sample(c(TRUE, FALSE), n - 1, TRUE)))
rowsum_辅助程序已被删除


在@Ben删除的一条评论和“答案”中,f是有序的,并且是递增的

n = 1e7; x = runif(n);
f <- cumsum(c(1L, sample(c(TRUE, FALSE), n - 1, TRUE)))
rowsum_辅助程序已被删除


为了补充Martin的代码,这里有一些基于Rcpp的版本

int increment_maybe(int value, double vec_i){
    return vec_i == 0 ? value : ( value +1 ) ;  
}

// [[Rcpp::export]]
NumericVector cpprowsum2(NumericVector x, IntegerVector f){
    std::vector<double> vec(10) ;
    vec.reserve(1000); 
    int n=x.size(); 
    for( int i=0; i<n; i++){
        int index=f[i]; 
        while( index >= vec.size() ){
            vec.resize( vec.size() * 2 ) ;    
        }
        vec[ index ] += x[i] ;
    }
    // count the number of non zeros
    int s = std::accumulate( vec.begin(), vec.end(), 0, increment_maybe) ; 
    NumericVector result(s) ;
    CharacterVector names(s) ;

    std::vector<double>::iterator it = vec.begin() ;
    for( int i=0, j=0 ; j<s; j++ ,++it, ++i ){
        // move until the next non zero value
        while( ! *it ){ i++ ; ++it ;}
        result[j] = *it ;
        names[j]  = i ;
    }
    result.attr( "dim" ) = IntegerVector::create(s, 1) ;
    result.attr( "dimnames" ) = List::create(names, R_NilValue) ; 
    return result ;
}

为了补充Martin的代码,这里有一些基于Rcpp的版本

int increment_maybe(int value, double vec_i){
    return vec_i == 0 ? value : ( value +1 ) ;  
}

// [[Rcpp::export]]
NumericVector cpprowsum2(NumericVector x, IntegerVector f){
    std::vector<double> vec(10) ;
    vec.reserve(1000); 
    int n=x.size(); 
    for( int i=0; i<n; i++){
        int index=f[i]; 
        while( index >= vec.size() ){
            vec.resize( vec.size() * 2 ) ;    
        }
        vec[ index ] += x[i] ;
    }
    // count the number of non zeros
    int s = std::accumulate( vec.begin(), vec.end(), 0, increment_maybe) ; 
    NumericVector result(s) ;
    CharacterVector names(s) ;

    std::vector<double>::iterator it = vec.begin() ;
    for( int i=0, j=0 ; j<s; j++ ,++it, ++i ){
        // move until the next non zero value
        while( ! *it ){ i++ ; ++it ;}
        result[j] = *it ;
        names[j]  = i ;
    }
    result.attr( "dim" ) = IntegerVector::create(s, 1) ;
    result.attr( "dimnames" ) = List::create(names, R_NilValue) ; 
    return result ;
}

代码不使用提供的数据。当向量设计用于矩阵时,您正在向量上使用行和。您没有提供Cpp代码。在上述情况下,rowsum分派rowsum.default,这已经调用了C代码,因此它应该已经相当快了。通过直接调用rowsum.default或.Internalrowsum_matrix,您可能会获得一点速度提升。。。尽管后者是不被鼓励的,而且在CRAN上是不允许的。你在这里查阅过犰狳手册了吗:至少有一些求和函数,这符合你的目的吗?听起来像是一些数据。table将擅长于…代码不使用提供的数据。当向量设计用于矩阵时,您正在向量上使用行和。您没有提供Cpp代码。在上述情况下,rowsum分派rowsum.default,这已经调用了C代码,因此它应该已经相当快了。通过直接调用rowsum.default或.Internalrowsum_matrix,您可能会获得一点速度提升。。。尽管后者是不被鼓励的,而且在CRAN上是不允许的。你检查过犰狳手册了吗?这里至少有一些求和函数

,这适合你的目的吗?听起来像是data.table所擅长的……哇,我印象深刻!这正是我想要的!我的框架需要rowsum1的规范,x是数字,f是整数。无论如何,您的函数比我的计算机上的rowsum快4倍马丁。IIRC,Rf_allocVector未将初始值设置为0.0。我认为res[I]的初始值是garbage@RomainFrancois哎呀,修好了,谢谢。还有,再想想这个。行SUM1中的which测试可能是错误的,关于在我的答案上保留相同的问题,我猜,例如,x=c1,-1,1,-1和f=c1L,1L,2L,2L。whicht!=0更好,再次感谢罗曼。更令人沮丧的是——大量的工作、不完整的实现、新的bug,但仍然没有那么快。哇,我印象深刻!这正是我想要的!我的框架需要rowsum1的规范,x是数字,f是整数。无论如何,您的函数比我的计算机上的rowsum快4倍马丁。IIRC,Rf_allocVector未将初始值设置为0.0。我认为res[I]的初始值是garbage@RomainFrancois哎呀,修好了,谢谢。还有,再想想这个。行SUM1中的which测试可能是错误的,关于在我的答案上保留相同的问题,我猜,例如,x=c1,-1,1,-1和f=c1L,1L,2L,2L。whicht!=0更好,再次感谢罗曼。更令人沮丧的是——大量的工作、不完整的实现、新的bug,但仍然没有那么快。很棒的代码!非常感谢你!伟大的代码!非常感谢你!此处相同,注意res的未初始化值。@romainfranco为否,原始版本已正确初始化r;但是,整数并没有做好事!此处相同,注意res的未初始化值。@romainfranco为否,原始版本已正确初始化r;但是,整数并没有做好事!干得好。想为Rcpp画廊写一篇吗-大概无论如何,我必须熟悉整个过程。没什么-不要让git的东西迷惑你。查看任何一篇文章,查看“来源”链接并查看其来源。带注释的.cpp或.Rmd。如果你愿意,我们可以继续通过谷歌聊天/闲逛。@德克,你应该这么做!:干得好。想为Rcpp画廊写一篇吗-大概无论如何,我必须熟悉整个过程。没什么-不要让git的东西迷惑你。查看任何一篇文章,查看“来源”链接并查看其来源。带注释的.cpp或.Rmd。如果你愿意,我们可以继续通过谷歌聊天/闲逛。@德克,你应该这么做!: