From 1cf8b236bb43257161f2c014b2384aa4709315c9 Mon Sep 17 00:00:00 2001 From: John Darrington Date: Sat, 3 Dec 2011 13:06:32 +0100 Subject: [PATCH] ONEWAY: Fixed crash when the dependent variable contained only missing values. When forming a struct categoricals, there is no way in advance to know if the given categorical variables will actually contain any values. If one or more of them contains no non-missing values, then it's not meaningfull to talk about categories. Such cases were causing oneway to crash. Thanks to Dave Purdy for reporting this problem. --- src/language/stats/oneway.c | 24 ++++++++++++++++++++++-- src/math/categoricals.c | 17 +++++++++++++++-- src/math/categoricals.h | 9 +++++++-- tests/language/stats/oneway.at | 29 ++++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 7 deletions(-) diff --git a/src/language/stats/oneway.c b/src/language/stats/oneway.c index 49a34f2e..199d6252 100644 --- a/src/language/stats/oneway.c +++ b/src/language/stats/oneway.c @@ -758,7 +758,15 @@ run_oneway (const struct oneway_spec *cmd, gsl_matrix *cm; struct per_var_ws *pvw = &ws.vws[v]; const struct categoricals *cats = covariance_get_categoricals (pvw->cov); - categoricals_done (cats); + const bool ok = categoricals_done (cats); + + if ( ! ok) + { + msg (MW, + _("Dependent variable %s has no non-missing values. No analysis for this variable will be done."), + var_get_name (cmd->vars[v])); + continue; + } cm = covariance_calculate_unnormalized (pvw->cov); @@ -783,6 +791,11 @@ run_oneway (const struct oneway_spec *cmd, { const struct categoricals *cats = covariance_get_categoricals (ws.vws[v].cov); + if ( ! categoricals_is_complete (cats)) + { + continue; + } + if (categoricals_n_total (cats) > ws.actual_number_of_groups) ws.actual_number_of_groups = categoricals_n_total (cats); } @@ -859,7 +872,12 @@ output_oneway (const struct oneway_spec *cmd, struct oneway_workspace *ws) { int v; for (v = 0 ; v < cmd->n_vars; ++v) - show_comparisons (cmd, ws, v); + { + const struct categoricals *cats = covariance_get_categoricals (ws->vws[v].cov); + + if ( categoricals_is_complete (cats)) + show_comparisons (cmd, ws, v); + } } } @@ -1068,6 +1086,7 @@ show_descriptives (const struct oneway_spec *cmd, const struct oneway_workspace tab_double (t, 9, row + count, 0, dd->maximum, fmt); } + if (categoricals_is_complete (cats)) { double T; double n, mean, variance; @@ -1099,6 +1118,7 @@ show_descriptives (const struct oneway_spec *cmd, const struct oneway_workspace tab_double (t, 7, row + count, 0, mean + T * std_error, NULL); + /* Min and Max */ tab_double (t, 8, row + count, 0, ws->dd_total[v]->minimum, fmt); tab_double (t, 9, row + count, 0, ws->dd_total[v]->maximum, fmt); diff --git a/src/math/categoricals.c b/src/math/categoricals.c index 2ec1dc19..d8606031 100644 --- a/src/math/categoricals.c +++ b/src/math/categoricals.c @@ -440,7 +440,8 @@ categoricals_df (const struct categoricals *cat, size_t n) size_t categoricals_n_total (const struct categoricals *cat) { - assert (cat->reverse_variable_map_long); + if (!categoricals_is_complete (cat)) + return 0; return cat->n_cats_total; } @@ -451,9 +452,16 @@ categoricals_df_total (const struct categoricals *cat) return cat->df_sum; } +bool +categoricals_is_complete (const struct categoricals *cat) +{ + return (NULL != cat->reverse_variable_map_short); +} + + /* This function must be called *before* any call to categoricals_get_*_by subscript and *after* all calls to categoricals_update */ -void +bool categoricals_done (const struct categoricals *cat_) { /* Implementation Note: Whilst this function is O(n) in cat->n_cats_total, in most @@ -486,6 +494,9 @@ categoricals_done (const struct categoricals *cat_) struct variable_node *vn = lookup_variable (&cat->varmap, var, hash_pointer (var, 0)); + if (hmap_count (&vn->valmap) == 0) + return false; + cat->iap[i].df_prod[v] = df * (hmap_count (&vn->valmap) - 1); df = cat->iap[i].df_prod[v]; @@ -566,6 +577,8 @@ categoricals_done (const struct categoricals *cat_) } } } + + return true; } diff --git a/src/math/categoricals.h b/src/math/categoricals.h index ec708efa..2ea47d51 100644 --- a/src/math/categoricals.h +++ b/src/math/categoricals.h @@ -68,11 +68,16 @@ size_t categoricals_df_total (const struct categoricals *cat); size_t categoricals_get_n_variables (const struct categoricals *cat); +bool categoricals_is_complete (const struct categoricals *cat); + + /* Must be called (once) before any call to the *_by_subscript or *_by_category - functions, but AFTER any calls to categoricals_update + functions, but AFTER any calls to categoricals_update. + If this function returns false, then no calls to _by_subscript or *_by_category + are allowed. */ -void categoricals_done (const struct categoricals *cat); +bool categoricals_done (const struct categoricals *cat); /* diff --git a/tests/language/stats/oneway.at b/tests/language/stats/oneway.at index c824d9a4..7602eff4 100644 --- a/tests/language/stats/oneway.at +++ b/tests/language/stats/oneway.at @@ -920,4 +920,31 @@ oneway test by x. AT_CHECK([pspp -O format=csv crash.sps], [0], [ignore]) -AT_CLEANUP \ No newline at end of file +AT_CLEANUP + + + +AT_SETUP([ONEWAY crash on missing dependent variable]) +AT_DATA([crash2.sps],[dnl +data list notable list /dv1 * dv2 * y * . +begin data. +2 . 2 +1 . 2 +1 . 1 +2 . 4 +3 . 4 +4 . 4 +5 . 4 +end data. + +ONEWAY + /VARIABLES= dv1 dv2 BY y + /STATISTICS = DESCRIPTIVES + /POSTHOC = BONFERRONI LSD SCHEFFE SIDAK TUKEY + /MISSING = ANALYSIS + . +]) + +AT_CHECK([pspp -O format=csv crash2.sps], [0], [ignore]) + +AT_CLEANUP -- 2.30.2