Fix bug in CLABELS when a missing category was moved, and add test.
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 2 Jun 2022 05:56:33 +0000 (22:56 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 2 Jun 2022 05:56:33 +0000 (22:56 -0700)
doc/pspp-figures/ctables17.sps
doc/statistics.texi
src/language/stats/ctables.c
tests/language/stats/ctables.at

index 590e600ff585a4f3e1ae564868ae3a6561ecd6a0..191703384a313f2cc6fbd2dfba556c4fa98c9615 100644 (file)
@@ -1,4 +1,4 @@
 GET FILE='nhtsa.sav'.
-*CTABLES /TABLE AgeGroup BY qns3a /CLABELS ROWLABELS=OPPOSITE.
+CTABLES /TABLE AgeGroup BY qns3a /CLABELS ROWLABELS=OPPOSITE.
 CTABLES /TABLE AgeGroup BY qns3a /CLABELS COLLABELS=OPPOSITE.
 
index be57f1a735d70f4df9cf410528d95f2ba8ce94a5..f199069c94868e81d5eef8cde06ba88710841a28 100644 (file)
@@ -1434,10 +1434,10 @@ CTABLES /TABLE AgeGroup BY qns3a.
 @end example
 @psppoutput {ctables16}
 
-With @t{ROWLABELS=OPPOSITE} or @t{COLLABELS=OPPOSITE}, row or column
-variable labels, respectively, move to the opposite axis.  Only one
-axis's labels may be moved, and the setting affects only the innermost
-variable on the given axis.  For example:
+@t{ROWLABELS=OPPOSITE} or @t{COLLABELS=OPPOSITE} move row or column
+variable category labels, respectively, to the opposite axis.  The
+setting affects only the innermost variable on the given axis.  For
+example:
 
 @example
 CTABLES /TABLE AgeGroup BY qns3a /CLABELS ROWLABELS=OPPOSITE.
@@ -1445,6 +1445,12 @@ CTABLES /TABLE AgeGroup BY qns3a /CLABELS COLLABELS=OPPOSITE.
 @end example
 @psppoutput {ctables17}
 
+@t{ROWLABELS=LAYER} or @t{COLLABELS=LAYER} move the innermost row or
+column variable category labels, respectively, to the layer axis.
+
+Only one axis's labels may be moved, whether to the opposite axis or
+to the layer axis.
+
 @node CTABLES Per-Variable Category Options
 @subsection Per-Variable Category Options
 
index 51493b0012a3c268310ea085c15fe150eb628d38..e34621e2fa4ad270ede00e0d80d39059e35edf58 100644 (file)
@@ -3002,6 +3002,20 @@ ctables_value_find__ (struct ctables_table *t, const union value *value,
   return NULL;
 }
 
+static void
+ctables_value_insert (struct ctables_table *t, const union value *value,
+                      int width)
+{
+  unsigned int hash = value_hash (value, width, 0);
+  struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
+  if (!clv)
+    {
+      clv = xmalloc (sizeof *clv);
+      value_clone (&clv->value, value, width);
+      hmap_insert (&t->clabels_values_map, &clv->node, hash);
+    }
+}
+
 static struct ctables_value *
 ctables_value_find (struct ctables_table *t,
                     const union value *value, int width)
@@ -3547,7 +3561,8 @@ ctables_table_output (struct ctables *ct, struct ctables_table *t)
                   const struct variable *var = clabels_nest->vars[clabels_nest->n - 1];
                   const union value *value = &cell->axes[t->clabels_from_axis].cvs[clabels_nest->n - 1].value;
                   const struct ctables_value *ctv = ctables_value_find (t, value, var_get_width (var));
-                  assert (ctv != NULL);
+                  if (!ctv)
+                    continue;
                   dindexes[n_dindexes++] = ctv->leaf;
                 }
 
@@ -3828,25 +3843,14 @@ ctables_insert_clabels_values (struct ctables_table *t, const struct ccase *c,
     {
       const struct ctables_nest *nest = &stack->nests[i];
       const struct variable *var = nest->vars[nest->n - 1];
-      int width = var_get_width (var);
       const union value *value = case_data (c, var);
 
       if (var_is_numeric (var) && value->f == SYSMIS)
         continue;
 
-      if (!ctables_categories_match (t->categories [var_get_dict_index (var)],
-                                     value, var))
-        continue;
-
-      unsigned int hash = value_hash (value, width, 0);
-
-      struct ctables_value *clv = ctables_value_find__ (t, value, width, hash);
-      if (!clv)
-        {
-          clv = xmalloc (sizeof *clv);
-          value_clone (&clv->value, value, width);
-          hmap_insert (&t->clabels_values_map, &clv->node, hash);
-        }
+      if (ctables_categories_match (t->categories [var_get_dict_index (var)],
+                                    value, var))
+        ctables_value_insert (t, value, var_get_width (var));
     }
 }
 
@@ -3864,7 +3868,18 @@ compare_clabels_values_3way (const void *a_, const void *b_, const void *width_)
 static void
 ctables_sort_clabels_values (struct ctables_table *t)
 {
-  int width = var_get_width (t->clabels_example);
+  const struct variable *v0 = t->clabels_example;
+  int width = var_get_width (v0);
+
+  struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
+  if (c0->show_empty)
+    {
+      const struct val_labs *val_labs = var_get_value_labels (v0);
+      for (const struct val_lab *vl = val_labs_first (val_labs); vl;
+           vl = val_labs_next (val_labs, vl))
+        if (ctables_categories_match (c0, &vl->value, v0))
+          ctables_value_insert (t, &vl->value, width);
+    }
 
   size_t n = hmap_count (&t->clabels_values_map);
   t->clabels_values = xnmalloc (n, sizeof *t->clabels_values);
@@ -4847,7 +4862,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
           [PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN,
           [PIVOT_AXIS_LAYER] = PIVOT_AXIS_LAYER,
         },
-        .clabels_from_axis = PIVOT_AXIS_LAYER, 
+        .clabels_from_axis = PIVOT_AXIS_LAYER,
         .categories = categories,
         .n_categories = n_vars,
         .cilevel = 95,
index e19007e65e17d3a6b7c0620c6b2abb0d2f0890fa..ec89cab1a56507f48a07f7c873711a7afe7873a5 100644 (file)
@@ -860,3 +860,56 @@ AT_CHECK([pspp ctables.sps -O box=unicode -O width=120], [0], [dnl
 ╰─────────────────────────────────────────────────────────┴───────┴───────┴─────────┴───────┴────────┴──────┴──────────╯
 ])
 AT_CLEANUP
+
+AT_SETUP([CTABLES CLABELS])
+AT_CHECK([ln $top_srcdir/examples/nhtsa.sav . || cp $top_srcdir/examples/nhtsa.sav .])
+AT_DATA([ctables.sps],
+[[GET 'nhtsa.sav'.
+CTABLES /TABLE AgeGroup BY qns3a /CLABELS ROWLABELS=OPPOSITE.
+CTABLES /TABLE AgeGroup BY qns3a /CLABELS COLLABELS=OPPOSITE.
+]])
+AT_CHECK([pspp ctables.sps -O box=unicode -O width=120], [0], [dnl
+                                                      Custom Tables
+╭───────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
+│       │                                                 S3a. GENDER:                                                 │
+│       ├──────────────────────────────────────────────────────┬───────────────────────────────────────────────────────┤
+│       │                         Male                         │                         Female                        │
+│       ├─────────┬───────┬──────┬──────┬──────┬───────┬───────┼──────────┬──────┬───────┬──────┬──────┬──────┬────────┤
+│       │  15 or  │ 16 to │ 26 to│ 36 to│ 46 to│ 56 to │ 66 or │   15 or  │ 16 to│ 26 to │ 36 to│ 46 to│ 56 to│  66 or │
+│       │ younger │   25  │  35  │  45  │  55  │   65  │ older │  younger │  25  │   35  │  45  │  55  │  65  │  older │
+│       ├─────────┼───────┼──────┼──────┼──────┼───────┼───────┼──────────┼──────┼───────┼──────┼──────┼──────┼────────┤
+│       │  Count  │ Count │ Count│ Count│ Count│ Count │ Count │   Count  │ Count│ Count │ Count│ Count│ Count│  Count │
+├───────┼─────────┼───────┼──────┼──────┼──────┼───────┼───────┼──────────┼──────┼───────┼──────┼──────┼──────┼────────┤
+│Age    │        0│    594│   476│   489│   526│    516│    531│         0│   505│    491│   548│   649│   731│     943│
+│group  │         │       │      │      │      │       │       │          │      │       │      │      │      │        │
+╰───────┴─────────┴───────┴──────┴──────┴──────┴───────┴───────┴──────────┴──────┴───────┴──────┴──────┴──────┴────────╯
+
+                Custom Tables
+╭──────────────────────────────┬────────────╮
+│                              │S3a. GENDER:│
+│                              ├────────────┤
+│                              │    Count   │
+├──────────────────────────────┼────────────┤
+│Age group 15 or younger Male  │           0│
+│                        Female│           0│
+│         ╶────────────────────┼────────────┤
+│          16 to 25      Male  │         594│
+│                        Female│         505│
+│         ╶────────────────────┼────────────┤
+│          26 to 35      Male  │         476│
+│                        Female│         491│
+│         ╶────────────────────┼────────────┤
+│          36 to 45      Male  │         489│
+│                        Female│         548│
+│         ╶────────────────────┼────────────┤
+│          46 to 55      Male  │         526│
+│                        Female│         649│
+│         ╶────────────────────┼────────────┤
+│          56 to 65      Male  │         516│
+│                        Female│         731│
+│         ╶────────────────────┼────────────┤
+│          66 or older   Male  │         531│
+│                        Female│         943│
+╰──────────────────────────────┴────────────╯
+])
+AT_CLEANUP
\ No newline at end of file