From cb00cc9f9dc738f2850cd4b5a9776031f87de8a2 Mon Sep 17 00:00:00 2001 From: John Darrington Date: Thu, 26 Jan 2012 22:39:33 +0100 Subject: [PATCH] Don't crash on Games-Howell test when there are small numbers of cases per category. Fixes bug #34978 --- lib/tukey/ptukey.c | 4 +--- lib/tukey/qtukey.c | 19 +++++++++++-------- src/language/stats/oneway.c | 20 +++++++++++++++++++- tests/language/stats/oneway.at | 24 ++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/lib/tukey/ptukey.c b/lib/tukey/ptukey.c index c86ef236..9a89104c 100644 --- a/lib/tukey/ptukey.c +++ b/lib/tukey/ptukey.c @@ -371,9 +371,7 @@ ptukey (double q, double rr, double cc, double df, int lower_tail, int log_p) double ans, f2, f21, f2lf, ff4, otsum, qsqz, rotsum, t1, twa1, ulen, wprb; int i, j, jj; -#ifdef IEEE_754 - abort (! (ISNAN (q) || ISNAN (rr) || ISNAN (cc) || ISNAN (df))); -#endif + assert (! (isnan (q) || isnan (rr) || isnan (cc) || isnan (df))); if (q <= 0) return R_DT_0; diff --git a/lib/tukey/qtukey.c b/lib/tukey/qtukey.c index 08253df7..ec146001 100644 --- a/lib/tukey/qtukey.c +++ b/lib/tukey/qtukey.c @@ -81,10 +81,8 @@ static double fmax2(double x, double y) { -#ifdef IEEE_754 - if (ISNAN(x) || ISNAN(y)) + if (isnan(x) || isnan(y)) return x + y; -#endif return (x < y) ? y : x; } @@ -188,15 +186,20 @@ double qtukey(double p, double rr, double cc, double df, double ans = 0.0, valx0, valx1, x0, x1, xabs; int iter; -#ifdef IEEE_754 - if (ISNAN(p) || ISNAN(rr) || ISNAN(cc) || ISNAN(df)) { - ML_ERROR(ME_DOMAIN, "qtukey"); + if (isnan(p) || isnan(rr) || isnan(cc) || isnan(df)) { + /* ML_ERROR(ME_DOMAIN, "qtukey"); */ return p + rr + cc + df; } -#endif /* df must be > 1 ; there must be at least two values */ - assert (! (df < 2 || rr < 1 || cc < 2) ); + /* ^^ + JMD: The comment says 1 but the code says 2. + Which is correct? + */ + assert (df >= 2); + assert (rr >= 1); + assert (cc >= 2); + R_Q_P01_boundaries (p, 0, ML_POSINF); diff --git a/src/language/stats/oneway.c b/src/language/stats/oneway.c index 2c401f56..5ea77473 100644 --- a/src/language/stats/oneway.c +++ b/src/language/stats/oneway.c @@ -164,6 +164,9 @@ df_individual (const struct per_var_ws *pvw UNUSED, const struct moments1 *mom_i moments1_calculate (mom_i, &n_i, NULL, &var_i, 0, 0); moments1_calculate (mom_j, &n_j, NULL, &var_j, 0, 0); + + if ( n_i <= 1.0 || n_j <= 1.0) + return SYSMIS; nom = pow2 (var_i/n_i + var_j/n_j); denom = pow2 (var_i/n_i) / (n_i - 1) + pow2 (var_j/n_j) / (n_j - 1); @@ -191,6 +194,9 @@ static double sidak_pinv (double std_err, double alpha, double df, int k, const static double tukey_pinv (double std_err, double alpha, double df, int k, const struct moments1 *mom_i UNUSED, const struct moments1 *mom_j UNUSED) { + if ( k < 2 || df < 2) + return SYSMIS; + return std_err / sqrt (2.0) * qtukey (1 - alpha, 1.0, k, df, 1, 0); } @@ -211,6 +217,9 @@ static double gh_pinv (double std_err UNUSED, double alpha, double df, int k, co m = sqrt ((var_i/n_i + var_j/n_j) / 2.0); + if ( k < 2 || df < 2) + return SYSMIS; + return m * qtukey (1 - alpha, 1.0, k, df, 1, 0); } @@ -224,6 +233,8 @@ multiple_comparison_sig (double std_err, int k = pvw->n_groups; double df = ph->dff (pvw, dd_i->mom, dd_j->mom); double ts = ph->tsf (k, dd_i->mom, dd_j->mom, std_err); + if ( df == SYSMIS) + return SYSMIS; return ph->p1f (ts, k - 1, df); } @@ -232,13 +243,20 @@ mc_half_range (const struct oneway_spec *cmd, const struct per_var_ws *pvw, doub { int k = pvw->n_groups; double df = ph->dff (pvw, dd_i->mom, dd_j->mom); + if ( df == SYSMIS) + return SYSMIS; return ph->pinv (std_err, cmd->alpha, df, k, dd_i->mom, dd_j->mom); } static double tukey_1tailsig (double ts, double df1, double df2) { - double twotailedsig = 1.0 - ptukey (ts, 1.0, df1 + 1, df2, 1, 0); + double twotailedsig; + + if (df2 < 2 || df1 < 1) + return SYSMIS; + + twotailedsig = 1.0 - ptukey (ts, 1.0, df1 + 1, df2, 1, 0); return twotailedsig / 2.0; } diff --git a/tests/language/stats/oneway.at b/tests/language/stats/oneway.at index 7602eff4..dfb43bbe 100644 --- a/tests/language/stats/oneway.at +++ b/tests/language/stats/oneway.at @@ -948,3 +948,27 @@ ONEWAY AT_CHECK([pspp -O format=csv crash2.sps], [0], [ignore]) AT_CLEANUP + + + + +AT_SETUP([ONEWAY Games-Howell test with few cases]) +AT_DATA([crash3.sps],[dnl +data list notable list /dv * y * . +begin data. +2 2 +1 2 +1 1 +2 4 +3 4 +end data. + +ONEWAY + /VARIABLES= dv BY y + /POSTHOC = GH + . +]) + +AT_CHECK([pspp -O format=csv crash3.sps], [0], [ignore]) + +AT_CLEANUP -- 2.30.2