c++ - 具有任意类型向量输入的 Rcpp 快速统计模式函数
问题描述
我正在尝试为 R 构建一个超快速模式函数,用于聚合大型分类数据集。该函数应采用所有支持的 R 类型的向量输入并返回模式。我已阅读这篇文章、此帮助页面和其他文章,但我无法让该函数接受所有 R 数据类型。我的代码现在适用于数字向量,我依赖于 Rcpp 糖包装函数:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
int Mode(NumericVector x, bool narm = false)
{
if (narm) x = x[!is_na(x)];
NumericVector ux = unique(x);
int y = ux[which_max(table(match(x, ux)))];
return y;
}
此外,我想知道是否可以将“ narm ”参数重命名为“ na.rm ”而不会出错,当然,如果有更快的方法在 C++ 中编写模式函数,我将不胜感激。
解决方案
为了使该函数适用于任何向量输入,您可以为您想要支持的任何数据类型实现@JosephWood 算法,并从switch(TYPEOF(x))
. 但这将是很多代码重复。相反,最好创建一个可以处理任何Vector<RTYPE>
参数的通用函数。如果我们遵循 R 的范式,即一切都是向量,并让函数也返回 a Vector<RTYPE>
,那么我们可以利用RCPP_RETURN_VECTOR
. 请注意,我们需要 C++11 才能将其他参数传递给RCPP_RETURN_VECTOR
. 一件棘手的事情是,您需要存储类型Vector<RTYPE>
才能创建合适的std::unordered_map
. 这里Rcpp::traits::storage_type<RTYPE>::type
来救援。但是,std::unordered_map
不知道如何处理 R 中的复数。为简单起见,我禁用了这种特殊情况。
把它们放在一起:
#include <Rcpp.h>
using namespace Rcpp ;
// [[Rcpp::plugins(cpp11)]]
#include <unordered_map>
template <int RTYPE>
Vector<RTYPE> fastModeImpl(Vector<RTYPE> x, bool narm){
if (narm) x = x[!is_na(x)];
int myMax = 1;
Vector<RTYPE> myMode(1);
// special case for factors == INTSXP with "class" and "levels" attribute
if (x.hasAttribute("levels")){
myMode.attr("class") = x.attr("class");
myMode.attr("levels") = x.attr("levels");
}
std::unordered_map<typename Rcpp::traits::storage_type<RTYPE>::type, 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[0] = x[i];
}
} else {
modeMap.insert({x[i], 1});
}
}
return myMode;
}
template <>
Vector<CPLXSXP> fastModeImpl(Vector<CPLXSXP> x, bool narm) {
stop("Not supported SEXP type!");
}
// [[Rcpp::export]]
SEXP fastMode( SEXP x, bool narm = false ){
RCPP_RETURN_VECTOR(fastModeImpl, x, narm);
}
/*** R
set.seed(1234)
s <- sample(1e5, replace = TRUE)
fastMode(s)
fastMode(s + 0.1)
l <- sample(c(TRUE, FALSE), 11, replace = TRUE)
fastMode(l)
c <- sample(letters, 1e5, replace = TRUE)
fastMode(c)
f <- as.factor(c)
fastMode(f)
*/
输出:
> set.seed(1234)
> s <- sample(1e5, replace = TRUE)
> fastMode(s)
[1] 85433
> fastMode(s + 0.1)
[1] 85433.1
> l <- sample(c(TRUE, FALSE), 11, replace = TRUE)
> fastMode(l)
[1] TRUE
> c <- sample(letters, 1e5, replace = TRUE)
> fastMode(c)
[1] "z"
> f <- as.factor(c)
> fastMode(f)
[1] z
Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z
如上所述,使用的算法来自Joseph Wood 的答案,该答案已在 CC-BY-SA 和 GPL >= 2 下明确获得双重许可。我关注 Joseph 并特此根据GPL(版本 2 )许可此答案中的代码或更高版本)除了隐式 CC-BY-SA 许可证。
推荐阅读
- java - 使用 new File(directory).mkdir() 创建文件时出错
- java - Java Compute C(n,k) 和使用 biginteger 的阶乘
- javascript - 只获取 load() jquery 的文本内部值,没有标签没有类没有 ids 没有样式
- reactjs - 无法通过嵌入播放 YouTube 视频
- protocol-buffers - 使用 proto3 编译器编译 proto2 语法文件
- kotlin - Kotlin:在 lambda 中无法访问自动 getter
- apache-spark - spark 2.3.1 是否可以使用 Raw SQL 指定水印?
- javascript - 如何更改 img src 属性,因为它是父选择
- javascript - 生成 A-Frame 屏幕截图时出现 JavaScript 错误
- html - 垂直对齐两列输入字段,一列带标签,另一列不带标签