[gnumeric] fn-r: implement skew-t cdf and inverse.



commit c7582a2644a5bb8b1ec6120eeb9726473d6729e3
Author: Morten Welinder <terra gnome org>
Date:   Thu Apr 4 14:46:01 2013 -0400

    fn-r: implement skew-t cdf and inverse.

 NEWS                       |    2 +-
 plugins/fn-r/ChangeLog     |    4 ++
 plugins/fn-r/extra.c       |   77 +++++++++++++++++++++++++++++--
 plugins/fn-r/functions.c   |  110 ++++++++++++++++++++++++++++++++++++++++++-
 plugins/fn-r/generate      |    6 +--
 plugins/fn-r/plugin.xml.in |    3 +
 src/mathfunc.c             |    2 +-
 src/mathfunc.h             |    1 +
 tools/import-R             |    2 +-
 9 files changed, 193 insertions(+), 14 deletions(-)
---
diff --git a/NEWS b/NEWS
index 1ebceda..bcd7aa0 100644
--- a/NEWS
+++ b/NEWS
@@ -20,7 +20,7 @@ Morten:
        * Fix focus problem in cell comment dialog.  [#696826]
        * Move sheet renaming into a dialog.
        * Make it easier to see what sheet is selected.  [#659317]
-
+       * Implement R.PST, R.QST, and R.QSNORM.
 
 --------------------------------------------------------------------------
 Gnumeric 1.12.1
diff --git a/plugins/fn-r/ChangeLog b/plugins/fn-r/ChangeLog
index 64651ef..f18192f 100644
--- a/plugins/fn-r/ChangeLog
+++ b/plugins/fn-r/ChangeLog
@@ -1,3 +1,7 @@
+2013-04-04  Morten Welinder  <terra gnome org>
+
+       * extra.c (pst): Implement.
+
 2013-03-09  Morten Welinder <terra gnome org>
 
        * Release 1.12.1
diff --git a/plugins/fn-r/extra.c b/plugins/fn-r/extra.c
index 6366b79..9a50c3f 100644
--- a/plugins/fn-r/extra.c
+++ b/plugins/fn-r/extra.c
@@ -210,13 +210,22 @@ dst (gnm_float x, gnm_float n, gnm_float shape, gboolean give_log)
        }
 }
 
-
 gnm_float
 pst (gnm_float x, gnm_float n, gnm_float shape, gboolean lower_tail, gboolean log_p)
 {
+       gnm_float p;
+
+       if (n <= 0)
+               return gnm_nan;
+
        if (shape == 0.)
                return pt (x, n, lower_tail, log_p);
 
+       if (n > 100) {
+               /* Approximation */
+               return psnorm (x, shape, 0.0, 1.0, lower_tail, log_p);
+       }
+
        /* Generic fallback.  */
        if (!lower_tail)
                return log_p
@@ -225,10 +234,70 @@ pst (gnm_float x, gnm_float n, gnm_float shape, gboolean lower_tail, gboolean lo
        if (log_p)
                gnm_log (pst (x, n, shape, TRUE, FALSE));
 
-       /* FIXME: Numerical integration of dst from -inf to x.  */
+       if (n != gnm_floor (n)) {
+               /* We would need numerical integration for this.  */
+               return gnm_nan;
+       }
+
+       /*
+        * Use recurrence formula from "Recurrent relations for
+        * distributions of a skew-t and a linear combination of order
+        * statistics form a bivariate-t", Computational Statistics
+        * and Data Analysis volume 52, 2009 by Jamallizadeh,
+        * Khosravi, Balakrishnan.
+        *
+        * This brings us down to n==1 or n==2 for which explicit formulas
+        * are available.
+        */
+
+       p = 0;
+       while (n > 2) {
+               double a, lb, c, d, pv, v = n - 1;
+
+               d = v == 2
+                       ? M_LN2gnum - gnm_log (M_PIgnum) + gnm_log (3) / 2
+                       : (0.5 + M_LN2gnum / 2 - gnm_log (M_PIgnum) / 2 +
+                          v / 2 * (gnm_log1p (-1 / (v - 1)) + gnm_log (v + 1)) -
+                          0.5 * (gnm_log (v - 2) + gnm_log (v + 1)) +
+                          stirlerr (v / 2 - 1) -
+                          stirlerr ((v - 1) / 2));
+
+               a = v + 1 + x * x;
+               lb = (d - gnm_log (a) * v / 2);
+               c = pt (gnm_sqrt (v) * shape * x / gnm_sqrt (a), v, TRUE, FALSE);
+               pv = x * gnm_exp (lb) * c;
+               p += pv;
+
+               n -= 2;
+               x *= gnm_sqrt ((v - 1) / (v + 1));
+       }
+
+       g_return_val_if_fail (n == 1 || n == 2, gnm_nan);
+       if (n == 1) {
+               gnm_float p1;
+
+               p1 = (gnm_atan (x) + gnm_acos (shape / gnm_sqrt ((1 + shape * shape) * (1 + x * x)))) / 
M_PIgnum;
+               p += p1;
+       } else if (n == 2) {
+               gnm_float p2, f;
+
+               f = x / gnm_sqrt (2 + x * x);
+
+               p2 = (0.5 - gnm_atan (shape) / M_PIgnum) +
+                       f * (0.5 + gnm_atan (shape * f) / M_PIgnum);
+
+               p += p2;
+       } else {
+               return gnm_nan;
+       }
+
+       /*
+        * Negatives can occur due to rounding errors and hopefully for no
+        * other reason.
+        */
+       p = CLAMP (p, 0.0, 1.0);
 
-       /* Give up.  */
-       return gnm_nan;
+       return p;
 }
 
 
diff --git a/plugins/fn-r/functions.c b/plugins/fn-r/functions.c
index 45ef4ce..066bd4b 100644
--- a/plugins/fn-r/functions.c
+++ b/plugins/fn-r/functions.c
@@ -1187,7 +1187,7 @@ static GnmFuncHelp const help_r_dsnorm[] = {
        { GNM_FUNC_HELP_ARG, F_("scale:the scale parameter of the distribution") },
        { GNM_FUNC_HELP_ARG, F_("give_log:if true, log of the result will be returned instead") },
        { GNM_FUNC_HELP_DESCRIPTION, F_("This function returns the probability density function of the 
skew-normal distribution.") },
-       { GNM_FUNC_HELP_SEEALSO, "R.PSNORM" },
+       { GNM_FUNC_HELP_SEEALSO, "R.PSNORM,R.QSNORM" },
        { GNM_FUNC_HELP_END }
 };
 
@@ -1215,7 +1215,7 @@ static GnmFuncHelp const help_r_psnorm[] = {
        { GNM_FUNC_HELP_ARG, F_("lower_tail:if true (the default), the lower tail of the distribution is 
considered") },
        { GNM_FUNC_HELP_ARG, F_("log_p:if true, log of the probability is used") },
        { GNM_FUNC_HELP_DESCRIPTION, F_("This function returns the cumulative distribution function of the 
skew-normal distribution.") },
-       { GNM_FUNC_HELP_SEEALSO, "R.DSNORM" },
+       { GNM_FUNC_HELP_SEEALSO, "R.DSNORM,R.QSNORM" },
        { GNM_FUNC_HELP_END }
 };
 
@@ -1235,6 +1235,35 @@ gnumeric_r_psnorm (GnmFuncEvalInfo *ei, GnmValue const * const *args)
 /* ------------------------------------------------------------------------- */
 
 
+static GnmFuncHelp const help_r_qsnorm[] = {
+       { GNM_FUNC_HELP_NAME, F_("R.QSNORM:probability quantile function of the skew-normal distribution") },
+       { GNM_FUNC_HELP_ARG, F_("p:probability") },
+       { GNM_FUNC_HELP_ARG, F_("shape:the shape parameter of the distribution") },
+       { GNM_FUNC_HELP_ARG, F_("location:the location parameter of the distribution") },
+       { GNM_FUNC_HELP_ARG, F_("scale:the scale parameter of the distribution") },
+       { GNM_FUNC_HELP_ARG, F_("lower_tail:if true (the default), the lower tail of the distribution is 
considered") },
+       { GNM_FUNC_HELP_ARG, F_("log_p:if true, log of the probability is used") },
+       { GNM_FUNC_HELP_DESCRIPTION, F_("This function returns the probability quantile function, i.e., the 
inverse of the cumulative distribution function, of the skew-normal distribution.") },
+       { GNM_FUNC_HELP_SEEALSO, "R.DSNORM,R.PSNORM" },
+       { GNM_FUNC_HELP_END }
+};
+
+static GnmValue *
+gnumeric_r_qsnorm (GnmFuncEvalInfo *ei, GnmValue const * const *args)
+{
+       gnm_float p = value_get_as_float (args[0]);
+       gnm_float shape = value_get_as_float (args[1]);
+       gnm_float location = value_get_as_float (args[2]);
+       gnm_float scale = value_get_as_float (args[3]);
+       gboolean lower_tail = args[4] ? value_get_as_checked_bool (args[4]) : TRUE;
+       gboolean log_p = args[5] ? value_get_as_checked_bool (args[5]) : FALSE;
+
+       return value_new_float (qsnorm (p, shape, location, scale, lower_tail, log_p));
+}
+
+/* ------------------------------------------------------------------------- */
+
+
 static GnmFuncHelp const help_r_dst[] = {
        { GNM_FUNC_HELP_NAME, F_("R.DST:probability density function of the skew-t distribution") },
        { GNM_FUNC_HELP_ARG, F_("x:observation") },
@@ -1242,7 +1271,7 @@ static GnmFuncHelp const help_r_dst[] = {
        { GNM_FUNC_HELP_ARG, F_("shape:the shape parameter of the distribution") },
        { GNM_FUNC_HELP_ARG, F_("give_log:if true, log of the result will be returned instead") },
        { GNM_FUNC_HELP_DESCRIPTION, F_("This function returns the probability density function of the skew-t 
distribution.") },
-       { GNM_FUNC_HELP_SEEALSO, "" },
+       { GNM_FUNC_HELP_SEEALSO, "R.PST,R.QST" },
        { GNM_FUNC_HELP_END }
 };
 
@@ -1259,6 +1288,60 @@ gnumeric_r_dst (GnmFuncEvalInfo *ei, GnmValue const * const *args)
 
 /* ------------------------------------------------------------------------- */
 
+
+static GnmFuncHelp const help_r_pst[] = {
+       { GNM_FUNC_HELP_NAME, F_("R.PST:cumulative distribution function of the skew-t distribution") },
+       { GNM_FUNC_HELP_ARG, F_("x:observation") },
+       { GNM_FUNC_HELP_ARG, F_("n:the number of degrees of freedom of the distribution") },
+       { GNM_FUNC_HELP_ARG, F_("shape:the shape parameter of the distribution") },
+       { GNM_FUNC_HELP_ARG, F_("lower_tail:if true (the default), the lower tail of the distribution is 
considered") },
+       { GNM_FUNC_HELP_ARG, F_("log_p:if true, log of the probability is used") },
+       { GNM_FUNC_HELP_DESCRIPTION, F_("This function returns the cumulative distribution function of the 
skew-t distribution.") },
+       { GNM_FUNC_HELP_SEEALSO, "R.DST,R.QST" },
+       { GNM_FUNC_HELP_END }
+};
+
+static GnmValue *
+gnumeric_r_pst (GnmFuncEvalInfo *ei, GnmValue const * const *args)
+{
+       gnm_float x = value_get_as_float (args[0]);
+       gnm_float n = value_get_as_float (args[1]);
+       gnm_float shape = value_get_as_float (args[2]);
+       gboolean lower_tail = args[3] ? value_get_as_checked_bool (args[3]) : TRUE;
+       gboolean log_p = args[4] ? value_get_as_checked_bool (args[4]) : FALSE;
+
+       return value_new_float (pst (x, n, shape, lower_tail, log_p));
+}
+
+/* ------------------------------------------------------------------------- */
+
+
+static GnmFuncHelp const help_r_qst[] = {
+       { GNM_FUNC_HELP_NAME, F_("R.QST:probability quantile function of the skew-t distribution") },
+       { GNM_FUNC_HELP_ARG, F_("p:probability") },
+       { GNM_FUNC_HELP_ARG, F_("n:the number of degrees of freedom of the distribution") },
+       { GNM_FUNC_HELP_ARG, F_("shape:the shape parameter of the distribution") },
+       { GNM_FUNC_HELP_ARG, F_("lower_tail:if true (the default), the lower tail of the distribution is 
considered") },
+       { GNM_FUNC_HELP_ARG, F_("log_p:if true, log of the probability is used") },
+       { GNM_FUNC_HELP_DESCRIPTION, F_("This function returns the probability quantile function, i.e., the 
inverse of the cumulative distribution function, of the skew-t distribution.") },
+       { GNM_FUNC_HELP_SEEALSO, "R.DST,R.PST" },
+       { GNM_FUNC_HELP_END }
+};
+
+static GnmValue *
+gnumeric_r_qst (GnmFuncEvalInfo *ei, GnmValue const * const *args)
+{
+       gnm_float p = value_get_as_float (args[0]);
+       gnm_float n = value_get_as_float (args[1]);
+       gnm_float shape = value_get_as_float (args[2]);
+       gboolean lower_tail = args[3] ? value_get_as_checked_bool (args[3]) : TRUE;
+       gboolean log_p = args[4] ? value_get_as_checked_bool (args[4]) : FALSE;
+
+       return value_new_float (qst (p, n, shape, lower_tail, log_p));
+}
+
+/* ------------------------------------------------------------------------- */
+
 G_MODULE_EXPORT void
 go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
 {
@@ -1602,11 +1685,32 @@ GnmFuncDescriptor const stat_functions[] = {
                GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE,
        },
        {
+               "r.qsnorm",
+               "ffff|bb",
+               help_r_qsnorm,
+               gnumeric_r_qsnorm, NULL, NULL, NULL,
+               GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE,
+       },
+       {
                "r.dst",
                "fff|b",
                help_r_dst,
                gnumeric_r_dst, NULL, NULL, NULL,
                GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE,
        },
+       {
+               "r.pst",
+               "fff|bb",
+               help_r_pst,
+               gnumeric_r_pst, NULL, NULL, NULL,
+               GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE,
+       },
+       {
+               "r.qst",
+               "fff|bb",
+               help_r_qst,
+               gnumeric_r_qst, NULL, NULL, NULL,
+               GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_UNIQUE_TO_GNUMERIC, GNM_FUNC_TEST_STATUS_NO_TESTSUITE,
+       },
        { NULL }
 };
diff --git a/plugins/fn-r/generate b/plugins/fn-r/generate
index 53e18f7..c136a8d 100644
--- a/plugins/fn-r/generate
+++ b/plugins/fn-r/generate
@@ -121,15 +121,13 @@ my %argoverride = ();
         ({ 'psuc' => "the probability of success in each trial",
            @common })];
 
-    $funcs{'dsnorm'} =  $funcs{'psnorm'} =  
-#$funcs{'qsnorm'} =
+    $funcs{'dsnorm'} =  $funcs{'psnorm'} =  $funcs{'qsnorm'} =
        [\&distribution,
         'skew-normal',
         ({ 'location' => "the location parameter $of",
            @common })];
 
-    $funcs{'dst'} = 
-#$funcs{'pst'} = $funcs{'qst'} =
+    $funcs{'dst'} = $funcs{'pst'} = $funcs{'qst'} =
        [\&distribution,
         'skew-t',
         ({ 'n' => "the number of degrees of freedom $of",
diff --git a/plugins/fn-r/plugin.xml.in b/plugins/fn-r/plugin.xml.in
index b5ee8c3..901f254 100644
--- a/plugins/fn-r/plugin.xml.in
+++ b/plugins/fn-r/plugin.xml.in
@@ -42,6 +42,7 @@
                                <function name="r.pnorm"/>
                                <function name="r.ppois"/>
                                <function name="r.psnorm"/>
+                               <function name="r.pst"/>
                                <function name="r.pt"/>
                                <function name="r.pweibull"/>
                                <function name="r.qbeta"/>
@@ -57,6 +58,8 @@
                                <function name="r.qnbinom"/>
                                <function name="r.qnorm"/>
                                <function name="r.qpois"/>
+                               <function name="r.qsnorm"/>
+                               <function name="r.qst"/>
                                <function name="r.qt"/>
                                <function name="r.qweibull"/>
                        </functions>
diff --git a/src/mathfunc.c b/src/mathfunc.c
index 18680dc..bd8e827 100644
--- a/src/mathfunc.c
+++ b/src/mathfunc.c
@@ -905,7 +905,7 @@ gnm_float ppois(gnm_float x, gnm_float lambda, gboolean lower_tail, gboolean log
  * see also lgammacor() in ./lgammacor.c  which computes almost the same!
  */
 
-static gnm_float stirlerr(gnm_float n)
+gnm_float stirlerr(gnm_float n)
 {
 
 #define S0 GNM_const(0.083333333333333333333)       /* 1/12 */
diff --git a/src/mathfunc.h b/src/mathfunc.h
index 73304f8..67acb33 100644
--- a/src/mathfunc.h
+++ b/src/mathfunc.h
@@ -34,6 +34,7 @@ gnm_float gnm_trunc (gnm_float x);
 gnm_float logfbit (gnm_float x);
 gnm_float logspace_add (gnm_float logx, gnm_float logy);
 gnm_float logspace_sub (gnm_float logx, gnm_float logy);
+gnm_float stirlerr(gnm_float n);
 
 gnm_float gnm_cot (gnm_float x);
 gnm_float gnm_acot (gnm_float x);
diff --git a/tools/import-R b/tools/import-R
index 6b3f1a0..7b8b8ee 100644
--- a/tools/import-R
+++ b/tools/import-R
@@ -152,7 +152,7 @@ sub import_file {
            s~([-+]?\d+\.\d*)(\s*/\s*)([-+e0-9.]+)~GNM_const\($1\)$2$3~;
 
            # These are made static.
-           
s/^gnm_float\s+(pbeta_both|stirlerr|bd0|dpois_raw|chebyshev_eval|lgammacor|lbeta|pbeta_raw|dbinom_raw)/static 
gnm_float $1/;
+           
s/^gnm_float\s+(pbeta_both|bd0|dpois_raw|chebyshev_eval|lgammacor|lbeta|pbeta_raw|dbinom_raw)/static 
gnm_float $1/;
            s/^int\s+(chebyshev_init)/static int $1/;
 
            # Fix a bug...


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]