tab: Make tab_value() take a variable instead of a dictionary.
[pspp] / src / language / stats / chisquare.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2006, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "language/stats/chisquare.h"
20
21 #include <gsl/gsl_cdf.h>
22 #include <math.h>
23 #include <stdlib.h>
24
25 #include "data/case.h"
26 #include "data/casereader.h"
27 #include "data/dataset.h"
28 #include "data/dictionary.h"
29 #include "data/format.h"
30 #include "data/value-labels.h"
31 #include "data/variable.h"
32 #include "language/stats/freq.h"
33 #include "language/stats/npar.h"
34 #include "libpspp/array.h"
35 #include "libpspp/assertion.h"
36 #include "libpspp/cast.h"
37 #include "libpspp/compiler.h"
38 #include "libpspp/hash-functions.h"
39 #include "libpspp/message.h"
40 #include "libpspp/taint.h"
41 #include "output/tab.h"
42
43 #include "gl/xalloc.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47
48 /* Adds frequency counts of each value of VAR in INPUT between LO and HI to
49    FREQ_HASH.  LO and HI and each input value is truncated to an integer.
50    Returns true if successful, false on input error.  It is the caller's
51    responsibility to initialize FREQ_HASH and to free it when no longer
52    required, even on failure. */
53 static bool
54 create_freq_hash_with_range (const struct dictionary *dict,
55                              struct casereader *input,
56                              const struct variable *var,
57                              double lo_, double hi_,
58                              struct hmap *freq_hash)
59 {
60   struct freq **entries;
61   bool warn = true;
62   struct ccase *c;
63   double lo, hi;
64   double i_d;
65
66   assert (var_is_numeric (var));
67   lo = trunc (lo_);
68   hi = trunc (hi_);
69
70   /* Populate the hash with zero entries */
71   entries = xnmalloc (hi - lo + 1, sizeof *entries);
72   for (i_d = lo; i_d <= hi; i_d += 1.0 )
73     {
74       size_t ofs = i_d - lo;
75       union value value = { i_d };
76       entries[ofs] = freq_hmap_insert (freq_hash, &value, 0,
77                                        value_hash (&value, 0, 0));
78     }
79
80   for (; (c = casereader_read (input)) != NULL; case_unref (c))
81     {
82       double x = trunc (case_num (c, var));
83       if (x >= lo && x <= hi)
84         {
85           size_t ofs = x - lo;
86           struct freq *fr = entries[ofs];
87           fr->count += dict_get_case_weight (dict, c, &warn);
88         }
89     }
90
91   return casereader_destroy (input);
92 }
93
94 /* Adds frequency counts of each value of VAR in INPUT to FREQ_HASH.  LO and HI
95    and each input value is truncated to an integer.  Returns true if
96    successful, false on input error.  It is the caller's responsibility to
97    initialize FREQ_HASH and to free it when no longer required, even on
98    failure. */
99 static bool
100 create_freq_hash (const struct dictionary *dict,
101                   struct casereader *input,
102                   const struct variable *var,
103                   struct hmap *freq_hash)
104 {
105   int width = var_get_width (var);
106   bool warn = true;
107   struct ccase *c;
108
109   for (; (c = casereader_read (input)) != NULL; case_unref (c))
110     {
111       const union value *value = case_data (c, var);
112       size_t hash = value_hash (value, width, 0);
113       double weight = dict_get_case_weight (dict, c, &warn);
114       struct freq *f;
115
116       f = freq_hmap_search (freq_hash, value, width, hash);
117       if (f == NULL)
118         f = freq_hmap_insert (freq_hash, value, width, hash);
119
120       f->count += weight;
121     }
122
123   return casereader_destroy (input);
124 }
125
126 static struct tab_table *
127 create_variable_frequency_table (const struct dictionary *dict,
128                                  struct casereader *input,
129                                  const struct chisquare_test *test,
130                                  int v, struct hmap *freq_hash)
131
132 {
133   int i;
134   const struct one_sample_test *ost = (const struct one_sample_test*)test;
135   int n_cells;
136   struct tab_table *table ;
137   const struct variable *var =  ost->vars[v];
138
139   hmap_init (freq_hash);
140   if (!create_freq_hash (dict, input, var, freq_hash))
141     {
142       freq_hmap_destroy (freq_hash, var_get_width (var));
143       return NULL;
144     }
145
146   n_cells = hmap_count (freq_hash);
147
148   if ( test->n_expected > 0 && n_cells != test->n_expected )
149     {
150       msg(ME, _("CHISQUARE test specified %d expected values, but"
151                 " %d distinct values were encountered in variable %s."),
152           test->n_expected, n_cells,
153           var_get_name (var)
154           );
155       return NULL;
156     }
157
158   table = tab_create(4, n_cells + 2);
159
160   tab_title (table, "%s", var_to_string(var));
161   tab_text (table, 1, 0, TAB_LEFT, _("Observed N"));
162   tab_text (table, 2, 0, TAB_LEFT, _("Expected N"));
163   tab_text (table, 3, 0, TAB_LEFT, _("Residual"));
164
165   tab_headers (table, 1, 0, 1, 0);
166
167   tab_box (table, TAL_1, TAL_1, -1, -1,
168            0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
169
170   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 1);
171
172   tab_vline (table, TAL_2, 1, 0, tab_nr(table) - 1);
173   for ( i = 2 ; i < 4 ; ++i )
174     tab_vline (table, TAL_1, i, 0, tab_nr(table) - 1);
175
176
177   tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
178
179   return table;
180 }
181
182
183 static struct tab_table *
184 create_combo_frequency_table (const struct chisquare_test *test)
185 {
186   int i;
187   const struct one_sample_test *ost = (const struct one_sample_test*)test;
188
189   struct tab_table *table ;
190
191   int n_cells = test->hi - test->lo + 1;
192
193   table = tab_create(1 + ost->n_vars * 4, n_cells + 3);
194
195   tab_title (table, _("Frequencies"));
196   for ( i = 0 ; i < ost->n_vars ; ++i )
197     {
198       const struct variable *var = ost->vars[i];
199       tab_text (table, i * 4 + 1, 1, TAB_LEFT, _("Category"));
200       tab_text (table, i * 4 + 2, 1, TAB_LEFT, _("Observed N"));
201       tab_text (table, i * 4 + 3, 1, TAB_LEFT, _("Expected N"));
202       tab_text (table, i * 4 + 4, 1, TAB_LEFT, _("Residual"));
203
204       tab_vline (table, TAL_2, i * 4 + 1,
205                  0, tab_nr (table) - 1);
206
207       tab_vline (table, TAL_1, i * 4 + 2,
208                  0, tab_nr (table) - 1);
209
210       tab_vline (table, TAL_1, i * 4 + 3,
211                  1, tab_nr (table) - 1);
212
213       tab_vline (table, TAL_1, i * 4 + 4,
214                  1, tab_nr (table) - 1);
215
216
217       tab_joint_text (table,
218                       i * 4 + 1, 0,
219                       i * 4 + 4, 0,
220                       TAB_CENTER,
221                       var_to_string (var));
222     }
223
224   for ( i = test->lo ; i <= test->hi ; ++i )
225     tab_fixed (table, 0, 2 + i - test->lo,
226                 TAB_LEFT, 1 + i - test->lo, 8, 0);
227
228   tab_headers (table, 1, 0, 2, 0);
229
230   tab_box (table, TAL_1, TAL_1, -1, -1,
231            0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
232
233   tab_hline (table, TAL_1, 1, tab_nc(table) - 1, 1);
234   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 2);
235
236   tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
237
238   return table;
239 }
240
241
242 static struct tab_table *
243 create_stats_table (const struct chisquare_test *test)
244 {
245   const struct one_sample_test *ost = (const struct one_sample_test*) test;
246
247   struct tab_table *table;
248   table = tab_create (1 + ost->n_vars, 4);
249   tab_title (table, _("Test Statistics"));
250   tab_headers (table, 1, 0, 1, 0);
251
252   tab_box (table, TAL_1, TAL_1, -1, -1,
253            0, 0, tab_nc(table) - 1, tab_nr(table) - 1 );
254
255   tab_box (table, -1, -1, -1, TAL_1,
256            1, 0, tab_nc(table) - 1, tab_nr(table) - 1 );
257
258
259   tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
260   tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1);
261
262
263   tab_text (table, 0, 1, TAB_LEFT, _("Chi-Square"));
264   tab_text (table, 0, 2, TAB_LEFT, _("df"));
265   tab_text (table, 0, 3, TAB_LEFT, _("Asymp. Sig."));
266
267   return table;
268 }
269
270
271 void
272 chisquare_execute (const struct dataset *ds,
273                    struct casereader *input,
274                    enum mv_class exclude,
275                    const struct npar_test *test,
276                    bool exact UNUSED,
277                    double timer UNUSED)
278 {
279   const struct dictionary *dict = dataset_dict (ds);
280   int v, i;
281   struct chisquare_test *cst = UP_CAST (test, struct chisquare_test,
282                                         parent.parent);
283   struct one_sample_test *ost = &cst->parent;
284   int n_cells = 0;
285   double total_expected = 0.0;
286   const struct variable *wvar = dict_get_weight (dict);
287   const struct fmt_spec *wfmt = wvar ?
288     var_get_print_format (wvar) : & F_8_0;
289
290   double *df = xzalloc (sizeof (*df) * ost->n_vars);
291   double *xsq = xzalloc (sizeof (*df) * ost->n_vars);
292   bool ok;
293
294   for ( i = 0 ; i < cst->n_expected ; ++i )
295     total_expected += cst->expected[i];
296
297   if ( cst->ranged == false )
298     {
299       for ( v = 0 ; v < ost->n_vars ; ++v )
300         {
301           const struct variable *var = ost->vars[v];
302           double total_obs = 0.0;
303           struct hmap freq_hash;
304           struct casereader *reader =
305             casereader_create_filter_missing (casereader_clone (input),
306                                               &var, 1, exclude,
307                                               NULL, NULL);
308           struct tab_table *freq_table =
309             create_variable_frequency_table(dict, reader, cst, v, &freq_hash);
310
311           struct freq **ff;
312
313           if ( NULL == freq_table )
314             continue;
315           ff = freq_hmap_sort (&freq_hash, var_get_width (var));
316
317           n_cells = hmap_count (&freq_hash);
318
319           for ( i = 0 ; i < n_cells ; ++i )
320             total_obs += ff[i]->count;
321
322           xsq[v] = 0.0;
323           for ( i = 0 ; i < n_cells ; ++i )
324             {
325               struct string str;
326               double exp;
327               const union value *observed_value = &ff[i]->value;
328
329               ds_init_empty (&str);
330               var_append_value_name (var, observed_value, &str);
331
332               /* The key */
333               tab_text (freq_table, 0, i + 1, TAB_LEFT, ds_cstr (&str));
334               ds_destroy (&str);
335
336
337               /* The observed N */
338               tab_double (freq_table, 1, i + 1, TAB_NONE,
339                          ff[i]->count, wfmt);
340
341               if ( cst->n_expected > 0 )
342                 exp = cst->expected[i] * total_obs / total_expected ;
343               else
344                 exp = total_obs / (double) n_cells;
345
346               tab_double (freq_table, 2, i + 1, TAB_NONE,
347                          exp, NULL);
348
349               /* The residual */
350               tab_double (freq_table, 3, i + 1, TAB_NONE,
351                          ff[i]->count - exp, NULL);
352
353               xsq[v] += (ff[i]->count - exp) * (ff[i]->count - exp) / exp;
354             }
355
356           df[v] = n_cells - 1.0;
357
358           tab_double (freq_table, 1, i + 1, TAB_NONE,
359                      total_obs, wfmt);
360
361           tab_submit (freq_table);
362
363           freq_hmap_destroy (&freq_hash, var_get_width (var));
364           free (ff);
365         }
366     }
367   else  /* ranged == true */
368     {
369       struct tab_table *freq_table = create_combo_frequency_table (cst);
370
371       n_cells = cst->hi - cst->lo + 1;
372
373       for ( v = 0 ; v < ost->n_vars ; ++v )
374         {
375           const struct variable *var = ost->vars[v];
376           double total_obs = 0.0;
377           struct casereader *reader =
378             casereader_create_filter_missing (casereader_clone (input),
379                                               &var, 1, exclude,
380                                               NULL, NULL);
381           struct hmap freq_hash;
382           struct freq **ff;
383
384           hmap_init (&freq_hash);
385           if (!create_freq_hash_with_range (dict, reader, var,
386                                             cst->lo, cst->hi, &freq_hash))
387             {
388               freq_hmap_destroy (&freq_hash, var_get_width (var));
389               continue;
390             }
391
392           ff = freq_hmap_sort (&freq_hash, var_get_width (var));
393
394           for ( i = 0 ; i < hmap_count (&freq_hash) ; ++i )
395             total_obs += ff[i]->count;
396
397           xsq[v] = 0.0;
398           for ( i = 0 ; i < hmap_count (&freq_hash) ; ++i )
399             {
400               struct string str;
401               double exp;
402
403               const union value *observed_value = &ff[i]->value;
404
405               ds_init_empty (&str);
406               var_append_value_name (ost->vars[v], observed_value, &str);
407               /* The key */
408               tab_text  (freq_table, v * 4 + 1, i + 2 , TAB_LEFT,
409                          ds_cstr (&str));
410               ds_destroy (&str);
411
412               /* The observed N */
413               tab_double (freq_table, v * 4 + 2, i + 2 , TAB_NONE,
414                          ff[i]->count, wfmt);
415
416               if ( cst->n_expected > 0 )
417                 exp = cst->expected[i] * total_obs / total_expected ;
418               else
419                 exp = total_obs / (double) hmap_count (&freq_hash);
420
421               /* The expected N */
422               tab_double (freq_table, v * 4 + 3, i + 2 , TAB_NONE,
423                          exp, NULL);
424
425               /* The residual */
426               tab_double (freq_table, v * 4 + 4, i + 2 , TAB_NONE,
427                          ff[i]->count - exp, NULL);
428
429               xsq[v] += (ff[i]->count - exp) * (ff[i]->count - exp) / exp;
430             }
431
432
433           tab_double (freq_table, v * 4 + 2, tab_nr (freq_table) - 1, TAB_NONE,
434                      total_obs, wfmt);
435
436           df[v] = n_cells - 1.0;
437
438           freq_hmap_destroy (&freq_hash, var_get_width (var));
439           free (ff);
440         }
441
442       tab_submit (freq_table);
443     }
444   ok = !taint_has_tainted_successor (casereader_get_taint (input));
445   casereader_destroy (input);
446
447   if (ok)
448     {
449       struct tab_table *stats_table = create_stats_table (cst);
450
451       /* Populate the summary statistics table */
452       for ( v = 0 ; v < ost->n_vars ; ++v )
453         {
454           const struct variable *var = ost->vars[v];
455
456           tab_text (stats_table, 1 + v, 0, TAB_CENTER, var_get_name (var));
457
458           tab_double (stats_table, 1 + v, 1, TAB_NONE, xsq[v], NULL);
459           tab_fixed (stats_table, 1 + v, 2, TAB_NONE, df[v], 8, 0);
460
461           tab_double (stats_table, 1 + v, 3, TAB_NONE,
462                      gsl_cdf_chisq_Q (xsq[v], df[v]), NULL);
463         }
464       tab_submit (stats_table);
465     }
466
467   free (xsq);
468   free (df);
469 }
470