Re-implemented the T-TEST command and the levene calculation.
[pspp] / src / language / stats / t-test-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 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 "libpspp/message.h"
20
21 #include "data/casegrouper.h"
22 #include "data/casereader.h"
23 #include "data/dictionary.h"
24 #include "data/dataset.h"
25 #include "data/missing-values.h"
26
27 #include "language/lexer/lexer.h"
28 #include "language/command.h"
29 #include "language/lexer/variable-parser.h"
30 #include "language/lexer/value-parser.h"
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34
35 #include "t-test.h"
36
37 int
38 cmd_t_test (struct lexer *lexer, struct dataset *ds)
39 {
40   bool ok;
41   const struct dictionary *dict = dataset_dict (ds);
42   struct tt tt;
43   int mode_count = 0;
44
45   /* Variables pertaining to the paired mode */
46   const struct variable **v1 = NULL;
47   size_t n_v1;
48   const struct variable **v2 = NULL;
49   size_t n_v2;
50           
51   size_t n_pairs;
52   vp *pairs = NULL;
53
54
55   /* One sample mode */
56   double testval;
57
58   /* Independent samples mode */
59   const struct variable *gvar;
60   union value gval0;
61   union value gval1;
62   bool cut;
63
64   tt.wv = dict_get_weight (dict);
65   tt.dict = dict;
66   tt.confidence = 0.95;
67   tt.exclude = MV_ANY;
68   tt.missing_type = MISS_ANALYSIS;
69   tt.n_vars = 0;
70   tt.vars = NULL;
71
72   lex_match (lexer, T_EQUALS);
73
74   for (; lex_token (lexer) != T_ENDCMD; )
75     {
76       lex_match (lexer, T_SLASH);
77       if (lex_match_id (lexer, "TESTVAL"))
78         {
79           mode_count++;
80           tt.mode = MODE_SINGLE;
81           lex_match (lexer, T_EQUALS);
82           lex_force_num (lexer);
83           testval = lex_number (lexer);
84           lex_get (lexer);
85         }
86       else if (lex_match_id (lexer, "GROUPS"))
87         {
88           mode_count++;
89           cut = false;
90           tt.mode = MODE_INDEP;
91           lex_match (lexer, T_EQUALS);
92
93           if (NULL == (gvar = parse_variable (lexer, dict)))
94             goto parse_failed;
95       
96           if (lex_match (lexer, T_LPAREN))
97             {
98
99               value_init (&gval0, var_get_width (gvar));
100               parse_value (lexer, &gval0, gvar);
101               cut = true;
102               if (lex_match (lexer, T_COMMA))
103                 {
104                   value_init (&gval1, var_get_width (gvar));
105                   parse_value (lexer, &gval1, gvar);
106                   cut = false;
107                 }
108
109               lex_force_match (lexer, T_RPAREN);
110             }
111           else
112             {
113               value_init (&gval0, 0);
114               value_init (&gval1, 0);
115               gval0.f = 1.0;
116               gval1.f = 2.0;
117               cut = false;
118             }
119
120           if ( cut == true && var_is_alpha (gvar))
121             {
122               msg (SE, _("When applying GROUPS to a string variable, two "
123                          "values must be specified."));
124               goto parse_failed;
125             }
126         }
127       else if (lex_match_id (lexer, "PAIRS"))
128         {
129           bool with = false;
130           bool paired = false;
131           mode_count++;
132           tt.mode = MODE_PAIRED;
133           lex_match (lexer, T_EQUALS);
134
135           if (!parse_variables_const (lexer, dict,
136                                       &v1, &n_v1,
137                                       PV_NO_DUPLICATE | PV_NUMERIC))
138             goto parse_failed;
139
140           if ( lex_match (lexer, T_WITH))
141             {
142               with = true;
143               if (!parse_variables_const (lexer, dict,
144                                           &v2, &n_v2,
145                                           PV_NO_DUPLICATE | PV_NUMERIC))
146                 goto parse_failed;
147
148               if (lex_match (lexer, T_LPAREN)
149                   && lex_match_id (lexer, "PAIRED")
150                   && lex_match (lexer, T_RPAREN))
151                 {
152                   paired = true;
153                   if (n_v1 != n_v2)
154                     {
155                       msg (SE, _("PAIRED was specified but the number of variables "
156                                  "preceding WITH (%zu) did not match the number "
157                                  "following (%zu)."),
158                            n_v1, n_v2);
159                       goto parse_failed;
160                     }
161                 }
162             }
163           {
164             int i;
165
166             if ( !with )
167               n_pairs = (n_v1 * (n_v1 - 1)) / 2.0;
168             else if ( paired )
169               n_pairs = n_v1;
170             else
171               n_pairs = n_v1 * n_v2;
172           
173             pairs = xcalloc (sizeof *pairs, n_pairs);
174
175
176             if ( with)
177               {
178                 int x = 0;
179                 if (paired)
180                   {
181                     for (i = 0 ; i < n_v1; ++i)
182                       {
183                         vp *pair = &pairs[i];
184                         (*pair)[0] = v1[i];
185                         (*pair)[1] = v2[i];
186                       } 
187                   }
188                 else
189                   {
190                     for (i = 0 ; i < n_v1; ++i)
191                       {
192                         int j;
193                         for (j = 0 ; j < n_v2; ++j)
194                           {
195                             vp *pair = &pairs[x++];
196                             (*pair)[0] = v1[i];
197                             (*pair)[1] = v2[j];
198                           }
199                       }
200                   }
201               }
202             else
203               {
204                 int x = 0;
205                 for (i = 0 ; i < n_v1; ++i)
206                   {
207                     int j;
208
209                     for (j = i + 1 ; j < n_v1; ++j)
210                       {
211                         vp *pair = &pairs[x++];
212                         (*pair)[0] = v1[i];
213                         (*pair)[1] = v1[j];
214                       }
215                   }
216               }
217
218           }
219         }
220       else if (lex_match_id (lexer, "VARIABLES"))
221         {
222           if ( tt.mode == MODE_PAIRED)
223             {
224               msg (SE, _("VARIABLES subcommand may not be used with PAIRS."));
225               goto parse_failed;
226             }
227
228           lex_match (lexer, T_EQUALS);
229
230           if (!parse_variables_const (lexer, dict,
231                                       &tt.vars,
232                                       &tt.n_vars,
233                                       PV_NO_DUPLICATE | PV_NUMERIC))
234             goto parse_failed;
235         }
236       else if ( lex_match_id (lexer, "MISSING"))
237         {
238           lex_match (lexer, T_EQUALS);
239           while (lex_token (lexer) != T_ENDCMD && lex_token (lexer) != T_SLASH)
240             {
241               if (lex_match_id (lexer, "INCLUDE"))
242                 {
243                   tt.exclude = MV_SYSTEM;
244                 }
245               else if (lex_match_id (lexer, "EXCLUDE"))
246                 {
247                   tt.exclude = MV_ANY;
248                 }
249               else if (lex_match_id (lexer, "LISTWISE"))
250                 {
251                   tt.missing_type = MISS_LISTWISE;
252                 }
253               else if (lex_match_id (lexer, "ANALYSIS"))
254                 {
255                   tt.missing_type = MISS_ANALYSIS;
256                 }
257               else
258                 {
259                   lex_error (lexer, NULL);
260                   goto parse_failed;
261                 }
262               lex_match (lexer, T_COMMA);
263             }
264         }
265       else if (lex_match_id (lexer, "CRITERIA"))
266         {
267           lex_match (lexer, T_EQUALS);
268           if ( lex_force_match_id (lexer, "CIN"))
269             if ( lex_force_match (lexer, T_LPAREN))
270               {
271                 lex_force_num (lexer);
272                 tt.confidence = lex_number (lexer);
273                 lex_get (lexer);
274                 lex_force_match (lexer, T_RPAREN);
275               }
276         }
277       else 
278         {
279           lex_error (lexer, NULL);
280           goto parse_failed;
281         }
282     }
283
284   if ( mode_count != 1)
285     {
286       msg (SE, _("Exactly one of TESTVAL, GROUPS and PAIRS subcommands "
287                  "must be specified."));
288       goto parse_failed;
289     }
290
291   if (tt.n_vars == 0 && tt.mode != MODE_PAIRED)
292     {
293       msg (SE, _("One or more VARIABLES must be specified."));
294       goto parse_failed;
295     }
296
297
298
299   /* Deal with splits etc */
300   {
301     struct casereader *group;
302     struct casegrouper *grouper = casegrouper_create_splits (proc_open (ds), dict);
303
304     while (casegrouper_get_next_group (grouper, &group))
305       {
306         if ( tt.mode == MODE_SINGLE)
307           {
308             if ( tt.missing_type == MISS_LISTWISE )
309               group  = casereader_create_filter_missing (group,
310                                                          tt.vars, tt.n_vars,
311                                                          tt.exclude,
312                                                          NULL,  NULL);
313             one_sample_run (&tt, testval, group);
314           }
315         else if ( tt.mode == MODE_PAIRED)
316           {
317             if ( tt.missing_type == MISS_LISTWISE )
318               {
319                 group  = casereader_create_filter_missing (group,
320                                                            v1, n_v1,
321                                                            tt.exclude,
322                                                            NULL,  NULL);
323                 group  = casereader_create_filter_missing (group,
324                                                            v2, n_v2,
325                                                            tt.exclude,
326                                                            NULL,  NULL);
327               }
328
329             paired_run (&tt, n_pairs, pairs, group);
330           }
331         else /* tt.mode == MODE_INDEP */
332           {
333             if ( tt.missing_type == MISS_LISTWISE )
334               {
335                 group  = casereader_create_filter_missing (group,
336                                                            tt.vars, tt.n_vars,
337                                                            tt.exclude,
338                                                            NULL,  NULL);
339
340                 group  = casereader_create_filter_missing (group,
341                                                            &gvar, 1,
342                                                            tt.exclude,
343                                                            NULL,  NULL);
344
345               }
346
347             indep_run (&tt, gvar, cut, &gval0, &gval1, group);
348           }
349       }
350
351     ok = casegrouper_destroy (grouper);
352     ok = proc_commit (ds) && ok;
353   }
354
355   free (pairs);
356   free (v1);
357   free (v2);
358
359   free (tt.vars);
360
361   return ok ? CMD_SUCCESS : CMD_FAILURE;
362
363  parse_failed:
364   return CMD_FAILURE;
365 }
366