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