Самый быстрый способ вычислить 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

person chameau13    schedule 19.05.2015    source источник
comment
Приведите репрезентативные примеры a и ind и функции, которую вы пытаетесь применить.   -  person nrussell    schedule 20.05.2015
comment
Вы читали эту публикацию Rcpp Gallery о подразделах?   -  person Dirk Eddelbuettel    schedule 20.05.2015
comment
@DirkEddelbuettel Я, конечно, прочитал эти примеры и надеюсь, что мой вопрос ясно дает понять, что я знаю о синтаксисе find(x==1). Я хотел выполнить подмножество и выполнить итерацию за один шаг, не сохраняя объект между ними, потому что у меня сложилось впечатление, что это ускоряет мой код. Теперь я нашел способ обойти проблему. Я отредактирую вопрос.   -  person chameau13    schedule 20.05.2015
comment
Если вы читали пост, почему вы применяете функции Armadillo к типам Rcpp? Кроме того, ваш пример не является ни полным, ни воспроизводимым, тогда как наша документация в галерее Rcpp является и тем, и другим. Я бы начал с этого и модифицировал соответственно.   -  person Dirk Eddelbuettel    schedule 20.05.2015
comment
@DirkEddelbuettel Теперь я опубликовал весь свой код, может быть, это прояснит ситуацию. Я конвертирую все в arma::mat или arma::vec в начале, и функция для применения pnorm к векторам написана так, чтобы принимать объекты arma. Мне очень жаль, если я совершаю основные ошибки, но я начал изучать C++ три дня назад.   -  person chameau13    schedule 20.05.2015
comment
Мы часто используем термин минимально воспроизводимый пример. Ваш codedump не является ни минимальным, ни (из-за отсутствия входных данных) воспроизводимым.   -  person Dirk Eddelbuettel    schedule 20.05.2015
comment
@DirkEddelbuettel Я думаю, что есть вопросы по Stackoverflow с гораздо большими дампами кода, чем эти 51 строка, и я хотел прояснить контекст, я имею в виду, что я ссылаюсь на соответствующие части кода в тексте. Однако я постараюсь как-то предоставить данные. Однако мой первоначальный вопрос сводится к следующему: возможно ли в Armadillo создавать последовательные подмножества (подмножества подмножеств и т. д.) за один шаг?   -  person chameau13    schedule 20.05.2015
comment
@DirkEddelbuettel Теперь я полностью переписал вопрос. К сожалению, я не нашел специального способа вытащить ссылку Dropbox в R программно в коде.   -  person chameau13    schedule 20.05.2015
comment
То есть я полностью переписал свой вопрос и теперь без каких-либо комментариев он отложен? Я предполагаю, что люди, которые проголосовали за его удаление раньше, даже не просмотрели новую версию.   -  person chameau13    schedule 20.05.2015
comment
Я снова проверил код, как он представлен в вопросе, он работает без проблем.   -  person chameau13    schedule 21.05.2015
comment
@DirkEddelbuettel Я снова полностью переписал вопрос. Пожалуйста, проголосуйте за возобновление работы.   -  person chameau13    schedule 22.05.2015
comment
Выглядит намного лучше, но 3) Может ли векторизованный Rcpp::pnorm быть альтернативой? Насколько это точно? не имеет смысла. Как вы думаете, откуда взялся векторизованный pnorm()? Я просто зацикливаюсь на вас, в остальном это та же самая функция и код...   -  person Dirk Eddelbuettel    schedule 22.05.2015
comment
Многочисленные комментарии поучают меня, как задать мой вопрос, но ни одного комментария по самому вопросу. Грустный.   -  person chameau13    schedule 24.05.2015


Ответы (1)


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

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

person Martin Mächler    schedule 26.05.2015
comment
Спасибо за ваш ответ: 1) По комментарию Дирка Эддельбюттеля выше я предполагаю, что векторизованная версия pnorm и код, который я фактически пишу, эквивалентны. 2) Я использую журналы, конечно. Может быть, я был небрежен с моим языком и должен был написать, что это будет частью функции логарифмического правдоподобия. Но можете ли вы в общих чертах описать, как я могу работать с dnorm и какие у этого преимущества? MLE - это иерархическая упорядоченная пробит-модель, и пока я не прочитал ваш комментарий, у меня всегда было впечатление, что мне нужен cdf. таким образом pnorm для него .... - person chameau13; 26.05.2015
comment
Если вам действительно нужна пробит-модель, вам нужна pnorm(). OTOH, многие статистики предпочитают логит по умолчанию пробиту... одна из причин заключается в том, что вероятность становится намного проще, включая ее градиент и т. д. Таким образом, для вашей проблемы вы можете сначала вычислить логит-решение (которое имеет дешевую логарифмическую вероятность ), а затем использовать его решение в качестве отправной точки для более дорогого пробит-решения. Я бы порекомендовал остаться с вычислением на основе pnorm(). Потеря скорости составляет менее 50%, так какое вам дело? Надежность и портативность важнее, я бы сказал. - person Martin Mächler; 29.05.2015