在您的Mode 函数中,由于您主要调用糖包装函数,因此您不会看到比基本R 有太多改进。事实上,只需编写一个忠实的基础 R 翻译,我们就有:
baseMode <- function(x, narm = FALSE) {
if (narm) x <- x[!is.na(x)]
ux <- unique(x)
ux[which.max(table(match(x, ux)))]
}
而基准测试,我们有:
set.seed(1234)
s <- sample(1e5, replace = TRUE)
library(microbenchmark)
microbenchmark(Mode(s), baseMode(s), times = 10, unit = "relative")
Unit: relative
expr min lq mean median uq max neval
Mode(s) 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 10
baseMode(s) 1.490765 1.645367 1.571132 1.616061 1.637181 1.448306 10
通常,当我们努力编写自己的编译代码时,我们会期望获得更大的收益。简单地将这些已经有效的编译函数包装在Rcpp 中不会神奇地让您获得预期的收益。事实上,在更大的例子上,基本解决方案更快。观察:
set.seed(1234)
sBig <- sample(1e6, replace = TRUE)
system.time(Mode(sBig))
user system elapsed
1.410 0.036 1.450
system.time(baseMode(sBig))
user system elapsed
0.915 0.025 0.943
为了解决您编写更快模式函数的问题,我们可以使用std::unordered_map,它与底层的table 非常相似(即它们本质上都是哈希表)。此外,由于您返回的是单个整数,我们可以放心地假设我们可以将 NumericVector 替换为 IntegerVector 并且您不关心返回出现次数最多的每个值。 p>
可以修改下面的算法以返回true mode,但我将把它留作练习(提示:您将需要std::vector 以及在it->second == myMax 时采取某种行动)。注:您还需要在 cpp 文件的顶部为 std::unordered_map 和 auto 添加 // [[Rcpp::plugins(cpp11)]]。
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::plugins(cpp11)]]
#include <unordered_map>
// [[Rcpp::export]]
int fastIntMode(IntegerVector x, bool narm = false) {
if (narm) x = x[!is_na(x)];
int myMax = 1;
int myMode = 0;
std::unordered_map<int, int> modeMap;
modeMap.reserve(x.size());
for (std::size_t i = 0, len = x.size(); i < len; ++i) {
auto it = modeMap.find(x[i]);
if (it != modeMap.end()) {
++(it->second);
if (it->second > myMax) {
myMax = it->second;
myMode = x[i];
}
} else {
modeMap.insert({x[i], 1});
}
}
return myMode;
}
以及基准测试:
microbenchmark(Mode(s), baseMode(s), fastIntMode(s), times = 15, unit = "relative")
Unit: relative
expr min lq mean median uq max neval
Mode(s) 6.428343 6.268131 6.622914 6.134388 6.881746 7.78522 15
baseMode(s) 9.757491 9.404101 9.454857 9.169315 9.018938 10.16640 15
fastIntMode(s) 1.000000 1.000000 1.000000 1.000000 1.000000 1.00000 15
现在我们正在谈论...大约比原始版本快 6 倍,比基础版本快 9 倍。它们都返回相同的值:
fastIntMode(s)
##[1] 85433
baseMode(s)
##[1] 85433
Mode(s)
##[1] 85433
对于我们更大的例子:
## base R returned in 0.943s
system.time(fastIntMode(s))
user system elapsed
0.217 0.006 0.224
除了隐式 CC-BY-SA 许可之外,我特此在 GPL >= 2 下许可此答案中的代码。