Re-implement MEANS.
[pspp] / src / language / stats / means-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2011, 2012, 2013, 2019 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 "data/case.h"
20 #include "data/casegrouper.h"
21 #include "data/casereader.h"
22 #include "data/dataset.h"
23 #include "data/dictionary.h"
24 #include "data/format.h"
25 #include "data/variable.h"
26
27 #include "language/command.h"
28 #include "language/lexer/lexer.h"
29 #include "language/lexer/variable-parser.h"
30
31 #include "libpspp/hmap.h"
32 #include "libpspp/bt.h"
33 #include "libpspp/misc.h"
34 #include "libpspp/pool.h"
35
36 #include "means.h"
37
38 /* Parse the /TABLES stanza of the command.  */
39 static bool
40 parse_means_table_syntax (struct lexer *lexer, const struct means *cmd,
41                           struct mtable *table)
42 {
43   memset (table, 0, sizeof *table);
44
45   /* Dependent variable (s) */
46   if (!parse_variables_const_pool (lexer, cmd->pool, cmd->dict,
47                                    &table->dep_vars, &table->n_dep_vars,
48                                    PV_NO_DUPLICATE | PV_NUMERIC))
49     return false;
50
51   /* Factor variable (s) */
52   while (lex_match (lexer, T_BY))
53     {
54       struct layer *layer = pool_zalloc (cmd->pool, sizeof *layer);
55
56       table->layers =
57         pool_nrealloc (cmd->pool, table->layers, table->n_layers + 1,
58                        sizeof *table->layers);
59       table->layers[table->n_layers] = layer;
60       table->n_layers++;
61
62       if (!parse_variables_const_pool
63           (lexer, cmd->pool, cmd->dict,
64            &layer->factor_vars,
65            &layer->n_factor_vars,
66            PV_NO_DUPLICATE))
67         return false;
68     }
69
70   return true;
71 }
72
73 /* Match a variable.
74    If the match succeeds, the variable will be placed in VAR.
75    Returns true if successful */
76 static bool
77 lex_is_variable (struct lexer *lexer, const struct dictionary *dict,
78                  int n)
79 {
80   const char *tstr;
81   if (lex_next_token (lexer, n) !=  T_ID)
82     return false;
83
84   tstr = lex_next_tokcstr (lexer, n);
85
86   if (NULL == dict_lookup_var (dict, tstr) )
87     return false;
88
89   return true;
90 }
91
92 static bool
93 means_parse (struct lexer *lexer, struct means *means)
94 {
95   /*   Optional TABLES =   */
96   if (lex_match_id (lexer, "TABLES"))
97     {
98       if (! lex_force_match (lexer, T_EQUALS))
99         return false;
100     }
101
102   bool more_tables = true;
103   /* Parse the "tables" */
104   while (more_tables)
105     {
106       means->table = pool_realloc (means->pool, means->table,
107                                    (means->n_tables + 1) * sizeof (*means->table));
108
109       if (! parse_means_table_syntax (lexer, means,
110                                       &means->table[means->n_tables]))
111         {
112           return false;
113         }
114       means->n_tables ++;
115
116       /* Look ahead to see if there are more tables to be parsed */
117       more_tables = false;
118       if ( T_SLASH == lex_next_token (lexer, 0) )
119         {
120           if (lex_is_variable (lexer, means->dict, 1) )
121             {
122               more_tables = true;
123               lex_match (lexer, T_SLASH);
124             }
125         }
126     }
127
128   /* /MISSING subcommand */
129   while (lex_token (lexer) != T_ENDCMD)
130     {
131       lex_match (lexer, T_SLASH);
132
133       if (lex_match_id (lexer, "MISSING"))
134         {
135           /*
136             If no MISSING subcommand is specified, each combination of
137             a dependent variable and categorical variables is handled
138             separately.
139           */
140           lex_match (lexer, T_EQUALS);
141           if (lex_match_id (lexer, "INCLUDE"))
142             {
143               /*
144                 Use the subcommand  "/MISSING=INCLUDE" to include user-missing
145                 values in the analysis.
146               */
147
148               means->ctrl_exclude = MV_SYSTEM;
149               means->dep_exclude = MV_SYSTEM;
150             }
151           else if (lex_match_id (lexer, "DEPENDENT"))
152             /*
153               Use the command "/MISSING=DEPENDENT" to
154               include user-missing values for the categorical variables,
155               while excluding them for the dependent variables.
156
157               Cases are dropped only when user-missing values
158               appear in dependent  variables.  User-missing
159               values for categorical variables are treated according to
160               their face value.
161
162               Cases are ALWAYS dropped when System Missing values appear
163               in the categorical variables.
164             */
165             {
166               means->dep_exclude = MV_ANY;
167               means->ctrl_exclude = MV_SYSTEM;
168             }
169           else
170             {
171               lex_error (lexer, NULL);
172               return false;
173             }
174         }
175       else if (lex_match_id (lexer, "CELLS"))
176         {
177           lex_match (lexer, T_EQUALS);
178
179           /* The default values become overwritten */
180           means->n_statistics = 0;
181           pool_free (means->pool, means->statistics);
182           means->statistics = 0;
183           while (lex_token (lexer) != T_ENDCMD
184                  && lex_token (lexer) != T_SLASH)
185             {
186               if (lex_match (lexer, T_ALL))
187                 {
188                   pool_free (means->pool, means->statistics);
189                   means->statistics = pool_calloc (means->pool,
190                                                    n_MEANS_STATISTICS,
191                                                    sizeof (*means->statistics));
192                   means->n_statistics = n_MEANS_STATISTICS;
193                   int i;
194                   for (i = 0; i < n_MEANS_STATISTICS; ++i)
195                     {
196                       means->statistics[i] = i;
197                     }
198                 }
199               else if (lex_match_id (lexer, "NONE"))
200                 {
201                   means->n_statistics = 0;
202                   pool_free (means->pool, means->statistics);
203                   means->statistics = 0;
204                 }
205               else if (lex_match_id (lexer, "DEFAULT"))
206                 {
207                   pool_free (means->pool, means->statistics);
208                   means->statistics = pool_calloc (means->pool,
209                                                    3,
210                                                    sizeof *means->statistics);
211                   means->statistics[0] = MEANS_MEAN;
212                   means->statistics[1] = MEANS_N;
213                   means->statistics[2] = MEANS_STDDEV;
214                 }
215               else
216                 {
217                   int i;
218                   for (i = 0; i < n_MEANS_STATISTICS; ++i)
219                     {
220                       const struct cell_spec *cs = cell_spec + i;
221                       if (lex_match_id (lexer, cs->keyword))
222                         {
223                           means->statistics
224                             = pool_realloc (means->pool,
225                                            means->statistics,
226                                            (means->n_statistics + 1)
227                                            * sizeof (*means->statistics));
228
229                           means->statistics[means->n_statistics] = i;
230                           means->n_statistics++;
231                           break;
232                         }
233                     }
234
235                   if (i >= n_MEANS_STATISTICS)
236                     {
237                       lex_error (lexer, NULL);
238                       return false;
239                     }
240                 }
241             }
242         }
243       else
244         {
245           lex_error (lexer, NULL);
246           return false;
247         }
248     }
249   return true;
250 }
251
252
253 int
254 cmd_means (struct lexer *lexer, struct dataset *ds)
255 {
256   struct means means;
257   means.pool = pool_create ();
258
259   means.ctrl_exclude = MV_ANY;
260   means.dep_exclude = MV_ANY;
261   means.table = NULL;
262   means.n_tables = 0;
263
264   means.dict = dataset_dict (ds);
265
266   means.n_statistics = 3;
267   means.statistics = pool_calloc (means.pool, 3, sizeof *means.statistics);
268   means.statistics[0] = MEANS_MEAN;
269   means.statistics[1] = MEANS_N;
270   means.statistics[2] = MEANS_STDDEV;
271
272   if (! means_parse (lexer, &means))
273     goto error;
274
275   {
276     struct casegrouper *grouper;
277     struct casereader *group;
278     bool ok;
279
280     grouper = casegrouper_create_splits (proc_open (ds), means.dict);
281     while (casegrouper_get_next_group (grouper, &group))
282       {
283         run_means (&means, group, ds);
284       }
285     ok = casegrouper_destroy (grouper);
286     ok = proc_commit (ds) && ok;
287   }
288
289   for (int t = 0; t < means.n_tables; ++t)
290     {
291       const struct mtable *table = means.table + t;
292
293       means_case_processing_summary (table);
294       means_shipout (table, &means);
295     }
296   destroy_means (&means);
297   pool_destroy (means.pool);
298   return CMD_SUCCESS;
299
300  error:
301
302   destroy_means (&means);
303   pool_destroy (means.pool);
304   return CMD_FAILURE;
305 }