Javascript中的累积分布函数

16

你的数据是什么样子的? - Alexandre C.
6个回答

17
我在参考Python的erf()函数有哪些实现方法?和维基百科的知识帮助下,成功编写了自己的函数。
由于这只是一种近似计算方法,所以结果并不完全正确。
function normalcdf(mean, sigma, to) 
{
    var z = (to-mean)/Math.sqrt(2*sigma*sigma);
    var t = 1/(1+0.3275911*Math.abs(z));
    var a1 =  0.254829592;
    var a2 = -0.284496736;
    var a3 =  1.421413741;
    var a4 = -1.453152027;
    var a5 =  1.061405429;
    var erf = 1-(((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*Math.exp(-z*z);
    var sign = 1;
    if(z < 0)
    {
        sign = -1;
    }
    return (1/2)*(1+sign*erf);
}

normalcdf(30, 25, 1.4241); //-> 0.12651187738346226
//wolframalpha.com              0.12651200000000000

function normalcdf(b, a, c) { b = (c - b) / Math.sqrt(2 * a * a); a = 1 / (1 + 0.3275911 * Math.abs(b)); a = 1 - ((((1.061405429 * a + -1.453152027) * a + 1.421413741) * a + -0.284496736) * a + 0.254829592) * a * Math.exp(-b * b); c = 1; 0 > b && (c = -1); return 0.5 * (1 + c * a) }; - 这样应该更紧凑。 - 19h
1
你确定这个计算的是CDF吗?对于一组均值为30,标准差为25的正态分布,WolframAlpha在1.4241处返回了0.126512:https://www.wolframalpha.com/input/?i=CDF%28normal+distribution+with+mean%3D+30+and+sd+%3D+25%2C+1.4241%29 - woobert
@woobert 我验证了代码片段中的注释是错误的,实际上呈现的函数返回的是 Wolfram 答案的近似值。已编辑帖子以反映这一点。 - V0ldek

13

math.js库提供了一个erf函数。根据在Wolfram Alpha找到的定义,cdfNormal函数可以用以下Javascript代码实现:

const mathjs = require('mathjs')

function cdfNormal (x, mean, standardDeviation) {
  return (1 - mathjs.erf((mean - x ) / (Math.sqrt(2) * standardDeviation))) / 2
}

node.js 控制台:

> console.log(cdfNormal(5, 30, 25))
> 0.15865525393145707 // Equal to Wolfram Alpha's result at: https://sandbox.open.wolframcloud.com/app/objects/4935c1cb-c245-4d8d-9668-4d353ad714ec#sidebar=compute

6

这个公式会给出正确的正态分布函数值,而不像当前被接受的答案那样

function ncdf(x, mean, std) {
  var x = (x - mean) / std
  var t = 1 / (1 + .2315419 * Math.abs(x))
  var d =.3989423 * Math.exp( -x * x / 2)
  var prob = d * t * (.3193815 + t * ( -.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))))
  if( x > 0 ) prob = 1 - prob
  return prob
}

这个答案来自于 math.ucla.edu,内容涉及IT技术。

4

这是一种暴力实现,但可以提高更多精度位数。上述逼近值在10^-7内准确。我的实现速度较慢(700纳秒),但精度在10^-14范围内。normal(25,30,1.4241) === 0.00022322110257305683,相比 Wolfram 的 0.000223221102572082 更为准确。

它采用标准正态分布的幂级数即钟形曲线,然后对级数进行积分计算。

我最初是用C语言编写的,所以我承认在JavaScript中有些优化看起来可能有些愚蠢。

function normal(x, mu, sigma) {
    return stdNormal((x-mu)/sigma);
}

function stdNormal(z) {
    var j, k, kMax, m, values, total, subtotal, item, z2, z4, a, b;

    // Power series is not stable at these extreme tail scenarios
    if (z < -6) { return 0; }
    if (z >  6) { return 1; }

    m      = 1;        // m(k) == (2**k)/factorial(k)
    b      = z;        // b(k) == z ** (2*k + 1)
    z2     = z * z;    // cache of z squared
    z4     = z2 * z2;  // cache of z to the 4th
    values = [];

    // Compute the power series in groups of two terms.
    // This reduces floating point errors because the series
    // alternates between positive and negative.
    for (k=0; k<100; k+=2) {
        a = 2*k + 1;
        item = b / (a*m);
        item *= (1 - (a*z2)/((a+1)*(a+2)));
        values.push(item);
        m *= (4*(k+1)*(k+2));
        b *= z4;
    }

    // Add the smallest terms to the total first that
    // way we minimize the floating point errors.
    total = 0;
    for (k=49; k>=0; k--) {
        total += values[k];
    }

    // Multiply total by 1/sqrt(2*PI)
    // Then add 0.5 so that stdNormal(0) === 0.5
    return 0.5 + 0.3989422804014327 * total;
}

3
这是一个解决方案,它给我提供了与 Excel 或 Google Sheets 提供的 NORMSDIST 函数相同的结果,而我发现其他所有函数都给我不同的结果。 - Max
这个答案是正确的,而被接受的答案不是。 - Ian H
我确认这与Excel等中的NORMSDIST相匹配,并且是我在进行了相当广泛的研究后在互联网上找到的唯一版本!@steve,请通过Twitter私信给我(https://twitter.com/HugoBailey - 或者我的其他社交媒体账号),因为我想请你喝杯咖啡 :-) - hobailey

4
过去因某些需要,我编写了一个javascript实现的分布函数。我的库可在github上获得。您可以访问https://github.com/chen0040/js-stats查看。

它提供了标准正态分布、学生t分布、f分布和卡方分布的CDF和反CDF的javascript实现。

使用js库获取CDF和反CDF:

jsstats = require('js-stats');

//====================NORMAL DISTRIBUTION====================//

var mu = 0.0; // mean
var sd = 1.0; // standard deviation
var normal_distribution = new jsstats.NormalDistribution(mu, sd);

var X = 10.0; // point estimate value 
var p = normal_distribution.cumulativeProbability(X); // cumulative probability

var p = 0.7; // cumulative probability
var X = normal_distribution.invCumulativeProbability(p); // point estimate value

//====================T DISTRIBUTION====================//

var df = 10; // degrees of freedom for t-distribution
var t_distribution = new jsstats.TDistribution(df);

var t_df = 10.0; // point estimate or test statistic
var p = t_distribution.cumulativeProbability(t_df); // cumulative probability

var p = 0.7;
var t_df = t_distribution.invCumulativeProbability(p); // point estimate or test statistic


//====================F DISTRIBUTION====================//

var df1 = 10; // degrees of freedom for f-distribution
var df2 = 20; // degrees of freedom for f-distribution
var f_distribution = new jsstats.FDistribution(df1, df2);

var F = 10.0; // point estimate or test statistic
var p = f_distribution.cumulativeProbability(F); // cumulative probability


//====================Chi Square DISTRIBUTION====================//

var df = 10; // degrees of freedom for cs-distribution
var cs_distribution = new jsstats.ChiSquareDistribution(df);

var X = 10.0; // point estimate or test statistic
var p = cs_distribution.cumulativeProbability(X); // cumulative probability


看了GitHub上的代码,非常漂亮。 - Peter Buchmann

1

您也可以在这里查看,这是一个用JavaScript实现的科学计算器,它包括erf函数,其作者声称对实现没有版权要求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接