从 Rcpp 中的矢量元素中擦除零

Erasing zeros from the vector element in Rcpp

提问人:ManAni 提问时间:10/27/2023 最后编辑:ThomasIsCodingManAni 更新时间:10/27/2023 访问量:95

问:

我编写了以下代码来从向量中删除零。我使用 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 基础比所有基础都更有效。

R 性能 向量 子集 RCPP

评论

0赞 zx8754 10/27/2023
为什么我们需要循环?
3赞 Roland 10/27/2023
@zx8754 你总是需要一个循环。它可能隐藏在一些糖语法后面,但它必须在那里。
1赞 ThomasIsCoding 10/27/2023
你可以看看我最新的基准测试,看来基础 R 已经足够高效了。

答:

5赞 Roland 10/27/2023 #1

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 糖。它更有效率:

#include <Rcpp.h>
using namespace Rcpp;

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

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

评论

0赞 ann 10/27/2023
谢谢!你的糖代码比其他两个有效的答案更有效。我尝试了速度测试,您的解决方案是赢家。
3赞 ThomasIsCoding 10/27/2023 #2

提交时,your 的大小会动态变化。您可以尝试如下xerasewhile

#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赞 ThomasIsCoding 10/27/2023 #3

这是一个基准测试,其中包含一系列方法与基本 R 子集,您将看到基本 R 方法 x[x!= 0] 已经是最有效的Rcpp

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

评论

0赞 ann 10/27/2023
感谢分享!这真的很有趣!
0赞 ThomasIsCoding 10/27/2023
@ManAni我认为基础 R 中的一些基本功能已经过优化,因此除非您确实需要根据特定用例调整它们,否则没有必要在 Rcpp 中重建轮子。
1赞 ann 10/27/2023
是的,没错,实际上,我需要这段代码来获取 Rcpp 中的更大代码。