处理比例很小的数字以及如何保持指数值

时间:2018-09-21 13:05:56

标签: r performance exponential

我目前正在R Studio中使用R版本3.4.4(2018-03-15)。

我需要计算两个值的比率。而且我在某些情况下有问题:

  • 分子可以是非常小的值:exp(-2408.9)R近似为0。
  • 分母也为:exp(-2405)被计算为0为R。

计算比率时,我得到一个NaN(因为0/0)。

第一个解决方案

我使用了 Brobdingnag 库,该库允许将数字保留为指数,并最终得出该比率实际上是:exp(-3.8987)= 0.02026725

但是,使用库 profvis 检查我的代码的性能时,我可以看到,尽管在我的案例中Brobdingnag库非常有用,但在花费方面性能。而且我不能保留这种解决方案,因为我必须对我的算法进行很多模拟。

其他解决方案的问题:

您是否听说过另一个可以处理很小(或很大)值的库?

在进行除法运算之前,我想将分子和分母保持在指数表达式中,但是我不知道该怎么做。当然,因为我的分子和分母是向量,一旦它们都被计算,就将它们除。 (没有分子向量我无法获得分母) 有没有办法“强制” R将值保留为exp而不是整数(和0 ...)?

在此先感谢您的帮助。

编辑:

这是我要计算的比率:

https://ibb.co/dFHx4z

我不确定是否可以使用技巧:exp(x)/ exp(y)= exp(x-y),因为我有一个加到定理中的和。 这就是为什么我需要exp公式直到执行比率... exp内的值是一个非常大的负数,这些数字的exp都为0。此外,我尝试将分子转换为对数,因此我可以将过去的记录对数+第二部分(没有exp),但有时将分子的第一部分(1 / sqrt ...)太小,其记录返回Inf ..

我认为有办法,但是我找不到。

感谢所有答案!

编辑2:

####### Fonction that calculate the density (with brobdingnag package) :

density <- function(nc,yc,X,beta,sig,k){

    # n_c is a vector of integer 
    # y_c is a vector of numeric 
    # X is a matrix 
    # beta is a vector of numeric 
    # sigma is a value

res<-as.brob((1/(2*pi*sig[k])))^(nc/2)*exp(as.brob(-(1/(2*sig[k]))*t(yc-(X %*% beta[,k])) %*% (yc-(X %*% beta[,k]))))
return(res)
}

####### Code for calculation of the ratio :

# n_c[c] : num [1] 340
# y_c[c] : num [1:340] 1.279 0.777 1.069 0.864 1.56 ...
# X[c] : num [1:340, 1:11] 1 1 1 1 1 1 1 1 1 1 ... (matrix of 0 and 1)
# beta : num [1:11, 1:2] 1.542 -0.226 -0.145 -0.438 -0.201 ...
# sigma : num [1:2] 21.694381  4.267277
# lambda : num [1] 0.5

# Numerator :

num_tau<-sapply(1:100,function(c){
        sapply(1:4,function(k){
            lambda[k]*density(n_c[c], y_c[c],X[c],beta,sigma,k)
        })
    })

# Denominator :

denom_tau<-list()
for (c in 1:100){
    val<-0
    for (k in 1:4){
        val<-val+num_tau[k,c][[1]]
    }
denom_tau[[c]]<-val
}

# Ratio :
for (l in 1:4){
    for (c in 1:100){
        tau[l,c]<-as.numeric(num_tau[l,c][[1]]/denom_tau[[c]])
    }
}

2 个答案:

答案 0 :(得分:0)

如果两个值都需要先进行指数运算,则可以使用公式:

e ^ x / e ^ y = e ^(x-y)

否则,您可以尝试使用Rmpfr软件包。

示例:

require(Rmpfr)
p = 40
x <- mpfr(-2408.9, p)
y <- mpfr(-2405, p)
exp(x)/exp(y)
# 1 'mpfr' number of precision  40   bits 
# [1] 0.02024191147598

答案 1 :(得分:0)

按照@minem的建议,您可以使用 Rmpfr 软件包。这是将其应用于您的案例的一种方法。

首先使用a * exp(b)= exp(b + log(a))的事实,将乘法器移动到分子的指数内。然后,重新编写您的density函数以计算日志分子:

log_numerator <- function(nc, yc, X, beta, sig, k, lambda){
  v <- yc - X %*% beta[,k]
  res <- -sum(v*v)/(2*sig[k]) - (nc/2)*log(2*pi*sig[k]) + log(lambda[k])
  drop(res)
}

请注意,lambda现在已传递给此函数。还要注意,如图所示,我们可以更有效地计算向量Y-X * beta的点积。

现在我们可以生成一些数据。在这里,我修复了c,只是k = 1:2。

set.seed(1)
n_c <- 340
y_c <- rnorm(340)
dat <- data.frame(fac = sample(letters[1:11], 340, replace = TRUE)
X_c <- model.matrix(~ fac, data = dat)
beta <- matrix(runif(22, -10, 10), 11, 2)
sigma <- c(21.694381,  4.267277)
lambda <- c(0.5, 0.5)

使用您的密度函数

x1 <- lambda[1] *density(n_c, y_c,X_c,beta,sigma,1)
y1 <- lambda[2] *density(n_c, y_c,X_c,beta,sigma,2)
x1
# [1] +exp(-1738.4)
y1
# [1] +exp(-1838.7)
as.numeric(y1/sum(x1, y1))
# [1] 2.780805e-44

使用对数分子功能

p <- 40
x <- mpfr(log_numerator(n_c, y_c,X_c,beta,sigma,1, lambda), p)
y <- mpfr(log_numerator(n_c, y_c,X_c,beta,sigma,2, lambda), p)
x
# 1 'mpfr' number of precision  40   bits 
# [1] -1738.379327798
y
# 1 'mpfr' number of precision  40   bits 
# [1] -1838.67033143
exp(y)/sum(exp(x), exp(y))
# 1 'mpfr' number of precision  53   bits 
# [1] 2.780805017186589e-44

因此可以肯定mpfr可以用来产生等效的结果,但是如果没有更好的测试代码,很难检查时间。

您还可以通过使用更多的向量化来提高效率。例如。我们可以在k上向量化log_numerator

log_numerator2 <- function(nc, yc, X, beta, sig, lambda){
  M <- yc - X %*% beta
  res <- -colSums(M*M)/(2*sig) - (nc/2)*log(2*pi*sig) + log(lambda)
  drop(res)
}
z <- log_numerator2(n_c, y_c, X_c, beta, sigma, lambda)
z
# [1] -1738.379 -1838.670

现在假设我们在c x k矩阵中有对数分子,为说明起见,假设所有c的值都与z相同,

log_num <- mpfr(matrix(z, byrow = TRUE, 3, 2), p)

您可以按如下方式计算比率

num <- exp(log_num)
denom <- apply(num, 1, sum) # rowSums not implemented for mpfr
num/denom
# 'mpfrMatrix' of dim(.) =  (3, 2) of precision  53   bits 
#     [,1]              [,2]                 
# [1,] 1.000000000000000 2.780805017186589e-44
# [2,] 1.000000000000000 2.780805017186589e-44
# [3,] 1.000000000000000 2.780805017186589e-44