开发者

Implementation of NORMSINV function in MySQL

I need to use the inverse normal cumulative distribution function (what in Excel is NORMSINV function) in MySQL, but there is no such function there. Maybe any of you has an implementation of it for MySQL?

Many thanks for your tim开发者_高级运维e.


Well, I've finally found this. It's not perfect but a quite good aproximation. The code isn't mine, its author is Geoffrey C. Barnes. I've just transformed it from VB.NET to MySQL.

DROP FUNCTION IF EXISTS NORMSINV;
DELIMITER //

CREATE FUNCTION NORMSINV (p DOUBLE) RETURNS DOUBLE
BEGIN

DECLARE q, r DOUBLE;
DECLARE A1, A2, A3, A4, A5, A6 DOUBLE;
DECLARE B1, B2, B3, B4, B5 DOUBLE;
DECLARE C1, C2, C3, C4, C5, C6 DOUBLE;
DECLARE D1, D2, D3, D4 DOUBLE;
DECLARE P_LOW, P_HIGH DOUBLE;

/* coefficients in rational approximations */
SET A1 = -39.696830286653757;
SET A2 = 220.9460984245205;
SET A3 = -275.92851044696869;
SET A4 = 138.357751867269;
SET A5 = -30.66479806614716;
SET A6 = 2.5066282774592392;

SET B1 = -54.476098798224058;
SET B2 = 161.58583685804089;
SET B3 = -155.69897985988661;
SET B4 = 66.80131188771972;
SET B5 = -13.280681552885721;

SET C1 = -0.0077848940024302926;
SET C2 = -0.32239645804113648;
SET C3 = -2.4007582771618381;
SET C4 = -2.5497325393437338;
SET C5 = 4.3746641414649678;
SET C6 = 2.9381639826987831;

SET D1 = 0.0077846957090414622;
SET D2 = 0.32246712907003983;
SET D3 = 2.445134137142996;
SET D4 = 3.7544086619074162;

/* define break points */
SET P_LOW = 0.02425;
SET P_HIGH = 1 - P_LOW;

IF (p > 0 AND p < P_LOW) THEN
/* rational approximation for lower region */
SET q = SQRT(-2 * LOG(p));
RETURN (((((C1 * q + C2) * q + C3) * q + C4) * q + C5) * q + C6) /  
       ((((D1 * q + D2) * q + D3) * q + D4) * q + 1);

ELSEIF (p >= P_LOW AND p <= P_HIGH) THEN
/* rational approximation for central region */
SET q = p - 0.5;
SET r = q * q;
RETURN (((((A1 * r + A2) * r + A3) * r + A4) * r + A5) * r + A6) * q / 
       (((((B1 * r + B2) * r + B3) * r + B4) * r + B5) * r + 1);

ELSEIF (p > P_HIGH AND p < 1) THEN
/* rational approximation for upper region */
SET q = SQRT(-2 * LOG(1 - p));
RETURN -(((((C1 * q + C2) * q + C3) * q + C4) * q + C5) * q + C6) / 
       ((((D1 * q + D2) * q + D3) * q + D4) * q + 1);

/* on error returning 0 */           
ELSE
RETURN 0;
END IF;

END//
DELIMITER ;


Below is an equivalence of Excel's NORMINV function.

CREATE FUNCTION NORMINV(p double, mu double, sigma double)
RETURNS decimal(20,6)
begin
    declare q   double;
    declare r   double;
    declare val double;
BEGIN
    IF p < 0 OR p > 1 THEN
        signal sqlstate '20100' set message_text= 'The probality p must be bigger than 0 and smaller than 1';
    END IF;
    IF sigma < 0 THEN
        signal sqlstate '20100' set message_text=  'The standard deviation sigma must be positive';
    END IF;
    IF p = 0 THEN 
        RETURN to_binary_double('-INF');    
    END IF;
    IF p = 1 THEN
        RETURN to_binary_double('INF');
    END IF;
    IF sigma = 0 THEN
        RETURN mu;
    END IF;

    set q:= p - 0.5;

    IF(ABS(q) <= .425) THEN
            set r:= .180625 - q * q;
            set val :=
                 q * (((((((r * 2509.0809287301226727 +
                            33430.575583588128105) * r + 67265.770927008700853) * r +
                          45921.953931549871457) * r + 13731.693765509461125) * r +
                        1971.5909503065514427) * r + 133.14166789178437745) * r +
                      3.387132872796366608)
                 / (((((((r * 5226.495278852854561 +
                          28729.085735721942674) * r + 39307.89580009271061) * r +
                        21213.794301586595867) * r + 5394.1960214247511077) * r +
                      687.1870074920579083) * r + 42.313330701600911252) * r + 1);
    ELSE
        /* r = min(p, 1-p) < 0.075 */
        IF q > 0 THEN
            set r:= 1 - p;
        ELSE
            set r:= p;
        END IF;

        set r:= SQRT(-LN(r));
        /* r = sqrt(-log(r))  <==>  min(p, 1-p) = exp( - r^2 ) */

        IF (r <= 5) THEN
            set r:= r - 1.6;
            set val := (((((((r * 7.7454501427834140764e-4 +
                         .0227238449892691845833) * r + .24178072517745061177) *
                       r + 1.27045825245236838258) * r +
                      3.64784832476320460504) * r + 5.7694972214606914055) *
                    r + 4.6303378461565452959) * r +
                   1.42343711074968357734)
                  / (((((((r *
                           1.05075007164441684324e-9 + 5.475938084995344946e-4) *
                          r + .0151986665636164571966) * r +
                         .14810397642748007459) * r + .68976733498510000455) *
                       r + 1.6763848301838038494) * r +
                      2.05319162663775882187) * r + 1);
        ELSE /* very close to  0 or 1 */
            set r := r - 5;
            set val := (((((((r * 2.01033439929228813265e-7 +
                         2.71155556874348757815e-5) * r +
                        .0012426609473880784386) * r + .026532189526576123093) *
                      r + .29656057182850489123) * r +
                     1.7848265399172913358) * r + 5.4637849111641143699) *
                   r + 6.6579046435011037772)
                  / (((((((r *
                           2.04426310338993978564e-15 + 1.4215117583164458887e-7) *
                          r + 1.8463183175100546818e-5) * r +
                         7.868691311456132591e-4) * r + .0148753612908506148525)
                       * r + .13692988092273580531) * r +
                      .59983220655588793769) * r + 1);
        END IF;

        IF q < 0.0 THEN
            set val := -val;
        END IF;
    END IF;

    RETURN mu + sigma * val;
END;
END

It has been adapted from [https://forums.mysql.com/read.php?98,411441,411458#msg-411458] which is Oracle equivalence. Have used it, so far so good. Hope it assists someone.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜