dc0d6c5beecfe4a160105e3fbbf2729631c5c7bd
[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       for (reader = casereader_clone (es[v].sorted_reader);
1637            (c = casereader_read (reader)) != NULL; case_unref (c))
1638         {
1639           const double val = case_data_idx (c, EX_VAL)->f;
1640           const double wt = case_data_idx (c, EX_WT)->f;
1641
1642           moments_pass_two (es[v].mom, val, wt);
1643
1644           if (es[v].histogram)
1645             histogram_add (es[v].histogram, val, wt);
1646
1647           if (imin < examine->calc_extremes)
1648             {
1649               int x;
1650               for (x = imin; x < examine->calc_extremes; ++x)
1651                 {
1652                   struct extremity *min = &es[v].minima[x];
1653                   min->val = val;
1654                   value_copy (&min->identity, case_data_idx (c, EX_ID), examine->id_width);
1655                 }
1656               imin ++;
1657             }
1658
1659           imax --;
1660           if (imax < examine->calc_extremes)
1661             {
1662               int x;
1663
1664               for (x = imax; x < imax + 1; ++x)
1665                 {
1666                   struct extremity *max;
1667
1668                   if (x >= examine->calc_extremes) 
1669                     break;
1670
1671                   max = &es[v].maxima[x];
1672                   max->val = val;
1673                   value_copy (&max->identity, case_data_idx (c, EX_ID), examine->id_width);
1674                 }
1675             }
1676         }
1677       casereader_destroy (reader);
1678
1679       if (examine->calc_extremes > 0 && es[v].non_missing > 0)
1680         {
1681           assert (es[v].minima[0].val == es[v].minimum);
1682           assert (es[v].maxima[0].val == es[v].maximum);
1683         }
1684
1685       {
1686         const int n_os = 5 + examine->n_percentiles;
1687         struct order_stats **os ;
1688         es[v].percentiles = pool_calloc (examine->pool, examine->n_percentiles, sizeof (*es[v].percentiles));
1689
1690         es[v].trimmed_mean = trimmed_mean_create (es[v].cc, 0.05);
1691
1692         os = xcalloc (n_os, sizeof *os);
1693         os[0] = &es[v].trimmed_mean->parent;
1694
1695         es[v].quartiles[0] = percentile_create (0.25, es[v].cc);
1696         es[v].quartiles[1] = percentile_create (0.5,  es[v].cc);
1697         es[v].quartiles[2] = percentile_create (0.75, es[v].cc);
1698
1699         os[1] = &es[v].quartiles[0]->parent;
1700         os[2] = &es[v].quartiles[1]->parent;
1701         os[3] = &es[v].quartiles[2]->parent;
1702
1703         es[v].hinges = tukey_hinges_create (es[v].cc, es[v].cmin);
1704         os[4] = &es[v].hinges->parent;
1705
1706         for (i = 0; i < examine->n_percentiles; ++i)
1707           {
1708             es[v].percentiles[i] = percentile_create (examine->ptiles[i] / 100.00, es[v].cc);
1709             os[5 + i] = &es[v].percentiles[i]->parent;
1710           }
1711
1712         order_stats_accumulate_idx (os, n_os,
1713                                     casereader_clone (es[v].sorted_reader),
1714                                     EX_WT, EX_VAL);
1715
1716         free (os);
1717       }
1718
1719       if (examine->boxplot)
1720         {
1721           struct order_stats *os;
1722
1723           es[v].box_whisker = box_whisker_create (es[v].hinges, 
1724                                                   EX_ID, examine->id_var);
1725
1726           os = &es[v].box_whisker->parent;
1727           order_stats_accumulate_idx (&os, 1,
1728                                       casereader_clone (es[v].sorted_reader),
1729                                       EX_WT, EX_VAL);
1730         }
1731
1732       if (examine->npplot)
1733         {
1734           double n, mean, var;
1735           struct order_stats *os;
1736
1737           moments_calculate (es[v].mom, &n, &mean, &var, NULL, NULL);
1738           
1739           es[v].np = np_create (n, mean, var);
1740
1741           os = &es[v].np->parent;
1742
1743           order_stats_accumulate_idx (&os, 1,
1744                                       casereader_clone (es[v].sorted_reader),
1745                                       EX_WT, EX_VAL);
1746         }
1747
1748     }
1749 }
1750
1751 static void
1752 cleanup_exploratory_stats (struct examine *cmd)
1753
1754   int i;
1755   for (i = 0; i < cmd->n_iacts; ++i)
1756     {
1757       int v;
1758       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1759
1760       for (v = 0; v < cmd->n_dep_vars; ++v)
1761         {
1762           int grp;
1763           for (grp = 0; grp < n_cats; ++grp)
1764             {
1765               int q;
1766               const struct exploratory_stats *es =
1767                 categoricals_get_user_data_by_category_real (cmd->cats, i, grp);
1768
1769               struct order_stats *os = &es[v].hinges->parent;
1770               struct statistic  *stat = &os->parent;
1771               stat->destroy (stat);
1772
1773               for (q = 0; q < 3 ; q++)
1774                 {
1775                   os = &es[v].quartiles[q]->parent;
1776                   stat = &os->parent;
1777                   stat->destroy (stat);
1778                 }
1779
1780               for (q = 0; q < cmd->n_percentiles ; q++)
1781                 {
1782                   os = &es[v].percentiles[q]->parent;
1783                   stat = &os->parent;
1784                   stat->destroy (stat);
1785                 }
1786
1787               os = &es[v].trimmed_mean->parent;
1788               stat = &os->parent;
1789               stat->destroy (stat);
1790
1791               os = &es[v].np->parent;
1792               if (os)
1793                 {
1794                   stat = &os->parent;
1795                   stat->destroy (stat);
1796                 }
1797
1798               statistic_destroy (&es[v].histogram->parent);
1799               moments_destroy (es[v].mom);
1800
1801               if (es[v].box_whisker)
1802                 {
1803                   stat = &es[v].box_whisker->parent.parent;
1804                   stat->destroy (stat);
1805                 }
1806
1807               casereader_destroy (es[v].sorted_reader);
1808             }
1809         }
1810     }
1811 }
1812
1813
1814 static void
1815 run_examine (struct examine *cmd, struct casereader *input)
1816 {
1817   int i;
1818   struct ccase *c;
1819   struct casereader *reader;
1820
1821   struct payload payload;
1822   payload.create = create_n;
1823   payload.update = update_n;
1824   payload.calculate = calculate_n;
1825   payload.destroy = NULL;
1826   
1827   cmd->wv = dict_get_weight (cmd->dict);
1828
1829   cmd->cats
1830     = categoricals_create (cmd->iacts, cmd->n_iacts,  
1831                            cmd->wv, cmd->dep_excl, cmd->fctr_excl);
1832
1833   categoricals_set_payload (cmd->cats, &payload, cmd, NULL);
1834
1835   if (cmd->id_var == NULL)
1836     {
1837       struct ccase *c = casereader_peek (input,  0);
1838
1839       cmd->id_idx = case_get_value_cnt (c);
1840       input = casereader_create_arithmetic_sequence (input, 1.0, 1.0);
1841
1842       case_unref (c);
1843     }
1844
1845   for (reader = input;
1846        (c = casereader_read (reader)) != NULL; case_unref (c))
1847     {
1848       categoricals_update (cmd->cats, c);
1849     }
1850   casereader_destroy (reader);
1851   categoricals_done (cmd->cats);
1852
1853   for (i = 0; i < cmd->n_iacts; ++i)
1854     {
1855       summary_report (cmd, i);
1856
1857       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1858       if (n_cats == 0)
1859         continue;
1860
1861       if (cmd->disp_extremes > 0)
1862         extremes_report (cmd, i);
1863
1864       if (cmd->n_percentiles > 0)
1865         percentiles_report (cmd, i);
1866
1867       if (cmd->boxplot)
1868         {
1869           switch (cmd->boxplot_mode)
1870             {
1871             case BP_GROUPS:
1872               show_boxplot_grouped (cmd, i);
1873               break;
1874             case BP_VARIABLES:
1875               show_boxplot_variabled (cmd, i);
1876               break;
1877             default:
1878               NOT_REACHED ();
1879               break;
1880             }
1881         }
1882
1883       if (cmd->histogramplot)
1884         show_histogram (cmd, i);
1885
1886       if (cmd->npplot)
1887         show_npplot (cmd, i);
1888
1889       if (cmd->spreadlevelplot)
1890         show_spreadlevel (cmd, i);
1891
1892       if (cmd->descriptives)
1893         descriptives_report (cmd, i);
1894     }
1895
1896   cleanup_exploratory_stats (cmd);
1897   categoricals_destroy (cmd->cats);
1898 }
1899
1900
1901 int
1902 cmd_examine (struct lexer *lexer, struct dataset *ds)
1903 {
1904   int i;
1905   bool nototals_seen = false;
1906   bool totals_seen = false;
1907
1908   struct interaction **iacts_mem = NULL;
1909   struct examine examine;
1910   bool percentiles_seen = false;
1911
1912   examine.missing_pw = false;
1913   examine.disp_extremes = 0;
1914   examine.calc_extremes = 0;
1915   examine.descriptives = false;
1916   examine.conf = 0.95;
1917   examine.pc_alg = PC_HAVERAGE;
1918   examine.ptiles = NULL;
1919   examine.n_percentiles = 0;
1920   examine.id_idx = -1;
1921   examine.id_width = 0;
1922   examine.id_var = NULL;
1923   examine.boxplot_mode = BP_GROUPS;
1924   
1925   examine.ex_proto = caseproto_create ();
1926
1927   examine.pool = pool_create ();
1928
1929   /* Allocate space for the first interaction.
1930      This is interaction is an empty one (for the totals).
1931      If no totals are requested, we will simply ignore this
1932      interaction.
1933   */
1934   examine.n_iacts = 1;
1935   examine.iacts = iacts_mem = pool_zalloc (examine.pool, sizeof (struct interaction *));
1936   examine.iacts[0] = interaction_create (NULL);
1937
1938   examine.dep_excl = MV_ANY;
1939   examine.fctr_excl = MV_ANY;
1940   examine.histogramplot = false;
1941   examine.npplot = false;
1942   examine.boxplot = false;
1943   examine.spreadlevelplot = false;
1944   examine.sl_power = 0;
1945   
1946   examine.dict = dataset_dict (ds);
1947
1948   /* Accept an optional, completely pointless "/VARIABLES=" */
1949   lex_match (lexer, T_SLASH);
1950   if (lex_match_id  (lexer, "VARIABLES"))
1951     {
1952       if (! lex_force_match (lexer, T_EQUALS) )
1953         goto error;
1954     }
1955
1956   if (!parse_variables_const (lexer, examine.dict,
1957                               &examine.dep_vars, &examine.n_dep_vars,
1958                               PV_NO_DUPLICATE | PV_NUMERIC))
1959     goto error;
1960
1961   if (lex_match (lexer, T_BY))
1962     {
1963       struct interaction *iact = NULL;
1964       do
1965         {
1966           iact = parse_interaction (lexer, &examine);
1967           if (iact)
1968             {
1969               examine.n_iacts++;
1970               iacts_mem = 
1971                 pool_nrealloc (examine.pool, iacts_mem,
1972                                examine.n_iacts,
1973                                sizeof (*iacts_mem));
1974               
1975               iacts_mem[examine.n_iacts - 1] = iact;
1976             }
1977         }
1978       while (iact);
1979     }
1980
1981
1982   while (lex_token (lexer) != T_ENDCMD)
1983     {
1984       lex_match (lexer, T_SLASH);
1985
1986       if (lex_match_id (lexer, "STATISTICS"))
1987         {
1988           lex_match (lexer, T_EQUALS);
1989
1990           while (lex_token (lexer) != T_ENDCMD
1991                  && lex_token (lexer) != T_SLASH)
1992             {
1993               if (lex_match_id (lexer, "DESCRIPTIVES"))
1994                 {
1995                   examine.descriptives = true;
1996                 }
1997               else if (lex_match_id (lexer, "EXTREME"))
1998                 {
1999                   int extr = 5;
2000                   if (lex_match (lexer, T_LPAREN))
2001                     {
2002                       extr = lex_integer (lexer);
2003
2004                       if (extr < 0)
2005                         {
2006                           msg (MW, _("%s may not be negative. Using default value (%g)."), "EXTREME", 5.0);
2007                           extr = 5;
2008                         }
2009
2010                       lex_get (lexer);
2011                       if (! lex_force_match (lexer, T_RPAREN))
2012                         goto error;
2013                     }
2014                   examine.disp_extremes  = extr;
2015                 }
2016               else if (lex_match_id (lexer, "NONE"))
2017                 {
2018                 }
2019               else if (lex_match (lexer, T_ALL))
2020                 {
2021                   if (examine.disp_extremes == 0)
2022                     examine.disp_extremes = 5;
2023                 }
2024               else
2025                 {
2026                   lex_error (lexer, NULL);
2027                   goto error;
2028                 }
2029             }
2030         }
2031       else if (lex_match_id (lexer, "PERCENTILES"))
2032         {
2033           percentiles_seen = true;
2034           if (lex_match (lexer, T_LPAREN))
2035             {
2036               while (lex_is_number (lexer))
2037                 {
2038                   double p = lex_number (lexer);
2039                   
2040                   if ( p <= 0 || p >= 100.0)
2041                     {
2042                       lex_error (lexer,
2043                                  _("Percentiles must lie in the range (0, 100)"));
2044                       goto error;
2045                     }
2046
2047                   examine.n_percentiles++;
2048                   examine.ptiles =
2049                     xrealloc (examine.ptiles,
2050                               sizeof (*examine.ptiles) *
2051                               examine.n_percentiles);
2052
2053                   examine.ptiles[examine.n_percentiles - 1] = p;
2054
2055                   lex_get (lexer);
2056                   lex_match (lexer, T_COMMA);
2057                 }
2058               if (!lex_force_match (lexer, T_RPAREN))
2059                 goto error;
2060             }
2061
2062           lex_match (lexer, T_EQUALS);
2063
2064           while (lex_token (lexer) != T_ENDCMD
2065                  && lex_token (lexer) != T_SLASH)
2066             {
2067               if (lex_match_id (lexer, "HAVERAGE"))
2068                 {
2069                   examine.pc_alg = PC_HAVERAGE;
2070                 }
2071               else if (lex_match_id (lexer, "WAVERAGE"))
2072                 {
2073                   examine.pc_alg = PC_WAVERAGE;
2074                 }
2075               else if (lex_match_id (lexer, "ROUND"))
2076                 {
2077                   examine.pc_alg = PC_ROUND;
2078                 }
2079               else if (lex_match_id (lexer, "EMPIRICAL"))
2080                 {
2081                   examine.pc_alg = PC_EMPIRICAL;
2082                 }
2083               else if (lex_match_id (lexer, "AEMPIRICAL"))
2084                 {
2085                   examine.pc_alg = PC_AEMPIRICAL;
2086                 }
2087               else if (lex_match_id (lexer, "NONE"))
2088                 {
2089                   examine.pc_alg = PC_NONE;
2090                 }
2091               else
2092                 {
2093                   lex_error (lexer, NULL);
2094                   goto error;
2095                 }
2096             }
2097         }
2098       else if (lex_match_id (lexer, "TOTAL"))
2099         {
2100           totals_seen = true;
2101         }
2102       else if (lex_match_id (lexer, "NOTOTAL"))
2103         {
2104           nototals_seen = true;
2105         }
2106       else if (lex_match_id (lexer, "MISSING"))
2107         {
2108           lex_match (lexer, T_EQUALS);
2109
2110           while (lex_token (lexer) != T_ENDCMD
2111                  && lex_token (lexer) != T_SLASH)
2112             {
2113               if (lex_match_id (lexer, "LISTWISE"))
2114                 {
2115                   examine.missing_pw = false;
2116                 }
2117               else if (lex_match_id (lexer, "PAIRWISE"))
2118                 {
2119                   examine.missing_pw = true;
2120                 }
2121               else if (lex_match_id (lexer, "EXCLUDE"))
2122                 {
2123                   examine.dep_excl = MV_ANY;
2124                 }
2125               else if (lex_match_id (lexer, "INCLUDE"))
2126                 {
2127                   examine.dep_excl = MV_SYSTEM;
2128                 }
2129               else if (lex_match_id (lexer, "REPORT"))
2130                 {
2131                   examine.fctr_excl = MV_NEVER;
2132                 }
2133               else if (lex_match_id (lexer, "NOREPORT"))
2134                 {
2135                   examine.fctr_excl = MV_ANY;
2136                 }
2137               else
2138                 {
2139                   lex_error (lexer, NULL);
2140                   goto error;
2141                 }
2142             }
2143         }
2144       else if (lex_match_id (lexer, "COMPARE"))
2145         {
2146           lex_match (lexer, T_EQUALS);
2147           if (lex_match_id (lexer, "VARIABLES"))
2148             {
2149               examine.boxplot_mode = BP_VARIABLES;
2150             }
2151           else if (lex_match_id (lexer, "GROUPS"))
2152             {
2153               examine.boxplot_mode = BP_GROUPS;
2154             }
2155           else
2156             {
2157               lex_error (lexer, NULL);
2158               goto error;
2159             }
2160         }
2161       else if (lex_match_id (lexer, "PLOT"))
2162         {
2163           lex_match (lexer, T_EQUALS);
2164
2165           while (lex_token (lexer) != T_ENDCMD
2166                  && lex_token (lexer) != T_SLASH)
2167             {
2168               if (lex_match_id (lexer, "BOXPLOT"))
2169                 {
2170                   examine.boxplot = true;
2171                 }
2172               else if (lex_match_id (lexer, "NPPLOT"))
2173                 {
2174                   examine.npplot = true;
2175                 }
2176               else if (lex_match_id (lexer, "HISTOGRAM"))
2177                 {
2178                   examine.histogramplot = true;
2179                 }
2180               else if (lex_match_id (lexer, "SPREADLEVEL"))
2181                 {
2182                   examine.spreadlevelplot = true;
2183                   examine.sl_power = 0;
2184                   if (lex_match (lexer, T_LPAREN) && lex_force_int (lexer))
2185                     {
2186                       examine.sl_power = lex_integer (lexer);
2187
2188                       lex_get (lexer);
2189                       if (! lex_force_match (lexer, T_RPAREN))
2190                         goto error;
2191                     }
2192                 }
2193               else if (lex_match_id (lexer, "NONE"))
2194                 {
2195                   examine.histogramplot = false;
2196                   examine.npplot = false;
2197                   examine.boxplot = false;
2198                 }
2199               else if (lex_match (lexer, T_ALL))
2200                 {
2201                   examine.histogramplot = true;
2202                   examine.npplot = true;
2203                   examine.boxplot = true;
2204                 }
2205               else 
2206                 {
2207                   lex_error (lexer, NULL);
2208                   goto error;
2209                 }
2210               lex_match (lexer, T_COMMA);
2211             }          
2212         }
2213       else if (lex_match_id (lexer, "CINTERVAL"))
2214         {
2215           if ( !lex_force_num (lexer))
2216             goto error;
2217         
2218           examine.conf = lex_number (lexer);
2219           lex_get (lexer);
2220         }
2221       else if (lex_match_id (lexer, "ID"))
2222         {
2223           lex_match (lexer, T_EQUALS);
2224
2225           examine.id_var = parse_variable_const (lexer, examine.dict);
2226         }
2227       else
2228         {
2229           lex_error (lexer, NULL);
2230           goto error;
2231         }
2232     }
2233
2234
2235   if ( totals_seen && nototals_seen)
2236     {
2237       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
2238       goto error;
2239     }
2240
2241   /* If totals have been requested or if there are no factors
2242      in this analysis, then the totals need to be included. */
2243   if ( !nototals_seen || examine.n_iacts == 1)
2244     {
2245       examine.iacts = &iacts_mem[0];
2246     }
2247   else
2248     {
2249       examine.n_iacts--;
2250       examine.iacts = &iacts_mem[1];
2251       interaction_destroy (iacts_mem[0]);
2252     }
2253
2254
2255   if ( examine.id_var )
2256     {
2257       examine.id_idx = var_get_case_index (examine.id_var);
2258       examine.id_width = var_get_width (examine.id_var);
2259     }
2260
2261   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* value */
2262   examine.ex_proto = caseproto_add_width (examine.ex_proto, examine.id_width);   /* id */
2263   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* weight */
2264
2265
2266   if (examine.disp_extremes > 0)
2267     {
2268       examine.calc_extremes = examine.disp_extremes;
2269     }
2270
2271   if (examine.descriptives && examine.calc_extremes == 0)
2272     {
2273       /* Descriptives always displays the max and min */
2274       examine.calc_extremes = 1;
2275     }
2276
2277   if (percentiles_seen && examine.n_percentiles == 0)
2278     {
2279       examine.n_percentiles = 7;
2280       examine.ptiles = xcalloc (examine.n_percentiles,
2281                                 sizeof (*examine.ptiles));
2282
2283       examine.ptiles[0] = 5;
2284       examine.ptiles[1] = 10;
2285       examine.ptiles[2] = 25;
2286       examine.ptiles[3] = 50;
2287       examine.ptiles[4] = 75;
2288       examine.ptiles[5] = 90;
2289       examine.ptiles[6] = 95;
2290     }
2291
2292   assert (examine.calc_extremes >= examine.disp_extremes);
2293   {
2294     struct casegrouper *grouper;
2295     struct casereader *group;
2296     bool ok;
2297     
2298     grouper = casegrouper_create_splits (proc_open (ds), examine.dict);
2299     while (casegrouper_get_next_group (grouper, &group))
2300       run_examine (&examine, group);
2301     ok = casegrouper_destroy (grouper);
2302     ok = proc_commit (ds) && ok;
2303   }
2304
2305   caseproto_unref (examine.ex_proto);
2306
2307   for (i = 0; i < examine.n_iacts; ++i)
2308     interaction_destroy (examine.iacts[i]);
2309   free (examine.ptiles);
2310   free (examine.dep_vars);
2311   pool_destroy (examine.pool);
2312
2313   return CMD_SUCCESS;
2314
2315  error:
2316   caseproto_unref (examine.ex_proto);
2317   examine.iacts = iacts_mem;
2318   for (i = 0; i < examine.n_iacts; ++i)
2319     interaction_destroy (examine.iacts[i]);
2320   free (examine.dep_vars);
2321   free (examine.ptiles);
2322   pool_destroy (examine.pool);
2323
2324   return CMD_FAILURE;
2325 }