Подтвердить что ты не робот

Самый быстрый способ вычислить cdf нормального распределения по векторам - R:: pnorm vs erfc vs?

Надеюсь, мой измененный вопрос теперь соответствует критериям Stackoverflow. Пожалуйста, рассмотрите приведенный ниже пример. Я пишу функцию логарифмического правдоподобия, в которой вычисление cdf над векторами является самой трудоемкой частью. В примере 1 используется R::pnorm, пример 2 аппроксимирует нормальный cdf с помощью erfc. Поскольку вы можете видеть, что результаты достаточно схожи, версия 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
4b9b3361

Ответ 1

1) Ну, вы действительно должны использовать R pnorm() как ваш 0-й пример. У вас нет, вы используете интерфейс Rcpp. R pnorm() уже красиво векторизован R-внутренне (то есть на уровне C), поэтому вполне может быть сравнительным или даже быстрее, чем Rcpp. Также у него есть преимущество, чтобы охватывать случаи NA, NaN, Inf и т.д.

2) Если вы говорите о MLE, и вы обеспокоены скоростью и точностью, вы почти наверняка должны работать с логарифмами и, возможно, не с pnorm(), а скорее dnorm()?