ONEWAY: Fixed crash when the dependent variable contained only missing values.
authorJohn Darrington <john@darrington.wattle.id.au>
Sat, 3 Dec 2011 12:06:32 +0000 (13:06 +0100)
committerJohn Darrington <john@darrington.wattle.id.au>
Sat, 3 Dec 2011 12:06:32 +0000 (13:06 +0100)
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
<David_Purdy@btinternet.com> for reporting this problem.

src/language/stats/oneway.c
src/math/categoricals.c
src/math/categoricals.h
tests/language/stats/oneway.at

index 49a34f2ed593f299cc15466d068d4c44857a2bb0..199d6252c55fa024f90275f81c219affb3dd10b5 100644 (file)
@@ -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);
index 2ec1dc19100a5caa7219ecdfe4e333e4536e9c00..d8606031c943b0925a1edc953e454693a9d8de8c 100644 (file)
@@ -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;
 }
 
 
index ec708efa178c91a3c8b621264a1fc3d05dffd3e8..2ea47d516a770bb7115df2ddca987f0e14abc99d 100644 (file)
@@ -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);
 
 
 /*
index c824d9a49394f975bef6dee5da76f482d439507e..7602eff465c7295c2c1832ecce550c36c492382b 100644 (file)
@@ -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