Fix some typos (found by codespell)
[pspp] / src / language / stats / crosstabs.q
index aa7e2457ee24ae9d41d40bd989da985b2c822fe3..08a182a456a4836d61d1fa9d2f847921f8ae97d3 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2012, 2013, 2014, 2016 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
@@ -41,6 +41,7 @@
 #include "data/value-labels.h"
 #include "data/variable.h"
 #include "language/command.h"
+#include "language/stats/freq.h"
 #include "language/dictionary/split-file.h"
 #include "language/lexer/lexer.h"
 #include "language/lexer/variable-parser.h"
@@ -55,6 +56,8 @@
 #include "libpspp/pool.h"
 #include "libpspp/str.h"
 #include "output/tab.h"
+#include "output/chart-item.h"
+#include "output/charts/barchart.h"
 
 #include "gl/minmax.h"
 #include "gl/xalloc.h"
      *^tables=custom;
      +variables=custom;
      missing=miss:!table/include/report;
+     count=roundwhat:asis/case/!cell,
+           roundhow:!round/truncate;
      +write[wr_]=none,cells,all;
      +format=val:!avalue/dvalue,
             indx:!noindex/index,
             tabl:!tables/notables,
             box:!box/nobox,
             pivot:!pivot/nopivot;
+     +barchart=;
      +cells[cl_]=count,expected,row,column,total,residual,sresidual,
                 asresidual,all,none;
      +statistics[st_]=chisq,phi,cc,lambda,uc,none,btau,ctau,risk,gamma,d,
 /* Number of directional statistics. */
 #define N_DIRECTIONAL 13
 
-/* A single table entry for general mode. */
-struct table_entry
-  {
-    struct hmap_node node;      /* Entry in hash table. */
-    double freq;                /* Frequency count. */
-    union value values[1];     /* Values. */
-  };
-
-static size_t
-table_entry_size (size_t n_values)
-{
-  return (offsetof (struct table_entry, values)
-          + n_values * sizeof (union value));
-}
 
 /* Indexes into the 'vars' member of struct pivot_table and
    struct crosstab member. */
@@ -136,7 +128,7 @@ struct pivot_table
 
     /* Data. */
     struct hmap data;
-    struct table_entry **entries;
+    struct freq **entries;
     size_t n_entries;
 
     /* Column values, number of columns. */
@@ -174,6 +166,7 @@ struct crosstabs_proc
     enum { INTEGER, GENERAL } mode;
     enum mv_class exclude;
     bool pivot;
+    bool barchart;
     bool bad_warn;
     struct fmt_spec weight_format;
 
@@ -191,6 +184,11 @@ struct crosstabs_proc
     unsigned int cells;         /* Bit k is 1 if cell k is requested. */
     int a_cells[CRS_CL_count];  /* 0...n_cells-1 are the requested cells. */
 
+    /* Rounding of cells. */
+    bool round_case_weights;    /* Round case weights? */
+    bool round_cells;           /* If !round_case_weights, round cells? */
+    bool round_down;            /* Round down? (otherwise to nearest) */
+
     /* STATISTICS. */
     unsigned int statistics;    /* Bit k is 1 if statistic k is requested. */
 
@@ -209,6 +207,12 @@ static void tabulate_integer_case (struct pivot_table *, const struct ccase *,
 static void postcalc (struct crosstabs_proc *);
 static void submit (struct pivot_table *, struct tab_table *);
 
+static double
+round_weight (const struct crosstabs_proc *proc, double weight)
+{
+  return proc->round_down ? floor (weight) : floor (weight + 0.5);
+}
+
 /* Parses and executes the CROSSTABS procedure. */
 int
 cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
@@ -241,10 +245,14 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
     }
 
   proc.mode = proc.n_variables ? INTEGER : GENERAL;
-
+  proc.barchart = cmd.sbc_barchart > 0;
 
   proc.descending = cmd.val == CRS_DVALUE;
 
+  proc.round_case_weights = cmd.sbc_count && cmd.roundwhat == CRS_CASE;
+  proc.round_cells = cmd.sbc_count && cmd.roundwhat == CRS_CELL;
+  proc.round_down = cmd.roundhow == CRS_TRUNCATE;
+
   /* CELLS. */
   if (!cmd.sbc_cells)
     proc.cells = 1u << CRS_CL_COUNT;
@@ -325,6 +333,12 @@ cmd_crosstabs (struct lexer *lexer, struct dataset *ds)
           {
             double weight = dict_get_case_weight (dataset_dict (ds), c,
                                                   &proc.bad_warn);
+            if (cmd.roundwhat == CRS_CASE)
+              {
+                weight = round_weight (&proc, weight);
+                if (weight == 0.)
+                  continue;
+              }
             if (should_tabulate_case (pt, c, proc.exclude))
               {
                 if (proc.mode == GENERAL)
@@ -417,10 +431,7 @@ crs_custom_tables (struct lexer *lexer, struct dataset *ds,
       if (!lex_match (lexer, T_BY))
        {
          if (n_by < 2)
-           {
-              lex_force_match (lexer, T_BY);
-             goto done;
-           }
+           goto done;
          else
            break;
        }
@@ -526,7 +537,7 @@ crs_custom_variables (struct lexer *lexer, struct dataset *ds,
 
           vr->var = var;
           vr->min = min;
-         vr->max = max + 1.;
+         vr->max = max;
          vr->count = max - min + 1;
           hmap_insert (&proc->var_ranges, &vr->hmap_node,
                        hash_pointer (var, 0));
@@ -579,7 +590,7 @@ should_tabulate_case (const struct pivot_table *pt, const struct ccase *c,
       if (range != NULL)
         {
           double num = case_num (c, var);
-          if (num < range->min || num > range->max)
+          if (num < range->min || num >= range->max + 1.)
             return false;
         }
     }
@@ -590,7 +601,7 @@ static void
 tabulate_integer_case (struct pivot_table *pt, const struct ccase *c,
                        double weight)
 {
-  struct table_entry *te;
+  struct freq *te;
   size_t hash;
   int j;
 
@@ -601,14 +612,14 @@ tabulate_integer_case (struct pivot_table *pt, const struct ccase *c,
       hash = hash_int (case_num (c, pt->vars[j]), hash);
     }
 
-  HMAP_FOR_EACH_WITH_HASH (te, struct table_entry, node, hash, &pt->data)
+  HMAP_FOR_EACH_WITH_HASH (te, struct freq, node, hash, &pt->data)
     {
       for (j = 0; j < pt->n_vars; j++)
         if ((int) case_num (c, pt->vars[j]) != (int) te->values[j].f)
           goto no_match;
 
       /* Found an existing entry. */
-      te->freq += weight;
+      te->count += weight;
       return;
 
     no_match: ;
@@ -616,7 +627,7 @@ tabulate_integer_case (struct pivot_table *pt, const struct ccase *c,
 
   /* No existing entry.  Create a new one. */
   te = xmalloc (table_entry_size (pt->n_vars));
-  te->freq = weight;
+  te->count = weight;
   for (j = 0; j < pt->n_vars; j++)
     te->values[j].f = (int) case_num (c, pt->vars[j]);
   hmap_insert (&pt->data, &te->node, hash);
@@ -626,7 +637,7 @@ static void
 tabulate_general_case (struct pivot_table *pt, const struct ccase *c,
                        double weight)
 {
-  struct table_entry *te;
+  struct freq *te;
   size_t hash;
   int j;
 
@@ -637,7 +648,7 @@ tabulate_general_case (struct pivot_table *pt, const struct ccase *c,
       hash = value_hash (case_data (c, var), var_get_width (var), hash);
     }
 
-  HMAP_FOR_EACH_WITH_HASH (te, struct table_entry, node, hash, &pt->data)
+  HMAP_FOR_EACH_WITH_HASH (te, struct freq, node, hash, &pt->data)
     {
       for (j = 0; j < pt->n_vars; j++)
         {
@@ -648,7 +659,7 @@ tabulate_general_case (struct pivot_table *pt, const struct ccase *c,
         }
 
       /* Found an existing entry. */
-      te->freq += weight;
+      te->count += weight;
       return;
 
     no_match: ;
@@ -656,7 +667,7 @@ tabulate_general_case (struct pivot_table *pt, const struct ccase *c,
 
   /* No existing entry.  Create a new one. */
   te = xmalloc (table_entry_size (pt->n_vars));
-  te->freq = weight;
+  te->count = weight;
   for (j = 0; j < pt->n_vars; j++)
     {
       const struct variable *var = pt->vars[j];
@@ -667,8 +678,8 @@ tabulate_general_case (struct pivot_table *pt, const struct ccase *c,
 \f
 /* Post-data reading calculations. */
 
-static int compare_table_entry_vars_3way (const struct table_entry *a,
-                                          const struct table_entry *b,
+static int compare_table_entry_vars_3way (const struct freq *a,
+                                          const struct freq *b,
                                           const struct pivot_table *pt,
                                           int idx0, int idx1);
 static int compare_table_entry_3way (const void *ap_, const void *bp_,
@@ -689,30 +700,51 @@ static bool find_crosstab (struct pivot_table *, size_t *row0p, size_t *row1p);
 static void
 postcalc (struct crosstabs_proc *proc)
 {
-  struct pivot_table *pt;
+
+  /* Round hash table entries, if requested
+
+     If this causes any of the cell counts to fall to zero, delete those
+     cells. */
+  if (proc->round_cells)
+    for (struct pivot_table *pt = proc->pivots;
+         pt < &proc->pivots[proc->n_pivots]; pt++)
+      {
+        struct freq *e, *next;
+        HMAP_FOR_EACH_SAFE (e, next, struct freq, node, &pt->data)
+          {
+            e->count = round_weight (proc, e->count);
+            if (e->count == 0.0)
+              {
+                hmap_delete (&pt->data, &e->node);
+                free (e);
+              }
+          }
+      }
 
   /* Convert hash tables into sorted arrays of entries. */
-  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
+  for (struct pivot_table *pt = proc->pivots;
+       pt < &proc->pivots[proc->n_pivots]; pt++)
     {
-      struct table_entry *e;
-      size_t i;
+      struct freq *e;
 
       pt->n_entries = hmap_count (&pt->data);
       pt->entries = xnmalloc (pt->n_entries, sizeof *pt->entries);
-      i = 0;
-      HMAP_FOR_EACH (e, struct table_entry, node, &pt->data)
+      size_t i = 0;
+      HMAP_FOR_EACH (e, struct freq, node, &pt->data)
         pt->entries[i++] = e;
       hmap_destroy (&pt->data);
 
       sort (pt->entries, pt->n_entries, sizeof *pt->entries,
             proc->descending ? compare_table_entry_3way_inv : compare_table_entry_3way,
            pt);
+
     }
 
   make_summary_table (proc);
 
   /* Output each pivot table. */
-  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
+  for (struct pivot_table *pt = proc->pivots;
+       pt < &proc->pivots[proc->n_pivots]; pt++)
     {
       if (proc->pivot || pt->n_vars == 2)
         output_pivot_table (proc, pt);
@@ -726,13 +758,15 @@ postcalc (struct crosstabs_proc *proc)
               output_pivot_table (proc, &subset);
             }
         }
+      if (proc->barchart)
+       chart_item_submit
+         (barchart_create (pt->vars, pt->n_vars, _("Count"), false, pt->entries, pt->n_entries));
     }
 
   /* Free output and prepare for next split file. */
-  for (pt = &proc->pivots[0]; pt < &proc->pivots[proc->n_pivots]; pt++)
+  for (struct pivot_table *pt = proc->pivots;
+       pt < &proc->pivots[proc->n_pivots]; pt++)
     {
-      size_t i;
-
       pt->missing = 0.0;
 
       /* Free the members that were allocated in this function(and the values
@@ -742,7 +776,7 @@ postcalc (struct crosstabs_proc *proc)
          lower level (in output_pivot_table), or both allocated and destroyed
          at a higher level (in crs_custom_tables and free_proc,
          respectively). */
-      for (i = 0; i < pt->n_vars; i++)
+      for (size_t i = 0; i < pt->n_vars; i++)
         {
           int width = var_get_width (pt->vars[i]);
           if (value_needs_init (width))
@@ -754,7 +788,7 @@ postcalc (struct crosstabs_proc *proc)
             }
         }
 
-      for (i = 0; i < pt->n_entries; i++)
+      for (size_t i = 0; i < pt->n_entries; i++)
         free (pt->entries[i]);
       free (pt->entries);
     }
@@ -780,8 +814,8 @@ make_pivot_table_subset (struct pivot_table *pt, size_t row0, size_t row1,
 }
 
 static int
-compare_table_entry_var_3way (const struct table_entry *a,
-                              const struct table_entry *b,
+compare_table_entry_var_3way (const struct freq *a,
+                              const struct freq *b,
                               const struct pivot_table *pt,
                               int idx)
 {
@@ -790,8 +824,8 @@ compare_table_entry_var_3way (const struct table_entry *a,
 }
 
 static int
-compare_table_entry_vars_3way (const struct table_entry *a,
-                               const struct table_entry *b,
+compare_table_entry_vars_3way (const struct freq *a,
+                               const struct freq *b,
                                const struct pivot_table *pt,
                                int idx0, int idx1)
 {
@@ -806,15 +840,15 @@ compare_table_entry_vars_3way (const struct table_entry *a,
   return 0;
 }
 
-/* Compare the struct table_entry at *AP to the one at *BP and
+/* Compare the struct freq at *AP to the one at *BP and
    return a strcmp()-type result. */
 static int
 compare_table_entry_3way (const void *ap_, const void *bp_, const void *pt_)
 {
-  const struct table_entry *const *ap = ap_;
-  const struct table_entry *const *bp = bp_;
-  const struct table_entry *a = *ap;
-  const struct table_entry *b = *bp;
+  const struct freq *const *ap = ap_;
+  const struct freq *const *bp = bp_;
+  const struct freq *a = *ap;
+  const struct freq *b = *bp;
   const struct pivot_table *pt = pt_;
   int cmp;
 
@@ -843,8 +877,8 @@ find_first_difference (const struct pivot_table *pt, size_t row)
     return pt->n_vars - 1;
   else
     {
-      const struct table_entry *a = pt->entries[row];
-      const struct table_entry *b = pt->entries[row - 1];
+      const struct freq *a = pt->entries[row];
+      const struct freq *b = pt->entries[row - 1];
       int col;
 
       for (col = pt->n_vars - 1; col >= 0; col--)
@@ -902,7 +936,7 @@ make_summary_table (struct crosstabs_proc *proc)
 
       valid = 0.;
       for (i = 0; i < pt->n_entries; i++)
-        valid += pt->entries[i]->freq;
+        valid += pt->entries[i]->count;
 
       n[0] = valid;
       n[1] = pt->missing;
@@ -1088,13 +1122,13 @@ build_matrix (struct pivot_table *x)
   const int row_var_width = var_get_width (x->vars[ROW_VAR]);
   int col, row;
   double *mp;
-  struct table_entry **p;
+  struct freq **p;
 
   mp = x->mat;
   col = row = 0;
   for (p = x->entries; p < &x->entries[x->n_entries]; p++)
     {
-      const struct table_entry *te = *p;
+      const struct freq *te = *p;
 
       while (!value_equal (&x->rows[row], &te->values[ROW_VAR], row_var_width))
         {
@@ -1110,7 +1144,7 @@ build_matrix (struct pivot_table *x)
           col++;
         }
 
-      *mp++ = te->freq;
+      *mp++ = te->count;
       if (++col >= x->n_cols)
         {
           col = 0;
@@ -1430,8 +1464,8 @@ find_crosstab (struct pivot_table *pt, size_t *row0p, size_t *row1p)
 
   for (row1 = row0 + 1; row1 < pt->n_entries; row1++)
     {
-      struct table_entry *a = pt->entries[row0];
-      struct table_entry *b = pt->entries[row1];
+      struct freq *a = pt->entries[row0];
+      struct freq *b = pt->entries[row1];
       if (compare_table_entry_vars_3way (a, b, pt, 2, pt->n_vars) != 0)
         break;
     }
@@ -1495,7 +1529,7 @@ enum_var_values (const struct pivot_table *pt, int var_idx,
       hmapx_init (&set);
       for (i = 0; i < pt->n_entries; i++)
         {
-          const struct table_entry *te = pt->entries[i];
+          const struct freq *te = pt->entries[i];
           const union value *value = &te->values[var_idx];
           size_t hash = value_hash (value, width, 0);
 
@@ -2605,6 +2639,7 @@ calc_symmetric (struct crosstabs_proc *proc, struct pivot_table *pt,
   /* Cohen's kappa. */
   if (proc->statistics & (1u << CRS_ST_KAPPA) && pt->ns_rows == pt->ns_cols)
     {
+      double ase_under_h0;
       double sum_fii, sum_rici, sum_fiiri_ci, sum_fijri_ci2, sum_riciri_ci;
       int i, j;
 
@@ -2633,24 +2668,23 @@ calc_symmetric (struct crosstabs_proc *proc, struct pivot_table *pt,
 
       v[8] = (pt->total * sum_fii - sum_rici) / (pow2 (pt->total) - sum_rici);
 
-      ase[8] = sqrt ((pow2 (pt->total) * sum_rici
-                     + sum_rici * sum_rici
-                     - pt->total * sum_riciri_ci)
-                    / (pt->total * (pow2 (pt->total) - sum_rici) * (pow2 (pt->total) - sum_rici)));
-#if 0
-      t[8] = v[8] / sqrt (pt->total * (((sum_fii * (pt->total - sum_fii))
+      ase_under_h0 = sqrt ((pow2 (pt->total) * sum_rici
+                           + sum_rici * sum_rici
+                           - pt->total * sum_riciri_ci)
+                          / (pt->total * (pow2 (pt->total) - sum_rici) * (pow2 (pt->total) - sum_rici)));
+
+      ase[8] = sqrt (pt->total * (((sum_fii * (pt->total - sum_fii))
                                / pow2 (pow2 (pt->total) - sum_rici))
                               + ((2. * (pt->total - sum_fii)
                                   * (2. * sum_fii * sum_rici
                                      - pt->total * sum_fiiri_ci))
-                                 / cube (pow2 (pt->total) - sum_rici))
+                                 / pow3 (pow2 (pt->total) - sum_rici))
                               + (pow2 (pt->total - sum_fii)
                                  * (pt->total * sum_fijri_ci2 - 4.
                                     * sum_rici * sum_rici)
                                  / pow4 (pow2 (pt->total) - sum_rici))));
-#else
-      t[8] = v[8] / ase[8];
-#endif
+
+      t[8] = v[8] / ase_under_h0;
     }
 
   return 1;