Whitespace changes only: Remove trailing whitespace
[pspp] / src / language / stats / examine.c
1 /*
2   PSPP - a program for statistical analysis.
3   Copyright (C) 2012, 2013, 2016  Free Software Foundation, Inc.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <math.h>
22 #include <gsl/gsl_cdf.h>
23
24 #include "libpspp/assertion.h"
25 #include "libpspp/message.h"
26 #include "libpspp/pool.h"
27
28
29 #include "data/dataset.h"
30 #include "data/dictionary.h"
31 #include "data/casegrouper.h"
32 #include "data/casereader.h"
33 #include "data/casewriter.h"
34 #include "data/caseproto.h"
35 #include "data/subcase.h"
36
37
38 #include "data/format.h"
39
40 #include "math/interaction.h"
41 #include "math/box-whisker.h"
42 #include "math/categoricals.h"
43 #include "math/chart-geometry.h"
44 #include "math/histogram.h"
45 #include "math/moments.h"
46 #include "math/np.h"
47 #include "math/sort.h"
48 #include "math/order-stats.h"
49 #include "math/percentiles.h"
50 #include "math/tukey-hinges.h"
51 #include "math/trimmed-mean.h"
52
53 #include "output/charts/boxplot.h"
54 #include "output/charts/np-plot.h"
55 #include "output/charts/spreadlevel-plot.h"
56 #include "output/charts/plot-hist.h"
57
58 #include "language/command.h"
59 #include "language/lexer/lexer.h"
60 #include "language/lexer/value-parser.h"
61 #include "language/lexer/variable-parser.h"
62
63 #include "output/tab.h"
64
65 #include "gettext.h"
66 #define _(msgid) gettext (msgid)
67 #define N_(msgid) msgid
68
69 static void
70 append_value_name (const struct variable *var, const union value *val, struct string *str)
71 {
72   var_append_value_name (var, val, str);
73   if ( var_is_value_missing (var, val, MV_ANY))
74     ds_put_cstr (str, _(" (missing)"));
75 }
76
77 enum bp_mode
78   {
79     BP_GROUPS,
80     BP_VARIABLES
81   };
82
83
84 /* Indices for the ex_proto member (below) */
85 enum
86   {
87     EX_VAL,  /* value */
88     EX_ID,   /* identity */
89     EX_WT    /* weight */
90   };
91
92
93 struct examine
94 {
95   struct pool *pool;
96
97   /* A caseproto used to contain the data subsets under examination,
98      see (enum above)   */
99   struct caseproto *ex_proto;
100
101   size_t n_dep_vars;
102   const struct variable **dep_vars;
103
104   size_t n_iacts;
105   struct interaction **iacts;
106
107   enum mv_class dep_excl;
108   enum mv_class fctr_excl;
109
110   const struct dictionary *dict;
111
112   struct categoricals *cats;
113
114   /* how many extremities to display */
115   int disp_extremes;
116   int calc_extremes;
117   bool descriptives;
118
119   double conf;
120
121   bool missing_pw;
122
123   /* The case index of the ID value (or -1) if not applicable */
124   size_t id_idx;
125   int id_width;
126
127   enum pc_alg pc_alg;
128   double *ptiles;
129   size_t n_percentiles;
130
131   bool npplot;
132   bool histogramplot;
133   bool boxplot;
134   bool spreadlevelplot;
135   int sl_power;
136
137   enum bp_mode boxplot_mode;
138
139   const struct variable *id_var;
140
141   const struct variable *wv;
142 };
143
144 struct extremity
145 {
146   /* The value of this extremity */
147   double val;
148
149   /* Either the casenumber or the value of the variable specified
150      by the /ID subcommand which corresponds to this extremity */
151   union value identity;
152 };
153
154 struct exploratory_stats
155 {
156   double missing;
157   double non_missing;
158
159   struct moments *mom;
160
161   /* Most operations need a sorted reader/writer */
162   struct casewriter *sorted_writer;
163   struct casereader *sorted_reader;
164
165   struct extremity *minima;
166   struct extremity *maxima;
167
168   /*
169      Minimum should alway equal mimima[0].val.
170      Likewise, maximum should alway equal maxima[0].val.
171      This redundancy exists as an optimisation effort.
172      Some statistics (eg histogram) require early calculation
173      of the min and max
174   */
175   double minimum;
176   double maximum;
177
178   struct trimmed_mean *trimmed_mean;
179   struct percentile *quartiles[3];
180   struct percentile **percentiles;
181
182   struct tukey_hinges *hinges;
183
184   /* The data for the NP Plots */
185   struct np *np;
186
187   struct histogram *histogram;
188
189   /* The data for the box plots */
190   struct box_whisker *box_whisker;
191
192   /* Total weight */
193   double cc;
194
195   /* The minimum weight */
196   double cmin;
197 };
198
199
200 /* Returns an array of (iact->n_vars) pointers to union value initialised to NULL.
201    The caller must free this array when no longer required. */
202 static const union value **
203 previous_value_alloc (const struct interaction *iact)
204 {
205   int ivar_idx;
206
207   const union value **prev_val = xcalloc (iact->n_vars, sizeof (*prev_val));
208
209   for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
210     prev_val[ivar_idx] = NULL;
211
212   return prev_val;
213 }
214
215 /* Set the contents of PREV_VAL to the values of C indexed by the variables of IACT */
216 static int
217 previous_value_record (const struct interaction *iact, const struct ccase *c, const union value **prev_val)
218 {
219   int ivar_idx;
220   int diff_idx = -1;
221
222   for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
223     {
224       const struct variable *ivar = iact->vars[ivar_idx];
225       const int width = var_get_width (ivar);
226       const union value *val = case_data (c, ivar);
227
228       if (prev_val[ivar_idx])
229         if (! value_equal (prev_val[ivar_idx], val, width))
230           {
231             diff_idx = ivar_idx;
232             break;
233           }
234     }
235
236   for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
237     {
238       const struct variable *ivar = iact->vars[ivar_idx];
239       const union value *val = case_data (c, ivar);
240
241       prev_val[ivar_idx] = val;
242     }
243   return diff_idx;
244 }
245
246
247 static void
248 show_boxplot_grouped (const struct examine *cmd, int iact_idx)
249 {
250   int v;
251
252   const struct interaction *iact = cmd->iacts[iact_idx];
253   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
254
255   for (v = 0; v < cmd->n_dep_vars; ++v)
256     {
257       double y_min = DBL_MAX;
258       double y_max = -DBL_MAX;
259       int grp;
260       struct boxplot *boxplot;
261       struct string title;
262       ds_init_empty (&title);
263
264       if (iact->n_vars > 0)
265         {
266           struct string istr;
267           ds_init_empty (&istr);
268           interaction_to_string (iact, &istr);
269           ds_put_format (&title, _("Boxplot of %s vs. %s"),
270                          var_to_string (cmd->dep_vars[v]),
271                          ds_cstr (&istr));
272           ds_destroy (&istr);
273         }
274       else
275         ds_put_format (&title, _("Boxplot of %s"), var_to_string (cmd->dep_vars[v]));
276
277       for (grp = 0; grp < n_cats; ++grp)
278         {
279           const struct exploratory_stats *es =
280             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
281
282           if ( y_min > es[v].minimum)
283             y_min = es[v].minimum;
284
285           if ( y_max < es[v].maximum)
286             y_max = es[v].maximum;
287         }
288
289       boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
290
291       ds_destroy (&title);
292
293       for (grp = 0; grp < n_cats; ++grp)
294         {
295           int ivar_idx;
296           struct string label;
297
298           const struct ccase *c =
299             categoricals_get_case_by_category_real (cmd->cats,  iact_idx, grp);
300
301           struct exploratory_stats *es =
302             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
303
304           ds_init_empty (&label);
305           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
306             {
307               struct string l;
308               const struct variable *ivar = iact->vars[ivar_idx];
309               const union value *val = case_data (c, ivar);
310               ds_init_empty (&l);
311
312               append_value_name (ivar, val, &l);
313               ds_ltrim (&l, ss_cstr (" "));
314
315               ds_put_substring (&label, l.ss);
316               if (ivar_idx < iact->n_vars - 1)
317                 ds_put_cstr (&label, "; ");
318
319               ds_destroy (&l);
320             }
321
322           boxplot_add_box (boxplot, es[v].box_whisker, ds_cstr (&label));
323           es[v].box_whisker = NULL;
324
325           ds_destroy (&label);
326         }
327
328       boxplot_submit (boxplot);
329     }
330 }
331
332 static void
333 show_boxplot_variabled (const struct examine *cmd, int iact_idx)
334 {
335   int grp;
336   const struct interaction *iact = cmd->iacts[iact_idx];
337   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
338
339   for (grp = 0; grp < n_cats; ++grp)
340     {
341       struct boxplot *boxplot;
342       int v;
343       double y_min = DBL_MAX;
344       double y_max = -DBL_MAX;
345
346       const struct ccase *c =
347         categoricals_get_case_by_category_real (cmd->cats,  iact_idx, grp);
348
349       struct string title;
350       ds_init_empty (&title);
351
352       for (v = 0; v < cmd->n_dep_vars; ++v)
353         {
354           const struct exploratory_stats *es =
355             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
356
357           if ( y_min > es[v].minimum)
358             y_min = es[v].minimum;
359
360           if ( y_max < es[v].maximum)
361             y_max = es[v].maximum;
362         }
363
364       if ( iact->n_vars == 0)
365         ds_put_format (&title, _("Boxplot"));
366       else
367         {
368           int ivar_idx;
369           struct string label;
370           ds_init_empty (&label);
371           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
372             {
373               const struct variable *ivar = iact->vars[ivar_idx];
374               const union value *val = case_data (c, ivar);
375
376               ds_put_cstr (&label, var_to_string (ivar));
377               ds_put_cstr (&label, " = ");
378               append_value_name (ivar, val, &label);
379               ds_put_cstr (&label, "; ");
380             }
381
382           ds_put_format (&title, _("Boxplot of %s"),
383                          ds_cstr (&label));
384
385           ds_destroy (&label);
386         }
387
388       boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
389
390       ds_destroy (&title);
391
392       for (v = 0; v < cmd->n_dep_vars; ++v)
393         {
394           struct exploratory_stats *es =
395             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
396
397           boxplot_add_box (boxplot, es[v].box_whisker,
398                            var_to_string (cmd->dep_vars[v]));
399           es[v].box_whisker = NULL;
400         }
401
402       boxplot_submit (boxplot);
403     }
404 }
405
406
407 static void
408 show_npplot (const struct examine *cmd, int iact_idx)
409 {
410   const struct interaction *iact = cmd->iacts[iact_idx];
411   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
412
413   int v;
414
415   for (v = 0; v < cmd->n_dep_vars; ++v)
416     {
417       int grp;
418       for (grp = 0; grp < n_cats; ++grp)
419         {
420           struct chart_item *npp, *dnpp;
421           struct casereader *reader;
422           struct np *np;
423
424           int ivar_idx;
425           const struct ccase *c =
426             categoricals_get_case_by_category_real (cmd->cats,
427                                                     iact_idx, grp);
428
429           const struct exploratory_stats *es =
430             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
431
432           struct string label;
433           ds_init_cstr (&label,
434                         var_to_string (cmd->dep_vars[v]));
435
436           if ( iact->n_vars > 0)
437             {
438               ds_put_cstr (&label, " (");
439               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
440                 {
441                   const struct variable *ivar = iact->vars[ivar_idx];
442                   const union value *val = case_data (c, ivar);
443
444                   ds_put_cstr (&label, var_to_string (ivar));
445                   ds_put_cstr (&label, " = ");
446                   append_value_name (ivar, val, &label);
447                   ds_put_cstr (&label, "; ");
448
449                 }
450               ds_put_cstr (&label, ")");
451             }
452
453           np = es[v].np;
454           reader = casewriter_make_reader (np->writer);
455           np->writer = NULL;
456
457           npp = np_plot_create (np, reader, ds_cstr (&label));
458           dnpp = dnp_plot_create (np, reader, ds_cstr (&label));
459
460           if (npp == NULL || dnpp == NULL)
461             {
462               msg (MW, _("Not creating NP plot because data set is empty."));
463               chart_item_unref (npp);
464               chart_item_unref (dnpp);
465             }
466           else
467             {
468               chart_item_submit (npp);
469               chart_item_submit (dnpp);
470             }
471           casereader_destroy (reader);
472
473           ds_destroy (&label);
474         }
475     }
476 }
477
478 static void
479 show_spreadlevel (const struct examine *cmd, int iact_idx)
480 {
481   const struct interaction *iact = cmd->iacts[iact_idx];
482   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
483
484   int v;
485
486   /* Spreadlevel when there are no levels is not useful */
487   if (iact->n_vars == 0)
488     return;
489
490   for (v = 0; v < cmd->n_dep_vars; ++v)
491     {
492       int grp;
493       struct chart_item *sl;
494
495       struct string label;
496       ds_init_cstr (&label,
497                     var_to_string (cmd->dep_vars[v]));
498
499       if (iact->n_vars > 0)
500         {
501           ds_put_cstr (&label, " (");
502           interaction_to_string (iact, &label);
503           ds_put_cstr (&label, ")");
504         }
505
506       sl = spreadlevel_plot_create (ds_cstr (&label), cmd->sl_power);
507
508       for (grp = 0; grp < n_cats; ++grp)
509         {
510           const struct exploratory_stats *es =
511             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
512
513           double median = percentile_calculate (es[v].quartiles[1], cmd->pc_alg);
514
515           double iqr = percentile_calculate (es[v].quartiles[2], cmd->pc_alg) -
516             percentile_calculate (es[v].quartiles[0], cmd->pc_alg);
517
518           spreadlevel_plot_add (sl, iqr, median);
519         }
520
521       if (sl == NULL)
522         msg (MW, _("Not creating spreadlevel chart for %s"), ds_cstr (&label));
523       else
524         chart_item_submit (sl);
525
526       ds_destroy (&label);
527     }
528 }
529
530
531 static void
532 show_histogram (const struct examine *cmd, int iact_idx)
533 {
534   const struct interaction *iact = cmd->iacts[iact_idx];
535   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
536
537   int v;
538
539   for (v = 0; v < cmd->n_dep_vars; ++v)
540     {
541       int grp;
542       for (grp = 0; grp < n_cats; ++grp)
543         {
544           double n, mean, var;
545           int ivar_idx;
546           const struct ccase *c =
547             categoricals_get_case_by_category_real (cmd->cats,
548                                                     iact_idx, grp);
549
550           const struct exploratory_stats *es =
551             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
552
553           struct string label;
554
555           if (es[v].histogram == NULL)
556             continue;
557
558           ds_init_cstr (&label,
559                         var_to_string (cmd->dep_vars[v]));
560
561           if ( iact->n_vars > 0)
562             {
563               ds_put_cstr (&label, " (");
564               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
565                 {
566                   const struct variable *ivar = iact->vars[ivar_idx];
567                   const union value *val = case_data (c, ivar);
568
569                   ds_put_cstr (&label, var_to_string (ivar));
570                   ds_put_cstr (&label, " = ");
571                   append_value_name (ivar, val, &label);
572                   ds_put_cstr (&label, "; ");
573
574                 }
575               ds_put_cstr (&label, ")");
576             }
577
578
579           moments_calculate (es[v].mom, &n, &mean, &var, NULL, NULL);
580
581           chart_item_submit
582             ( histogram_chart_create (es[v].histogram->gsl_hist,
583                                       ds_cstr (&label), n, mean,
584                                       sqrt (var), false));
585
586
587           ds_destroy (&label);
588         }
589     }
590 }
591
592 static void
593 percentiles_report (const struct examine *cmd, int iact_idx)
594 {
595   const struct interaction *iact = cmd->iacts[iact_idx];
596   int i, v;
597   const int heading_columns = 1 + iact->n_vars + 1;
598   const int heading_rows = 2;
599   struct tab_table *t;
600
601   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
602
603   const int rows_per_cat = 2;
604   const int rows_per_var = n_cats * rows_per_cat;
605
606   const int nr = heading_rows + cmd->n_dep_vars * rows_per_var;
607   const int nc = heading_columns + cmd->n_percentiles;
608
609   t = tab_create (nc, nr);
610
611   tab_title (t, _("Percentiles"));
612
613   tab_headers (t, heading_columns, 0, heading_rows, 0);
614
615   /* Internal Vertical lines */
616   tab_box (t, -1, -1, -1, TAL_1,
617            heading_columns, 0, nc - 1, nr - 1);
618
619   /* External Frame */
620   tab_box (t, TAL_2, TAL_2, -1, -1,
621            0, 0, nc - 1, nr - 1);
622
623   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
624   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
625
626   tab_joint_text (t, heading_columns, 0,
627                   nc - 1, 0,
628                   TAT_TITLE | TAB_CENTER,
629                   _("Percentiles")
630                   );
631
632   tab_hline (t, TAL_1, heading_columns, nc - 1, 1);
633
634
635   for (i = 0; i < cmd->n_percentiles; ++i)
636     {
637       tab_text_format (t, heading_columns + i, 1,
638                        TAT_TITLE | TAB_CENTER,
639                        _("%g"), cmd->ptiles[i]);
640     }
641
642   for (i = 0; i < iact->n_vars; ++i)
643     {
644       tab_text (t,
645                 1 + i, 1,
646                 TAT_TITLE,
647                 var_to_string (iact->vars[i])
648                 );
649     }
650
651
652
653   if (n_cats > 0)
654     {
655       tab_vline (t, TAL_1, heading_columns - 1, heading_rows, nr - 1);
656
657       for (v = 0; v < cmd->n_dep_vars; ++v)
658         {
659           const union value **prev_vals = previous_value_alloc (iact);
660
661           int ivar_idx;
662           if ( v > 0 )
663             tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * rows_per_var);
664
665           tab_text (t,
666                     0, heading_rows + v * rows_per_var,
667                     TAT_TITLE | TAB_LEFT,
668                     var_to_string (cmd->dep_vars[v])
669                     );
670
671           for (i = 0; i < n_cats; ++i)
672             {
673               const struct ccase *c =
674                 categoricals_get_case_by_category_real (cmd->cats,
675                                                         iact_idx, i);
676
677               const struct exploratory_stats *ess =
678                 categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
679
680               const struct exploratory_stats *es = ess + v;
681
682               int diff_idx = previous_value_record (iact, c, prev_vals);
683
684               double hinges[3];
685               int p;
686
687               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
688                 {
689                   const struct variable *ivar = iact->vars[ivar_idx];
690                   const union value *val = case_data (c, ivar);
691
692                   if (( diff_idx != -1 && diff_idx <= ivar_idx)
693                       || i == 0)
694                     {
695                       struct string str;
696                       ds_init_empty (&str);
697                       append_value_name (ivar, val, &str);
698
699                       tab_text (t,
700                                 1 + ivar_idx,
701                                 heading_rows + v * rows_per_var + i * rows_per_cat,
702                                 TAT_TITLE | TAB_LEFT,
703                                 ds_cstr (&str)
704                                 );
705
706                       ds_destroy (&str);
707                     }
708                 }
709
710               if ( diff_idx != -1 && diff_idx < iact->n_vars)
711                 {
712                   tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
713                              heading_rows + v * rows_per_var + i * rows_per_cat
714                              );
715                 }
716
717               tab_text (t, heading_columns - 1,
718                         heading_rows + v * rows_per_var + i * rows_per_cat,
719                         TAT_TITLE | TAB_LEFT,
720                         gettext (ptile_alg_desc [cmd->pc_alg]));
721
722               tukey_hinges_calculate (es->hinges, hinges);
723
724               for (p = 0; p < cmd->n_percentiles; ++p)
725                 {
726                   tab_double (t, heading_columns + p,
727                               heading_rows + v * rows_per_var + i * rows_per_cat,
728                               0,
729                               percentile_calculate (es->percentiles[p], cmd->pc_alg),
730                               NULL, RC_OTHER);
731
732                   if (cmd->ptiles[p] == 25.0)
733                     {
734                       tab_double (t, heading_columns + p,
735                                   heading_rows + v * rows_per_var + i * rows_per_cat + 1,
736                                   0,
737                                   hinges[0],
738                                   NULL, RC_OTHER);
739                     }
740                   else if (cmd->ptiles[p] == 50.0)
741                     {
742                       tab_double (t, heading_columns + p,
743                                   heading_rows + v * rows_per_var + i * rows_per_cat + 1,
744                                   0,
745                                   hinges[1],
746                                   NULL, RC_OTHER);
747                     }
748                   else if (cmd->ptiles[p] == 75.0)
749                     {
750                       tab_double (t, heading_columns + p,
751                                   heading_rows + v * rows_per_var + i * rows_per_cat + 1,
752                                   0,
753                                   hinges[2],
754                                   NULL, RC_OTHER);
755                     }
756                 }
757
758
759               tab_text (t, heading_columns - 1,
760                         heading_rows + v * rows_per_var + i * rows_per_cat + 1,
761                         TAT_TITLE | TAB_LEFT,
762                         _("Tukey's Hinges"));
763
764             }
765
766           free (prev_vals);
767         }
768     }
769   tab_submit (t);
770 }
771
772 static void
773 descriptives_report (const struct examine *cmd, int iact_idx)
774 {
775   const struct interaction *iact = cmd->iacts[iact_idx];
776   int i, v;
777   const int heading_columns = 1 + iact->n_vars + 2;
778   const int heading_rows = 1;
779   struct tab_table *t;
780
781   size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
782
783   const int rows_per_cat = 13;
784   const int rows_per_var = n_cats * rows_per_cat;
785
786   const int nr = heading_rows + cmd->n_dep_vars * rows_per_var;
787   const int nc = 2 + heading_columns;
788
789   t = tab_create (nc, nr);
790
791   tab_title (t, _("Descriptives"));
792
793   tab_headers (t, heading_columns, 0, heading_rows, 0);
794
795   /* Internal Vertical lines */
796   tab_box (t, -1, -1, -1, TAL_1,
797            heading_columns, 0, nc - 1, nr - 1);
798
799   /* External Frame */
800   tab_box (t, TAL_2, TAL_2, -1, -1,
801            0, 0, nc - 1, nr - 1);
802
803   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
804   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
805
806
807   tab_text (t, heading_columns, 0, TAB_CENTER | TAT_TITLE,
808             _("Statistic"));
809
810   tab_text (t, heading_columns + 1, 0, TAB_CENTER | TAT_TITLE,
811             _("Std. Error"));
812
813   for (i = 0; i < iact->n_vars; ++i)
814     {
815       tab_text (t,
816                 1 + i, 0,
817                 TAT_TITLE,
818                 var_to_string (iact->vars[i])
819                 );
820     }
821
822   for (v = 0; v < cmd->n_dep_vars; ++v)
823     {
824       const union value **prev_val = previous_value_alloc (iact);
825
826       int ivar_idx;
827       if ( v > 0 )
828         tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * rows_per_var);
829
830       tab_text (t,
831                 0, heading_rows + v * rows_per_var,
832                 TAT_TITLE | TAB_LEFT,
833                 var_to_string (cmd->dep_vars[v])
834                 );
835
836       for (i = 0; i < n_cats; ++i)
837         {
838           const struct ccase *c =
839             categoricals_get_case_by_category_real (cmd->cats,
840                                                     iact_idx, i);
841
842           const struct exploratory_stats *ess =
843             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
844
845           const struct exploratory_stats *es = ess + v;
846
847           const int diff_idx = previous_value_record (iact, c, prev_val);
848
849           double m0, m1, m2, m3, m4;
850           double tval;
851
852           moments_calculate (es->mom, &m0, &m1, &m2, &m3, &m4);
853
854           tval = gsl_cdf_tdist_Qinv ((1.0 - cmd->conf) / 2.0, m0 - 1.0);
855
856           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
857             {
858               const struct variable *ivar = iact->vars[ivar_idx];
859               const union value *val = case_data (c, ivar);
860
861               if (( diff_idx != -1 && diff_idx <= ivar_idx)
862                   || i == 0)
863                 {
864                   struct string str;
865                   ds_init_empty (&str);
866                   append_value_name (ivar, val, &str);
867
868                   tab_text (t,
869                             1 + ivar_idx,
870                             heading_rows + v * rows_per_var + i * rows_per_cat,
871                             TAT_TITLE | TAB_LEFT,
872                             ds_cstr (&str)
873                             );
874
875                   ds_destroy (&str);
876                 }
877             }
878
879           if ( diff_idx != -1 && diff_idx < iact->n_vars)
880             {
881               tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
882                          heading_rows + v * rows_per_var + i * rows_per_cat
883                          );
884             }
885
886           tab_text (t,
887                     1 + iact->n_vars,
888                     heading_rows + v * rows_per_var + i * rows_per_cat,
889                     TAB_LEFT,
890                     _("Mean")
891                     );
892
893           tab_double (t,
894                       1 + iact->n_vars + 2,
895                       heading_rows + v * rows_per_var + i * rows_per_cat,
896                       0, m1, NULL, RC_OTHER);
897
898           tab_double (t,
899                       1 + iact->n_vars + 3,
900                       heading_rows + v * rows_per_var + i * rows_per_cat,
901                       0, calc_semean (m2, m0), NULL, RC_OTHER);
902
903           tab_text_format (t,
904                            1 + iact->n_vars,
905                            heading_rows + v * rows_per_var + i * rows_per_cat + 1,
906                            TAB_LEFT,
907                            _("%g%% Confidence Interval for Mean"),
908                            cmd->conf * 100.0
909                            );
910
911           tab_text (t,
912                     1 + iact->n_vars + 1,
913                     heading_rows + v * rows_per_var + i * rows_per_cat + 1,
914                     TAB_LEFT,
915                     _("Lower Bound")
916                     );
917
918           tab_double (t,
919                       1 + iact->n_vars + 2,
920                       heading_rows + v * rows_per_var + i * rows_per_cat + 1,
921                       0, m1 - tval * calc_semean (m2, m0), NULL, RC_OTHER);
922
923
924           tab_text (t,
925                     1 + iact->n_vars + 1,
926                     heading_rows + v * rows_per_var + i * rows_per_cat + 2,
927                     TAB_LEFT,
928                     _("Upper Bound")
929                     );
930
931           tab_double (t,
932                       1 + iact->n_vars + 2,
933                       heading_rows + v * rows_per_var + i * rows_per_cat + 2,
934                       0, m1 + tval * calc_semean (m2, m0), NULL, RC_OTHER);
935
936
937           tab_text (t,
938                     1 + iact->n_vars,
939                     heading_rows + v * rows_per_var + i * rows_per_cat + 3,
940                     TAB_LEFT,
941                     _("5% Trimmed Mean")
942                     );
943
944           tab_double (t,
945                       1 + iact->n_vars + 2,
946                       heading_rows + v * rows_per_var + i * rows_per_cat + 3,
947                       0,
948                       trimmed_mean_calculate (es->trimmed_mean),
949                       NULL, RC_OTHER);
950
951           tab_text (t,
952                     1 + iact->n_vars,
953                     heading_rows + v * rows_per_var + i * rows_per_cat + 4,
954                     TAB_LEFT,
955                     _("Median")
956                     );
957
958           tab_double (t,
959                       1 + iact->n_vars + 2,
960                       heading_rows + v * rows_per_var + i * rows_per_cat + 4,
961                       0,
962                       percentile_calculate (es->quartiles[1], cmd->pc_alg),
963                       NULL, RC_OTHER);
964
965
966           tab_text (t,
967                     1 + iact->n_vars,
968                     heading_rows + v * rows_per_var + i * rows_per_cat + 5,
969                     TAB_LEFT,
970                     _("Variance")
971                     );
972
973           tab_double (t,
974                       1 + iact->n_vars + 2,
975                       heading_rows + v * rows_per_var + i * rows_per_cat + 5,
976                       0, m2, NULL, RC_OTHER);
977
978           tab_text (t,
979                     1 + iact->n_vars,
980                     heading_rows + v * rows_per_var + i * rows_per_cat + 6,
981                     TAB_LEFT,
982                     _("Std. Deviation")
983                     );
984
985           tab_double (t,
986                       1 + iact->n_vars + 2,
987                       heading_rows + v * rows_per_var + i * rows_per_cat + 6,
988                       0, sqrt (m2), NULL, RC_OTHER);
989
990           tab_text (t,
991                     1 + iact->n_vars,
992                     heading_rows + v * rows_per_var + i * rows_per_cat + 7,
993                     TAB_LEFT,
994                     _("Minimum")
995                     );
996
997           tab_double (t,
998                       1 + iact->n_vars + 2,
999                       heading_rows + v * rows_per_var + i * rows_per_cat + 7,
1000                       0,
1001                       es->minima[0].val,
1002                       NULL, RC_OTHER);
1003
1004           tab_text (t,
1005                     1 + iact->n_vars,
1006                     heading_rows + v * rows_per_var + i * rows_per_cat + 8,
1007                     TAB_LEFT,
1008                     _("Maximum")
1009                     );
1010
1011           tab_double (t,
1012                       1 + iact->n_vars + 2,
1013                       heading_rows + v * rows_per_var + i * rows_per_cat + 8,
1014                       0,
1015                       es->maxima[0].val,
1016                       NULL, RC_OTHER);
1017
1018           tab_text (t,
1019                     1 + iact->n_vars,
1020                     heading_rows + v * rows_per_var + i * rows_per_cat + 9,
1021                     TAB_LEFT,
1022                     _("Range")
1023                     );
1024
1025           tab_double (t,
1026                       1 + iact->n_vars + 2,
1027                       heading_rows + v * rows_per_var + i * rows_per_cat + 9,
1028                       0,
1029                       es->maxima[0].val - es->minima[0].val,
1030                       NULL, RC_OTHER);
1031
1032           tab_text (t,
1033                     1 + iact->n_vars,
1034                     heading_rows + v * rows_per_var + i * rows_per_cat + 10,
1035                     TAB_LEFT,
1036                     _("Interquartile Range")
1037                     );
1038
1039
1040           tab_double (t,
1041                       1 + iact->n_vars + 2,
1042                       heading_rows + v * rows_per_var + i * rows_per_cat + 10,
1043                       0,
1044                       percentile_calculate (es->quartiles[2], cmd->pc_alg) -
1045                       percentile_calculate (es->quartiles[0], cmd->pc_alg),
1046                       NULL, RC_OTHER);
1047
1048
1049
1050
1051           tab_text (t,
1052                     1 + iact->n_vars,
1053                     heading_rows + v * rows_per_var + i * rows_per_cat + 11,
1054                     TAB_LEFT,
1055                     _("Skewness")
1056                     );
1057
1058           tab_double (t,
1059                       1 + iact->n_vars + 2,
1060                       heading_rows + v * rows_per_var + i * rows_per_cat + 11,
1061                       0, m3, NULL, RC_OTHER);
1062
1063           tab_double (t,
1064                       1 + iact->n_vars + 3,
1065                       heading_rows + v * rows_per_var + i * rows_per_cat + 11,
1066                       0, calc_seskew (m0), NULL, RC_OTHER);
1067
1068           tab_text (t,
1069                     1 + iact->n_vars,
1070                     heading_rows + v * rows_per_var + i * rows_per_cat + 12,
1071                     TAB_LEFT,
1072                     _("Kurtosis")
1073                     );
1074
1075           tab_double (t,
1076                       1 + iact->n_vars + 2,
1077                       heading_rows + v * rows_per_var + i * rows_per_cat + 12,
1078                       0, m4, NULL, RC_OTHER);
1079
1080           tab_double (t,
1081                       1 + iact->n_vars + 3,
1082                       heading_rows + v * rows_per_var + i * rows_per_cat + 12,
1083                       0, calc_sekurt (m0), NULL, RC_OTHER);
1084         }
1085
1086       free (prev_val);
1087     }
1088   tab_submit (t);
1089 }
1090
1091
1092 static void
1093 extremes_report (const struct examine *cmd, int iact_idx)
1094 {
1095   const struct interaction *iact = cmd->iacts[iact_idx];
1096   int i, v;
1097   const int heading_columns = 1 + iact->n_vars + 2;
1098   const int heading_rows = 1;
1099   struct tab_table *t;
1100
1101   size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
1102
1103   const int rows_per_cat = 2 * cmd->disp_extremes;
1104   const int rows_per_var = n_cats * rows_per_cat;
1105
1106   const int nr = heading_rows + cmd->n_dep_vars * rows_per_var;
1107   const int nc = 2 + heading_columns;
1108
1109   t = tab_create (nc, nr);
1110
1111   tab_title (t, _("Extreme Values"));
1112
1113   tab_headers (t, heading_columns, 0, heading_rows, 0);
1114
1115   /* Internal Vertical lines */
1116   tab_box (t, -1, -1, -1, TAL_1,
1117            heading_columns, 0, nc - 1, nr - 1);
1118
1119   /* External Frame */
1120   tab_box (t, TAL_2, TAL_2, -1, -1,
1121            0, 0, nc - 1, nr - 1);
1122
1123   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
1124   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
1125
1126
1127   if ( cmd->id_var )
1128     tab_text (t, heading_columns, 0, TAB_CENTER | TAT_TITLE,
1129               var_to_string (cmd->id_var));
1130   else
1131     tab_text (t, heading_columns, 0, TAB_CENTER | TAT_TITLE,
1132               _("Case Number"));
1133
1134   tab_text (t, heading_columns + 1, 0, TAB_CENTER | TAT_TITLE,
1135             _("Value"));
1136
1137   for (i = 0; i < iact->n_vars; ++i)
1138     {
1139       tab_text (t,
1140                 1 + i, 0,
1141                 TAT_TITLE,
1142                 var_to_string (iact->vars[i])
1143                 );
1144     }
1145
1146   for (v = 0; v < cmd->n_dep_vars; ++v)
1147     {
1148       const union value **prev_val = previous_value_alloc (iact);
1149
1150       int ivar_idx;
1151       if ( v > 0 )
1152         tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * rows_per_var);
1153
1154       tab_text (t,
1155                 0, heading_rows + v * rows_per_var,
1156                 TAT_TITLE,
1157                 var_to_string (cmd->dep_vars[v])
1158                 );
1159
1160       for (i = 0; i < n_cats; ++i)
1161         {
1162           int e;
1163           const struct ccase *c =
1164             categoricals_get_case_by_category_real (cmd->cats, iact_idx, i);
1165
1166           const struct exploratory_stats *ess =
1167             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
1168
1169           const struct exploratory_stats *es = ess + v;
1170
1171           int diff_idx = previous_value_record (iact, c, prev_val);
1172
1173           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
1174             {
1175               const struct variable *ivar = iact->vars[ivar_idx];
1176               const union value *val = case_data (c, ivar);
1177
1178               if (( diff_idx != -1 && diff_idx <= ivar_idx)
1179                   || i == 0)
1180                 {
1181                   struct string str;
1182                   ds_init_empty (&str);
1183                   append_value_name (ivar, val, &str);
1184
1185                   tab_text (t,
1186                             1 + ivar_idx,
1187                             heading_rows + v * rows_per_var + i * rows_per_cat,
1188                             TAT_TITLE | TAB_LEFT,
1189                             ds_cstr (&str)
1190                             );
1191
1192                   ds_destroy (&str);
1193                 }
1194             }
1195
1196           if ( diff_idx != -1 && diff_idx < iact->n_vars)
1197             {
1198               tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
1199                          heading_rows + v * rows_per_var + i * rows_per_cat
1200                          );
1201             }
1202
1203           tab_text (t,
1204                     heading_columns - 2,
1205                     heading_rows + v * rows_per_var + i * rows_per_cat,
1206                     TAB_RIGHT,
1207                     _("Highest"));
1208
1209
1210           tab_hline (t, TAL_1, heading_columns - 2, nc - 1,
1211                      heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes
1212                      );
1213
1214           tab_text (t,
1215                     heading_columns - 2,
1216                     heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes,
1217                     TAB_RIGHT,
1218                     _("Lowest"));
1219
1220           for (e = 0 ; e < cmd->disp_extremes; ++e)
1221             {
1222               tab_double (t,
1223                           heading_columns - 1,
1224                           heading_rows + v * rows_per_var + i * rows_per_cat + e,
1225                           TAB_RIGHT,
1226                           e + 1,
1227                           NULL, RC_INTEGER);
1228
1229               /* The casenumber */
1230               if (cmd->id_var)
1231                 tab_value (t,
1232                            heading_columns,
1233                            heading_rows + v * rows_per_var + i * rows_per_cat + e,
1234                            TAB_RIGHT,
1235                            &es->maxima[e].identity,
1236                            cmd->id_var,
1237                            NULL);
1238               else
1239                 tab_double (t,
1240                           heading_columns,
1241                             heading_rows + v * rows_per_var + i * rows_per_cat + e,
1242                             TAB_RIGHT,
1243                             es->maxima[e].identity.f,
1244                             NULL, RC_INTEGER);
1245
1246               tab_double (t,
1247                          heading_columns + 1,
1248                          heading_rows + v * rows_per_var + i * rows_per_cat + e,
1249                          0,
1250                          es->maxima[e].val,
1251                           var_get_print_format (cmd->dep_vars[v]), RC_OTHER);
1252
1253
1254               tab_double (t,
1255                           heading_columns - 1,
1256                           heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1257                           TAB_RIGHT,
1258                           e + 1,
1259                           NULL, RC_INTEGER);
1260
1261               /* The casenumber */
1262               if (cmd->id_var)
1263                 tab_value (t,
1264                            heading_columns,
1265                            heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1266                            TAB_RIGHT,
1267                            &es->minima[e].identity,
1268                            cmd->id_var,
1269                            NULL);
1270               else
1271                 tab_double (t,
1272                             heading_columns,
1273                             heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1274                             TAB_RIGHT,
1275                             es->minima[e].identity.f,
1276                             NULL, RC_INTEGER);
1277
1278               tab_double (t,
1279                           heading_columns + 1,
1280                           heading_rows + v * rows_per_var + i * rows_per_cat + cmd->disp_extremes + e,
1281                           0,
1282                           es->minima[e].val,
1283                           var_get_print_format (cmd->dep_vars[v]), RC_OTHER);
1284             }
1285         }
1286       free (prev_val);
1287     }
1288
1289   tab_submit (t);
1290 }
1291
1292
1293 static void
1294 summary_report (const struct examine *cmd, int iact_idx)
1295 {
1296   const struct interaction *iact = cmd->iacts[iact_idx];
1297   int i, v;
1298   const int heading_columns = 1 + iact->n_vars;
1299   const int heading_rows = 3;
1300   struct tab_table *t;
1301
1302   const struct fmt_spec *wfmt = cmd->wv ? var_get_print_format (cmd->wv) : &F_8_0;
1303
1304   size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
1305
1306   const int nr = heading_rows + n_cats * cmd->n_dep_vars;
1307   const int nc = 6 + heading_columns;
1308
1309   t = tab_create (nc, nr);
1310   tab_set_format (t, RC_WEIGHT, wfmt);
1311   tab_title (t, _("Case Processing Summary"));
1312
1313   tab_headers (t, heading_columns, 0, heading_rows, 0);
1314
1315   /* Internal Vertical lines */
1316   tab_box (t, -1, -1, -1, TAL_1,
1317            heading_columns, 0, nc - 1, nr - 1);
1318
1319   /* External Frame */
1320   tab_box (t, TAL_2, TAL_2, -1, -1,
1321            0, 0, nc - 1, nr - 1);
1322
1323   tab_hline (t, TAL_2, 0, nc - 1, heading_rows);
1324   tab_vline (t, TAL_2, heading_columns, 0, nr - 1);
1325
1326   tab_joint_text (t, heading_columns, 0,
1327                   nc - 1, 0, TAB_CENTER | TAT_TITLE, _("Cases"));
1328   tab_joint_text (t,
1329                   heading_columns, 1,
1330                   heading_columns + 1, 1,
1331                   TAB_CENTER | TAT_TITLE, _("Valid"));
1332
1333   tab_joint_text (t,
1334                   heading_columns + 2, 1,
1335                   heading_columns + 3, 1,
1336                   TAB_CENTER | TAT_TITLE, _("Missing"));
1337
1338   tab_joint_text (t,
1339                   heading_columns + 4, 1,
1340                   heading_columns + 5, 1,
1341                   TAB_CENTER | TAT_TITLE, _("Total"));
1342
1343   for (i = 0; i < 3; ++i)
1344     {
1345       tab_text (t, heading_columns + i * 2, 2, TAB_CENTER | TAT_TITLE,
1346                 _("N"));
1347       tab_text (t, heading_columns + i * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
1348                 _("Percent"));
1349     }
1350
1351   for (i = 0; i < iact->n_vars; ++i)
1352     {
1353       tab_text (t,
1354                 1 + i, 2,
1355                 TAT_TITLE,
1356                 var_to_string (iact->vars[i])
1357                 );
1358     }
1359
1360   if (n_cats > 0)
1361     for (v = 0; v < cmd->n_dep_vars; ++v)
1362       {
1363         int ivar_idx;
1364         const union value **prev_values = previous_value_alloc (iact);
1365
1366         if ( v > 0 )
1367           tab_hline (t, TAL_1, 0, nc - 1, heading_rows + v * n_cats);
1368
1369         tab_text (t,
1370                   0, heading_rows + n_cats * v,
1371                   TAT_TITLE,
1372                   var_to_string (cmd->dep_vars[v])
1373                   );
1374
1375
1376         for (i = 0; i < n_cats; ++i)
1377           {
1378             double total;
1379             const struct exploratory_stats *es;
1380
1381             const struct ccase *c =
1382               categoricals_get_case_by_category_real (cmd->cats,
1383                                                       iact_idx, i);
1384             if (c)
1385               {
1386                 int diff_idx = previous_value_record (iact, c, prev_values);
1387
1388                 if ( diff_idx != -1 && diff_idx < iact->n_vars - 1)
1389                   tab_hline (t, TAL_1, 1 + diff_idx, nc - 1,
1390                              heading_rows + n_cats * v + i );
1391
1392                 for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
1393                   {
1394                     const struct variable *ivar = iact->vars[ivar_idx];
1395                     const union value *val = case_data (c, ivar);
1396
1397                     if (( diff_idx != -1 && diff_idx <= ivar_idx)
1398                         || i == 0)
1399                       {
1400                         struct string str;
1401                         ds_init_empty (&str);
1402                         append_value_name (ivar, val, &str);
1403
1404                         tab_text (t,
1405                                   1 + ivar_idx, heading_rows + n_cats * v + i,
1406                                   TAT_TITLE | TAB_LEFT,
1407                                   ds_cstr (&str)
1408                                   );
1409
1410                         ds_destroy (&str);
1411                       }
1412                   }
1413               }
1414
1415
1416             es = categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, i);
1417
1418
1419             total = es[v].missing + es[v].non_missing;
1420             tab_double (t,
1421                         heading_columns + 0,
1422                         heading_rows + n_cats * v + i,
1423                         0,
1424                         es[v].non_missing,
1425                         NULL, RC_WEIGHT);
1426
1427
1428             tab_text_format (t,
1429                              heading_columns + 1,
1430                              heading_rows + n_cats * v + i,
1431                              0,
1432                              "%g%%",
1433                              100.0 * es[v].non_missing / total
1434                              );
1435
1436
1437             tab_double (t,
1438                         heading_columns + 2,
1439                         heading_rows + n_cats * v + i,
1440                         0,
1441                         es[v].missing,
1442                         NULL, RC_WEIGHT);
1443
1444             tab_text_format (t,
1445                              heading_columns + 3,
1446                              heading_rows + n_cats * v + i,
1447                              0,
1448                              "%g%%",
1449                              100.0 * es[v].missing / total
1450                              );
1451             tab_double (t,
1452                         heading_columns + 4,
1453                         heading_rows + n_cats * v + i,
1454                         0,
1455                         total,
1456                         NULL, RC_WEIGHT);
1457
1458             /* This can only be 100% can't it? */
1459             tab_text_format (t,
1460                              heading_columns + 5,
1461                              heading_rows + n_cats * v + i,
1462                              0,
1463                              "%g%%",
1464                              100.0 * (es[v].missing + es[v].non_missing)/ total
1465                              );
1466           }
1467         free (prev_values);
1468       }
1469
1470   tab_hline (t, TAL_1, heading_columns, nc - 1, 1);
1471   tab_hline (t, TAL_1, heading_columns, nc - 1, 2);
1472
1473   tab_submit (t);
1474 }
1475
1476 /* Attempt to parse an interaction from LEXER */
1477 static struct interaction *
1478 parse_interaction (struct lexer *lexer, struct examine *ex)
1479 {
1480   const struct variable *v = NULL;
1481   struct interaction *iact = NULL;
1482
1483   if ( lex_match_variable (lexer, ex->dict, &v))
1484     {
1485       iact = interaction_create (v);
1486
1487       while (lex_match (lexer, T_BY))
1488         {
1489           if (!lex_match_variable (lexer, ex->dict, &v))
1490             {
1491               interaction_destroy (iact);
1492               return NULL;
1493             }
1494           interaction_add_variable (iact, v);
1495         }
1496       lex_match (lexer, T_COMMA);
1497     }
1498
1499   return iact;
1500 }
1501
1502
1503 static void *
1504 create_n (const void *aux1, void *aux2 UNUSED)
1505 {
1506   int v;
1507
1508   const struct examine *examine = aux1;
1509   struct exploratory_stats *es = pool_calloc (examine->pool, examine->n_dep_vars, sizeof (*es));
1510   struct subcase ordering;
1511   subcase_init (&ordering, 0, 0, SC_ASCEND);
1512
1513   for (v = 0; v < examine->n_dep_vars; v++)
1514     {
1515       es[v].sorted_writer = sort_create_writer (&ordering, examine->ex_proto);
1516       es[v].sorted_reader = NULL;
1517
1518       es[v].mom = moments_create (MOMENT_KURTOSIS);
1519       es[v].cmin = DBL_MAX;
1520
1521       es[v].maximum = -DBL_MAX;
1522       es[v].minimum =  DBL_MAX;
1523     }
1524
1525   subcase_destroy (&ordering);
1526   return es;
1527 }
1528
1529 static void
1530 update_n (const void *aux1, void *aux2 UNUSED, void *user_data,
1531           const struct ccase *c, double weight)
1532 {
1533   int v;
1534   const struct examine *examine = aux1;
1535   struct exploratory_stats *es = user_data;
1536
1537   bool this_case_is_missing = false;
1538   /* LISTWISE missing must be dealt with here */
1539   if (!examine->missing_pw)
1540     {
1541       for (v = 0; v < examine->n_dep_vars; v++)
1542         {
1543           const struct variable *var = examine->dep_vars[v];
1544
1545           if (var_is_value_missing (var, case_data (c, var), examine->dep_excl))
1546             {
1547               es[v].missing += weight;
1548               this_case_is_missing = true;
1549             }
1550         }
1551     }
1552
1553   if (this_case_is_missing)
1554     return;
1555
1556   for (v = 0; v < examine->n_dep_vars; v++)
1557     {
1558       struct ccase *outcase ;
1559       const struct variable *var = examine->dep_vars[v];
1560       const double x = case_data (c, var)->f;
1561
1562       if (var_is_value_missing (var, case_data (c, var), examine->dep_excl))
1563         {
1564           es[v].missing += weight;
1565           continue;
1566         }
1567
1568       outcase = case_create (examine->ex_proto);
1569
1570       if (x > es[v].maximum)
1571         es[v].maximum = x;
1572
1573       if (x < es[v].minimum)
1574         es[v].minimum =  x;
1575
1576       es[v].non_missing += weight;
1577
1578       moments_pass_one (es[v].mom, x, weight);
1579
1580       /* Save the value and the ID to the writer */
1581       assert (examine->id_idx != -1);
1582       case_data_rw_idx (outcase, EX_VAL)->f = x;
1583       value_copy (case_data_rw_idx (outcase, EX_ID),
1584                   case_data_idx (c, examine->id_idx), examine->id_width);
1585
1586       case_data_rw_idx (outcase, EX_WT)->f = weight;
1587
1588       es[v].cc += weight;
1589
1590       if (es[v].cmin > weight)
1591         es[v].cmin = weight;
1592
1593       casewriter_write (es[v].sorted_writer, outcase);
1594     }
1595 }
1596
1597 static void
1598 calculate_n (const void *aux1, void *aux2 UNUSED, void *user_data)
1599 {
1600   int v;
1601   const struct examine *examine = aux1;
1602   struct exploratory_stats *es = user_data;
1603
1604   for (v = 0; v < examine->n_dep_vars; v++)
1605     {
1606       int i;
1607       casenumber imin = 0;
1608       casenumber imax;
1609       struct casereader *reader;
1610       struct ccase *c;
1611
1612       if (examine->histogramplot && es[v].non_missing > 0)
1613         {
1614           /* Sturges Rule */
1615           double bin_width = fabs (es[v].minimum - es[v].maximum)
1616             / (1 + log2 (es[v].cc))
1617             ;
1618
1619           es[v].histogram =
1620             histogram_create (bin_width, es[v].minimum, es[v].maximum);
1621         }
1622
1623       es[v].sorted_reader = casewriter_make_reader (es[v].sorted_writer);
1624       es[v].sorted_writer = NULL;
1625
1626       imax = casereader_get_case_cnt (es[v].sorted_reader);
1627
1628       es[v].maxima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].maxima));
1629       es[v].minima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].minima));
1630       for (i = 0; i < examine->calc_extremes; ++i)
1631         {
1632           value_init_pool (examine->pool, &es[v].maxima[i].identity, examine->id_width) ;
1633           value_init_pool (examine->pool, &es[v].minima[i].identity, examine->id_width) ;
1634         }
1635
1636       bool warn = true;
1637       for (reader = casereader_clone (es[v].sorted_reader);
1638            (c = casereader_read (reader)) != NULL; case_unref (c))
1639         {
1640           const double val = case_data_idx (c, EX_VAL)->f;
1641           double wt = case_data_idx (c, EX_WT)->f;
1642           wt = var_force_valid_weight (examine->wv, wt, &warn);
1643
1644           moments_pass_two (es[v].mom, val, wt);
1645
1646           if (es[v].histogram)
1647             histogram_add (es[v].histogram, val, wt);
1648
1649           if (imin < examine->calc_extremes)
1650             {
1651               int x;
1652               for (x = imin; x < examine->calc_extremes; ++x)
1653                 {
1654                   struct extremity *min = &es[v].minima[x];
1655                   min->val = val;
1656                   value_copy (&min->identity, case_data_idx (c, EX_ID), examine->id_width);
1657                 }
1658               imin ++;
1659             }
1660
1661           imax --;
1662           if (imax < examine->calc_extremes)
1663             {
1664               int x;
1665
1666               for (x = imax; x < imax + 1; ++x)
1667                 {
1668                   struct extremity *max;
1669
1670                   if (x >= examine->calc_extremes)
1671                     break;
1672
1673                   max = &es[v].maxima[x];
1674                   max->val = val;
1675                   value_copy (&max->identity, case_data_idx (c, EX_ID), examine->id_width);
1676                 }
1677             }
1678         }
1679       casereader_destroy (reader);
1680
1681       if (examine->calc_extremes > 0 && es[v].non_missing > 0)
1682         {
1683           assert (es[v].minima[0].val == es[v].minimum);
1684           assert (es[v].maxima[0].val == es[v].maximum);
1685         }
1686
1687       {
1688         const int n_os = 5 + examine->n_percentiles;
1689         struct order_stats **os ;
1690         es[v].percentiles = pool_calloc (examine->pool, examine->n_percentiles, sizeof (*es[v].percentiles));
1691
1692         es[v].trimmed_mean = trimmed_mean_create (es[v].cc, 0.05);
1693
1694         os = xcalloc (n_os, sizeof *os);
1695         os[0] = &es[v].trimmed_mean->parent;
1696
1697         es[v].quartiles[0] = percentile_create (0.25, es[v].cc);
1698         es[v].quartiles[1] = percentile_create (0.5,  es[v].cc);
1699         es[v].quartiles[2] = percentile_create (0.75, es[v].cc);
1700
1701         os[1] = &es[v].quartiles[0]->parent;
1702         os[2] = &es[v].quartiles[1]->parent;
1703         os[3] = &es[v].quartiles[2]->parent;
1704
1705         es[v].hinges = tukey_hinges_create (es[v].cc, es[v].cmin);
1706         os[4] = &es[v].hinges->parent;
1707
1708         for (i = 0; i < examine->n_percentiles; ++i)
1709           {
1710             es[v].percentiles[i] = percentile_create (examine->ptiles[i] / 100.00, es[v].cc);
1711             os[5 + i] = &es[v].percentiles[i]->parent;
1712           }
1713
1714         order_stats_accumulate_idx (os, n_os,
1715                                     casereader_clone (es[v].sorted_reader),
1716                                     EX_WT, EX_VAL);
1717
1718         free (os);
1719       }
1720
1721       if (examine->boxplot)
1722         {
1723           struct order_stats *os;
1724
1725           es[v].box_whisker = box_whisker_create (es[v].hinges,
1726                                                   EX_ID, examine->id_var);
1727
1728           os = &es[v].box_whisker->parent;
1729           order_stats_accumulate_idx (&os, 1,
1730                                       casereader_clone (es[v].sorted_reader),
1731                                       EX_WT, EX_VAL);
1732         }
1733
1734       if (examine->npplot)
1735         {
1736           double n, mean, var;
1737           struct order_stats *os;
1738
1739           moments_calculate (es[v].mom, &n, &mean, &var, NULL, NULL);
1740
1741           es[v].np = np_create (n, mean, var);
1742
1743           os = &es[v].np->parent;
1744
1745           order_stats_accumulate_idx (&os, 1,
1746                                       casereader_clone (es[v].sorted_reader),
1747                                       EX_WT, EX_VAL);
1748         }
1749
1750     }
1751 }
1752
1753 static void
1754 cleanup_exploratory_stats (struct examine *cmd)
1755 {
1756   int i;
1757   for (i = 0; i < cmd->n_iacts; ++i)
1758     {
1759       int v;
1760       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1761
1762       for (v = 0; v < cmd->n_dep_vars; ++v)
1763         {
1764           int grp;
1765           for (grp = 0; grp < n_cats; ++grp)
1766             {
1767               int q;
1768               const struct exploratory_stats *es =
1769                 categoricals_get_user_data_by_category_real (cmd->cats, i, grp);
1770
1771               struct order_stats *os = &es[v].hinges->parent;
1772               struct statistic  *stat = &os->parent;
1773               stat->destroy (stat);
1774
1775               for (q = 0; q < 3 ; q++)
1776                 {
1777                   os = &es[v].quartiles[q]->parent;
1778                   stat = &os->parent;
1779                   stat->destroy (stat);
1780                 }
1781
1782               for (q = 0; q < cmd->n_percentiles ; q++)
1783                 {
1784                   os = &es[v].percentiles[q]->parent;
1785                   stat = &os->parent;
1786                   stat->destroy (stat);
1787                 }
1788
1789               os = &es[v].trimmed_mean->parent;
1790               stat = &os->parent;
1791               stat->destroy (stat);
1792
1793               os = &es[v].np->parent;
1794               if (os)
1795                 {
1796                   stat = &os->parent;
1797                   stat->destroy (stat);
1798                 }
1799
1800               statistic_destroy (&es[v].histogram->parent);
1801               moments_destroy (es[v].mom);
1802
1803               if (es[v].box_whisker)
1804                 {
1805                   stat = &es[v].box_whisker->parent.parent;
1806                   stat->destroy (stat);
1807                 }
1808
1809               casereader_destroy (es[v].sorted_reader);
1810             }
1811         }
1812     }
1813 }
1814
1815
1816 static void
1817 run_examine (struct examine *cmd, struct casereader *input)
1818 {
1819   int i;
1820   struct ccase *c;
1821   struct casereader *reader;
1822
1823   struct payload payload;
1824   payload.create = create_n;
1825   payload.update = update_n;
1826   payload.calculate = calculate_n;
1827   payload.destroy = NULL;
1828
1829   cmd->wv = dict_get_weight (cmd->dict);
1830
1831   cmd->cats
1832     = categoricals_create (cmd->iacts, cmd->n_iacts,
1833                            cmd->wv, cmd->dep_excl, cmd->fctr_excl);
1834
1835   categoricals_set_payload (cmd->cats, &payload, cmd, NULL);
1836
1837   if (cmd->id_var == NULL)
1838     {
1839       struct ccase *c = casereader_peek (input,  0);
1840
1841       cmd->id_idx = case_get_value_cnt (c);
1842       input = casereader_create_arithmetic_sequence (input, 1.0, 1.0);
1843
1844       case_unref (c);
1845     }
1846
1847   for (reader = input;
1848        (c = casereader_read (reader)) != NULL; case_unref (c))
1849     {
1850       categoricals_update (cmd->cats, c);
1851     }
1852   casereader_destroy (reader);
1853   categoricals_done (cmd->cats);
1854
1855   for (i = 0; i < cmd->n_iacts; ++i)
1856     {
1857       summary_report (cmd, i);
1858
1859       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1860       if (n_cats == 0)
1861         continue;
1862
1863       if (cmd->disp_extremes > 0)
1864         extremes_report (cmd, i);
1865
1866       if (cmd->n_percentiles > 0)
1867         percentiles_report (cmd, i);
1868
1869       if (cmd->boxplot)
1870         {
1871           switch (cmd->boxplot_mode)
1872             {
1873             case BP_GROUPS:
1874               show_boxplot_grouped (cmd, i);
1875               break;
1876             case BP_VARIABLES:
1877               show_boxplot_variabled (cmd, i);
1878               break;
1879             default:
1880               NOT_REACHED ();
1881               break;
1882             }
1883         }
1884
1885       if (cmd->histogramplot)
1886         show_histogram (cmd, i);
1887
1888       if (cmd->npplot)
1889         show_npplot (cmd, i);
1890
1891       if (cmd->spreadlevelplot)
1892         show_spreadlevel (cmd, i);
1893
1894       if (cmd->descriptives)
1895         descriptives_report (cmd, i);
1896     }
1897
1898   cleanup_exploratory_stats (cmd);
1899   categoricals_destroy (cmd->cats);
1900 }
1901
1902
1903 int
1904 cmd_examine (struct lexer *lexer, struct dataset *ds)
1905 {
1906   int i;
1907   bool nototals_seen = false;
1908   bool totals_seen = false;
1909
1910   struct interaction **iacts_mem = NULL;
1911   struct examine examine;
1912   bool percentiles_seen = false;
1913
1914   examine.missing_pw = false;
1915   examine.disp_extremes = 0;
1916   examine.calc_extremes = 0;
1917   examine.descriptives = false;
1918   examine.conf = 0.95;
1919   examine.pc_alg = PC_HAVERAGE;
1920   examine.ptiles = NULL;
1921   examine.n_percentiles = 0;
1922   examine.id_idx = -1;
1923   examine.id_width = 0;
1924   examine.id_var = NULL;
1925   examine.boxplot_mode = BP_GROUPS;
1926
1927   examine.ex_proto = caseproto_create ();
1928
1929   examine.pool = pool_create ();
1930
1931   /* Allocate space for the first interaction.
1932      This is interaction is an empty one (for the totals).
1933      If no totals are requested, we will simply ignore this
1934      interaction.
1935   */
1936   examine.n_iacts = 1;
1937   examine.iacts = iacts_mem = pool_zalloc (examine.pool, sizeof (struct interaction *));
1938   examine.iacts[0] = interaction_create (NULL);
1939
1940   examine.dep_excl = MV_ANY;
1941   examine.fctr_excl = MV_ANY;
1942   examine.histogramplot = false;
1943   examine.npplot = false;
1944   examine.boxplot = false;
1945   examine.spreadlevelplot = false;
1946   examine.sl_power = 0;
1947   examine.dep_vars = NULL;
1948   examine.n_dep_vars = 0;
1949   examine.dict = dataset_dict (ds);
1950
1951   /* Accept an optional, completely pointless "/VARIABLES=" */
1952   lex_match (lexer, T_SLASH);
1953   if (lex_match_id  (lexer, "VARIABLES"))
1954     {
1955       if (! lex_force_match (lexer, T_EQUALS) )
1956         goto error;
1957     }
1958
1959   if (!parse_variables_const (lexer, examine.dict,
1960                               &examine.dep_vars, &examine.n_dep_vars,
1961                               PV_NO_DUPLICATE | PV_NUMERIC))
1962     goto error;
1963
1964   if (lex_match (lexer, T_BY))
1965     {
1966       struct interaction *iact = NULL;
1967       do
1968         {
1969           iact = parse_interaction (lexer, &examine);
1970           if (iact)
1971             {
1972               examine.n_iacts++;
1973               iacts_mem =
1974                 pool_nrealloc (examine.pool, iacts_mem,
1975                                examine.n_iacts,
1976                                sizeof (*iacts_mem));
1977
1978               iacts_mem[examine.n_iacts - 1] = iact;
1979             }
1980         }
1981       while (iact);
1982     }
1983
1984
1985   while (lex_token (lexer) != T_ENDCMD)
1986     {
1987       lex_match (lexer, T_SLASH);
1988
1989       if (lex_match_id (lexer, "STATISTICS"))
1990         {
1991           lex_match (lexer, T_EQUALS);
1992
1993           while (lex_token (lexer) != T_ENDCMD
1994                  && lex_token (lexer) != T_SLASH)
1995             {
1996               if (lex_match_id (lexer, "DESCRIPTIVES"))
1997                 {
1998                   examine.descriptives = true;
1999                 }
2000               else if (lex_match_id (lexer, "EXTREME"))
2001                 {
2002                   int extr = 5;
2003                   if (lex_match (lexer, T_LPAREN))
2004                     {
2005                       if (!lex_force_int (lexer))
2006                         goto error;
2007                       extr = lex_integer (lexer);
2008
2009                       if (extr < 0)
2010                         {
2011                           msg (MW, _("%s may not be negative. Using default value (%g)."), "EXTREME", 5.0);
2012                           extr = 5;
2013                         }
2014
2015                       lex_get (lexer);
2016                       if (! lex_force_match (lexer, T_RPAREN))
2017                         goto error;
2018                     }
2019                   examine.disp_extremes  = extr;
2020                 }
2021               else if (lex_match_id (lexer, "NONE"))
2022                 {
2023                 }
2024               else if (lex_match (lexer, T_ALL))
2025                 {
2026                   if (examine.disp_extremes == 0)
2027                     examine.disp_extremes = 5;
2028                 }
2029               else
2030                 {
2031                   lex_error (lexer, NULL);
2032                   goto error;
2033                 }
2034             }
2035         }
2036       else if (lex_match_id (lexer, "PERCENTILES"))
2037         {
2038           percentiles_seen = true;
2039           if (lex_match (lexer, T_LPAREN))
2040             {
2041               while (lex_is_number (lexer))
2042                 {
2043                   double p = lex_number (lexer);
2044
2045                   if ( p <= 0 || p >= 100.0)
2046                     {
2047                       lex_error (lexer,
2048                                  _("Percentiles must lie in the range (0, 100)"));
2049                       goto error;
2050                     }
2051
2052                   examine.n_percentiles++;
2053                   examine.ptiles =
2054                     xrealloc (examine.ptiles,
2055                               sizeof (*examine.ptiles) *
2056                               examine.n_percentiles);
2057
2058                   examine.ptiles[examine.n_percentiles - 1] = p;
2059
2060                   lex_get (lexer);
2061                   lex_match (lexer, T_COMMA);
2062                 }
2063               if (!lex_force_match (lexer, T_RPAREN))
2064                 goto error;
2065             }
2066
2067           lex_match (lexer, T_EQUALS);
2068
2069           while (lex_token (lexer) != T_ENDCMD
2070                  && lex_token (lexer) != T_SLASH)
2071             {
2072               if (lex_match_id (lexer, "HAVERAGE"))
2073                 {
2074                   examine.pc_alg = PC_HAVERAGE;
2075                 }
2076               else if (lex_match_id (lexer, "WAVERAGE"))
2077                 {
2078                   examine.pc_alg = PC_WAVERAGE;
2079                 }
2080               else if (lex_match_id (lexer, "ROUND"))
2081                 {
2082                   examine.pc_alg = PC_ROUND;
2083                 }
2084               else if (lex_match_id (lexer, "EMPIRICAL"))
2085                 {
2086                   examine.pc_alg = PC_EMPIRICAL;
2087                 }
2088               else if (lex_match_id (lexer, "AEMPIRICAL"))
2089                 {
2090                   examine.pc_alg = PC_AEMPIRICAL;
2091                 }
2092               else if (lex_match_id (lexer, "NONE"))
2093                 {
2094                   examine.pc_alg = PC_NONE;
2095                 }
2096               else
2097                 {
2098                   lex_error (lexer, NULL);
2099                   goto error;
2100                 }
2101             }
2102         }
2103       else if (lex_match_id (lexer, "TOTAL"))
2104         {
2105           totals_seen = true;
2106         }
2107       else if (lex_match_id (lexer, "NOTOTAL"))
2108         {
2109           nototals_seen = true;
2110         }
2111       else if (lex_match_id (lexer, "MISSING"))
2112         {
2113           lex_match (lexer, T_EQUALS);
2114
2115           while (lex_token (lexer) != T_ENDCMD
2116                  && lex_token (lexer) != T_SLASH)
2117             {
2118               if (lex_match_id (lexer, "LISTWISE"))
2119                 {
2120                   examine.missing_pw = false;
2121                 }
2122               else if (lex_match_id (lexer, "PAIRWISE"))
2123                 {
2124                   examine.missing_pw = true;
2125                 }
2126               else if (lex_match_id (lexer, "EXCLUDE"))
2127                 {
2128                   examine.dep_excl = MV_ANY;
2129                 }
2130               else if (lex_match_id (lexer, "INCLUDE"))
2131                 {
2132                   examine.dep_excl = MV_SYSTEM;
2133                 }
2134               else if (lex_match_id (lexer, "REPORT"))
2135                 {
2136                   examine.fctr_excl = MV_NEVER;
2137                 }
2138               else if (lex_match_id (lexer, "NOREPORT"))
2139                 {
2140                   examine.fctr_excl = MV_ANY;
2141                 }
2142               else
2143                 {
2144                   lex_error (lexer, NULL);
2145                   goto error;
2146                 }
2147             }
2148         }
2149       else if (lex_match_id (lexer, "COMPARE"))
2150         {
2151           lex_match (lexer, T_EQUALS);
2152           if (lex_match_id (lexer, "VARIABLES"))
2153             {
2154               examine.boxplot_mode = BP_VARIABLES;
2155             }
2156           else if (lex_match_id (lexer, "GROUPS"))
2157             {
2158               examine.boxplot_mode = BP_GROUPS;
2159             }
2160           else
2161             {
2162               lex_error (lexer, NULL);
2163               goto error;
2164             }
2165         }
2166       else if (lex_match_id (lexer, "PLOT"))
2167         {
2168           lex_match (lexer, T_EQUALS);
2169
2170           while (lex_token (lexer) != T_ENDCMD
2171                  && lex_token (lexer) != T_SLASH)
2172             {
2173               if (lex_match_id (lexer, "BOXPLOT"))
2174                 {
2175                   examine.boxplot = true;
2176                 }
2177               else if (lex_match_id (lexer, "NPPLOT"))
2178                 {
2179                   examine.npplot = true;
2180                 }
2181               else if (lex_match_id (lexer, "HISTOGRAM"))
2182                 {
2183                   examine.histogramplot = true;
2184                 }
2185               else if (lex_match_id (lexer, "SPREADLEVEL"))
2186                 {
2187                   examine.spreadlevelplot = true;
2188                   examine.sl_power = 0;
2189                   if (lex_match (lexer, T_LPAREN) && lex_force_int (lexer))
2190                     {
2191                       examine.sl_power = lex_integer (lexer);
2192
2193                       lex_get (lexer);
2194                       if (! lex_force_match (lexer, T_RPAREN))
2195                         goto error;
2196                     }
2197                 }
2198               else if (lex_match_id (lexer, "NONE"))
2199                 {
2200                   examine.histogramplot = false;
2201                   examine.npplot = false;
2202                   examine.boxplot = false;
2203                 }
2204               else if (lex_match (lexer, T_ALL))
2205                 {
2206                   examine.histogramplot = true;
2207                   examine.npplot = true;
2208                   examine.boxplot = true;
2209                 }
2210               else
2211                 {
2212                   lex_error (lexer, NULL);
2213                   goto error;
2214                 }
2215               lex_match (lexer, T_COMMA);
2216             }
2217         }
2218       else if (lex_match_id (lexer, "CINTERVAL"))
2219         {
2220           if ( !lex_force_num (lexer))
2221             goto error;
2222
2223           examine.conf = lex_number (lexer);
2224           lex_get (lexer);
2225         }
2226       else if (lex_match_id (lexer, "ID"))
2227         {
2228           lex_match (lexer, T_EQUALS);
2229
2230           examine.id_var = parse_variable_const (lexer, examine.dict);
2231         }
2232       else
2233         {
2234           lex_error (lexer, NULL);
2235           goto error;
2236         }
2237     }
2238
2239
2240   if ( totals_seen && nototals_seen)
2241     {
2242       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
2243       goto error;
2244     }
2245
2246   /* If totals have been requested or if there are no factors
2247      in this analysis, then the totals need to be included. */
2248   if ( !nototals_seen || examine.n_iacts == 1)
2249     {
2250       examine.iacts = &iacts_mem[0];
2251     }
2252   else
2253     {
2254       examine.n_iacts--;
2255       examine.iacts = &iacts_mem[1];
2256       interaction_destroy (iacts_mem[0]);
2257     }
2258
2259
2260   if ( examine.id_var )
2261     {
2262       examine.id_idx = var_get_case_index (examine.id_var);
2263       examine.id_width = var_get_width (examine.id_var);
2264     }
2265
2266   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* value */
2267   examine.ex_proto = caseproto_add_width (examine.ex_proto, examine.id_width);   /* id */
2268   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* weight */
2269
2270
2271   if (examine.disp_extremes > 0)
2272     {
2273       examine.calc_extremes = examine.disp_extremes;
2274     }
2275
2276   if (examine.descriptives && examine.calc_extremes == 0)
2277     {
2278       /* Descriptives always displays the max and min */
2279       examine.calc_extremes = 1;
2280     }
2281
2282   if (percentiles_seen && examine.n_percentiles == 0)
2283     {
2284       examine.n_percentiles = 7;
2285       examine.ptiles = xcalloc (examine.n_percentiles,
2286                                 sizeof (*examine.ptiles));
2287
2288       examine.ptiles[0] = 5;
2289       examine.ptiles[1] = 10;
2290       examine.ptiles[2] = 25;
2291       examine.ptiles[3] = 50;
2292       examine.ptiles[4] = 75;
2293       examine.ptiles[5] = 90;
2294       examine.ptiles[6] = 95;
2295     }
2296
2297   assert (examine.calc_extremes >= examine.disp_extremes);
2298   {
2299     struct casegrouper *grouper;
2300     struct casereader *group;
2301     bool ok;
2302
2303     grouper = casegrouper_create_splits (proc_open (ds), examine.dict);
2304     while (casegrouper_get_next_group (grouper, &group))
2305       run_examine (&examine, group);
2306     ok = casegrouper_destroy (grouper);
2307     ok = proc_commit (ds) && ok;
2308   }
2309
2310   caseproto_unref (examine.ex_proto);
2311
2312   for (i = 0; i < examine.n_iacts; ++i)
2313     interaction_destroy (examine.iacts[i]);
2314   free (examine.ptiles);
2315   free (examine.dep_vars);
2316   pool_destroy (examine.pool);
2317
2318   return CMD_SUCCESS;
2319
2320  error:
2321   caseproto_unref (examine.ex_proto);
2322   examine.iacts = iacts_mem;
2323   for (i = 0; i < examine.n_iacts; ++i)
2324     interaction_destroy (examine.iacts[i]);
2325   free (examine.dep_vars);
2326   free (examine.ptiles);
2327   pool_destroy (examine.pool);
2328
2329   return CMD_FAILURE;
2330 }