pivot table procedure conceptually works
[pspp] / src / language / data-io / pivot.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 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 "data/casereader.h"
20 #include "data/dataset.h"
21 #include "data/subcase.h"
22 #include "language/command.h"
23 #include "language/lexer/lexer.h"
24 #include "language/lexer/variable-parser.h"
25 #include "math/sort.h"
26 #include "output/tab.h"
27 #include "output/table-item.h"
28
29 #include "gettext.h"
30 #define _(msgid) gettext (msgid)
31
32 enum
33   {
34     ROW_VAR,
35     COL_VAR,
36     DATA_VAR
37   };
38
39 struct cmd_pivot
40   {
41     const struct variable **vars[3];
42     size_t n_vars[3];
43   };
44
45 struct ccase *
46 take_first_case (struct ccase *first, struct ccase *second, void *aux UNUSED)
47 {
48   case_unref (second);
49   return first;
50 }
51
52 int
53 cmd_debug_pivot (struct lexer *lexer, struct dataset *ds)
54 {
55   const struct dictionary *dict = dataset_dict (ds);
56   struct cmd_pivot p;
57   struct casereader *reader;
58   struct subcase col_ordering, total_ordering, row_ordering;
59   struct casereader *columns, *cells, *columns_reader;
60   struct ccase *row, *prev, *column;
61   int col_idx;
62
63   memset (&p, 0, sizeof p);
64   while (lex_token (lexer) != T_ENDCMD)
65     {
66       lex_match (lexer, T_SLASH);
67       if (lex_match_id (lexer, "ROWS") )
68         {
69           lex_match (lexer, T_EQUALS);
70           if (!parse_variables_const (lexer, dict,
71                                       &p.vars[ROW_VAR], &p.n_vars[ROW_VAR],
72                                       PV_NO_DUPLICATE | PV_NO_SCRATCH))
73             return CMD_FAILURE;
74         }
75       else if (lex_match_id (lexer, "COLUMNS") )
76         {
77           lex_match (lexer, T_EQUALS);
78           if (!parse_variables_const (lexer, dict,
79                                       &p.vars[COL_VAR], &p.n_vars[COL_VAR],
80                                       PV_NO_DUPLICATE | PV_NO_SCRATCH))
81             return CMD_FAILURE;
82         }
83       else if (lex_match_id (lexer, "DATA") )
84         {
85           lex_match (lexer, T_EQUALS);
86           if (!parse_variables_const (lexer, dict,
87                                       &p.vars[DATA_VAR], &p.n_vars[DATA_VAR],
88                                       PV_NO_DUPLICATE | PV_NO_SCRATCH))
89             return CMD_FAILURE;
90         }
91     }
92
93
94   reader = proc_open (ds);
95
96   subcase_init_vars (&col_ordering, p.vars[COL_VAR], p.n_vars[COL_VAR]);
97   columns = sort_distinct_execute (casereader_clone (reader), &col_ordering,
98                                    take_first_case, NULL, NULL);
99   printf ("%lld column combinations\n",
100           (long long int) casereader_count_cases (columns));
101
102   subcase_init_vars (&total_ordering, p.vars[ROW_VAR], p.n_vars[ROW_VAR]);
103   subcase_add_vars_always (&total_ordering,
104                            p.vars[COL_VAR], p.n_vars[COL_VAR]);
105   cells = sort_distinct_execute (reader, &total_ordering,
106                                  take_first_case, NULL, NULL);
107   printf ("%lld cells\n",
108           (long long int) casereader_count_cases (cells));
109
110   row = prev = column = NULL;
111   columns_reader = NULL;
112   subcase_init_vars (&row_ordering, p.vars[ROW_VAR], p.n_vars[ROW_VAR]);
113   for (;;)
114     {
115       struct ccase *c;
116
117       c = casereader_read (cells);
118       if (!c)
119         break;
120
121       if (!row || !subcase_equal (&row_ordering, row, &row_ordering, c))
122         {
123           int i;
124
125           if (row)
126             putchar ('\n');
127           case_unref (prev);
128           prev = row;
129           row = case_ref (c);
130
131           i = 0;
132           if (prev)
133             for (; i < p.n_vars[ROW_VAR]; i++)
134               {
135                 if (!value_equal (case_data (row, p.vars[ROW_VAR][i]),
136                                   case_data (prev, p.vars[ROW_VAR][i]),
137                                   var_get_width (p.vars[ROW_VAR][i])))
138                   break;
139                 printf ("        ");
140               }
141           for (; i < p.n_vars[ROW_VAR]; i++)
142             {
143               union value value;
144               const char *label;
145
146               value.f = case_num (row, p.vars[ROW_VAR][i]);
147               label = var_lookup_value_label (p.vars[ROW_VAR][i], &value);
148               if (label)
149                 printf ("%7s ", label);
150               else
151                 printf ("%7.0f ", value.f);
152             }
153           printf ("| ");
154
155           case_unref (column);
156           casereader_destroy (columns_reader);
157           columns_reader = casereader_clone (columns);
158           column = casereader_read (columns_reader);
159           col_idx = 0;
160         }
161
162       while (!subcase_equal (&col_ordering, column, &col_ordering, c))
163         {
164           case_unref (column);
165           column = casereader_read (columns_reader);
166           printf ("        ");
167           col_idx++;
168         }
169
170       printf ("%7.0f ", case_num (c, p.vars[DATA_VAR][0]));
171       col_idx++;
172       column = casereader_read (columns_reader);
173     }
174   if (row)
175     putchar ('\n');
176   case_unref (row);
177   case_unref (prev);
178   case_unref (column);
179   casereader_destroy (columns_reader);
180
181   casereader_destroy (columns);
182   casereader_destroy (cells);
183   subcase_destroy (&col_ordering);
184   subcase_destroy (&row_ordering);
185
186   proc_commit (ds);
187
188   return CMD_SUCCESS;
189 }