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