计算向量上正态分布的cdf的最快方法 - R :: pnorm vs erfc vs?

时间:2015-05-19 15:59:27

标签: c++ r rcpp armadillo

我希望我的重写问题现在符合Stackoverflow的标准。请考虑以下示例。我正在编写一个Log-Likelihood函数,其中计算cdf over vectors是最耗时的部分。示例1使用R::pnorm,示例2使用erfc近似正常cdf。正如您所看到的结果非常相似,ercf版本更快一些。

实际上(在MLE中)然而事实证明,ercf并不精确,这使得算法可以进入inf区域,除非准确地设置约束。我的问题:

1)我错过了什么吗?是否有必要实现一些错误处理(对于erfc)?

2)您是否有任何其他建议来加快代码或替代方案?考虑并行化for循环是否有回报?

require(Rcpp)
require(RcppArmadillo)
require(microbenchmark)

#Example 1 : standard R::pnorm
src1 <- '
NumericVector ppnorm(const arma::vec& x,const arma::vec& mu,const     arma::vec& sigma, int lt, int lg) {
int n = x.size();
arma::vec res(n);
for (int i=0; i<n; i++) {
   res(i) = R::pnorm(x(i),mu(i),sigma(i),lt,lg);
}
return wrap(res);
}
'

#Example 2: approximation with ercf
src2 <- '
NumericVector ppnorm(const arma::vec& x,const arma::vec& mu,const    arma::vec& sigma, int lt, int lg) {
int n = x.size();
arma::vec res(n);
for (int i=0; i<n; i++) {
res(i) = 0.5 * erfc(-(x(i) - mu(i))/sigma(i) * M_SQRT1_2);
}
if (lt==0 & lg==0) {
   return wrap(1 - res);
}
if (lt==1 & lg==0) {
   return wrap(res);
}
if (lt==0 & lg==1) {
   return wrap(log(1 - res));
}
if (lt==1 & lg==1) {
   return wrap(log(res));
}
}
'

#some random numbers
xex  = rnorm(100,5,4)
muex = rnorm(100,3,1)
siex = rnorm(100,0.8,0.3)

#compile c++ functions 
func1 = cppFunction(depends = "RcppArmadillo",code=src1) #R::pnorm
func2 = cppFunction(depends = "RcppArmadillo",code=src2) #ercf

#run with exemplaric data
res1 = func1(xex,muex,siex,1,0)
res2 = func2(xex,muex,siex,1,0)

# sum of squared errors
sum((res1 - res2)^2,na.rm=T)
# 6.474419e-32 ... very small

#benchmarking
 microbenchmark(func1(xex,muex,siex,1,0),func2(xex,muex,siex,1,0),times=10000)
#Unit: microseconds
#expr    min      lq     mean median     uq     max neval
#func1(xex, muex, siex, 1, 0) 11.225 11.9725 13.72518 12.460 13.617 103.654 10000
#func2(xex, muex, siex, 1, 0)  8.360  9.1410 10.62114  9.669 10.769 205.784 10000
#my machine: Ubuntu 14.04 LTS, i7 2640M 2.8 Ghz x 4, 8GB memory, RRO 3.2.0 based on version R 3.2.0

1 个答案:

答案 0 :(得分:5)

1)嗯,你真的应该使用R pnorm()作为你的第0个例子。 你没有,你使用Rcpp接口。 R pnorm()已经很好地向量化R-内部(即在C级),因此可能比Rcpp更具比较性甚至更快。此外 还有优势涵盖NA,NaN,Inf等案例。

2)如果你在谈论MLE,并且你关注速度和准确性,你几乎肯定应该使用对数,而不是pnorm()而是dnorm()