case: Introduce new functions for numbers and substrings in cases.
[pspp] / src / language / stats / descriptives.c
index beb5ec0b4182bd4fd48eef0f610f29b4d34129ce..eefd0598e31a6dca612c84ebd5339a77ef7eb05b 100644 (file)
@@ -38,7 +38,7 @@
 #include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "math/moments.h"
-#include "output/tab.h"
+#include "output/pivot-table.h"
 
 #include "gl/xalloc.h"
 
@@ -76,7 +76,7 @@ struct dsc_trns
     size_t var_cnt;             /* Number of variables. */
     enum dsc_missing_type missing_type; /* Treatment of missing values. */
     enum mv_class exclude;      /* Classes of missing values to exclude. */
-    struct variable *filter;    /* Dictionary FILTER BY variable. */
+    const struct variable *filter;    /* Dictionary FILTER BY variable. */
     struct casereader *z_reader; /* Reader for count, mean, stddev. */
     casenumber count;            /* Number left in this SPLIT FILE group.*/
     bool ok;
@@ -136,26 +136,17 @@ struct dsc_var
     double stats[DSC_N_STATS]; /* All the stats' values. */
   };
 
-/* Output format. */
-enum dsc_format
-  {
-    DSC_LINE,           /* Abbreviated format. */
-    DSC_SERIAL          /* Long format. */
-  };
-
 /* A DESCRIPTIVES procedure. */
 struct dsc_proc
   {
     /* Per-variable info. */
+    struct dictionary *dict;    /* Dictionary. */
     struct dsc_var *vars;       /* Variables. */
     size_t var_cnt;             /* Number of variables. */
 
     /* User options. */
     enum dsc_missing_type missing_type; /* Treatment of missing values. */
     enum mv_class exclude;      /* Classes of missing values to exclude. */
-    int show_var_labels;        /* Nonzero to show variable labels. */
-    int show_index;             /* Nonzero to show variable index. */
-    enum dsc_format format;     /* Output format. */
 
     /* Accumulated results. */
     double missing_listwise;    /* Sum of weights of cases missing listwise. */
@@ -209,13 +200,11 @@ cmd_descriptives (struct lexer *lexer, struct dataset *ds)
 
   /* Create and initialize dsc. */
   dsc = xmalloc (sizeof *dsc);
+  dsc->dict = dict;
   dsc->vars = NULL;
   dsc->var_cnt = 0;
   dsc->missing_type = DSC_VARIABLE;
   dsc->exclude = MV_ANY;
-  dsc->show_var_labels = 1;
-  dsc->show_index = 0;
-  dsc->format = DSC_LINE;
   dsc->missing_listwise = 0.;
   dsc->valid = 0.;
   dsc->bad_warn = 1;
@@ -253,18 +242,15 @@ cmd_descriptives (struct lexer *lexer, struct dataset *ds)
           lex_match (lexer, T_EQUALS);
           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
             {
-              if (lex_match_id (lexer, "LABELS"))
-                dsc->show_var_labels = 1;
-              else if (lex_match_id (lexer, "NOLABELS"))
-                dsc->show_var_labels = 0;
-              else if (lex_match_id (lexer, "INDEX"))
-                dsc->show_index = 1;
-              else if (lex_match_id (lexer, "NOINDEX"))
-                dsc->show_index = 0;
-              else if (lex_match_id (lexer, "LINE"))
-                dsc->format = DSC_LINE;
-              else if (lex_match_id (lexer, "SERIAL"))
-                dsc->format = DSC_SERIAL;
+              if (lex_match_id (lexer, "LABELS")
+                  || lex_match_id (lexer, "NOLABELS")
+                  || lex_match_id (lexer, "INDEX")
+                  || lex_match_id (lexer, "NOINDEX")
+                  || lex_match_id (lexer, "LINE")
+                  || lex_match_id (lexer, "SERIAL"))
+                {
+                  /* Ignore. */
+                }
               else
                 {
                   lex_error (lexer, NULL);
@@ -284,7 +270,15 @@ cmd_descriptives (struct lexer *lexer, struct dataset *ds)
               else if (lex_match_id (lexer, "DEFAULT"))
                 dsc->show_stats |= DEFAULT_STATS;
               else
-               dsc->show_stats |= 1ul << (match_statistic (lexer));
+               {
+                 enum dsc_statistic s = match_statistic (lexer);
+                 if (s == DSC_NONE)
+                   {
+                     lex_error (lexer, NULL);
+                     goto error;
+                   }
+                 dsc->show_stats |= 1ul << s;
+               }
               lex_match (lexer, T_COMMA);
             }
           if (dsc->show_stats == 0)
@@ -298,7 +292,7 @@ cmd_descriptives (struct lexer *lexer, struct dataset *ds)
           else
            {
              dsc->sort_by_stat = match_statistic (lexer);
-             if (dsc->sort_by_stat == DSC_NONE )
+             if (dsc->sort_by_stat == DSC_NONE)
                dsc->sort_by_stat = DSC_MEAN;
            }
           if (lex_match (lexer, T_LPAREN))
@@ -309,7 +303,8 @@ cmd_descriptives (struct lexer *lexer, struct dataset *ds)
                 dsc->sort_ascending = 0;
               else
                 lex_error (lexer, NULL);
-              lex_force_match (lexer, T_RPAREN);
+              if (! lex_force_match (lexer, T_RPAREN))
+               goto error;
             }
         }
       else if (var_cnt == 0)
@@ -549,7 +544,7 @@ generate_z_varname (const struct dictionary *dict, struct dsc_proc *dsc,
   /* Generate a synthetic name. */
   for (;;)
     {
-      char name[8];
+      char name[16];
 
       (*z_cnt)++;
 
@@ -580,37 +575,29 @@ generate_z_varname (const struct dictionary *dict, struct dsc_proc *dsc,
 static void
 dump_z_table (struct dsc_proc *dsc)
 {
-  size_t cnt = 0;
-  struct tab_table *t;
+  struct pivot_table *table = pivot_table_create (
+    N_("Mapping of Variables to Z-scores"));
 
-  {
-    size_t i;
+  pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Names"),
+                          N_("Source"), N_("Target"));
 
-    for (i = 0; i < dsc->var_cnt; i++)
-      if (dsc->vars[i].z_name != NULL)
-       cnt++;
-  }
-
-  t = tab_create (2, cnt + 1);
-  tab_title (t, _("Mapping of variables to corresponding Z-scores."));
-  tab_headers (t, 0, 0, 1, 0);
-  tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
-  tab_hline (t, TAL_2, 0, 1, 1);
-  tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
-  tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
+  struct pivot_dimension *names = pivot_dimension_create (
+    table, PIVOT_AXIS_ROW, N_("Variables"));
+  names->hide_all_labels = true;
 
-  {
-    size_t i, y;
+  for (size_t i = 0; i < dsc->var_cnt; i++)
+    if (dsc->vars[i].z_name != NULL)
+      {
+        int row = pivot_category_create_leaf (names->root,
+                                              pivot_value_new_number (i));
 
-    for (i = 0, y = 1; i < dsc->var_cnt; i++)
-      if (dsc->vars[i].z_name != NULL)
-       {
-         tab_text (t, 0, y, TAB_LEFT, var_to_string (dsc->vars[i].v));
-         tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
-       }
-  }
+        pivot_table_put2 (table, 0, row,
+                          pivot_value_new_variable (dsc->vars[i].v));
+        pivot_table_put2 (table, 1, row,
+                          pivot_value_new_user_text (dsc->vars[i].z_name, -1));
+      }
 
-  tab_submit (t);
+  pivot_table_submit (table);
 }
 
 static void
@@ -619,7 +606,7 @@ descriptives_set_all_sysmis_zscores (const struct dsc_trns *t, struct ccase *c)
   const struct dsc_z_score *z;
 
   for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
-    case_data_rw (c, z->z_var)->f = SYSMIS;
+    *case_num_rw (c, z->z_var) = SYSMIS;
 }
 
 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
@@ -669,7 +656,9 @@ descriptives_trns_proc (void *trns_, struct ccase **c,
         {
           if (t->ok)
             {
-              msg (SE, _("Internal error processing Z scores"));
+              msg (SE,  _("Internal error processing Z scores.  "
+                         "Please report this to %s."),
+                  PACKAGE_BUGREPORT);
               t->ok = false;
             }
           descriptives_set_all_sysmis_zscores (t, *c);
@@ -695,7 +684,7 @@ descriptives_trns_proc (void *trns_, struct ccase **c,
   for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
     {
       double input = case_num (*c, z->src_var);
-      double *output = &case_data_rw (*c, z->z_var)->f;
+      double *output = case_num_rw (*c, z->z_var);
 
       if (z->mean == SYSMIS || z->std_dev == SYSMIS
           || var_is_num_missing (z->src_var, input, t->exclude))
@@ -738,7 +727,7 @@ setup_z_trns (struct dsc_proc *dsc, struct dataset *ds)
   t->z_score_cnt = cnt;
   t->missing_type = dsc->missing_type;
   t->exclude = dsc->exclude;
-  if ( t->missing_type == DSC_LISTWISE )
+  if (t->missing_type == DSC_LISTWISE)
     {
       t->var_cnt = dsc->var_cnt;
       t->vars = xnmalloc (t->var_cnt, sizeof *t->vars);
@@ -791,7 +780,7 @@ static void
 calc_descriptives (struct dsc_proc *dsc, struct casereader *group,
                    struct dataset *ds)
 {
-  struct variable *filter = dict_get_filter (dataset_dict (ds));
+  const struct variable *filter = dict_get_filter (dataset_dict (ds));
   struct casereader *pass1, *pass2;
   casenumber count;
   struct ccase *c;
@@ -915,7 +904,7 @@ calc_descriptives (struct dsc_proc *dsc, struct casereader *group,
     {
       c = case_create (casewriter_get_proto (dsc->z_writer));
       z_idx = 0;
-      case_data_rw_idx (c, z_idx++)->f = count;
+      *case_num_rw_idx (c, z_idx++) = count;
     }
   else
     c = NULL;
@@ -956,8 +945,8 @@ calc_descriptives (struct dsc_proc *dsc, struct casereader *group,
 
       if (dv->z_name && c != NULL)
         {
-          case_data_rw_idx (c, z_idx++)->f = dv->stats[DSC_MEAN];
-          case_data_rw_idx (c, z_idx++)->f = dv->stats[DSC_STDDEV];
+          *case_num_rw_idx (c, z_idx++) = dv->stats[DSC_MEAN];
+          *case_num_rw_idx (c, z_idx++) = dv->stats[DSC_STDDEV];
         }
     }
 
@@ -994,64 +983,53 @@ static algo_compare_func descriptives_compare_dsc_vars;
 static void
 display (struct dsc_proc *dsc)
 {
-  size_t i;
-  int nc;
-  struct tab_table *t;
-
-  nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
-  for (i = 0; i < DSC_N_STATS; i++)
+  struct pivot_table *table = pivot_table_create (
+    N_("Descriptive Statistics"));
+  pivot_table_set_weight_var (table, dict_get_weight (dsc->dict));
+
+  struct pivot_dimension *statistics = pivot_dimension_create (
+    table, PIVOT_AXIS_COLUMN, N_("Statistics"));
+  pivot_category_create_leaf_rc (
+    statistics->root, pivot_value_new_text (N_("N")), PIVOT_RC_COUNT);
+  for (int i = 0; i < DSC_N_STATS; i++)
     if (dsc->show_stats & (1ul << i))
-      nc++;
+      pivot_category_create_leaf (statistics->root,
+                                  pivot_value_new_text (dsc_info[i].name));
 
   if (dsc->sort_by_stat != DSC_NONE)
     sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
           descriptives_compare_dsc_vars, dsc);
 
-  t = tab_create (nc, dsc->var_cnt + 1);
-  tab_headers (t, 1, 0, 1, 0);
-  tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
-  tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
-  tab_hline (t, TAL_2, 0, nc - 1, 1);
-  tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
-
-  nc = 0;
-  tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
-  if (dsc->format == DSC_SERIAL)
+  struct pivot_dimension *variables = pivot_dimension_create (
+    table, PIVOT_AXIS_ROW, N_("Variable"));
+  for (size_t i = 0; i < dsc->var_cnt; i++)
     {
-      tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
-      tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
-    }
-  else
-    tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
+      const struct dsc_var *dv = &dsc->vars[i];
 
-  for (i = 0; i < DSC_N_STATS; i++)
-    if (dsc->show_stats & (1ul << i))
-      {
-       const char *title = gettext (dsc_info[i].name);
-       tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
-      }
+      int row = pivot_category_create_leaf (variables->root,
+                                            pivot_value_new_variable (dv->v));
 
-  for (i = 0; i < dsc->var_cnt; i++)
-    {
-      struct dsc_var *dv = &dsc->vars[i];
-      size_t j;
+      int column = 0;
+      pivot_table_put2 (table, column++, row,
+                        pivot_value_new_number (dv->valid));
 
-      nc = 0;
-      tab_text (t, nc++, i + 1, TAB_LEFT, var_to_string (dv->v));
-      tab_text_format (t, nc++, i + 1, 0, "%.*g", DBL_DIG + 1, dv->valid);
-      if (dsc->format == DSC_SERIAL)
-       tab_text_format (t, nc++, i + 1, 0, "%.*g", DBL_DIG + 1, dv->missing);
-
-      for (j = 0; j < DSC_N_STATS; j++)
+      for (int j = 0; j < DSC_N_STATS; j++)
        if (dsc->show_stats & (1ul << j))
-         tab_double (t, nc++, i + 1, TAB_NONE, dv->stats[j], NULL);
+          {
+            union value v = { .f = dv->stats[j] };
+            struct pivot_value *pv = (j == DSC_MIN || j == DSC_MAX
+                                      ? pivot_value_new_var_value (dv->v, &v)
+                                      : pivot_value_new_number (dv->stats[j]));
+            pivot_table_put2 (table, column++, row, pv);
+          }
     }
 
-  tab_title (t, _("Valid cases = %.*g; cases with missing value(s) = %.*g."),
-            DBL_DIG + 1, dsc->valid,
-             DBL_DIG + 1, dsc->missing_listwise);
-
-  tab_submit (t);
+  int row = pivot_category_create_leaves (
+    variables->root, N_("Valid N (listwise)"), N_("Missing N (listwise)"));
+  pivot_table_put2 (table, 0, row, pivot_value_new_number (dsc->valid));
+  pivot_table_put2 (table, 0, row + 1,
+                    pivot_value_new_number (dsc->missing_listwise));
+  pivot_table_submit (table);
 }
 
 /* Compares `struct dsc_var's A and B according to the ordering