如何计算java中的逆累积beta分布函数

时间:2012-08-20 08:56:55

标签: java distribution apache-commons-math

我正在寻找一个java库/实现,它支持计算β分布的逆累积分布函数(也就是分位数的估计) ,具有合理的精度

我当然尝试过apache commons math,但在第3版中,似乎还有一些issues with the precision。下面引出这个问题的问题被广泛描述。


假设我想通过大量试验来计算β分布的可信区间。在 apache commons math ...

final int trials = 161750;
final int successes = 10007;
final double alpha = 0.05d;

// the supplied precision is the default precision according to the source code
BetaDistribution betaDist = new BetaDistribution(successes + 1, trials - successes + 1, 1e-9);

System.out.println("2.5 percentile :" + betaDist.inverseCumulativeProbability(alpha / 2d));
System.out.println("mean: " + betaDist.getNumericalMean());
System.out.println("median: " + betaDist.inverseCumulativeProbability(0.5));
System.out.println("97.5 percentile :" + betaDist.inverseCumulativeProbability(1 - alpha / 2d));

提供

2.5 percentile :0.062030402074808505
mean: 0.06187249616697166
median: 0.062030258659508855
97.5 percentile :0.06305170793994147

问题是2.5百分位数和中位数是相同的,同时两者都大于均值。

相比之下, R -package binom 提供

binom.confint(10007+1,161750+2,methods=c("agresti-coull","exact","wilson"))
         method     x      n      mean      lower      upper
1 agresti-coull 10008 161752 0.0618725 0.06070873 0.06305707
2         exact 10008 161752 0.0618725 0.06070317 0.06305756
3        wilson 10008 161752 0.0618725 0.06070877 0.06305703

R -package stats

qbeta(c(0.025,0.975),10007+1,161750-10007+1)
[1] 0.06070355 0.06305171

为了得到R的结果,这里是 Wolfram Alpha 告诉我的

有关要求的最终说明:

  • 我需要运行很多这些计算。因此,任何解决方案都不应超过1秒(与41ms(虽然错误)apache commons math)相比仍然很多。
  • 我知道可以在java中使用R。由于我在此不再详述的原因,如果其他任何事情(纯java)失败,这是最后一个选项。

更新21.08.12

It seems该问题已经修复或至少在apache-commons-math的3.1-SNAPSHOT中有所改进。对于上面的用例

2.5 percentile :0.06070354581340706
mean: 0.06187249616697166
median: 0.06187069085946604
97.5 percentile :0.06305170793994147

更新23.02.13

虽然乍一看这个问题和它的回答可能过于局部化,但我认为它很好地说明了一些数字问题无法通过首先出现在头脑中的黑客方法来解决(有效)。所以我希望它仍然开放。

3 个答案:

答案 0 :(得分:2)

此问题已在 apache commons math 3.1.1

中修复

上面提供的测试用例

2.5 percentile :0.06070354581334864
mean: 0.06187249616697166
median: 0.06187069085930821
97.5 percentile :0.0630517079399996

匹配r-package统计信息的结果。 3.1-SNAPSHOT + x版本的广泛应用也没有引起任何问题。

答案 1 :(得分:0)

最有可能的是,这个问题一般无法解决,因为如果累积分布函数的图形非常平坦(通常它将朝向分布的尾部),则需要在垂直轴上具有非常高的精度。在水平轴上达到合理的精度。

因此,使用直接计算分位数的函数总是比从累积分布函数中导出分位数更好。

如果您不担心精度,当然可以用数字方式求解方程q = F(x)。由于F正在增加,这并不困难:

   double x_u = 0.0;
   double x_l = 0.0;

   // find some interval quantile is in
   if ( F (0.0) > q) {
      while ( F (x_l) > q) {
         x_u = x_l;
         x_l = 2.0 * x_l - 1.0;
      }
   } else {
      while ( F (x_u) < q) {
         x_l = x_u;
         x_u = 2.0 * x_u + 1.0;
      }
   }

   // narrow down interval to necessary precision
   while ( x_u - x_l > precision ) {
      double m = (x_u - x_l) / 2.0;
      if ( F (m) > q ) x_u = m; else x_l = m;
   }     
   // quantile will be within [x_l; x_u]

备注:我不清楚为什么精度应该是一个问题,特别是对于β分布,因为β分布存在于区间[0; 1]并且图形相当陡峭间隔的结束。

第二句:您对上分位数的计算是错误的;它应该读

System.out.println( "97.5 percentile :" + betaDist.inverseCumulativeProbability( 1 - alpha / 2d ) );

第三次修改:已修正算法。

答案 2 :(得分:0)

我找到并尝试了库JSci(版本1.2 27.07.2010)

代码段:

final int trials = 162000;
final int successes = 10000;
final double alpha =0.05d;

BetaDistribution betaDist = new BetaDistribution(successes + 1, trials - successes + 1);
long timeSum = 0;
for(double perc : new double[]{alpha/2,0.5,1-alpha/2}){
    long time = System.currentTimeMillis();
    System.out.println((perc*100) + " percentile :" + betaDist.inverse(perc));
    timeSum += System.currentTimeMillis()-time;
}
System.out.println("Took ~" + timeSum/3 + " per call");

返回

2.5 percentile :0.060561615036184686
50.0 percentile :0.06172659147924378
97.5 percentile :0.06290542466617127
Took ~2ms per call

在内部使用根发现方法作为JohnB的建议。可以扩展ProbabilityDistribution#inverse以请求更高的精度。不幸的是,即使有大量的迭代(100k)和10 ^ -10的请求精度,算法仍然会返回

2.5 percentile :0.06056698485628473
50.0 percentile :0.06173200221779383
97.5 percentile :0.06291087598052053
Took ~564ms per call

现在:谁的代码错误少了? R还是JSci?我赞成拥有更大用户群的人......