case: Introduce new functions for numbers and substrings in cases.
[pspp] / src / language / data-io / matrix-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2017, 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 "matrix-reader.h"
20
21 #include <stdbool.h>
22
23 #include <libpspp/message.h>
24 #include <libpspp/str.h>
25 #include <data/casegrouper.h>
26 #include <data/casereader.h>
27 #include <data/dictionary.h>
28 #include <data/variable.h>
29 #include <data/data-out.h>
30 #include <data/format.h>
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34 #define N_(msgid) msgid
35
36
37 /*
38 This module interprets a "data matrix", typically generated by the command
39 MATRIX DATA.  The dictionary of such a matrix takes the form:
40
41  s_0, s_1, ... s_m, ROWTYPE_, VARNAME_, v_0, v_1, .... v_n
42
43 where s_0, s_1 ... s_m are the variables defining the splits, and
44 v_0, v_1 ... v_n are the continuous variables.
45
46 m >= 0; n >= 0
47
48 The ROWTYPE_ variable is of type A8.
49 The VARNAME_ variable is a string type whose width is not predetermined.
50 The variables s_x are of type F4.0 (although this reader accepts any type),
51 and v_x are of any numeric type.
52
53 The values of the ROWTYPE_ variable are in the set {MEAN, STDDEV, N, CORR, COV}
54 and determine the purpose of that case.
55 The values of the VARNAME_ variable must correspond to the names of the varibles
56 in {v_0, v_1 ... v_n} and indicate the rows of the correlation or covariance
57 matrices.
58
59
60
61 A typical example is as follows:
62
63 s_0 ROWTYPE_   VARNAME_   v_0         v_1         v_2
64
65 0   MEAN                5.0000       4.0000       3.0000
66 0   STDDEV              1.0000       2.0000       3.0000
67 0   N                   9.0000       9.0000       9.0000
68 0   CORR       V1       1.0000        .6000        .7000
69 0   CORR       V2        .6000       1.0000        .8000
70 0   CORR       V3        .7000        .8000       1.0000
71 1   MEAN                9.0000       8.0000       7.0000
72 1   STDDEV              5.0000       6.0000       7.0000
73 1   N                   9.0000       9.0000       9.0000
74 1   CORR       V1       1.0000        .4000        .3000
75 1   CORR       V2        .4000       1.0000        .2000
76 1   CORR       V3        .3000        .2000       1.0000
77
78 */
79
80 struct matrix_reader
81 {
82   const struct dictionary *dict;
83   const struct variable *varname;
84   const struct variable *rowtype;
85   struct casegrouper *grouper;
86
87   gsl_matrix *n_vectors;
88   gsl_matrix *mean_vectors;
89   gsl_matrix *var_vectors;
90 };
91
92 struct matrix_reader *
93 create_matrix_reader_from_case_reader (const struct dictionary *dict, struct casereader *in_reader,
94                                        const struct variable ***vars, size_t *n_vars)
95 {
96   struct matrix_reader *mr = xzalloc (sizeof *mr);
97
98   mr->varname = dict_lookup_var (dict, "varname_");
99   mr->dict = dict;
100   if (mr->varname == NULL)
101     {
102       msg (ME, _("Matrix dataset lacks a variable called %s."), "VARNAME_");
103       free (mr);
104       return NULL;
105     }
106
107   if (!var_is_alpha (mr->varname))
108     {
109       msg (ME, _("Matrix dataset variable %s should be of string type."),
110            "VARNAME_");
111       free (mr);
112       return NULL;
113     }
114
115   mr->rowtype = dict_lookup_var (dict, "rowtype_");
116   if (mr->rowtype == NULL)
117     {
118       msg (ME, _("Matrix dataset lacks a variable called %s."), "ROWTYPE_");
119       free (mr);
120       return NULL;
121     }
122
123   if (!var_is_alpha (mr->rowtype))
124     {
125       msg (ME, _("Matrix dataset variable %s should be of string type."),
126            "ROWTYPE_");
127       free (mr);
128       return NULL;
129     }
130
131   size_t dvarcnt;
132   const struct variable **dvars = NULL;
133   dict_get_vars (dict, &dvars, &dvarcnt, DC_SCRATCH);
134
135   if (n_vars)
136     *n_vars = dvarcnt - var_get_dict_index (mr->varname) - 1;
137
138   if (vars)
139     {
140       int i;
141       *vars = xcalloc (*n_vars, sizeof (struct variable **));
142
143       for (i = 0; i < *n_vars; ++i)
144         {
145           (*vars)[i] = dvars[i + var_get_dict_index (mr->varname) + 1];
146         }
147     }
148
149   /* All the variables before ROWTYPE_ (if any) are split variables */
150   mr->grouper = casegrouper_create_vars (in_reader, dvars, var_get_dict_index (mr->rowtype));
151
152   free (dvars);
153
154   return mr;
155 }
156
157 bool
158 destroy_matrix_reader (struct matrix_reader *mr)
159 {
160   if (mr == NULL)
161     return false;
162   bool ret = casegrouper_destroy (mr->grouper);
163   free (mr);
164   return ret;
165 }
166
167
168 /*
169    Allocates MATRIX if necessary,
170    and populates row MROW, from the data in C corresponding to
171    variables in VARS. N_VARS is the length of VARS.
172 */
173 static void
174 matrix_fill_row (gsl_matrix **matrix,
175       const struct ccase *c, int mrow,
176       const struct variable **vars, size_t n_vars)
177 {
178   int col;
179   if (*matrix == NULL)
180     *matrix = gsl_matrix_alloc (n_vars, n_vars);
181
182   for (col = 0; col < n_vars; ++col)
183     {
184       const struct variable *cv = vars [col];
185       double x = case_num (c, cv);
186       assert (col  < (*matrix)->size2);
187       assert (mrow < (*matrix)->size1);
188       gsl_matrix_set (*matrix, mrow, col, x);
189     }
190 }
191
192 bool
193 next_matrix_from_reader (struct matrix_material *mm,
194                          struct matrix_reader *mr,
195                          const struct variable **vars, int n_vars)
196 {
197   struct casereader *group;
198
199   assert (vars);
200
201   gsl_matrix_free (mr->n_vectors);
202   gsl_matrix_free (mr->mean_vectors);
203   gsl_matrix_free (mr->var_vectors);
204
205   if (!casegrouper_get_next_group (mr->grouper, &group))
206     return false;
207
208   mr->n_vectors    = gsl_matrix_alloc (n_vars, n_vars);
209   mr->mean_vectors = gsl_matrix_alloc (n_vars, n_vars);
210   mr->var_vectors  = gsl_matrix_alloc (n_vars, n_vars);
211
212   mm->n = mr->n_vectors;
213   mm->mean_matrix = mr->mean_vectors;
214   mm->var_matrix = mr->var_vectors;
215
216   struct substring *var_names = XCALLOC (n_vars,  struct substring);
217   for (int i = 0; i < n_vars; ++i)
218     {
219       ss_alloc_substring (var_names + i, ss_cstr (var_get_name (vars[i])));
220     }
221
222   struct ccase *c;
223   for (; (c = casereader_read (group)); case_unref (c))
224     {
225       const union value *uv = case_data (c, mr->rowtype);
226       const char *row_type = CHAR_CAST (const char *, uv->s);
227       int col, row;
228       for (col = 0; col < n_vars; ++col)
229         {
230           const struct variable *cv = vars[col];
231           double x = case_data (c, cv)->f;
232           if (0 == strncasecmp (row_type, "N       ", 8))
233             for (row = 0; row < n_vars; ++row)
234               gsl_matrix_set (mr->n_vectors, row, col, x);
235           else if (0 == strncasecmp (row_type, "MEAN    ", 8))
236             for (row = 0; row < n_vars; ++row)
237               gsl_matrix_set (mr->mean_vectors, row, col, x);
238           else if (0 == strncasecmp (row_type, "STDDEV  ", 8))
239             for (row = 0; row < n_vars; ++row)
240               gsl_matrix_set (mr->var_vectors, row, col, x * x);
241         }
242
243       const char *enc = dict_get_encoding (mr->dict);
244
245       const union value *uvv  = case_data (c, mr->varname);
246       int w = var_get_width (mr->varname);
247
248       struct fmt_spec fmt = { .type = FMT_A };
249       fmt.w = w;
250       char *vname = data_out (uvv, enc, &fmt, settings_get_fmt_settings ());
251       struct substring the_name = ss_cstr (vname);
252
253       int mrow = -1;
254       for (int i = 0; i < n_vars; ++i)
255         {
256           if (ss_equals (var_names[i], the_name))
257             {
258               mrow = i;
259               break;
260             }
261         }
262       free (vname);
263
264       if (mrow == -1)
265         continue;
266
267       if (0 == strncasecmp (row_type, "CORR    ", 8))
268         {
269           matrix_fill_row (&mm->corr, c, mrow, vars, n_vars);
270         }
271       else if (0 == strncasecmp (row_type, "COV     ", 8))
272         {
273           matrix_fill_row (&mm->cov, c, mrow, vars, n_vars);
274         }
275     }
276
277   casereader_destroy (group);
278
279   for (int i = 0; i < n_vars; ++i)
280     ss_dealloc (var_names + i);
281   free (var_names);
282
283   return true;
284 }