AUTORECODE: Properly handle value labels.
[pspp] / src / language / stats / autorecode.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010, 2012, 2013, 2014 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 <float.h>
20 #include <stdlib.h>
21
22 #include "data/case.h"
23 #include "data/casereader.h"
24 #include "data/dataset.h"
25 #include "data/dictionary.h"
26 #include "data/transformations.h"
27 #include "data/variable.h"
28 #include "language/command.h"
29 #include "language/lexer/lexer.h"
30 #include "language/lexer/variable-parser.h"
31 #include "libpspp/array.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/hash-functions.h"
34 #include "libpspp/hmap.h"
35 #include "libpspp/i18n.h"
36 #include "libpspp/message.h"
37 #include "libpspp/pool.h"
38 #include "libpspp/str.h"
39
40 #include "gl/xalloc.h"
41 #include "gl/c-xvasprintf.h"
42 #include "gl/mbiter.h"
43 #include "gl/size_max.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47
48 /* FIXME: Implement PRINT subcommand. */
49
50 /* Explains how to recode one value. */
51 struct arc_item
52   {
53     struct hmap_node hmap_node; /* Element in "struct arc_spec" hash table. */
54     union value from;           /* Original value. */
55     int width;                  /* Width of the original value */
56     bool missing;               /* Is 'from' missing in its source varible? */
57     char *value_label;          /* Value label in source variable, if any. */
58
59     double to;                  /* Recoded value. */
60   };
61
62 /* Explains how to recode an AUTORECODE variable. */
63 struct arc_spec
64   {
65     int width;                  /* Variable width. */
66     int src_idx;                /* Case index of source variable. */
67     struct variable *dst;       /* Target variable. */
68     struct missing_values mv;   /* Missing values of source variable. */
69     struct rec_items *items;
70   };
71
72 /* Descending or ascending sort order. */
73 enum arc_direction
74   {
75     ASCENDING,
76     DESCENDING
77   };
78
79 struct rec_items
80 {
81   struct hmap ht;         /* Hash table of "struct arc_item"s. */
82 };
83
84
85
86 /* AUTORECODE data. */
87 struct autorecode_pgm
88 {
89   struct arc_spec *specs;
90   size_t n_specs;
91
92   bool blank_valid;
93 };
94
95 static trns_proc_func autorecode_trns_proc;
96 static trns_free_func autorecode_trns_free;
97
98 static int compare_arc_items (const void *, const void *, const void *aux);
99 static void arc_free (struct autorecode_pgm *);
100 static struct arc_item *find_arc_item (
101   const struct rec_items *, const union value *, int width,
102   size_t hash);
103
104 /* Returns WIDTH with any trailing spaces in VALUE trimmed off (except that a
105    minimum width of 1 is always returned because otherwise the width would
106    indicate a numeric type). */
107 static int
108 value_trim_spaces (const union value *value, int width)
109 {
110   while (width > 1 && value->s[width - 1] == ' ')
111     width--;
112   return width;
113 }
114
115 /* Performs the AUTORECODE procedure. */
116 int
117 cmd_autorecode (struct lexer *lexer, struct dataset *ds)
118 {
119   struct dictionary *dict = dataset_dict (ds);
120
121   const struct variable **src_vars = NULL;
122   size_t n_srcs = 0;
123
124   char **dst_names = NULL;
125   size_t n_dsts = 0;
126
127   enum arc_direction direction = ASCENDING;
128
129   /* Create procedure. */
130   struct autorecode_pgm *arc = xzalloc (sizeof *arc);
131   arc->blank_valid = true;
132
133   /* Parse variable lists. */
134   lex_match_id (lexer, "VARIABLES");
135   lex_match (lexer, T_EQUALS);
136   if (!parse_variables_const (lexer, dict, &src_vars, &n_srcs,
137                               PV_NO_DUPLICATE | PV_NO_SCRATCH))
138     goto error;
139   lex_match (lexer, T_SLASH);
140   if (!lex_force_match_id (lexer, "INTO"))
141     goto error;
142   lex_match (lexer, T_EQUALS);
143   if (!parse_DATA_LIST_vars (lexer, dict, &dst_names, &n_dsts,
144                              PV_NO_DUPLICATE))
145     goto error;
146   if (n_dsts != n_srcs)
147     {
148       msg (SE, _("Source variable count (%zu) does not match "
149                  "target variable count (%zu)."),
150            n_srcs, n_dsts);
151
152       goto error;
153     }
154   for (size_t i = 0; i < n_dsts; i++)
155     {
156       const char *name = dst_names[i];
157
158       if (dict_lookup_var (dict, name) != NULL)
159         {
160           msg (SE, _("Target variable %s duplicates existing variable %s."),
161                name, name);
162           goto error;
163         }
164     }
165
166   /* Parse options. */
167   bool group = false;
168   while (lex_match (lexer, T_SLASH))
169     {
170       if (lex_match_id (lexer, "DESCENDING"))
171         direction = DESCENDING;
172       else if (lex_match_id (lexer, "PRINT"))
173         {
174           /* Not yet implemented. */
175         }
176       else if (lex_match_id (lexer, "GROUP"))
177         group = true;
178       else if (lex_match_id (lexer, "BLANK"))
179         {
180           lex_match (lexer, T_EQUALS);
181           if (lex_match_id (lexer, "VALID"))
182             {
183               arc->blank_valid = true;
184             }
185           else if (lex_match_id (lexer, "MISSING"))
186             {
187               arc->blank_valid = false;
188             }
189           else
190             {
191               lex_error_expecting (lexer, "VALID", "MISSING");
192               goto error;
193             }
194         }
195       else
196         {
197           lex_error_expecting (lexer, "DESCENDING", "PRINT", "GROUP", "BLANK");
198           goto error;
199         }
200     }
201
202   if (lex_token (lexer) != T_ENDCMD)
203     {
204       lex_error (lexer, _("expecting end of command"));
205       goto error;
206     }
207
208   /* If GROUP is specified, verify that the variables are all string or all
209      numeric.  */
210   if (group)
211     {
212       enum val_type type = var_get_type (src_vars[0]);
213       for (size_t i = 1; i < n_dsts; i++)
214         {
215           if (var_get_type (src_vars[i]) != type)
216             {
217               size_t string_idx = type == VAL_STRING ? 0 : i;
218               size_t numeric_idx = type == VAL_STRING ? i : 0;
219               lex_error (lexer, _("With GROUP, variables may not mix string "
220                                   "variables (such as %s) and numeric "
221                                   "variables (such as %s)."),
222                          var_get_name (src_vars[string_idx]),
223                          var_get_name (src_vars[numeric_idx]));
224               goto error;
225             }
226         }
227     }
228
229   /* Allocate all the specs and the rec_items that they point to.
230
231      If GROUP is specified, there is only a single global rec_items, with the
232      maximum width 'width', and all of the specs point to it; otherwise each
233      spec has its own rec_items. */
234   arc->specs = xmalloc (n_dsts * sizeof *arc->specs);
235   arc->n_specs = n_dsts;
236   for (size_t i = 0; i < n_dsts; i++)
237     {
238       struct arc_spec *spec = &arc->specs[i];
239
240       spec->width = var_get_width (src_vars[i]);
241       spec->src_idx = var_get_case_index (src_vars[i]);
242
243       if (group && i > 0)
244         spec->items = arc->specs[0].items;
245       else
246         {
247           spec->items = xzalloc (sizeof (*spec->items));
248           hmap_init (&spec->items->ht);
249         }
250     }
251
252   /* Initialize specs[*]->mv to the user-missing values for each
253      source variable. */
254   if (group)
255     {
256       /* Use the first source variable that has any user-missing values. */
257       size_t mv_idx = 0;
258       for (size_t i = 0; i < n_dsts; i++)
259         if (var_has_missing_values (src_vars[i]))
260           {
261             mv_idx = i;
262             break;
263           }
264
265       for (size_t i = 0; i < n_dsts; i++)
266         mv_copy (&arc->specs[i].mv, var_get_missing_values (src_vars[mv_idx]));
267     }
268   else
269     {
270       /* Each variable uses its own user-missing values. */
271       for (size_t i = 0; i < n_dsts; i++)
272         mv_copy (&arc->specs[i].mv, var_get_missing_values (src_vars[i]));
273     }
274
275   /* Execute procedure. */
276   struct casereader *input = proc_open (ds);
277   struct ccase *c;
278   for (; (c = casereader_read (input)) != NULL; case_unref (c))
279     for (size_t i = 0; i < arc->n_specs; i++)
280       {
281         struct arc_spec *spec = &arc->specs[i];
282         const union value *value = case_data_idx (c, spec->src_idx);
283         if (spec->width == 0 && value->f == SYSMIS)
284           {
285             /* AUTORECODE never changes the system-missing value.
286                (Leaving it out of the translation table has this
287                effect automatically because values not found in the
288                translation table get translated to system-missing.) */
289             continue;
290           }
291
292         int width = value_trim_spaces (value, spec->width);
293         if (width == 1 && value->s[0] == ' ' && !arc->blank_valid)
294           continue;
295
296         size_t hash = value_hash (value, width, 0);
297         if (find_arc_item (spec->items, value, width, hash))
298           continue;
299
300         struct string value_label = DS_EMPTY_INITIALIZER;
301         var_append_value_name__ (src_vars[i], value,
302                                  SETTINGS_VALUE_SHOW_LABEL, &value_label);
303
304         struct arc_item *item = xmalloc (sizeof *item);
305         item->width = width;
306         value_clone (&item->from, value, width);
307         item->missing = mv_is_value_missing_varwidth (&spec->mv, value, spec->width,
308                                                       MV_ANY);
309         item->value_label = ds_steal_cstr (&value_label);
310         hmap_insert (&spec->items->ht, &item->hmap_node, hash);
311
312         ds_destroy (&value_label);
313       }
314   bool ok = casereader_destroy (input);
315   ok = proc_commit (ds) && ok;
316
317   /* Re-fetch dictionary because it might have changed (if TEMPORARY was in
318      use). */
319   dict = dataset_dict (ds);
320
321   /* Create transformation. */
322   for (size_t i = 0; i < arc->n_specs; i++)
323     {
324       struct arc_spec *spec = &arc->specs[i];
325       struct arc_item **items;
326       struct arc_item *item;
327       size_t n_items;
328       size_t j;
329
330       /* Create destination variable. */
331       spec->dst = dict_create_var_assert (dict, dst_names[i], 0);
332
333       /* Create array of pointers to items. */
334       n_items = hmap_count (&spec->items->ht);
335       items = xmalloc (n_items * sizeof *items);
336       j = 0;
337       HMAP_FOR_EACH (item, struct arc_item, hmap_node, &spec->items->ht)
338         items[j++] = item;
339
340       assert (j == n_items);
341
342       /* Sort array by value. */
343       sort (items, n_items, sizeof *items, compare_arc_items, &direction);
344
345       /* Assign recoded values in sorted order. */
346       for (j = 0; j < n_items; j++)
347         items[j]->to = j + 1;
348
349       /* Assign user-missing values.
350
351          User-missing values in the source variable(s) must be marked
352          as user-missing values in the destination variable.  There
353          might be an arbitrary number of missing values, since the
354          source variable might have a range.  Our sort function always
355          puts missing values together at the top of the range, so that
356          means that we can use a missing value range to cover all of
357          the user-missing values in any case (but we avoid it unless
358          necessary because user-missing value ranges are an obscure
359          feature). */
360       size_t n_missing = n_items;
361       for (size_t k = 0; k < n_items; k++)
362         if (!items[n_items - k - 1]->missing)
363           {
364             n_missing = k;
365             break;
366           }
367       if (n_missing > 0)
368         {
369           size_t lo = n_items - (n_missing - 1);
370           size_t hi = n_items;
371
372           struct missing_values mv;
373           mv_init (&mv, 0);
374           if (n_missing > 3)
375             mv_add_range (&mv, lo, hi);
376           else if (n_missing > 0)
377             for (size_t k = 0; k < n_missing; k++)
378               mv_add_num (&mv, lo + k);
379           var_set_missing_values (spec->dst, &mv);
380           mv_destroy (&mv);
381         }
382
383       /* Add value labels to the destination variable. */
384       for (j = 0; j < n_items; j++)
385         {
386           const char *value_label = items[j]->value_label;
387           if (value_label && value_label[0])
388             {
389               union value to_val = { .f = items[j]->to };
390               var_add_value_label (spec->dst, &to_val, value_label);
391             }
392         }
393
394       /* Free array. */
395       free (items);
396     }
397   add_transformation (ds, autorecode_trns_proc, autorecode_trns_free, arc);
398
399   for (size_t i = 0; i < n_dsts; i++)
400     free (dst_names[i]);
401   free (dst_names);
402   free (src_vars);
403
404   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
405
406 error:
407   for (size_t i = 0; i < n_dsts; i++)
408     free (dst_names[i]);
409   free (dst_names);
410   free (src_vars);
411   arc_free (arc);
412   return CMD_CASCADING_FAILURE;
413 }
414
415 static void
416 arc_free (struct autorecode_pgm *arc)
417 {
418   if (arc != NULL)
419     {
420       for (size_t i = 0; i < arc->n_specs; i++)
421         {
422           struct arc_spec *spec = &arc->specs[i];
423           struct arc_item *item, *next;
424
425           HMAP_FOR_EACH_SAFE (item, next, struct arc_item, hmap_node,
426                               &spec->items->ht)
427             {
428               value_destroy (&item->from, item->width);
429               free (item->value_label);
430               hmap_delete (&spec->items->ht, &item->hmap_node);
431               free (item);
432             }
433           mv_destroy (&spec->mv);
434         }
435
436       size_t n_rec_items =
437         (arc->n_specs == 1 || arc->specs[0].items == arc->specs[1].items
438          ? 1
439          : arc->n_specs);
440       for (size_t i = 0; i < n_rec_items; i++)
441         {
442           struct arc_spec *spec = &arc->specs[i];
443           hmap_destroy (&spec->items->ht);
444           free (spec->items);
445         }
446
447       free (arc->specs);
448       free (arc);
449     }
450 }
451
452 static struct arc_item *
453 find_arc_item (const struct rec_items *items,
454                const union value *value, int width,
455                size_t hash)
456 {
457   struct arc_item *item;
458
459   HMAP_FOR_EACH_WITH_HASH (item, struct arc_item, hmap_node, hash, &items->ht)
460     if (item->width == width && value_equal (value, &item->from, width))
461       return item;
462   return NULL;
463 }
464
465 static int
466 compare_arc_items (const void *a_, const void *b_, const void *direction_)
467 {
468   const struct arc_item *const *ap = a_;
469   const struct arc_item *const *bp = b_;
470   const struct arc_item *a = *ap;
471   const struct arc_item *b = *bp;
472
473   /* User-missing values always sort to the highest target values
474      (regardless of sort direction). */
475   if (a->missing != b->missing)
476     return a->missing < b->missing ? -1 : 1;
477
478   /* Otherwise, compare the data. */
479   int aw = a->width;
480   int bw = b->width;
481   int cmp;
482   if (aw == bw)
483     cmp = value_compare_3way (&a->from, &b->from, aw);
484   else
485     {
486       assert (aw && bw);
487       cmp = buf_compare_rpad (CHAR_CAST_BUG (const char *, a->from.s), aw,
488                               CHAR_CAST_BUG (const char *, b->from.s), bw);
489     }
490
491   /* Then apply sort direction. */
492   const enum arc_direction *directionp = direction_;
493   enum arc_direction direction = *directionp;
494   return direction == ASCENDING ? cmp : -cmp;
495 }
496
497 static int
498 autorecode_trns_proc (void *arc_, struct ccase **c,
499                       casenumber case_idx UNUSED)
500 {
501   struct autorecode_pgm *arc = arc_;
502
503   *c = case_unshare (*c);
504   for (size_t i = 0; i < arc->n_specs; i++)
505     {
506       const struct arc_spec *spec = &arc->specs[i];
507       const union value *value = case_data_idx (*c, spec->src_idx);
508       int width = value_trim_spaces (value, spec->width);
509       size_t hash = value_hash (value, width, 0);
510       const struct arc_item *item = find_arc_item (spec->items, value, width,
511                                                    hash);
512       case_data_rw (*c, spec->dst)->f = item ? item->to : SYSMIS;
513     }
514
515   return TRNS_CONTINUE;
516 }
517
518 static bool
519 autorecode_trns_free (void *arc_)
520 {
521   struct autorecode_pgm *arc = arc_;
522
523   arc_free (arc);
524   return true;
525 }