domains work
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 2 Jan 2022 20:49:26 +0000 (12:49 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sat, 2 Apr 2022 01:48:55 +0000 (18:48 -0700)
src/language/stats/ctables.c

index 47d2c9bce0978f31bb19d6c976f556c94b1379d6..88c326c8d0c4576f216159b5f0671a43054f80c9 100644 (file)
@@ -144,27 +144,24 @@ enum {
 #undef S
 };
 
-struct ctables_subtable
+enum ctables_domain_type
   {
-    /* In struct ctables's 'subtables' hmap.  Indexed by all the values in all
-       the axes except the innermost row and column variable and the scalar
-       variable, if any.  (If the scalar variable is the innermost row or
-       column variable, then the second-to-innermost variable is also omitted
-       along that axis.) */
-    struct hmap_node node;
-
-    const struct ctables_freq *example;
-
-    double valid;
-    double missing;
+    CTDT_TABLE,                  /* Entire table. */
+    CTDT_SUBTABLE,               /* Innermost variable in a single layer. */
+    CTDT_ROW,                    /* Row within a subtable. */
+    CTDT_COL,                    /* Column within a subtable. */
+    CTDT_LAYER,                  /* Entire layer. */
+    CTDT_LAYERROW,               /* Row within a layer. */
+    CTDT_LAYERCOL,               /* Column within a layer. */
+#define N_CTDTS 7
   };
 
-struct ctables_layer
+struct ctables_domain
   {
-    /* In struct ctables's 'layers' hmap.  Indexed by all the values in the
-       layer axis, except the scalar variable, if any. */
     struct hmap_node node;
 
+    const struct ctables_freq *example;
+
     double valid;
     double missing;
   };
@@ -175,10 +172,8 @@ struct ctables_freq
        axes (except the scalar variable, if any). */
     struct hmap_node node;
 
-    /* The subtable that contains this cell. */
-    struct ctables_subtable *subtable;
-
-    /* The layer that contains this cell. */
+    /* The domains that contains this cell. */
+    struct ctables_domain *domains[N_CTDTS];
 
     struct
       {
@@ -274,7 +269,8 @@ struct var_array
     struct variable **vars;
     size_t n;
     size_t scale_idx;
-    size_t subtable_idx;
+    size_t *domains[N_CTDTS];
+    size_t n_domains[N_CTDTS];
 
     struct ctables_summary_spec *summaries;
     size_t n_summaries;
@@ -293,7 +289,7 @@ struct ctables_table
     struct var_array2 vaas[PIVOT_N_AXES];
     enum pivot_axis_type summary_axis;
     struct hmap ft;
-    struct hmap subtables;
+    struct hmap domains[N_CTDTS];
 
     enum pivot_axis_type slabels_position;
     bool slabels_visible;
@@ -1747,14 +1743,26 @@ ctables_summary_value (const struct ctables_freq *f,
       return s->valid;
 
     case CTSF_SUBTABLEPCT_COUNT:
-      return f->subtable->valid ? s->valid / f->subtable->valid * 100 : SYSMIS;
+      return f->domains[CTDT_SUBTABLE]->valid ? s->valid / f->domains[CTDT_SUBTABLE]->valid * 100 : SYSMIS;
 
     case CTSF_ROWPCT_COUNT:
+      return f->domains[CTDT_ROW]->valid ? s->valid / f->domains[CTDT_ROW]->valid * 100 : SYSMIS;
+
     case CTSF_COLPCT_COUNT:
+      return f->domains[CTDT_COL]->valid ? s->valid / f->domains[CTDT_COL]->valid * 100 : SYSMIS;
+
     case CTSF_TABLEPCT_COUNT:
+      return f->domains[CTDT_TABLE]->valid ? s->valid / f->domains[CTDT_TABLE]->valid * 100 : SYSMIS;
+
     case CTSF_LAYERPCT_COUNT:
+      return f->domains[CTDT_LAYER]->valid ? s->valid / f->domains[CTDT_LAYER]->valid * 100 : SYSMIS;
+
     case CTSF_LAYERROWPCT_COUNT:
+      return f->domains[CTDT_LAYERROW]->valid ? s->valid / f->domains[CTDT_LAYERROW]->valid * 100 : SYSMIS;
+
     case CTSF_LAYERCOLPCT_COUNT:
+      return f->domains[CTDT_LAYERCOL]->valid ? s->valid / f->domains[CTDT_LAYERCOL]->valid * 100 : SYSMIS;
+
     case CTSF_ROWPCT_VALIDN:
     case CTSF_COLPCT_VALIDN:
     case CTSF_TABLEPCT_VALIDN:
@@ -1917,49 +1925,55 @@ ctables_freq_compare_3way (const void *a_, const void *b_, const void *aux_)
        Fill the table entry using the indexes from before.
  */
 
-static struct ctables_subtable *
-ctables_subtable_insert (struct ctables_table *t, struct ctables_freq *f)
+static struct ctables_domain *
+ctables_domain_insert (struct ctables_table *t, struct ctables_freq *f,
+                       enum ctables_domain_type domain)
 {
+  /* XXX how can we handle CTDT_LAYERROW and CTDT_LAYERCOL?  Crossing subtables
+     is hard.  */
   size_t hash = 0;
   for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
     {
       size_t idx = f->axes[a].vaa_idx;
       const struct var_array *va = &t->vaas[a].vas[idx];
       hash = hash_int (idx, hash);
-      for (size_t i = 0; i < va->n; i++)
-        if (i != va->scale_idx && i != va->subtable_idx)
-          hash = value_hash (&f->axes[a].values[i],
-                             var_get_width (va->vars[i]), hash);
+      for (size_t i = 0; i < va->n_domains[domain]; i++)
+        {
+          size_t v_idx = va->domains[domain][i];
+          hash = value_hash (&f->axes[a].values[v_idx],
+                             var_get_width (va->vars[v_idx]), hash);
+        }
     }
 
-  struct ctables_subtable *st;
-  HMAP_FOR_EACH_WITH_HASH (st, struct ctables_subtable, node, hash, &t->subtables)
+  struct ctables_domain *d;
+  HMAP_FOR_EACH_WITH_HASH (d, struct ctables_domain, node, hash, &t->domains[domain])
     {
-      const struct ctables_freq *stf = st->example;
+      const struct ctables_freq *df = d->example;
       for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
         {
           size_t idx = f->axes[a].vaa_idx;
-          if (idx != stf->axes[a].vaa_idx)
+          if (idx != df->axes[a].vaa_idx)
             goto not_equal;
 
           const struct var_array *va = &t->vaas[a].vas[idx];
-          for (size_t i = 0; i < va->n; i++)
-            if (i != va->scale_idx && i != va->subtable_idx
-                && !value_equal (&stf->axes[a].values[i],
-                                 &f->axes[a].values[i],
-                                 var_get_width (va->vars[i])))
+          for (size_t i = 0; i < va->n_domains[domain]; i++)
+            {
+              size_t v_idx = va->domains[domain][i];
+              if (!value_equal (&df->axes[a].values[v_idx],
+                                &f->axes[a].values[v_idx],
+                                var_get_width (va->vars[v_idx])))
                 goto not_equal;
+            }
         }
-
-      return st;
+      return d;
 
     not_equal: ;
     }
 
-  st = xmalloc (sizeof *st);
-  *st = (struct ctables_subtable) { .example = f };
-  hmap_insert (&t->subtables, &st->node, hash);
-  return st;
+  d = xmalloc (sizeof *d);
+  *d = (struct ctables_domain) { .example = f };
+  hmap_insert (&t->domains[domain], &d->node, hash);
+  return d;
 }
 
 static void
@@ -2022,14 +2036,16 @@ ctables_freqtab_insert (struct ctables_table *t,
   f->summaries = xmalloc (ss->n_summaries * sizeof *f->summaries);
   for (size_t i = 0; i < ss->n_summaries; i++)
     ctables_summary_init (&f->summaries[i], &ss->summaries[i]);
-  f->subtable = ctables_subtable_insert (t, f);
+  for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+    f->domains[dt] = ctables_domain_insert (t, f, dt);
   hmap_insert (&t->ft, &f->node, hash);
 
 summarize:
   for (size_t i = 0; i < ss->n_summaries; i++)
     ctables_summary_add (&f->summaries[i], &ss->summaries[i], ss->summary_var,
                          case_data (c, ss->summary_var), weight);
-  f->subtable->valid += weight;
+  for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+    f->domains[dt]->valid += weight;
 }
 
 static bool
@@ -2042,15 +2058,58 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
         if (t->axes[a])
           {
             t->vaas[a] = enumerate_fts (a, t->axes[a]);
+
             for (size_t j = 0; j < t->vaas[a].n; j++)
               {
                 struct var_array *va = &t->vaas[a].vas[j];
-                va->subtable_idx = (
-                  a == PIVOT_AXIS_LAYER ? SIZE_MAX
-                  : va->n == 0 ? SIZE_MAX
-                  : va->scale_idx != va->n - 1 ? va->n - 1
-                  : va->n == 1 ? SIZE_MAX
-                  : va->n - 2);
+                for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+                  {
+                    va->domains[dt] = xmalloc (va->n * sizeof *va->domains[dt]);
+                    va->n_domains[dt] = 0;
+
+                    for (size_t k = 0; k < va->n; k++)
+                      {
+                        if (k == va->scale_idx)
+                          continue;
+
+                        switch (dt)
+                          {
+                          case CTDT_TABLE:
+                            continue;
+
+                          case CTDT_LAYER:
+                            if (a != PIVOT_AXIS_LAYER)
+                              continue;
+                            break;
+
+                          case CTDT_SUBTABLE:
+                          case CTDT_ROW:
+                          case CTDT_COL:
+                            if (dt == CTDT_SUBTABLE ? a != PIVOT_AXIS_LAYER
+                                : dt == CTDT_ROW ? a == PIVOT_AXIS_COLUMN
+                                : a == PIVOT_AXIS_ROW)
+                              {
+                                if (k == va->n - 1
+                                    || (va->scale_idx == va->n - 1
+                                        && k == va->n - 2))
+                                  continue;
+                              }
+                            break;
+
+                          case CTDT_LAYERROW:
+                            if (a == PIVOT_AXIS_COLUMN)
+                              continue;
+                            break;
+
+                          case CTDT_LAYERCOL:
+                            if (a == PIVOT_AXIS_ROW)
+                              continue;
+                            break;
+                          }
+
+                        va->domains[dt][va->n_domains[dt]++] = k;
+                      }
+                  }
               }
           }
         else
@@ -2523,7 +2582,6 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
       struct ctables_table *t = xmalloc (sizeof *t);
       *t = (struct ctables_table) {
         .ft = HMAP_INITIALIZER (t->ft),
-        .subtables = HMAP_INITIALIZER (t->subtables),
         .slabels_position = PIVOT_AXIS_COLUMN,
         .slabels_visible = true,
         .row_labels = CTLP_NORMAL,
@@ -2533,6 +2591,8 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
         .n_categories = dict_get_n_vars (dataset_dict (ds)),
         .cilevel = 95,
       };
+      for (enum ctables_domain_type dt = 0; dt < N_CTDTS; dt++)
+        hmap_init (&t->domains[dt]);
       ct->tables[ct->n_tables++] = t;
 
       lex_match (lexer, T_EQUALS);