2b5864ee1ef7ba3a970971d8c06878c4f126f760
[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   free (entries);
92
93   return casereader_destroy (input);
94 }
95
96 /* Adds frequency counts of each value of VAR in INPUT to FREQ_HASH.  LO and HI
97    and each input value is truncated to an integer.  Returns true if
98    successful, false on input error.  It is the caller's responsibility to
99    initialize FREQ_HASH and to free it when no longer required, even on
100    failure. */
101 static bool
102 create_freq_hash (const struct dictionary *dict,
103                   struct casereader *input,
104                   const struct variable *var,
105                   struct hmap *freq_hash)
106 {
107   int width = var_get_width (var);
108   bool warn = true;
109   struct ccase *c;
110
111   for (; (c = casereader_read (input)) != NULL; case_unref (c))
112     {
113       const union value *value = case_data (c, var);
114       size_t hash = value_hash (value, width, 0);
115       double weight = dict_get_case_weight (dict, c, &warn);
116       struct freq *f;
117
118       f = freq_hmap_search (freq_hash, value, width, hash);
119       if (f == NULL)
120         f = freq_hmap_insert (freq_hash, value, width, hash);
121
122       f->count += weight;
123     }
124
125   return casereader_destroy (input);
126 }
127
128 static struct tab_table *
129 create_variable_frequency_table (const struct dictionary *dict,
130                                  struct casereader *input,
131                                  const struct chisquare_test *test,
132                                  int v, struct hmap *freq_hash)
133
134 {
135   int i;
136   const struct one_sample_test *ost = (const struct one_sample_test*)test;
137   int n_cells;
138   struct tab_table *table ;
139   const struct variable *var =  ost->vars[v];
140
141   hmap_init (freq_hash);
142   if (!create_freq_hash (dict, input, var, freq_hash))
143     {
144       freq_hmap_destroy (freq_hash, var_get_width (var));
145       return NULL;
146     }
147
148   n_cells = hmap_count (freq_hash);
149
150   if ( test->n_expected > 0 && n_cells != test->n_expected )
151     {
152       msg(ME, _("CHISQUARE test specified %d expected values, but"
153                 " %d distinct values were encountered in variable %s."),
154           test->n_expected, n_cells,
155           var_get_name (var)
156           );
157       freq_hmap_destroy (freq_hash, var_get_width (var));
158       return NULL;
159     }
160
161   table = tab_create(4, n_cells + 2);
162
163   tab_title (table, "%s", var_to_string(var));
164   tab_text (table, 1, 0, TAB_LEFT, _("Observed N"));
165   tab_text (table, 2, 0, TAB_LEFT, _("Expected N"));
166   tab_text (table, 3, 0, TAB_LEFT, _("Residual"));
167
168   tab_headers (table, 1, 0, 1, 0);
169
170   tab_box (table, TAL_1, TAL_1, -1, -1,
171            0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
172
173   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 1);
174
175   tab_vline (table, TAL_2, 1, 0, tab_nr(table) - 1);
176   for ( i = 2 ; i < 4 ; ++i )
177     tab_vline (table, TAL_1, i, 0, tab_nr(table) - 1);
178
179
180   tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
181
182   return table;
183 }
184
185
186 static struct tab_table *
187 create_combo_frequency_table (const struct chisquare_test *test)
188 {
189   int i;
190   const struct one_sample_test *ost = (const struct one_sample_test*)test;
191
192   struct tab_table *table ;
193
194   int n_cells = test->hi - test->lo + 1;
195
196   table = tab_create(1 + ost->n_vars * 4, n_cells + 3);
197
198   tab_title (table, _("Frequencies"));
199   for ( i = 0 ; i < ost->n_vars ; ++i )
200     {
201       const struct variable *var = ost->vars[i];
202       tab_text (table, i * 4 + 1, 1, TAB_LEFT, _("Category"));
203       tab_text (table, i * 4 + 2, 1, TAB_LEFT, _("Observed N"));
204       tab_text (table, i * 4 + 3, 1, TAB_LEFT, _("Expected N"));
205       tab_text (table, i * 4 + 4, 1, TAB_LEFT, _("Residual"));
206
207       tab_vline (table, TAL_2, i * 4 + 1,
208                  0, tab_nr (table) - 1);
209
210       tab_vline (table, TAL_1, i * 4 + 2,
211                  0, tab_nr (table) - 1);
212
213       tab_vline (table, TAL_1, i * 4 + 3,
214                  1, tab_nr (table) - 1);
215
216       tab_vline (table, TAL_1, i * 4 + 4,
217                  1, tab_nr (table) - 1);
218
219
220       tab_joint_text (table,
221                       i * 4 + 1, 0,
222                       i * 4 + 4, 0,
223                       TAB_CENTER,
224                       var_to_string (var));
225     }
226
227   for ( i = test->lo ; i <= test->hi ; ++i )
228     tab_fixed (table, 0, 2 + i - test->lo,
229                 TAB_LEFT, 1 + i - test->lo, 8, 0);
230
231   tab_headers (table, 1, 0, 2, 0);
232
233   tab_box (table, TAL_1, TAL_1, -1, -1,
234            0, 0, tab_nc (table) - 1, tab_nr(table) - 1 );
235
236   tab_hline (table, TAL_1, 1, tab_nc(table) - 1, 1);
237   tab_hline (table, TAL_1, 0, tab_nc(table) - 1, 2);
238
239   tab_text (table, 0, tab_nr (table) - 1, TAB_LEFT, _("Total"));
240
241   return table;
242 }
243
244
245 static struct tab_table *
246 create_stats_table (const struct chisquare_test *test)
247 {
248   const struct one_sample_test *ost = (const struct one_sample_test*) test;
249
250   struct tab_table *table;
251   table = tab_create (1 + ost->n_vars, 4);
252   tab_title (table, _("Test Statistics"));
253   tab_headers (table, 1, 0, 1, 0);
254
255   tab_box (table, TAL_1, TAL_1, -1, -1,
256            0, 0, tab_nc(table) - 1, tab_nr(table) - 1 );
257
258   tab_box (table, -1, -1, -1, TAL_1,
259            1, 0, tab_nc(table) - 1, tab_nr(table) - 1 );
260
261
262   tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);
263   tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1);
264
265
266   tab_text (table, 0, 1, TAB_LEFT, _("Chi-Square"));
267   tab_text (table, 0, 2, TAB_LEFT, _("df"));
268   tab_text (table, 0, 3, TAB_LEFT, _("Asymp. Sig."));
269
270   return table;
271 }
272
273
274 void
275 chisquare_execute (const struct dataset *ds,
276                    struct casereader *input,
277                    enum mv_class exclude,
278                    const struct npar_test *test,
279                    bool exact UNUSED,
280                    double timer UNUSED)
281 {
282   const struct dictionary *dict = dataset_dict (ds);
283   int v, i;
284   struct chisquare_test *cst = UP_CAST (test, struct chisquare_test,
285                                         parent.parent);
286   struct one_sample_test *ost = &cst->parent;
287   int n_cells = 0;
288   double total_expected = 0.0;
289   const struct variable *wvar = dict_get_weight (dict);
290   const struct fmt_spec *wfmt = wvar ?
291     var_get_print_format (wvar) : & F_8_0;
292
293   double *df = xzalloc (sizeof (*df) * ost->n_vars);
294   double *xsq = xzalloc (sizeof (*df) * ost->n_vars);
295   bool ok;
296
297   for ( i = 0 ; i < cst->n_expected ; ++i )
298     total_expected += cst->expected[i];
299
300   if ( cst->ranged == false )
301     {
302       for ( v = 0 ; v < ost->n_vars ; ++v )
303         {
304           const struct variable *var = ost->vars[v];
305           double total_obs = 0.0;
306           struct hmap freq_hash;
307           struct casereader *reader =
308             casereader_create_filter_missing (casereader_clone (input),
309                                               &var, 1, exclude,
310                                               NULL, NULL);
311           struct tab_table *freq_table =
312             create_variable_frequency_table(dict, reader, cst, v, &freq_hash);
313
314           struct freq **ff;
315
316           if ( NULL == freq_table )
317             continue;
318           ff = freq_hmap_sort (&freq_hash, var_get_width (var));
319
320           n_cells = hmap_count (&freq_hash);
321
322           for ( i = 0 ; i < n_cells ; ++i )
323             total_obs += ff[i]->count;
324
325           xsq[v] = 0.0;
326           for ( i = 0 ; i < n_cells ; ++i )
327             {
328               struct string str;
329               double exp;
330               const union value *observed_value = &ff[i]->value;
331
332               ds_init_empty (&str);
333               var_append_value_name (var, observed_value, &str);
334
335               /* The key */
336               tab_text (freq_table, 0, i + 1, TAB_LEFT, ds_cstr (&str));
337               ds_destroy (&str);
338
339
340               /* The observed N */
341               tab_double (freq_table, 1, i + 1, TAB_NONE,
342                          ff[i]->count, wfmt);
343
344               if ( cst->n_expected > 0 )
345                 exp = cst->expected[i] * total_obs / total_expected ;
346               else
347                 exp = total_obs / (double) n_cells;
348
349               tab_double (freq_table, 2, i + 1, TAB_NONE,
350                          exp, NULL);
351
352               /* The residual */
353               tab_double (freq_table, 3, i + 1, TAB_NONE,
354                          ff[i]->count - exp, NULL);
355
356               xsq[v] += (ff[i]->count - exp) * (ff[i]->count - exp) / exp;
357             }
358
359           df[v] = n_cells - 1.0;
360
361           tab_double (freq_table, 1, i + 1, TAB_NONE,
362                      total_obs, wfmt);
363
364           tab_submit (freq_table);
365
366           freq_hmap_destroy (&freq_hash, var_get_width (var));
367           free (ff);
368         }
369     }
370   else  /* ranged == true */
371     {
372       struct tab_table *freq_table = create_combo_frequency_table (cst);
373
374       n_cells = cst->hi - cst->lo + 1;
375
376       for ( v = 0 ; v < ost->n_vars ; ++v )
377         {
378           const struct variable *var = ost->vars[v];
379           double total_obs = 0.0;
380           struct casereader *reader =
381             casereader_create_filter_missing (casereader_clone (input),
382                                               &var, 1, exclude,
383                                               NULL, NULL);
384           struct hmap freq_hash;
385           struct freq **ff;
386
387           hmap_init (&freq_hash);
388           if (!create_freq_hash_with_range (dict, reader, var,
389                                             cst->lo, cst->hi, &freq_hash))
390             {
391               freq_hmap_destroy (&freq_hash, var_get_width (var));
392               continue;
393             }
394
395           ff = freq_hmap_sort (&freq_hash, var_get_width (var));
396
397           for ( i = 0 ; i < hmap_count (&freq_hash) ; ++i )
398             total_obs += ff[i]->count;
399
400           xsq[v] = 0.0;
401           for ( i = 0 ; i < hmap_count (&freq_hash) ; ++i )
402             {
403               struct string str;
404               double exp;
405
406               const union value *observed_value = &ff[i]->value;
407
408               ds_init_empty (&str);
409               var_append_value_name (ost->vars[v], observed_value, &str);
410               /* The key */
411               tab_text  (freq_table, v * 4 + 1, i + 2 , TAB_LEFT,
412                          ds_cstr (&str));
413               ds_destroy (&str);
414
415               /* The observed N */
416               tab_double (freq_table, v * 4 + 2, i + 2 , TAB_NONE,
417                          ff[i]->count, wfmt);
418
419               if ( cst->n_expected > 0 )
420                 exp = cst->expected[i] * total_obs / total_expected ;
421               else
422                 exp = total_obs / (double) hmap_count (&freq_hash);
423
424               /* The expected N */
425               tab_double (freq_table, v * 4 + 3, i + 2 , TAB_NONE,
426                          exp, NULL);
427
428               /* The residual */
429               tab_double (freq_table, v * 4 + 4, i + 2 , TAB_NONE,
430                          ff[i]->count - exp, NULL);
431
432               xsq[v] += (ff[i]->count - exp) * (ff[i]->count - exp) / exp;
433             }
434
435
436           tab_double (freq_table, v * 4 + 2, tab_nr (freq_table) - 1, TAB_NONE,
437                      total_obs, wfmt);
438
439           df[v] = n_cells - 1.0;
440
441           freq_hmap_destroy (&freq_hash, var_get_width (var));
442           free (ff);
443         }
444
445       tab_submit (freq_table);
446     }
447   ok = !taint_has_tainted_successor (casereader_get_taint (input));
448   casereader_destroy (input);
449
450   if (ok)
451     {
452       struct tab_table *stats_table = create_stats_table (cst);
453
454       /* Populate the summary statistics table */
455       for ( v = 0 ; v < ost->n_vars ; ++v )
456         {
457           const struct variable *var = ost->vars[v];
458
459           tab_text (stats_table, 1 + v, 0, TAB_CENTER, var_get_name (var));
460
461           tab_double (stats_table, 1 + v, 1, TAB_NONE, xsq[v], NULL);
462           tab_fixed (stats_table, 1 + v, 2, TAB_NONE, df[v], 8, 0);
463
464           tab_double (stats_table, 1 + v, 3, TAB_NONE,
465                      gsl_cdf_chisq_Q (xsq[v], df[v]), NULL);
466         }
467       tab_submit (stats_table);
468     }
469
470   free (xsq);
471   free (df);
472 }
473