PPROPERTIES FORMAT
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 4 Jul 2022 19:50:28 +0000 (12:50 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 4 Jul 2022 19:52:23 +0000 (12:52 -0700)
src/language/stats/ctables.c
tests/language/stats/ctables.at

index ee35fd6bd3e790ba6a17446ed30139769819e97b..a29361cc3cc3be987e7d67ab5be8703c596243eb 100644 (file)
@@ -733,7 +733,7 @@ ctables_summary_spec_clone (struct ctables_summary_spec *dst,
                             const struct ctables_summary_spec *src)
 {
   *dst = *src;
-  dst->label = xstrdup (src->label);
+  dst->label = xstrdup_if_nonnull (src->label);
 }
 
 static void
@@ -3546,6 +3546,7 @@ struct ctables_pcexpr_evaluate_ctx
     const struct ctables_categories *cats;
     enum pivot_axis_type pc_a;
     size_t pc_a_idx;
+    size_t summary_idx;
   };
 
 static double ctables_pcexpr_evaluate (
@@ -3626,8 +3627,8 @@ found: ;
   const struct ctables_table *t = s->table;
   const struct ctables_nest *specs_nest = s->nests[t->summary_axis];
   const struct ctables_summary_spec_set *specs = &specs_nest->specs[tc->sv];
-  size_t j = 0 /* XXX */;
-  return ctables_summary_value (tc, &tc->summaries[j], &specs->specs[j]);
+  return ctables_summary_value (tc, &tc->summaries[ctx->summary_idx],
+                                &specs->specs[ctx->summary_idx]);
 }
 
 static double
@@ -3697,27 +3698,61 @@ ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
   NOT_REACHED ();
 }
 
+/* XXX what if there is a postcompute in more than one dimension?? */
+static const struct ctables_postcompute *
+ctables_cell_postcompute (const struct ctables_section *s,
+                          const struct ctables_cell *cell,
+                          enum pivot_axis_type *pc_a_p,
+                          size_t *pc_a_idx_p)
+{
+  assert (cell->postcompute);
+  for (enum pivot_axis_type pc_a = 0; ; pc_a++)
+    {
+      assert (pc_a < PIVOT_N_AXES);
+      for (size_t pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
+        {
+          const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
+          if (cv->category->type == CCT_POSTCOMPUTE)
+            {
+              if (pc_a_p)
+                *pc_a_p = pc_a;
+              if (pc_a_idx_p)
+                *pc_a_idx_p = pc_a_idx;
+              return cv->category->pc;
+            }
+        }
+    }
+
+  NOT_REACHED ();
+}
+
 static double
 ctables_cell_calculate_postcompute (const struct ctables_section *s,
-                                    const struct ctables_cell *cell)
+                                    const struct ctables_cell *cell,
+                                    const struct ctables_summary_spec *ss,
+                                    struct fmt_spec *format,
+                                    bool *is_ctables_format,
+                                    size_t summary_idx)
 {
   enum pivot_axis_type pc_a;
   size_t pc_a_idx;
-  const struct ctables_postcompute *pc;
-  for (pc_a = 0; ; pc_a++)
+  const struct ctables_postcompute *pc = ctables_cell_postcompute (
+    s, cell, &pc_a, &pc_a_idx);
+
+  if (pc->specs)
     {
-      assert (pc_a < PIVOT_N_AXES);
-      for (pc_a_idx = 0; pc_a_idx < s->nests[pc_a]->n; pc_a_idx++)
+      for (size_t i = 0; i < pc->specs->n; i++)
         {
-          const struct ctables_cell_value *cv = &cell->axes[pc_a].cvs[pc_a_idx];
-          if (cv->category->type == CCT_POSTCOMPUTE)
+          const struct ctables_summary_spec *ss2 = &pc->specs->specs[i];
+          if (ss->function == ss2->function
+              && ss->percentile == ss2->percentile)
             {
-              pc = cv->category->pc;
-              goto found;
+              *format = ss2->format;
+              *is_ctables_format = ss2->is_ctables_format;
+              break;
             }
         }
     }
-found: ;
 
   const struct variable *var = s->nests[pc_a]->vars[pc_a_idx];
   const struct ctables_categories *cats = s->table->categories[
@@ -3728,6 +3763,7 @@ found: ;
     .cats = cats,
     .pc_a = pc_a,
     .pc_a_idx = pc_a_idx,
+    .summary_idx = summary_idx,
   };
   return ctables_pcexpr_evaluate (&ctx, pc->expr);
 }
@@ -4034,15 +4070,18 @@ ctables_table_output (struct ctables *ct, struct ctables_table *t)
 
               const struct ctables_summary_spec *ss = &specs->specs[j];
 
+              struct fmt_spec format = specs->specs[j].format;
+              bool is_ctables_format = ss->is_ctables_format;
               double d = (cell->postcompute
-                          ? ctables_cell_calculate_postcompute (s, cell)
-                          : ctables_summary_value (cell, &cell->summaries[j], ss));
+                          ? ctables_cell_calculate_postcompute (
+                            s, cell, ss, &format, &is_ctables_format, j)
+                          : ctables_summary_value (cell, &cell->summaries[j],
+                                                   ss));
+
               struct pivot_value *value;
               if (ct->hide_threshold != 0
                   && d < ct->hide_threshold
-                  && (cell->postcompute
-                      ? false /* XXX */
-                      : ctables_summary_function_is_count (ss->function)))
+                  && ctables_summary_function_is_count (ss->function))
                 {
                   value = pivot_value_new_user_text_nocopy (
                     xasprintf ("<%d", ct->hide_threshold));
@@ -4051,18 +4090,17 @@ ctables_table_output (struct ctables *ct, struct ctables_table *t)
                 value = pivot_value_new_user_text (ct->zero, SIZE_MAX);
               else if (d == SYSMIS && ct->missing)
                 value = pivot_value_new_user_text (ct->missing, SIZE_MAX);
-              else if (specs->specs[j].is_ctables_format)
+              else if (is_ctables_format)
                 {
                   char *s = data_out_stretchy (&(union value) { .f = d },
-                                               "UTF-8",
-                                               &specs->specs[j].format,
+                                               "UTF-8", &format,
                                                &ct->ctables_formats, NULL);
                   value = pivot_value_new_user_text_nocopy (s);
                 }
               else
                 {
                   value = pivot_value_new_number (d);
-                  value->numeric.format = specs->specs[j].format;
+                  value->numeric.format = format;
                 }
               pivot_table_put (pt, dindexes, n_dindexes, value);
             }
@@ -5038,9 +5076,8 @@ ctables_parse_pproperties_format (struct lexer *lexer,
 
       /* Parse format. */
       struct fmt_spec format;
-      if (!parse_format_specifier (lexer, &format)
-          || !fmt_check_output (&format)
-          || !fmt_check_type_compat (&format, VAL_NUMERIC))
+      bool is_ctables_format;
+      if (!parse_ctables_format_specifier (lexer, &format, &is_ctables_format))
         goto error;
 
       if (sss->n >= sss->allocated)
@@ -5050,6 +5087,7 @@ ctables_parse_pproperties_format (struct lexer *lexer,
         .function = function,
         .percentile = percentile,
         .format = format,
+        .is_ctables_format = is_ctables_format,
       };
     }
   return true;
index 1d787428d3fd96cd8a8585e7228595f349f5a3f2..124b73938f1c0cbd5e35cbb918de3b810a9e353e 100644 (file)
@@ -14,11 +14,9 @@ dnl   * .SE suffixes.
 dnl - CATEGORIES:
 dnl   * Data-dependent sorting.
 dnl - PCOMPUTE:
-dnl   * multi-dimensional
+dnl   * multi-dimensional (multiple CCT_POSTCOMPUTE in one cell
 dnl   * MISSING, OTHERNM
 dnl   * strings
-dnl - PPROPERTIES:
-dnl   * summary statistics and formats?
 dnl
 dnl Features not yet tested:
 dnl - Parsing (positive and negative)
@@ -28,6 +26,7 @@ dnl - test CLABELS ROWLABELS=LAYER.
 dnl - Test VLABELS.
 dnl - Test WEIGHT and adjustment weights.
 dnl - Test PCOMPUTE and PPROPERTIES.
+dnl   * PCOMPUTE for more than one kind of summary (e.g. [COUNT, ROWPCT]).
 dnl - EMPTY=INCLUDE For string ranges.
 dnl - Summary functions:
 dnl   * Separate summary functions for totals and subtotals.
@@ -858,31 +857,31 @@ AT_DATA([ctables.sps],
 CTABLES
     /PCOMPUTE &x=EXPR([3] + [4])
     /PCOMPUTE &y=EXPR([4] + [5])
-    /PPROPERTIES &x LABEL='3+4' HIDESOURCECATS=YES
+    /PPROPERTIES &x LABEL='3+4' HIDESOURCECATS=YES FORMAT=COUNT F8.2
     /PPROPERTIES &y LABEL='4+5'
     /TABLE=qn105ba BY qns1
     /CATEGORIES VARIABLES=qns1 [1, 2, SUBTOTAL, 3, 4, 5, &x, &y, SUBTOTAL]
 ]])
 AT_CHECK([pspp ctables.sps -O box=unicode -O width=120], [0], [dnl
                                                       Custom Tables
\95­â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¬────────────────────────────────────────────────────────────╮
-│                                                         │ S1. Including yourself, how many members of this household │
-│                                                         │                    are age 16 or older?                    │
-│                                                         ├───────┬───────┬─────────┬───────┬────────┬──────┬──────────┤
-│                                                         │   1   │   2   │ Subtotal│   5   │   3+4  │  4+5 │ Subtotal │
-│                                                         ├───────┼───────┼─────────┼───────┼────────┼──────┼──────────┤
-│                                                         │ Count │ Count │  Count  │ Count │  Count │ Count│   Count  │
\94\9câ\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¼â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¼â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¼─────────┼───────┼────────┼──────┼──────────┤
-│105b. How likely is it that drivers who have  Almost     │    147│    246│      393│     11│      81│    30│        92│
-│had too much to drink to drive safely will A. certain    │       │       │         │       │        │      │          │
-│Get stopped by the police?                    Very likely│    384│    552│      936│     14│     171│    65│       185│
-│                                              Somewhat   │    590│   1249│     1839│     20│     265│    92│       285│
-│                                              likely     │       │       │         │       │        │      │          │
-│                                              Somewhat   │    278│    647│      925│      6│     116│    38│       122│
-│                                              unlikely   │       │       │         │       │        │      │          │
-│                                              Very       │    141│    290│      431│      4│      59│    22│        63│
-│                                              unlikely   │       │       │         │       │        │      │          │
\95°â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94´â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94´â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94´─────────┴───────┴────────┴──────┴──────────╯
\95­â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¬â\94\80────────────────────────────────────────────────────────────╮
+│                                                        │  S1. Including yourself, how many members of this household │
+│                                                        │                     are age 16 or older?                    │
+│                                                        ├───────┬───────┬──────────┬───────┬────────┬──────┬──────────┤
+│                                                        │   1   │   2   │ Subtotal │   5   │   3+4  │  4+5 │ Subtotal │
+│                                                        ├───────┼───────┼──────────┼───────┼────────┼──────┼──────────┤
+│                                                        │ Count │ Count │   Count  │ Count │  Count │ Count│   Count  │
\94\9câ\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¼â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¼â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94¼â\94\80─────────┼───────┼────────┼──────┼──────────┤
+│105b. How likely is it that drivers who have Almost     │    147│    246│       393│     11│   81.00│    30│        92│
+│had too much to drink to drive safely will   certain    │       │       │          │       │        │      │          │
+│A. Get stopped by the police?                Very likely│    384│    552│       936│     14│  171.00│    65│       185│
+│                                             Somewhat   │    590│   1249│      1839│     20│  265.00│    92│       285│
+│                                             likely     │       │       │          │       │        │      │          │
+│                                             Somewhat   │    278│    647│       925│      6│  116.00│    38│       122│
+│                                             unlikely   │       │       │          │       │        │      │          │
+│                                             Very       │    141│    290│       431│      4│   59.00│    22│        63│
+│                                             unlikely   │       │       │          │       │        │      │          │
\95°â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94´â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94´â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94\80â\94´â\94\80─────────┴───────┴────────┴──────┴──────────╯
 ])
 AT_CLEANUP