Implemented the NPAR TESTS command.
[pspp-builds.git] / src / language / stats / binomial.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3    Written by John Darrington <john@darrington.wattle.id.au>
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include <libpspp/compiler.h>
22 #include <output/table.h>
23 #include <libpspp/alloc.h>
24
25 #include <data/case.h>
26 #include <data/casefile.h>
27 #include <data/dictionary.h>
28 #include <data/procedure.h>
29 #include <data/variable.h>
30 #include <data/value.h>
31 #include <data/value-labels.h>
32 #include <data/casefilter.h>
33
34 #include <libpspp/message.h>
35 #include <libpspp/assertion.h>
36
37 #include "binomial.h"
38 #include "freq.h"
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42
43 #include <libpspp/misc.h>
44
45 #include <gsl/gsl_cdf.h>
46 #include <gsl/gsl_randist.h>
47 #include <gsl-extras/gsl-extras.h>
48
49 #include <minmax.h>
50
51 #include <libpspp/hash.h>
52
53 static double calculate_binomial_internal (double n1, double n2,
54                                            double p);
55
56
57 static void
58 swap (double *i1, double *i2)
59 {
60   double temp = *i1;
61   *i1 = *i2;
62   *i2 = temp;
63 }
64
65 static double
66 calculate_binomial (double n1, double n2, double p)
67 {
68   const double n = n1 + n2;
69   const bool test_reversed = (n1 / n > p ) ;
70   if ( test_reversed )
71     {
72       p = 1 - p ;
73       swap (&n1, &n2);
74     }
75
76   return calculate_binomial_internal (n1, n2, p);
77 }
78
79 static double
80 calculate_binomial_internal (double n1, double n2, double p)
81 {
82   /* SPSS Statistical Algorithms has completely different and WRONG 
83      advice here. */
84
85   double sig1tailed = gslextras_cdf_binomial_P (n1, n1 + n2, p);
86
87   if ( p == 0.5 )
88     return sig1tailed > 0.5 ? 1.0 :sig1tailed * 2.0;
89
90   return sig1tailed ;
91 }
92
93 static void
94 do_binomial (const struct dictionary *dict,
95              const struct casefile *cf,
96              const struct binomial_test *bst,
97              struct freq *cat1,
98              struct freq *cat2,
99              const struct casefilter *filter
100              )
101 {
102   bool warn = true;
103
104   const struct one_sample_test *ost = (const struct one_sample_test *) bst;
105   struct ccase c;
106   struct casereader *r = casefile_get_reader (cf, NULL);
107
108   while (casereader_read(r, &c))
109     {
110       int v;
111       double w =
112         dict_get_case_weight (dict, &c, &warn);
113
114       for (v = 0 ; v < ost->n_vars ; ++v )
115         {
116           const struct variable *var = ost->vars[v];
117           const union value *value = case_data (&c, var);
118
119           if ( casefilter_variable_missing (filter, &c, var))
120             break;
121
122           if ( NULL == cat1[v].value )
123             {
124               cat1[v].value = value_dup (value, var_get_width (var));
125               cat1[v].count = w;
126             }
127           else if ( 0 == compare_values (cat1[v].value, value,
128                                          var_get_width (var)))
129             cat1[v].count += w;
130           else if ( NULL == cat2[v].value )
131             {
132               cat2[v].value = value_dup (value, var_get_width (var));
133               cat2[v].count = w;
134             }
135           else if ( 0 == compare_values (cat2[v].value, value,
136                                          var_get_width (var)))
137             cat2[v].count += w;
138           else if ( bst->category1 == SYSMIS)
139             msg (ME, _("Variable %s is not dichotomous"), var_get_name (var));
140         }
141
142       case_destroy (&c);
143     }
144   casereader_destroy (r);
145 }
146
147
148
149 void
150 binomial_execute (const struct dataset *ds,
151                   const struct casefile *cf,
152                   struct casefilter *filter,
153                   const struct npar_test *test)
154 {
155   int v;
156   const struct binomial_test *bst = (const struct binomial_test *) test;
157   const struct one_sample_test *ost = (const struct one_sample_test*) test;
158
159   struct freq *cat1 = xzalloc (sizeof (*cat1) * ost->n_vars);
160   struct freq *cat2 = xzalloc (sizeof (*cat1) * ost->n_vars);
161   struct tab_table *table ;
162
163   assert ((bst->category1 == SYSMIS) == (bst->category2 == SYSMIS) );
164
165   if ( bst->category1 != SYSMIS )
166     {
167       union value v;
168       v.f = bst->category1;
169       cat1->value = value_dup (&v, 0);
170     }
171
172   if ( bst->category2 != SYSMIS )
173     {
174       union value v;
175       v.f = bst->category2;
176       cat2->value = value_dup (&v, 0);
177     }
178
179   do_binomial (dataset_dict(ds), cf, bst, cat1, cat2, filter);
180
181   table = tab_create (7, ost->n_vars * 3 + 1, 0);
182
183   tab_dim (table, tab_natural_dimensions);
184
185   tab_title (table, _("Binomial Test"));
186
187   tab_headers (table, 2, 0, 1, 0);
188
189   tab_box (table, TAL_1, TAL_1, -1, TAL_1,
190            0, 0, table->nc - 1, tab_nr(table) - 1 );
191
192   for (v = 0 ; v < ost->n_vars; ++v)
193     {
194       double n_total, sig;
195       const struct variable *var = ost->vars[v];
196       tab_hline (table, TAL_1, 0, tab_nc (table) -1, 1 + v * 3);
197
198       /* Titles */
199       tab_text (table, 0, 1 + v * 3, TAB_LEFT,
200                 var_to_string (var));
201
202       tab_text (table, 1, 1 + v * 3, TAB_LEFT,
203                 _("Group1"));
204
205       tab_text (table, 1, 2 + v * 3, TAB_LEFT,
206                 _("Group2"));
207
208       tab_text (table, 1, 3 + v * 3, TAB_LEFT,
209                 _("Total"));
210
211       /* Test Prop */
212       tab_float (table, 5, 1 + v * 3, TAB_NONE, bst->p, 8, 3);
213
214       /* Category labels */
215       tab_text (table, 2, 1 + v * 3, TAB_NONE,
216                 var_get_value_name (var, cat1[v].value));
217
218       tab_text (table, 2, 2 + v * 3, TAB_NONE,
219                 var_get_value_name (var, cat2[v].value));
220
221       /* Observed N */
222       tab_float (table, 3, 1 + v * 3, TAB_NONE,
223                  cat1[v].count, 8, 0);
224
225       tab_float (table, 3, 2 + v * 3, TAB_NONE,
226                  cat2[v].count, 8, 0);
227
228       n_total = cat1[v].count + cat2[v].count;
229
230
231       tab_float (table, 3, 3 + v * 3, TAB_NONE,
232                  n_total, 8, 0);
233
234       /* Observed Proportions */
235
236       tab_float (table, 4, 1 + v * 3, TAB_NONE,
237                  cat1[v].count / n_total, 8, 3);
238
239       tab_float (table, 4, 2 + v * 3, TAB_NONE,
240                  cat2[v].count / n_total, 8, 3);
241
242       tab_float (table, 4, 3 + v * 3, TAB_NONE,
243                  (cat1[v].count + cat2[v].count) / n_total, 8, 2);
244
245
246       /* Significance */
247       sig = calculate_binomial (cat1[v].count, cat2[v].count,
248                                        bst->p);
249
250       tab_float (table, 6, 1 + v * 3, TAB_NONE,
251                  sig, 8, 3);
252     }
253
254   tab_text (table,  2, 0,  TAB_CENTER, _("Category"));
255   tab_text (table,  3, 0,  TAB_CENTER, _("N"));
256   tab_text (table,  4, 0,  TAB_CENTER, _("Observed Prop."));
257   tab_text (table,  5, 0,  TAB_CENTER, _("Test Prop."));
258
259   tab_text (table,  6, 0,  TAB_CENTER | TAT_PRINTF,
260             _("Exact Sig. (%d-tailed)"),
261             bst->p == 0.5 ? 2: 1);
262
263   tab_vline (table, TAL_2, 2, 0, tab_nr (table) -1);
264
265   free (cat1);
266   free (cat2);
267
268   tab_submit (table);
269
270 }