在Rcpp中从向量元素中删除零

4
我写了以下代码来从向量中删除零。我使用了Rcpp库中的erase(i)函数。
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector erase_zero(NumericVector x) {
  for (int i = 0; i < x.size(); i++) {
    if (x[i] == 0) {
      x.erase(i);
    }
  }
  return x;
}

一切都好,现在的问题是函数的输出,也就是说。
> erase_zero(c(0,1,2,3,0))
[1] 1 2 3
> erase_zero(c(0,0,1,2,3,0,0))
[1] 0 1 2 3 0
> erase_zero(c(0,0,0,1,2,3,0,0,0))
[1] 0 1 2 3 0
> erase_zero(c(0,0,0,0,1,2,3,0,0,0,0))
[1] 0 0 1 2 3 0 0

我不知道为什么会发生这种情况。
在阅读下面所有的答案之后,我只是简单地尝试了一下速度测试。
> microbenchmark(erase_zero(s), erase_zero1(s), erase_zero_sugar(s))
Unit: microseconds
                expr    min      lq     mean median      uq    max neval
       erase_zero(s) 19.311 21.2790 22.54262 22.181 22.8780 35.342   100
      erase_zero1(s) 18.573 21.0945 21.95222 21.771 22.4680 36.490   100
 erase_zero_sugar(s)  1.968  2.0910  2.57070  2.296  2.5215 24.887   100

erase_zero1 是 Roland 的第一个代码。此外,ThomasIsCoding 的 R 基础比所有其他人都更高效。


为什么我们需要循环? - undefined
3
@zx8754 你总是需要一个循环。它可能被一些糖语法隐藏起来,但它必须存在。 - undefined
1
你可以看一下我的最新基准测试,似乎基础的 R 已经足够高效了。 - undefined
3个回答

5
erase改变了向量的大小。这将产生预期的输出。
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector erase_zero(NumericVector x) {
  R_xlen_t n = x.size();
  for (R_xlen_t i = 0; i < n; i++) {
    if (x[i] == 0) {
      x.erase(i);
      i--;
      n--;
    }
  }
  return x;
}

/*** R
erase_zero(c(0,1,2,3,0))
erase_zero(c(0,0,1,2,3,0,0))
erase_zero(c(0,0,0,1,2,3,0,0,0))
erase_zero(c(0,0,0,0,1,2,3,0,0,0,0))
*/

然而,你应该只是使用一些Rcpp sugar。这样更高效。
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector erase_zero_sugar(NumericVector x) {
  return x[x != 0];
}

你还应该阅读为什么这些数字不相等

谢谢!你的糖代码比其他两个有效答案更高效。我进行了速度测试,你的解决方案是赢家。 - undefined

3
你的 x 的大小在执行 erase 时会动态改变。你可以尝试像下面这样使用 while
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector erase_zero(NumericVector x) {
    int i = 0;
    while (i < x.size()) {
        if (x[i]==0) {
            x.erase(i);
        } else {
            i++;
        }
    }
    return x;
}

示例输出
library(Rcpp)

sourceCpp(
    code = "
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector erase_zero(NumericVector x) {
    int i = 0;
    while (i < x.size()) {
        if (x[i]==0) {
            x.erase(i);
        } else {
            i++;
        }
    }
    return x;
}
"
)

x <- c(0, 0, 5, 0, 1, 2, 3, 0, 6, 0, 0)
erase_zero(x)

你会看到
[1] 5 1 2 3 6

2
这里是一组使用Rcpp方法与基本的R子集比较的基准测试,你会发现基本的R方法x[x!= 0]已经是最高效的了

Rcpp代码

library(Rcpp)

sourceCpp(
    code = "
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector f0(NumericVector x) {
    int i = 0;
    while (i < x.size()) {
        if (x[i]==0) {
            x.erase(i);
        } else {
            i++;
        }
    }
    return x;
}
"
)

sourceCpp(
    code = "
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector f1(NumericVector x) {
  R_xlen_t n = x.size();
  for (R_xlen_t i = 0; i < n; i++) {
    if (x[i] == 0) {
      x.erase(i);
      i--;
      n--;
    }
  }
  return x;
}
"
)

sourceCpp(
    code = "
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector f2(NumericVector x) {
  R_xlen_t n = x.size();
  NumericVector res;
  for (R_xlen_t i = 0; i < n; i++) {
    if (x[i] != 0) {
      res.push_back(x[i]);
    }
  }
  return res;
}
"
)

sourceCpp(
    code = "
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector f3(NumericVector x) {
  return x[x != 0];
}
"
)

以及用于比较的代码

set.seed(0)
x <- sample(0:5, 1e5, replace = TRUE)
microbenchmark(
    fwhile = f0(x),
    ffor1 = f1(x),
    ffor2 = f2(x),
    fsuger = f3(x),
    baseR = x[x != 0],
    unit = "relative",
    times = 10L
)

输出

Unit: relative
   expr         min         lq        mean      median          uq         max
 fwhile 4574.766987 3877.57877 2491.634303 3541.983516 2149.808409 1152.438181
  ffor1 4204.952786 3690.07333 2340.518164 3275.927345 2060.156985 1117.993311
  ffor2 8270.203280 7302.53550 4754.341310 6746.984478 4158.206201 2221.732950
 fsuger    1.236079    1.13896    1.299927    1.110674    1.091769    1.579036
  baseR    1.000000    1.00000    1.000000    1.000000    1.000000    1.000000
 neval
    10
    10
    10
    10
    10

谢谢分享!真的很有趣! - undefined
@ManAni 我认为基本的R语言功能已经经过优化,所以除非你真的需要将它们适应到你的特定用例中,否则不需要在Rcpp中重新构建这些功能。 - undefined
1
是的,没错,实际上,我需要这段代码用于一个更大的Rcpp代码。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接