subtables work
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 2 Jan 2022 19:57:59 +0000 (11:57 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sat, 2 Apr 2022 01:48:55 +0000 (18:48 -0700)
src/language/stats/ctables.c

index 53b99519dcadb8bdfb853e44edb0158c30b95df4..47d2c9bce0978f31bb19d6c976f556c94b1379d6 100644 (file)
@@ -144,6 +144,53 @@ enum {
 #undef S
 };
 
+struct ctables_subtable
+  {
+    /* 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;
+  };
+
+struct ctables_layer
+  {
+    /* 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;
+
+    double valid;
+    double missing;
+  };
+
+struct ctables_freq
+  {
+    /* In struct ctables's 'ft' hmap.  Indexed by all the values in all the
+       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. */
+
+    struct
+      {
+        size_t vaa_idx;
+        union value *values;
+        int leaf;
+      }
+    axes[PIVOT_N_AXES];
+
+    union ctables_summary *summaries;
+  };
+
 struct ctables
   {
     struct pivot_table_look *look;
@@ -227,6 +274,7 @@ struct var_array
     struct variable **vars;
     size_t n;
     size_t scale_idx;
+    size_t subtable_idx;
 
     struct ctables_summary_spec *summaries;
     size_t n_summaries;
@@ -245,6 +293,7 @@ struct ctables_table
     struct var_array2 vaas[PIVOT_N_AXES];
     enum pivot_axis_type summary_axis;
     struct hmap ft;
+    struct hmap subtables;
 
     enum pivot_axis_type slabels_position;
     bool slabels_visible;
@@ -1687,7 +1736,8 @@ ctables_summary_add (union ctables_summary *s,
 }
 
 static double
-ctables_summary_value (union ctables_summary *s,
+ctables_summary_value (const struct ctables_freq *f,
+                       union ctables_summary *s,
                        const struct ctables_summary_spec *ss)
 {
   switch (ss->function)
@@ -1696,10 +1746,12 @@ ctables_summary_value (union ctables_summary *s,
     case CTSF_ECOUNT:
       return s->valid;
 
+    case CTSF_SUBTABLEPCT_COUNT:
+      return f->subtable->valid ? s->valid / f->subtable->valid * 100 : SYSMIS;
+
     case CTSF_ROWPCT_COUNT:
     case CTSF_COLPCT_COUNT:
     case CTSF_TABLEPCT_COUNT:
-    case CTSF_SUBTABLEPCT_COUNT:
     case CTSF_LAYERPCT_COUNT:
     case CTSF_LAYERROWPCT_COUNT:
     case CTSF_LAYERCOLPCT_COUNT:
@@ -1814,43 +1866,6 @@ ctables_summary_value (union ctables_summary *s,
   NOT_REACHED ();
 }
 
-struct ctables_freq
-  {
-    struct hmap_node node;      /* Element in hash table. */
-
-    struct
-      {
-        size_t vaa_idx;
-        union value *values;
-        int leaf;
-      }
-    axes[PIVOT_N_AXES];
-
-    union ctables_summary *summaries;
-  };
-
-#if 0
-static struct ctables_freq *
-ctables_freq_create (struct ctables_freqtab *ft)
-{
-  struct ctables_freq *f = xmalloc (sizeof *f + ft->vars.n * sizeof *f->values);
-  f->summaries = xmalloc (ft->n_summaries * sizeof *f->summaries);
-  for (size_t i = 0; i < ft->n_summaries; i++)
-    ctables_summary_init (&f->summaries[i], &ft->summaries[i]);
-  return f;
-}
-
-static void
-ctables_freq_add (struct ctables_freqtab *ft, struct ctables_freq *f,
-                  const struct variable *var, const union value *value,
-                  double weight)
-{
-  for (size_t i = 0; i < ft->n_summaries; i++)
-    ctables_summary_add (&f->summaries[i], &ft->summaries[i],
-                         var, value, weight);
-}
-#endif
-
 struct ctables_freq_sort_aux
   {
     const struct ctables_table *t;
@@ -1902,6 +1917,51 @@ 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)
+{
+  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);
+    }
+
+  struct ctables_subtable *st;
+  HMAP_FOR_EACH_WITH_HASH (st, struct ctables_subtable, node, hash, &t->subtables)
+    {
+      const struct ctables_freq *stf = st->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)
+            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])))
+                goto not_equal;
+        }
+
+      return st;
+
+    not_equal: ;
+    }
+
+  st = xmalloc (sizeof *st);
+  *st = (struct ctables_subtable) { .example = f };
+  hmap_insert (&t->subtables, &st->node, hash);
+  return st;
+}
+
 static void
 ctables_freqtab_insert (struct ctables_table *t,
                         const struct ccase *c,
@@ -1962,12 +2022,14 @@ 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);
   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;
 }
 
 static bool
@@ -1978,7 +2040,19 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
       struct ctables_table *t = ct->tables[i];
       for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
         if (t->axes[a])
-          t->vaas[a] = enumerate_fts (a, 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);
+              }
+          }
         else
           {
             struct var_array *va = xmalloc (sizeof *va);
@@ -2191,7 +2265,7 @@ ctables_execute (struct dataset *ds, struct ctables *ct)
                     dindexes[n_dindexes++] = leaf;
                   }
 
-              double d = ctables_summary_value (&f->summaries[j], &ss->summaries[j]);
+              double d = ctables_summary_value (f, &f->summaries[j], &ss->summaries[j]);
               struct pivot_value *value = pivot_value_new_number (d);
               value->numeric.format = ss->summaries[j].format;
               pivot_table_put (pt, dindexes, n_dindexes, value);
@@ -2449,6 +2523,7 @@ 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,