Add optional callback functions to categoricals.
[pspp] / src / language / stats / oneway.c
index ce4f016d99063acabf0dd71abab6d36245229aa3..f421371c1d43201c8e1112717e16ba3c266a8aa1 100644 (file)
 #include <data/casegrouper.h>
 #include <data/casereader.h>
 
+#include <math/covariance.h>
+#include <math/categoricals.h>
+#include <math/moments.h>
+#include <gsl/gsl_matrix.h>
+#include <linreg/sweep.h>
+
 #include <libpspp/ll.h>
 
 #include <language/lexer/lexer.h>
@@ -28,9 +34,9 @@
 #include <language/command.h>
 
 #include <data/procedure.h>
+#include <data/value.h>
 #include <data/dictionary.h>
 
-
 #include <language/dictionary/split-file.h>
 #include <libpspp/hash.h>
 #include <libpspp/taint.h>
@@ -81,8 +87,6 @@ struct oneway_spec
   size_t n_vars;
   const struct variable **vars;
 
-  const struct dictionary *dict;
-
   const struct variable *indep_var;
 
   enum statistics stats;
@@ -92,6 +96,24 @@ struct oneway_spec
 
   /* List of contrasts */
   struct ll_list contrast_list;
+
+  /* The weight variable */
+  const struct variable *wv;
+};
+
+
+/* Workspace variable for each dependent variable */
+struct per_var_ws
+{
+  struct covariance *cov;
+
+  double sst;
+  double sse;
+  double ssa;
+
+  int n_groups;
+
+  double cc;
 };
 
 struct oneway_workspace
@@ -103,12 +125,18 @@ struct oneway_workspace
   /* A  hash table containing all the distinct values of the independent
      variable */
   struct hsh_table *group_hash;
+
+  struct per_var_ws *vws;
+
+  struct moments1 *totals;
+  double minimum;
+  double maximum;
 };
 
 /* Routines to show the output tables */
-static void show_anova_table (const struct oneway_spec *);
-static void show_descriptives (const struct oneway_spec *, const struct dictionary *dict);
-static void show_homogeneity (const struct oneway_spec *);
+static void show_anova_table (const struct oneway_spec *, const struct oneway_workspace *);
+static void show_descriptives (const struct oneway_spec *, const struct oneway_workspace *);
+static void show_homogeneity (const struct oneway_spec *, const struct oneway_workspace *);
 
 static void output_oneway (const struct oneway_spec *, struct oneway_workspace *ws);
 static void run_oneway (const struct oneway_spec *cmd, struct casereader *input, const struct dataset *ds);
@@ -116,6 +144,7 @@ static void run_oneway (const struct oneway_spec *cmd, struct casereader *input,
 int
 cmd_oneway (struct lexer *lexer, struct dataset *ds)
 {
+  const struct dictionary *dict = dataset_dict (ds);  
   struct oneway_spec oneway ;
   oneway.n_vars = 0;
   oneway.vars = NULL;
@@ -123,7 +152,7 @@ cmd_oneway (struct lexer *lexer, struct dataset *ds)
   oneway.stats = 0;
   oneway.missing_type = MISS_ANALYSIS;
   oneway.exclude = MV_ANY;
-  oneway.dict = dataset_dict (ds);  
+  oneway.wv = dict_get_weight (dict);
 
   ll_init (&oneway.contrast_list);
 
@@ -137,14 +166,14 @@ cmd_oneway (struct lexer *lexer, struct dataset *ds)
       lex_match (lexer, '=');
     }
 
-  if (!parse_variables_const (lexer, oneway.dict,
+  if (!parse_variables_const (lexer, dict,
                              &oneway.vars, &oneway.n_vars,
                              PV_NO_DUPLICATE | PV_NUMERIC))
     goto error;
 
   lex_force_match (lexer, T_BY);
 
-  oneway.indep_var = parse_variable_const (lexer, oneway.dict);
+  oneway.indep_var = parse_variable_const (lexer, dict);
 
   while (lex_token (lexer) != '.')
     {
@@ -239,7 +268,9 @@ cmd_oneway (struct lexer *lexer, struct dataset *ds)
     struct casereader *group;
     bool ok;
 
-    grouper = casegrouper_create_splits (proc_open (ds), oneway.dict);
+
+
+    grouper = casegrouper_create_splits (proc_open (ds), dict);
     while (casegrouper_get_next_group (grouper, &group))
       run_oneway (&oneway, group, ds);
     ok = casegrouper_destroy (grouper);
@@ -279,15 +310,64 @@ free_double (void *value_, const void *aux UNUSED)
   free (value);
 }
 
+
+
 static void postcalc (const struct oneway_spec *cmd);
 static void  precalc (const struct oneway_spec *cmd);
 
+struct descriptive_data
+{
+  struct moments1 *mom;
+  double minimum;
+  double maximum;
+};
+
+static void *
+makeit (void)
+{
+  struct descriptive_data *dd = xmalloc (sizeof *dd);
+  dd->mom = moments1_create (MOMENT_VARIANCE);
+  dd->minimum = DBL_MAX;
+  dd->maximum = -DBL_MAX;
+
+  return dd;
+}
+
+static void 
+updateit (void *user_data, const struct variable *wv, 
+         const struct variable *catvar, const struct ccase *c, void *aux)
+{
+  const union value *val = case_data_idx (c, 0);
+  struct descriptive_data *dd = user_data;
+  struct oneway_workspace *ws = aux;
+
+  double weight = 1.0;
+  if (wv)
+    weight = case_data (c, wv)->f;
+
+  moments1_add (dd->mom, val->f, weight);
+  moments1_add (ws->totals, val->f, weight);
+
+  if (val->f * weight < dd->minimum)
+    dd->minimum = val->f * weight;
+
+  if (val->f * weight > dd->maximum)
+    dd->maximum = val->f * weight;
+
+
+  if (val->f * weight < ws->minimum)
+    ws->minimum = val->f * weight;
+
+  if (val->f * weight > ws->maximum)
+    ws->maximum = val->f * weight;
+}
 
 static void
 run_oneway (const struct oneway_spec *cmd,
             struct casereader *input,
             const struct dataset *ds)
 {
+  int v;
   struct taint *taint;
   struct dictionary *dict = dataset_dict (ds);
   struct casereader *reader;
@@ -295,6 +375,26 @@ run_oneway (const struct oneway_spec *cmd,
 
   struct oneway_workspace ws;
 
+  ws.vws = xmalloc (cmd->n_vars * sizeof (*ws.vws));
+
+  ws.totals = moments1_create (MOMENT_VARIANCE);
+  ws.minimum = DBL_MAX;
+  ws.maximum = -DBL_MAX;
+
+
+  for (v = 0; v < cmd->n_vars; ++v)
+    {
+      struct categoricals *cats = categoricals_create (&cmd->indep_var, 1,
+                                                      cmd->wv, cmd->exclude, 
+                                                      makeit,
+                                                      updateit, &ws);
+
+      ws.vws[v].cov = covariance_2pass_create (1, &cmd->vars[v],
+                                              cats, 
+                                              cmd->wv, cmd->exclude);
+      ws.vws[v].cc = 0;
+    }
+
   c = casereader_peek (input, 0);
   if (c == NULL)
     {
@@ -322,6 +422,7 @@ run_oneway (const struct oneway_spec *cmd,
   input = casereader_create_filter_weight (input, dict, NULL, NULL);
 
   reader = casereader_clone (input);
+
   for (; (c = casereader_read (reader)) != NULL; case_unref (c))
     {
       size_t i;
@@ -338,6 +439,13 @@ run_oneway (const struct oneway_spec *cmd,
 
       for (i = 0; i < cmd->n_vars; ++i)
        {
+         {
+           struct per_var_ws *pvw = &ws.vws[i];
+
+           pvw->cc += weight;
+           covariance_accumulate_pass1 (pvw->cov, c);
+         }
+
          const struct variable *v = cmd->vars[i];
 
          const union value *val = case_data (c, v);
@@ -393,9 +501,44 @@ run_oneway (const struct oneway_spec *cmd,
 
     }
   casereader_destroy (reader);
+  reader = casereader_clone (input);
+  for ( ; (c = casereader_read (reader) ); case_unref (c))
+    {
+      int i;
+      for (i = 0; i < cmd->n_vars; ++i)
+       {
+         struct per_var_ws *pvw = &ws.vws[i];
+         covariance_accumulate_pass2 (pvw->cov, c);
+       }
+    }
+  casereader_destroy (reader);
+
+  for (v = 0; v < cmd->n_vars; ++v)
+    {
+      struct per_var_ws *pvw = &ws.vws[v];
+      gsl_matrix *cm = covariance_calculate_unnormalized (pvw->cov);
+      const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
+
+      pvw->sst = gsl_matrix_get (cm, 0, 0);
+
+      reg_sweep (cm, 0);
+
+      pvw->sse = gsl_matrix_get (cm, 0, 0);
+
+      pvw->ssa = pvw->sst - pvw->sse;
+
+      pvw->n_groups = categoricals_total (cats);
+    }
 
   postcalc (cmd);
 
+  for (v = 0; v < cmd->n_vars; ++v)
+    {
+      struct categoricals *cats = covariance_get_categoricals (ws.vws[v].cov);
+
+      categoricals_done (cats);
+    }
+
   if ( cmd->stats & STATS_HOMOGENEITY )
     levene (dict, casereader_clone (input), cmd->indep_var,
            cmd->n_vars, cmd->vars, cmd->exclude);
@@ -479,8 +622,8 @@ postcalc (const struct oneway_spec *cmd)
     }
 }
 
-static void show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws);
-static void show_contrast_tests (const struct oneway_spec *cmd, struct oneway_workspace *ws);
+static void show_contrast_coeffs (const struct oneway_spec *cmd, const struct oneway_workspace *ws);
+static void show_contrast_tests (const struct oneway_spec *cmd, const struct oneway_workspace *ws);
 
 static void
 output_oneway (const struct oneway_spec *cmd, struct oneway_workspace *ws)
@@ -512,12 +655,12 @@ output_oneway (const struct oneway_spec *cmd, struct oneway_workspace *ws)
     }
 
   if (cmd->stats & STATS_DESCRIPTIVES)
-    show_descriptives (cmd, cmd->dict);
+    show_descriptives (cmd, ws);
 
   if (cmd->stats & STATS_HOMOGENEITY)
-    show_homogeneity (cmd);
+    show_homogeneity (cmd, ws);
 
-  show_anova_table (cmd);
+  show_anova_table (cmd, ws);
 
 
   if (ll_count (&cmd->contrast_list) > 0)
@@ -541,7 +684,7 @@ output_oneway (const struct oneway_spec *cmd, struct oneway_workspace *ws)
 
 /* Show the ANOVA table */
 static void
-show_anova_table (const struct oneway_spec *cmd)
+show_anova_table (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
 {
   size_t i;
   int n_cols =7;
@@ -570,21 +713,13 @@ show_anova_table (const struct oneway_spec *cmd)
 
   for (i = 0; i < cmd->n_vars; ++i)
     {
-      struct group_statistics *totals = &group_proc_get (cmd->vars[i])->ugs;
-      struct hsh_table *group_hash = group_proc_get (cmd->vars[i])->group_hash;
-      struct hsh_iterator g;
-      struct group_statistics *gs;
-      double ssa = 0;
-      const char *s = var_to_string (cmd->vars[i]);
-
-      for (gs =  hsh_first (group_hash, &g);
-          gs != 0;
-          gs = hsh_next (group_hash, &g))
-       {
-         ssa += pow2 (gs->sum) / gs->n;
-       }
+      const struct per_var_ws *pvw = &ws->vws[i];
+      struct group_proc *gp = group_proc_get (cmd->vars[i]);
+      const double df1 = pvw->n_groups - 1;
+      const double df2 = pvw->cc - pvw->n_groups;
+      const double msa = pvw->ssa / df1;
 
-      ssa -= pow2 (totals->sum) / totals->n;
+      const char *s = var_to_string (cmd->vars[i]);
 
       tab_text (t, 0, i * 3 + 1, TAB_LEFT | TAT_TITLE, s);
       tab_text (t, 1, i * 3 + 1, TAB_LEFT | TAT_TITLE, _("Between Groups"));
@@ -594,44 +729,35 @@ show_anova_table (const struct oneway_spec *cmd)
       if (i > 0)
        tab_hline (t, TAL_1, 0, n_cols - 1, i * 3 + 1);
 
-      {
-        struct group_proc *gp = group_proc_get (cmd->vars[i]);
-       const double sst = totals->ssq - pow2 (totals->sum) / totals->n;
-       const double df1 = gp->n_groups - 1;
-       const double df2 = totals->n - gp->n_groups;
-       const double msa = ssa / df1;
-
-       gp->mse  = (sst - ssa) / df2;
 
+      gp->mse  = (pvw->sst - pvw->ssa) / df2;
 
-       /* Sums of Squares */
-       tab_double (t, 2, i * 3 + 1, 0, ssa, NULL);
-       tab_double (t, 2, i * 3 + 3, 0, sst, NULL);
-       tab_double (t, 2, i * 3 + 2, 0, sst - ssa, NULL);
+      /* Sums of Squares */
+      tab_double (t, 2, i * 3 + 1, 0, pvw->ssa, NULL);
+      tab_double (t, 2, i * 3 + 3, 0, pvw->sst, NULL);
+      tab_double (t, 2, i * 3 + 2, 0, pvw->sse, NULL);
 
 
-       /* Degrees of freedom */
-       tab_fixed (t, 3, i * 3 + 1, 0, df1, 4, 0);
-       tab_fixed (t, 3, i * 3 + 2, 0, df2, 4, 0);
-       tab_fixed (t, 3, i * 3 + 3, 0, totals->n - 1, 4, 0);
+      /* Degrees of freedom */
+      tab_fixed (t, 3, i * 3 + 1, 0, df1, 4, 0);
+      tab_fixed (t, 3, i * 3 + 2, 0, df2, 4, 0);
+      tab_fixed (t, 3, i * 3 + 3, 0, pvw->cc - 1, 4, 0);
 
-       /* Mean Squares */
-       tab_double (t, 4, i * 3 + 1, TAB_RIGHT, msa, NULL);
-       tab_double (t, 4, i * 3 + 2, TAB_RIGHT, gp->mse, NULL);
+      /* Mean Squares */
+      tab_double (t, 4, i * 3 + 1, TAB_RIGHT, msa, NULL);
+      tab_double (t, 4, i * 3 + 2, TAB_RIGHT, gp->mse, NULL);
 
-       {
-         const double F = msa / gp->mse ;
+      {
+       const double F = msa / gp->mse ;
 
-         /* The F value */
-         tab_double (t, 5, i * 3 + 1, 0,  F, NULL);
+       /* The F value */
+       tab_double (t, 5, i * 3 + 1, 0,  F, NULL);
 
-         /* The significance */
-         tab_double (t, 6, i * 3 + 1, 0, gsl_cdf_fdist_Q (F, df1, df2), NULL);
-       }
+       /* The significance */
+       tab_double (t, 6, i * 3 + 1, 0, gsl_cdf_fdist_Q (F, df1, df2), NULL);
       }
     }
 
-
   tab_title (t, _("ANOVA"));
   tab_submit (t);
 }
@@ -639,7 +765,7 @@ show_anova_table (const struct oneway_spec *cmd)
 
 /* Show the descriptives table */
 static void
-show_descriptives (const struct oneway_spec *cmd, const struct dictionary *dict)
+show_descriptives (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
 {
   size_t v;
   int n_cols = 10;
@@ -649,18 +775,16 @@ show_descriptives (const struct oneway_spec *cmd, const struct dictionary *dict)
   const double confidence = 0.95;
   const double q = (1.0 - confidence) / 2.0;
 
-  const struct variable *wv = dict_get_weight (dict);
-  const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
+  const struct fmt_spec *wfmt = cmd->wv ? var_get_print_format (cmd->wv) : &F_8_0;
 
   int n_rows = 2;
 
-  for ( v = 0; v < cmd->n_vars; ++v )
-    n_rows += group_proc_get (cmd->vars[v])->n_groups + 1;
+  for (v = 0; v < cmd->n_vars; ++v)
+    n_rows += ws->actual_number_of_groups + 1;
 
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
 
-
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
           TAL_2, TAL_2,
@@ -690,39 +814,41 @@ show_descriptives (const struct oneway_spec *cmd, const struct dictionary *dict)
   tab_text (t, 8, 1, TAB_CENTER | TAT_TITLE, _("Minimum"));
   tab_text (t, 9, 1, TAB_CENTER | TAT_TITLE, _("Maximum"));
 
-
   tab_title (t, _("Descriptives"));
 
-
   row = 2;
   for (v = 0; v < cmd->n_vars; ++v)
     {
-      double T;
-      double std_error;
-
-      struct group_proc *gp = group_proc_get (cmd->vars[v]);
-
-      struct group_statistics *gs;
-      struct group_statistics *totals = &gp->ugs;
-
       const char *s = var_to_string (cmd->vars[v]);
       const struct fmt_spec *fmt = var_get_print_format (cmd->vars[v]);
 
-      struct group_statistics *const *gs_array =
-       (struct group_statistics *const *) hsh_sort (gp->group_hash);
       int count = 0;
 
+      struct per_var_ws *pvw = &ws->vws[v];
+      const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
+
       tab_text (t, 0, row, TAB_LEFT | TAT_TITLE, s);
       if ( v > 0)
        tab_hline (t, TAL_1, 0, n_cols - 1, row);
 
-      for (count = 0; count < hsh_count (gp->group_hash); ++count)
+      for (count = 0; count < categoricals_total (cats); ++count)
        {
+         double T;
+         double n, mean, variance;
+
+         const union value *gval = categoricals_get_value_by_subscript (cats, count);
+         const struct descriptive_data *dd = categoricals_get_user_data_by_subscript (cats, count);
+
+         moments1_calculate (dd->mom, &n, &mean, &variance, NULL, NULL);
+
+         double std_dev = sqrt (variance);
+         double std_error = std_dev / sqrt (n) ;
+
          struct string vstr;
+
          ds_init_empty (&vstr);
-         gs = gs_array[count];
 
-         var_append_value_name (cmd->indep_var, &gs->id, &vstr);
+         var_append_value_name (cmd->indep_var, gval, &vstr);
 
          tab_text (t, 1, row + count,
                    TAB_LEFT | TAT_TITLE,
@@ -732,61 +858,68 @@ show_descriptives (const struct oneway_spec *cmd, const struct dictionary *dict)
 
          /* Now fill in the numbers ... */
 
-         tab_fixed (t, 2, row + count, 0, gs->n, 8, 0);
+         tab_fixed (t, 2, row + count, 0, n, 8, 0);
 
-         tab_double (t, 3, row + count, 0, gs->mean, NULL);
+         tab_double (t, 3, row + count, 0, mean, NULL);
 
-         tab_double (t, 4, row + count, 0, gs->std_dev, NULL);
+         tab_double (t, 4, row + count, 0, std_dev, NULL);
 
-         std_error = gs->std_dev / sqrt (gs->n) ;
-         tab_double (t, 5, row + count, 0,
-                     std_error, NULL);
+
+         tab_double (t, 5, row + count, 0, std_error, NULL);
 
          /* Now the confidence interval */
 
-         T = gsl_cdf_tdist_Qinv (q, gs->n - 1);
+         T = gsl_cdf_tdist_Qinv (q, n - 1);
 
          tab_double (t, 6, row + count, 0,
-                     gs->mean - T * std_error, NULL);
+                     mean - T * std_error, NULL);
 
          tab_double (t, 7, row + count, 0,
-                     gs->mean + T * std_error, NULL);
+                     mean + T * std_error, NULL);
 
          /* Min and Max */
 
-         tab_double (t, 8, row + count, 0,  gs->minimum, fmt);
-         tab_double (t, 9, row + count, 0,  gs->maximum, fmt);
+         tab_double (t, 8, row + count, 0,  dd->minimum, fmt);
+         tab_double (t, 9, row + count, 0,  dd->maximum, fmt);
        }
 
-      tab_text (t, 1, row + count,
-               TAB_LEFT | TAT_TITLE, _("Total"));
+      {
+       double T;
+       double n, mean, variance;
 
-      tab_double (t, 2, row + count, 0, totals->n, wfmt);
+       moments1_calculate (ws->totals, &n, &mean, &variance, NULL, NULL);
 
-      tab_double (t, 3, row + count, 0, totals->mean, NULL);
+       double std_dev = sqrt (variance);
+       double std_error = std_dev / sqrt (n) ;
 
-      tab_double (t, 4, row + count, 0, totals->std_dev, NULL);
+       tab_text (t, 1, row + count,
+                 TAB_LEFT | TAT_TITLE, _("Total"));
 
-      std_error = totals->std_dev / sqrt (totals->n) ;
+       tab_double (t, 2, row + count, 0, n, wfmt);
 
-      tab_double (t, 5, row + count, 0, std_error, NULL);
+       tab_double (t, 3, row + count, 0, mean, NULL);
 
-      /* Now the confidence interval */
+       tab_double (t, 4, row + count, 0, std_dev, NULL);
 
-      T = gsl_cdf_tdist_Qinv (q, totals->n - 1);
+       tab_double (t, 5, row + count, 0, std_error, NULL);
 
-      tab_double (t, 6, row + count, 0,
-                 totals->mean - T * std_error, NULL);
+       /* Now the confidence interval */
 
-      tab_double (t, 7, row + count, 0,
-                 totals->mean + T * std_error, NULL);
+       T = gsl_cdf_tdist_Qinv (q, n - 1);
 
-      /* Min and Max */
+       tab_double (t, 6, row + count, 0,
+                   mean - T * std_error, NULL);
 
-      tab_double (t, 8, row + count, 0,  totals->minimum, fmt);
-      tab_double (t, 9, row + count, 0,  totals->maximum, fmt);
+       tab_double (t, 7, row + count, 0,
+                   mean + T * std_error, NULL);
+
+       /* Min and Max */
+
+       tab_double (t, 8, row + count, 0,  ws->minimum, fmt);
+       tab_double (t, 9, row + count, 0,  ws->maximum, fmt);
+      }
 
-      row += gp->n_groups + 1;
+      row += categoricals_total (cats) + 1;
     }
 
   tab_submit (t);
@@ -794,19 +927,15 @@ show_descriptives (const struct oneway_spec *cmd, const struct dictionary *dict)
 
 /* Show the homogeneity table */
 static void
-show_homogeneity (const struct oneway_spec *cmd)
+show_homogeneity (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
 {
   size_t v;
   int n_cols = 5;
   size_t n_rows = cmd->n_vars + 1;
 
-  struct tab_table *t;
-
-
-  t = tab_create (n_cols, n_rows);
+  struct tab_table *t = tab_create (n_cols, n_rows);
   tab_headers (t, 1, 0, 1, 0);
 
-
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
           TAL_2, TAL_2,
@@ -818,7 +947,6 @@ show_homogeneity (const struct oneway_spec *cmd)
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 1, 0, n_rows - 1);
 
-
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Levene Statistic"));
   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("df1"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("df2"));
@@ -828,24 +956,26 @@ show_homogeneity (const struct oneway_spec *cmd)
 
   for (v = 0; v < cmd->n_vars; ++v)
     {
-      double F;
+      const struct per_var_ws *pvw = &ws->vws[v];
+      const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
+
       const struct variable *var = cmd->vars[v];
       const struct group_proc *gp = group_proc_get (cmd->vars[v]);
       const char *s = var_to_string (var);
-      const struct group_statistics *totals = &gp->ugs;
 
-      const double df1 = gp->n_groups - 1;
-      const double df2 = totals->n - gp->n_groups;
+      const double df1 = pvw->n_groups - 1;
+      const double df2 = pvw->cc - pvw->n_groups;
+      double F = gp->levene;
 
       tab_text (t, 0, v + 1, TAB_LEFT | TAT_TITLE, s);
 
-      F = gp->levene;
+
       tab_double (t, 1, v + 1, TAB_RIGHT, F, NULL);
       tab_fixed (t, 2, v + 1, TAB_RIGHT, df1, 8, 0);
       tab_fixed (t, 3, v + 1, TAB_RIGHT, df2, 8, 0);
 
       /* Now the significance */
-      tab_double (t, 4, v + 1, TAB_RIGHT,gsl_cdf_fdist_Q (F, df1, df2), NULL);
+      tab_double (t, 4, v + 1, TAB_RIGHT, gsl_cdf_fdist_Q (F, df1, df2), NULL);
     }
 
   tab_submit (t);
@@ -854,7 +984,7 @@ show_homogeneity (const struct oneway_spec *cmd)
 
 /* Show the contrast coefficients table */
 static void
-show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws)
+show_contrast_coeffs (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
 {
   int c_num = 0;
   struct ll *cli;
@@ -863,10 +993,10 @@ show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws
   int n_cols = 2 + ws->actual_number_of_groups;
   int n_rows = 2 + n_contrasts;
 
-  void *const *group_values;
-
   struct tab_table *t;
 
+  const struct covariance *cov = ws->vws[0].cov ;
+
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
 
@@ -902,31 +1032,27 @@ show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws
   tab_joint_text (t, 2, 0, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
                  var_to_string (cmd->indep_var));
 
-  group_values = hsh_sort (ws->group_hash);
-
   for ( cli = ll_head (&cmd->contrast_list);
        cli != ll_null (&cmd->contrast_list);
        cli = ll_next (cli))
     {
       int count = 0;
       struct contrasts_node *cn = ll_data (cli, struct contrasts_node, ll);
-      struct ll *coeffi = ll_head (&cn->coefficient_list);
+      struct ll *coeffi ;
 
       tab_text_format (t, 1, c_num + 2, TAB_CENTER, "%d", c_num + 1);
 
-      for (count = 0;
-          count < hsh_count (ws->group_hash) && coeffi != ll_null (&cn->coefficient_list);
-          ++count)
+      for (coeffi = ll_head (&cn->coefficient_list);
+          coeffi != ll_null (&cn->coefficient_list);
+          ++count, coeffi = ll_next (coeffi))
        {
-         double *group_value_p;
-         union value group_value;
+         const struct categoricals *cats = covariance_get_categoricals (cov);
+         const union value *val = categoricals_get_value_by_subscript (cats, count);
          struct string vstr;
 
          ds_init_empty (&vstr);
 
-         group_value_p = group_values[count];
-         group_value.f = *group_value_p;
-         var_append_value_name (cmd->indep_var, &group_value, &vstr);
+         var_append_value_name (cmd->indep_var, val, &vstr);
 
          tab_text (t, count + 2, 1, TAB_CENTER | TAT_TITLE, ds_cstr (&vstr));
 
@@ -940,8 +1066,6 @@ show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws
 
              tab_text_format (t, count + 2, c_num + 2, TAB_RIGHT, "%g", coeffn->coeff);
            }
-
-         coeffi = ll_next (coeffi);
        }
       ++c_num;
     }
@@ -952,7 +1076,7 @@ show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws
 
 /* Show the results of the contrast tests */
 static void
-show_contrast_tests (const struct oneway_spec *cmd, struct oneway_workspace *ws)
+show_contrast_tests (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
 {
   int n_contrasts = ll_count (&cmd->contrast_list);
   size_t v;
@@ -980,7 +1104,6 @@ show_contrast_tests (const struct oneway_spec *cmd, struct oneway_workspace *ws)
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 3, 0, n_rows - 1);
 
-
   tab_title (t, _("Contrast Tests"));
 
   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Contrast"));