doc: Update SPV file format documentation.
[pspp] / src / language / stats / graph.c
index be138ed6c215da21c315ea49828c0881f199e764..0992627593aa526ab35d1a72b432c8fe991619b3 100644 (file)
@@ -1,6 +1,6 @@
 /*
   PSPP - a program for statistical analysis.
-  Copyright (C) 2012, 2013, 2015 Free Software Foundation, Inc.
+  Copyright (C) 2012, 2013, 2015, 2019 Free Software Foundation, Inc.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -413,9 +413,11 @@ run_barchart (struct graph *cmd, struct casereader *input)
 
   input = sort_execute (input, &cmd->ordering);
 
-  struct freq **freqs = NULL;
-  int n_freqs = 0;
+  struct freq **cells = NULL;
+  int n_cells = 0;
 
+  struct hmap columns = HMAP_INITIALIZER (columns);
+  assert (cmd->n_by_vars <= 2);
   for (grouper = casegrouper_create_vars (input, cmd->by_var,
                                           cmd->n_by_vars);
        casegrouper_get_next_group (grouper, &group);
@@ -439,22 +441,44 @@ run_barchart (struct graph *cmd, struct casereader *input)
          continue;
        }
 
-      freqs = xrealloc (freqs, sizeof (*freqs) * ++n_freqs);
-      freqs[n_freqs - 1] = xzalloc (sizeof (**freqs)
+      cells = xrealloc (cells, sizeof (*cells) * ++n_cells);
+      cells[n_cells - 1] = xzalloc (sizeof (**cells)
                                    + sizeof (union value)
                                    * (cmd->n_by_vars - 1));
 
-      if (ag_func[cmd->agr].cumulative && n_freqs >= 2)
-       freqs[n_freqs - 1]->count = freqs[n_freqs - 2]->count;
+      if (ag_func[cmd->agr].cumulative && n_cells >= 2)
+       cells[n_cells - 1]->count = cells[n_cells - 2]->count;
       else
-       freqs[n_freqs - 1]->count = 0;
+       cells[n_cells - 1]->count = 0;
       if (ag_func[cmd->agr].pre)
-       freqs[n_freqs - 1]->count = ag_func[cmd->agr].pre();
+       cells[n_cells - 1]->count = ag_func[cmd->agr].pre();
 
+      if (cmd->n_by_vars > 1)
+      {
+       const union value *vv = case_data (c, cmd->by_var[1]);
+       const double weight = dict_get_case_weight (cmd->dict, c, NULL);
+       int v1_width = var_get_width (cmd->by_var[1]);
+       size_t hash = value_hash (vv, v1_width, 0);
+
+       struct freq *fcol = NULL;
+       HMAP_FOR_EACH_WITH_HASH (fcol, struct freq, node, hash, &columns)
+         if (value_equal (vv, &fcol->values[0], v1_width))
+           break;
+
+       if (fcol)
+         fcol->count += weight;
+       else
+         {
+           fcol = xzalloc (sizeof *fcol);
+           fcol->count = weight;
+           value_clone (&fcol->values[0], vv, v1_width);
+           hmap_insert (&columns, &fcol->node, hash);
+         }
+      }
 
       for (v = 0; v < cmd->n_by_vars; ++v)
        {
-         value_clone (&freqs[n_freqs - 1]->values[v],
+         value_clone (&cells[n_cells - 1]->values[v],
                       case_data (c, cmd->by_var[v]),
                       var_get_width (cmd->by_var[v]));
        }
@@ -469,25 +493,55 @@ run_barchart (struct graph *cmd, struct casereader *input)
 
          cc += weight;
 
-         freqs[n_freqs - 1]->count
-           = ag_func[cmd->agr].calc (freqs[n_freqs - 1]->count, x, weight);
+         cells[n_cells - 1]->count
+           = ag_func[cmd->agr].calc (cells[n_cells - 1]->count, x, weight);
        }
 
       if (ag_func[cmd->agr].post)
-       freqs[n_freqs - 1]->count
-         = ag_func[cmd->agr].post (freqs[n_freqs - 1]->count, cc);
+       cells[n_cells - 1]->count
+         = ag_func[cmd->agr].post (cells[n_cells - 1]->count, cc);
 
       ccc += cc;
     }
 
   casegrouper_destroy (grouper);
 
-  for (int i = 0; i < n_freqs; ++i)
+  for (int i = 0; i < n_cells; ++i)
     {
       if (ag_func[cmd->agr].ppost)
-       freqs[i]->count = ag_func[cmd->agr].ppost (freqs[i]->count, ccc);
+       {
+         struct freq *cell = cells[i];
+         if (cmd->n_by_vars > 1)
+           {
+             const union value *vv = &cell->values[1];
+
+             int v1_width = var_get_width (cmd->by_var[1]);
+             size_t hash = value_hash (vv, v1_width, 0);
+
+             struct freq *fcol = NULL;
+             HMAP_FOR_EACH_WITH_HASH (fcol, struct freq, node, hash, &columns)
+               if (value_equal (vv, &fcol->values[0], v1_width))
+                 break;
+
+             cell->count = ag_func[cmd->agr].ppost (cell->count, fcol->count);
+           }
+         else
+           cell->count = ag_func[cmd->agr].ppost (cell->count, ccc);
+       }
     }
 
+  if (cmd->n_by_vars > 1)
+    {
+      struct freq *col_cell;
+      struct freq *next;
+      HMAP_FOR_EACH_SAFE (col_cell, next, struct freq, node, &columns)
+       {
+
+         value_destroy (col_cell->values, var_get_width (cmd->by_var[1]));
+         free (col_cell);
+       }
+    }
+  hmap_destroy (&columns);
 
   {
     struct string label;
@@ -503,15 +557,15 @@ run_barchart (struct graph *cmd, struct casereader *input)
 
     chart_item_submit (barchart_create (cmd->by_var, cmd->n_by_vars,
                                        ds_cstr (&label), false,
-                                       freqs, n_freqs));
+                                       cells, n_cells));
 
     ds_destroy (&label);
   }
 
-  for (int i = 0; i < n_freqs; ++i)
-    free (freqs[i]);
+  for (int i = 0; i < n_cells; ++i)
+    free (cells[i]);
 
-  free (freqs);
+  free (cells);
 }
 
 
@@ -747,7 +801,7 @@ cmd_graph (struct lexer *lexer, struct dataset *ds)
                }
              else
                {
-                 lex_error_expecting (lexer, "BIVARIATE", NULL);
+                 lex_error_expecting (lexer, "BIVARIATE");
                  goto error;
                }
              if (!lex_force_match (lexer, T_RPAREN))
@@ -899,7 +953,7 @@ cmd_graph (struct lexer *lexer, struct dataset *ds)
     case CT_BAR:
       break;
     case CT_NONE:
-      lex_error_expecting (lexer, "HISTOGRAM", "SCATTERPLOT", "BAR", NULL);
+      lex_error_expecting (lexer, "HISTOGRAM", "SCATTERPLOT", "BAR");
       goto error;
     default:
       NOT_REACHED ();