cb78ebd4f889bd5976615b1d9541c4bca4b7b8d0
[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/pivot-table.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 #define PLOT_HISTOGRAM      0x1
94 #define PLOT_BOXPLOT        0x2
95 #define PLOT_NPPLOT         0x4
96 #define PLOT_SPREADLEVEL    0x8
97
98
99 struct examine
100 {
101   struct pool *pool;
102
103   /* A caseproto used to contain the data subsets under examination,
104      see (enum above)   */
105   struct caseproto *ex_proto;
106
107   size_t n_dep_vars;
108   const struct variable **dep_vars;
109
110   size_t n_iacts;
111   struct interaction **iacts;
112
113   enum mv_class dep_excl;
114   enum mv_class fctr_excl;
115
116   const struct dictionary *dict;
117
118   struct categoricals *cats;
119
120   /* how many extremities to display */
121   int disp_extremes;
122   int calc_extremes;
123   bool descriptives;
124
125   double conf;
126
127   bool missing_pw;
128
129   /* The case index of the ID value (or -1) if not applicable */
130   size_t id_idx;
131   int id_width;
132
133   enum pc_alg pc_alg;
134   double *ptiles;
135   size_t n_percentiles;
136
137   unsigned int plot;
138   int sl_power;
139
140   enum bp_mode boxplot_mode;
141
142   const struct variable *id_var;
143
144   const struct variable *wv;
145 };
146
147 struct extremity
148 {
149   /* The value of this extremity */
150   double val;
151
152   /* Either the casenumber or the value of the variable specified
153      by the /ID subcommand which corresponds to this extremity */
154   union value identity;
155 };
156
157 struct exploratory_stats
158 {
159   double missing;
160   double non_missing;
161
162   struct moments *mom;
163
164   /* Most operations need a sorted reader/writer */
165   struct casewriter *sorted_writer;
166   struct casereader *sorted_reader;
167
168   struct extremity *minima;
169   struct extremity *maxima;
170
171   /*
172      Minimum should alway equal mimima[0].val.
173      Likewise, maximum should alway equal maxima[0].val.
174      This redundancy exists as an optimisation effort.
175      Some statistics (eg histogram) require early calculation
176      of the min and max
177   */
178   double minimum;
179   double maximum;
180
181   struct trimmed_mean *trimmed_mean;
182   struct percentile *quartiles[3];
183   struct percentile **percentiles;
184
185   struct tukey_hinges *hinges;
186
187   /* The data for the NP Plots */
188   struct np *np;
189
190   struct histogram *histogram;
191
192   /* The data for the box plots */
193   struct box_whisker *box_whisker;
194
195   /* Total weight */
196   double cc;
197
198   /* The minimum weight */
199   double cmin;
200 };
201
202 static void
203 show_boxplot_grouped (const struct examine *cmd, int iact_idx)
204 {
205   int v;
206
207   const struct interaction *iact = cmd->iacts[iact_idx];
208   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
209
210   for (v = 0; v < cmd->n_dep_vars; ++v)
211     {
212       double y_min = DBL_MAX;
213       double y_max = -DBL_MAX;
214       int grp;
215       struct boxplot *boxplot;
216       struct string title;
217       ds_init_empty (&title);
218
219       if (iact->n_vars > 0)
220         {
221           struct string istr;
222           ds_init_empty (&istr);
223           interaction_to_string (iact, &istr);
224           ds_put_format (&title, _("Boxplot of %s vs. %s"),
225                          var_to_string (cmd->dep_vars[v]),
226                          ds_cstr (&istr));
227           ds_destroy (&istr);
228         }
229       else
230         ds_put_format (&title, _("Boxplot of %s"), var_to_string (cmd->dep_vars[v]));
231
232       for (grp = 0; grp < n_cats; ++grp)
233         {
234           const struct exploratory_stats *es =
235             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
236
237           if ( y_min > es[v].minimum)
238             y_min = es[v].minimum;
239
240           if ( y_max < es[v].maximum)
241             y_max = es[v].maximum;
242         }
243
244       boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
245
246       ds_destroy (&title);
247
248       for (grp = 0; grp < n_cats; ++grp)
249         {
250           int ivar_idx;
251           struct string label;
252
253           const struct ccase *c =
254             categoricals_get_case_by_category_real (cmd->cats,  iact_idx, grp);
255
256           struct exploratory_stats *es =
257             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
258
259           ds_init_empty (&label);
260           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
261             {
262               struct string l;
263               const struct variable *ivar = iact->vars[ivar_idx];
264               const union value *val = case_data (c, ivar);
265               ds_init_empty (&l);
266
267               append_value_name (ivar, val, &l);
268               ds_ltrim (&l, ss_cstr (" "));
269
270               ds_put_substring (&label, l.ss);
271               if (ivar_idx < iact->n_vars - 1)
272                 ds_put_cstr (&label, "; ");
273
274               ds_destroy (&l);
275             }
276
277           boxplot_add_box (boxplot, es[v].box_whisker, ds_cstr (&label));
278           es[v].box_whisker = NULL;
279
280           ds_destroy (&label);
281         }
282
283       boxplot_submit (boxplot);
284     }
285 }
286
287 static void
288 show_boxplot_variabled (const struct examine *cmd, int iact_idx)
289 {
290   int grp;
291   const struct interaction *iact = cmd->iacts[iact_idx];
292   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
293
294   for (grp = 0; grp < n_cats; ++grp)
295     {
296       struct boxplot *boxplot;
297       int v;
298       double y_min = DBL_MAX;
299       double y_max = -DBL_MAX;
300
301       const struct ccase *c =
302         categoricals_get_case_by_category_real (cmd->cats,  iact_idx, grp);
303
304       struct string title;
305       ds_init_empty (&title);
306
307       for (v = 0; v < cmd->n_dep_vars; ++v)
308         {
309           const struct exploratory_stats *es =
310             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
311
312           if ( y_min > es[v].minimum)
313             y_min = es[v].minimum;
314
315           if ( y_max < es[v].maximum)
316             y_max = es[v].maximum;
317         }
318
319       if ( iact->n_vars == 0)
320         ds_put_format (&title, _("Boxplot"));
321       else
322         {
323           int ivar_idx;
324           struct string label;
325           ds_init_empty (&label);
326           for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
327             {
328               const struct variable *ivar = iact->vars[ivar_idx];
329               const union value *val = case_data (c, ivar);
330
331               ds_put_cstr (&label, var_to_string (ivar));
332               ds_put_cstr (&label, " = ");
333               append_value_name (ivar, val, &label);
334               ds_put_cstr (&label, "; ");
335             }
336
337           ds_put_format (&title, _("Boxplot of %s"),
338                          ds_cstr (&label));
339
340           ds_destroy (&label);
341         }
342
343       boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
344
345       ds_destroy (&title);
346
347       for (v = 0; v < cmd->n_dep_vars; ++v)
348         {
349           struct exploratory_stats *es =
350             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
351
352           boxplot_add_box (boxplot, es[v].box_whisker,
353                            var_to_string (cmd->dep_vars[v]));
354           es[v].box_whisker = NULL;
355         }
356
357       boxplot_submit (boxplot);
358     }
359 }
360
361
362 static void
363 show_npplot (const struct examine *cmd, int iact_idx)
364 {
365   const struct interaction *iact = cmd->iacts[iact_idx];
366   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
367
368   int v;
369
370   for (v = 0; v < cmd->n_dep_vars; ++v)
371     {
372       int grp;
373       for (grp = 0; grp < n_cats; ++grp)
374         {
375           struct chart_item *npp, *dnpp;
376           struct casereader *reader;
377           struct np *np;
378
379           int ivar_idx;
380           const struct ccase *c =
381             categoricals_get_case_by_category_real (cmd->cats,
382                                                     iact_idx, grp);
383
384           const struct exploratory_stats *es =
385             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
386
387           struct string label;
388           ds_init_cstr (&label,
389                         var_to_string (cmd->dep_vars[v]));
390
391           if ( iact->n_vars > 0)
392             {
393               ds_put_cstr (&label, " (");
394               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
395                 {
396                   const struct variable *ivar = iact->vars[ivar_idx];
397                   const union value *val = case_data (c, ivar);
398
399                   ds_put_cstr (&label, var_to_string (ivar));
400                   ds_put_cstr (&label, " = ");
401                   append_value_name (ivar, val, &label);
402                   ds_put_cstr (&label, "; ");
403
404                 }
405               ds_put_cstr (&label, ")");
406             }
407
408           np = es[v].np;
409           reader = casewriter_make_reader (np->writer);
410           np->writer = NULL;
411
412           npp = np_plot_create (np, reader, ds_cstr (&label));
413           dnpp = dnp_plot_create (np, reader, ds_cstr (&label));
414
415           if (npp == NULL || dnpp == NULL)
416             {
417               msg (MW, _("Not creating NP plot because data set is empty."));
418               chart_item_unref (npp);
419               chart_item_unref (dnpp);
420             }
421           else
422             {
423               chart_item_submit (npp);
424               chart_item_submit (dnpp);
425             }
426           casereader_destroy (reader);
427
428           ds_destroy (&label);
429         }
430     }
431 }
432
433 static void
434 show_spreadlevel (const struct examine *cmd, int iact_idx)
435 {
436   const struct interaction *iact = cmd->iacts[iact_idx];
437   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
438
439   int v;
440
441   /* Spreadlevel when there are no levels is not useful */
442   if (iact->n_vars == 0)
443     return;
444
445   for (v = 0; v < cmd->n_dep_vars; ++v)
446     {
447       int grp;
448       struct chart_item *sl;
449
450       struct string label;
451       ds_init_cstr (&label,
452                     var_to_string (cmd->dep_vars[v]));
453
454       if (iact->n_vars > 0)
455         {
456           ds_put_cstr (&label, " (");
457           interaction_to_string (iact, &label);
458           ds_put_cstr (&label, ")");
459         }
460
461       sl = spreadlevel_plot_create (ds_cstr (&label), cmd->sl_power);
462
463       for (grp = 0; grp < n_cats; ++grp)
464         {
465           const struct exploratory_stats *es =
466             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
467
468           double median = percentile_calculate (es[v].quartiles[1], cmd->pc_alg);
469
470           double iqr = percentile_calculate (es[v].quartiles[2], cmd->pc_alg) -
471             percentile_calculate (es[v].quartiles[0], cmd->pc_alg);
472
473           spreadlevel_plot_add (sl, iqr, median);
474         }
475
476       if (sl == NULL)
477         msg (MW, _("Not creating spreadlevel chart for %s"), ds_cstr (&label));
478       else
479         chart_item_submit (sl);
480
481       ds_destroy (&label);
482     }
483 }
484
485
486 static void
487 show_histogram (const struct examine *cmd, int iact_idx)
488 {
489   const struct interaction *iact = cmd->iacts[iact_idx];
490   const size_t n_cats =  categoricals_n_count (cmd->cats, iact_idx);
491
492   int v;
493
494   for (v = 0; v < cmd->n_dep_vars; ++v)
495     {
496       int grp;
497       for (grp = 0; grp < n_cats; ++grp)
498         {
499           double n, mean, var;
500           int ivar_idx;
501           const struct ccase *c =
502             categoricals_get_case_by_category_real (cmd->cats,
503                                                     iact_idx, grp);
504
505           const struct exploratory_stats *es =
506             categoricals_get_user_data_by_category_real (cmd->cats, iact_idx, grp);
507
508           struct string label;
509
510           if (es[v].histogram == NULL)
511             continue;
512
513           ds_init_cstr (&label,
514                         var_to_string (cmd->dep_vars[v]));
515
516           if ( iact->n_vars > 0)
517             {
518               ds_put_cstr (&label, " (");
519               for (ivar_idx = 0; ivar_idx < iact->n_vars; ++ivar_idx)
520                 {
521                   const struct variable *ivar = iact->vars[ivar_idx];
522                   const union value *val = case_data (c, ivar);
523
524                   ds_put_cstr (&label, var_to_string (ivar));
525                   ds_put_cstr (&label, " = ");
526                   append_value_name (ivar, val, &label);
527                   ds_put_cstr (&label, "; ");
528
529                 }
530               ds_put_cstr (&label, ")");
531             }
532
533
534           moments_calculate (es[v].mom, &n, &mean, &var, NULL, NULL);
535
536           chart_item_submit
537             ( histogram_chart_create (es[v].histogram->gsl_hist,
538                                       ds_cstr (&label), n, mean,
539                                       sqrt (var), false));
540
541
542           ds_destroy (&label);
543         }
544     }
545 }
546
547 static struct pivot_value *
548 new_value_with_missing_footnote (const struct variable *var,
549                                  const union value *value,
550                                  struct pivot_footnote *missing_footnote)
551 {
552   struct pivot_value *pv = pivot_value_new_var_value (var, value);
553   if (var_is_value_missing (var, value, MV_USER))
554     pivot_value_add_footnote (pv, missing_footnote);
555   return pv;
556 }
557
558 static void
559 create_interaction_dimensions (struct pivot_table *table,
560                                const struct categoricals *cats,
561                                const struct interaction *iact,
562                                struct pivot_footnote *missing_footnote)
563 {
564   for (size_t i = iact->n_vars; i-- > 0; )
565     {
566       const struct variable *var = iact->vars[i];
567       struct pivot_dimension *d = pivot_dimension_create__ (
568         table, PIVOT_AXIS_ROW, pivot_value_new_variable (var));
569       d->root->show_label = true;
570
571       size_t n;
572       union value *values = categoricals_get_var_values (cats, var, &n);
573       for (size_t j = 0; j < n; j++)
574         pivot_category_create_leaf (
575           d->root, new_value_with_missing_footnote (var, &values[j],
576                                                     missing_footnote));
577     }
578 }
579
580 static struct pivot_footnote *
581 create_missing_footnote (struct pivot_table *table)
582 {
583   return pivot_table_create_footnote (
584     table, pivot_value_new_text (N_("User-missing value.")));
585 }
586
587 static void
588 percentiles_report (const struct examine *cmd, int iact_idx)
589 {
590   struct pivot_table *table = pivot_table_create (N_("Percentiles"));
591   table->omit_empty = true;
592
593   struct pivot_dimension *percentiles = pivot_dimension_create (
594     table, PIVOT_AXIS_COLUMN, N_("Percentiles"));
595   percentiles->root->show_label = true;
596   for (int i = 0; i < cmd->n_percentiles; ++i)
597     pivot_category_create_leaf (
598       percentiles->root,
599       pivot_value_new_user_text_nocopy (xasprintf ("%g", cmd->ptiles[i])));
600
601   pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Statistics"),
602                           N_("Weighted Average"), N_("Tukey's Hinges"));
603
604   const struct interaction *iact = cmd->iacts[iact_idx];
605   struct pivot_footnote *missing_footnote = create_missing_footnote (table);
606   create_interaction_dimensions (table, cmd->cats, iact, missing_footnote);
607
608   struct pivot_dimension *dep_dim = pivot_dimension_create (
609     table, PIVOT_AXIS_ROW, N_("Dependent Variables"));
610
611   size_t *indexes = xnmalloc (table->n_dimensions, sizeof *indexes);
612
613   size_t n_cats = categoricals_n_count (cmd->cats, iact_idx);
614   for (size_t v = 0; v < cmd->n_dep_vars; ++v)
615     {
616       indexes[table->n_dimensions - 1] = pivot_category_create_leaf (
617         dep_dim->root, pivot_value_new_variable (cmd->dep_vars[v]));
618
619       for (size_t i = 0; i < n_cats; ++i)
620         {
621           for (size_t j = 0; j < iact->n_vars; j++)
622             {
623               int idx = categoricals_get_value_index_by_category_real (
624                 cmd->cats, iact_idx, i, j);
625               indexes[table->n_dimensions - 2 - j] = idx;
626             }
627
628           const struct exploratory_stats *ess
629             = categoricals_get_user_data_by_category_real (
630               cmd->cats, iact_idx, i);
631           const struct exploratory_stats *es = ess + v;
632
633           double hinges[3];
634           tukey_hinges_calculate (es->hinges, hinges);
635
636           for (size_t pc_idx = 0; pc_idx < cmd->n_percentiles; ++pc_idx)
637             {
638               indexes[0] = pc_idx;
639
640               indexes[1] = 0;
641               double value = percentile_calculate (es->percentiles[pc_idx],
642                                                    cmd->pc_alg);
643               pivot_table_put (table, indexes, table->n_dimensions,
644                                pivot_value_new_number (value));
645
646               double hinge = (cmd->ptiles[pc_idx] == 25.0 ? hinges[0]
647                               : cmd->ptiles[pc_idx] == 50.0 ? hinges[1]
648                               : cmd->ptiles[pc_idx] == 75.0 ? hinges[2]
649                               : SYSMIS);
650               if (hinge != SYSMIS)
651                 {
652                   indexes[1] = 1;
653                   pivot_table_put (table, indexes, table->n_dimensions,
654                                    pivot_value_new_number (hinge));
655                 }
656             }
657         }
658
659     }
660   free (indexes);
661
662   pivot_table_submit (table);
663 }
664
665 static void
666 descriptives_report (const struct examine *cmd, int iact_idx)
667 {
668   struct pivot_table *table = pivot_table_create (N_("Descriptives"));
669   table->omit_empty = true;
670
671   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Aspect"),
672                           N_("Statistic"), N_("Std. Error"));
673
674   struct pivot_dimension *statistics = pivot_dimension_create (
675     table, PIVOT_AXIS_ROW, N_("Statistics"), N_("Mean"));
676   struct pivot_category *interval = pivot_category_create_group__ (
677     statistics->root,
678     pivot_value_new_text_format (N_("%g%% Confidence Interval for Mean"),
679                                  cmd->conf * 100.0));
680   pivot_category_create_leaves (interval, N_("Lower Bound"),
681                                 N_("Upper Bound"));
682   pivot_category_create_leaves (
683     statistics->root, N_("5% Trimmed Mean"), N_("Median"), N_("Variance"),
684     N_("Std. Deviation"), N_("Minimum"), N_("Maximum"), N_("Range"),
685     N_("Interquartile Range"), N_("Skewness"), N_("Kurtosis"));
686
687   const struct interaction *iact = cmd->iacts[iact_idx];
688   struct pivot_footnote *missing_footnote = create_missing_footnote (table);
689   create_interaction_dimensions (table, cmd->cats, iact, missing_footnote);
690
691   struct pivot_dimension *dep_dim = pivot_dimension_create (
692     table, PIVOT_AXIS_ROW, N_("Dependent Variables"));
693
694   size_t *indexes = xnmalloc (table->n_dimensions, sizeof *indexes);
695
696   size_t n_cats = categoricals_n_count (cmd->cats, iact_idx);
697   for (size_t v = 0; v < cmd->n_dep_vars; ++v)
698     {
699       indexes[table->n_dimensions - 1] = pivot_category_create_leaf (
700         dep_dim->root, pivot_value_new_variable (cmd->dep_vars[v]));
701
702       for (size_t i = 0; i < n_cats; ++i)
703         {
704           for (size_t j = 0; j < iact->n_vars; j++)
705             {
706               int idx = categoricals_get_value_index_by_category_real (
707                 cmd->cats, iact_idx, i, j);
708               indexes[table->n_dimensions - 2 - j] = idx;
709             }
710
711           const struct exploratory_stats *ess
712             = categoricals_get_user_data_by_category_real (cmd->cats,
713                                                            iact_idx, i);
714           const struct exploratory_stats *es = ess + v;
715
716           double m0, m1, m2, m3, m4;
717           moments_calculate (es->mom, &m0, &m1, &m2, &m3, &m4);
718           double tval = gsl_cdf_tdist_Qinv ((1.0 - cmd->conf) / 2.0, m0 - 1.0);
719
720           struct entry
721             {
722               int stat_idx;
723               int aspect_idx;
724               double x;
725             }
726           entries[] = {
727             { 0, 0, m1 },
728             { 0, 1, calc_semean (m2, m0) },
729             { 1, 0, m1 - tval * calc_semean (m2, m0) },
730             { 2, 0, m1 + tval * calc_semean (m2, m0) },
731             { 3, 0, trimmed_mean_calculate (es->trimmed_mean) },
732             { 4, 0, percentile_calculate (es->quartiles[1], cmd->pc_alg) },
733             { 5, 0, m2 },
734             { 6, 0, sqrt (m2) },
735             { 7, 0, es->minima[0].val },
736             { 8, 0, es->maxima[0].val },
737             { 9, 0, es->maxima[0].val - es->minima[0].val },
738             { 10, 0, (percentile_calculate (es->quartiles[2], cmd->pc_alg) -
739                       percentile_calculate (es->quartiles[0], cmd->pc_alg)) },
740             { 11, 0, m3 },
741             { 11, 1, calc_seskew (m0) },
742             { 12, 0, m4 },
743             { 12, 1, calc_sekurt (m0) },
744           };
745           for (size_t j = 0; j < sizeof entries / sizeof *entries; j++)
746             {
747               const struct entry *e = &entries[j];
748               indexes[0] = e->aspect_idx;
749               indexes[1] = e->stat_idx;
750               pivot_table_put (table, indexes, table->n_dimensions,
751                                pivot_value_new_number (e->x));
752             }
753         }
754     }
755
756   free (indexes);
757
758   pivot_table_submit (table);
759 }
760
761
762 static void
763 extremes_report (const struct examine *cmd, int iact_idx)
764 {
765   struct pivot_table *table = pivot_table_create (N_("Extreme Values"));
766   table->omit_empty = true;
767
768   struct pivot_dimension *statistics = pivot_dimension_create (
769     table, PIVOT_AXIS_COLUMN, N_("Statistics"));
770   pivot_category_create_leaf (statistics->root,
771                               (cmd->id_var
772                                ? pivot_value_new_variable (cmd->id_var)
773                                : pivot_value_new_text (N_("Case Number"))));
774   pivot_category_create_leaves (statistics->root, N_("Value"));
775
776   struct pivot_dimension *order = pivot_dimension_create (
777     table, PIVOT_AXIS_ROW, N_("Order"));
778   for (size_t i = 0; i < cmd->disp_extremes; i++)
779     pivot_category_create_leaf (order->root, pivot_value_new_integer (i + 1));
780
781   pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Extreme"),
782                           N_("Highest"), N_("Lowest"));
783
784   const struct interaction *iact = cmd->iacts[iact_idx];
785   struct pivot_footnote *missing_footnote = create_missing_footnote (table);
786   create_interaction_dimensions (table, cmd->cats, iact, missing_footnote);
787
788   struct pivot_dimension *dep_dim = pivot_dimension_create (
789     table, PIVOT_AXIS_ROW, N_("Dependent Variables"));
790
791   size_t *indexes = xnmalloc (table->n_dimensions, sizeof *indexes);
792
793   size_t n_cats = categoricals_n_count (cmd->cats, iact_idx);
794   for (size_t v = 0; v < cmd->n_dep_vars; ++v)
795     {
796       indexes[table->n_dimensions - 1] = pivot_category_create_leaf (
797         dep_dim->root, pivot_value_new_variable (cmd->dep_vars[v]));
798
799       for (size_t i = 0; i < n_cats; ++i)
800         {
801           for (size_t j = 0; j < iact->n_vars; j++)
802             {
803               int idx = categoricals_get_value_index_by_category_real (
804                 cmd->cats, iact_idx, i, j);
805               indexes[table->n_dimensions - 2 - j] = idx;
806             }
807
808           const struct exploratory_stats *ess
809             = categoricals_get_user_data_by_category_real (cmd->cats,
810                                                            iact_idx, i);
811           const struct exploratory_stats *es = ess + v;
812
813           for (int e = 0 ; e < cmd->disp_extremes; ++e)
814             {
815               indexes[1] = e;
816
817               for (size_t j = 0; j < 2; j++)
818                 {
819                   const struct extremity *extremity
820                     = j ? &es->minima[e] : &es->maxima[e];
821                   indexes[2] = j;
822
823                   indexes[0] = 0;
824                   pivot_table_put (
825                     table, indexes, table->n_dimensions,
826                     (cmd->id_var
827                      ? new_value_with_missing_footnote (cmd->id_var,
828                                                         &extremity->identity,
829                                                         missing_footnote)
830                      : pivot_value_new_integer (extremity->identity.f)));
831
832                   indexes[0] = 1;
833                   union value val = { .f = extremity->val };
834                   pivot_table_put (
835                     table, indexes, table->n_dimensions,
836                     new_value_with_missing_footnote (cmd->dep_vars[v], &val,
837                                                      missing_footnote));
838                 }
839             }
840         }
841     }
842   free (indexes);
843
844   pivot_table_submit (table);
845 }
846
847
848 static void
849 summary_report (const struct examine *cmd, int iact_idx)
850 {
851   struct pivot_table *table = pivot_table_create (
852     N_("Case Processing Summary"));
853   table->omit_empty = true;
854   pivot_table_set_weight_var (table, dict_get_weight (cmd->dict));
855
856   pivot_dimension_create (table, PIVOT_AXIS_COLUMN, N_("Statistics"),
857                           N_("N"), PIVOT_RC_COUNT,
858                           N_("Percent"), PIVOT_RC_PERCENT);
859   struct pivot_dimension *cases = pivot_dimension_create (
860     table, PIVOT_AXIS_COLUMN, N_("Cases"), N_("Valid"), N_("Missing"),
861     N_("Total"));
862   cases->root->show_label = true;
863
864   const struct interaction *iact = cmd->iacts[iact_idx];
865   struct pivot_footnote *missing_footnote = create_missing_footnote (table);
866   create_interaction_dimensions (table, cmd->cats, iact, missing_footnote);
867
868   struct pivot_dimension *dep_dim = pivot_dimension_create (
869     table, PIVOT_AXIS_ROW, N_("Dependent Variables"));
870
871   size_t *indexes = xnmalloc (table->n_dimensions, sizeof *indexes);
872
873   size_t n_cats = categoricals_n_count (cmd->cats, iact_idx);
874   for (size_t v = 0; v < cmd->n_dep_vars; ++v)
875     {
876       indexes[table->n_dimensions - 1] = pivot_category_create_leaf (
877         dep_dim->root, pivot_value_new_variable (cmd->dep_vars[v]));
878
879       for (size_t i = 0; i < n_cats; ++i)
880         {
881           for (size_t j = 0; j < iact->n_vars; j++)
882             {
883               int idx = categoricals_get_value_index_by_category_real (
884                 cmd->cats, iact_idx, i, j);
885               indexes[table->n_dimensions - 2 - j] = idx;
886             }
887
888           const struct exploratory_stats *es
889             = categoricals_get_user_data_by_category_real (
890               cmd->cats, iact_idx, i);
891
892           double total = es[v].missing + es[v].non_missing;
893           struct entry
894             {
895               int stat_idx;
896               int case_idx;
897               double x;
898             }
899           entries[] = {
900             { 0, 0, es[v].non_missing },
901             { 1, 0, 100.0 * es[v].non_missing / total },
902             { 0, 1, es[v].missing },
903             { 1, 1, 100.0 * es[v].missing / total },
904             { 0, 2, total },
905             { 1, 2, 100.0 },
906           };
907           for (size_t j = 0; j < sizeof entries / sizeof *entries; j++)
908             {
909               const struct entry *e = &entries[j];
910               indexes[0] = e->stat_idx;
911               indexes[1] = e->case_idx;
912               pivot_table_put (table, indexes, table->n_dimensions,
913                                pivot_value_new_number (e->x));
914             }
915         }
916     }
917
918   free (indexes);
919
920   pivot_table_submit (table);
921 }
922
923 /* Attempt to parse an interaction from LEXER */
924 static struct interaction *
925 parse_interaction (struct lexer *lexer, struct examine *ex)
926 {
927   const struct variable *v = NULL;
928   struct interaction *iact = NULL;
929
930   if ( lex_match_variable (lexer, ex->dict, &v))
931     {
932       iact = interaction_create (v);
933
934       while (lex_match (lexer, T_BY))
935         {
936           if (!lex_match_variable (lexer, ex->dict, &v))
937             {
938               interaction_destroy (iact);
939               return NULL;
940             }
941           interaction_add_variable (iact, v);
942         }
943       lex_match (lexer, T_COMMA);
944     }
945
946   return iact;
947 }
948
949
950 static void *
951 create_n (const void *aux1, void *aux2 UNUSED)
952 {
953   int v;
954
955   const struct examine *examine = aux1;
956   struct exploratory_stats *es = pool_calloc (examine->pool, examine->n_dep_vars, sizeof (*es));
957   struct subcase ordering;
958   subcase_init (&ordering, 0, 0, SC_ASCEND);
959
960   for (v = 0; v < examine->n_dep_vars; v++)
961     {
962       es[v].sorted_writer = sort_create_writer (&ordering, examine->ex_proto);
963       es[v].sorted_reader = NULL;
964
965       es[v].mom = moments_create (MOMENT_KURTOSIS);
966       es[v].cmin = DBL_MAX;
967
968       es[v].maximum = -DBL_MAX;
969       es[v].minimum =  DBL_MAX;
970     }
971
972   subcase_destroy (&ordering);
973   return es;
974 }
975
976 static void
977 update_n (const void *aux1, void *aux2 UNUSED, void *user_data,
978           const struct ccase *c, double weight)
979 {
980   int v;
981   const struct examine *examine = aux1;
982   struct exploratory_stats *es = user_data;
983
984   bool this_case_is_missing = false;
985   /* LISTWISE missing must be dealt with here */
986   if (!examine->missing_pw)
987     {
988       for (v = 0; v < examine->n_dep_vars; v++)
989         {
990           const struct variable *var = examine->dep_vars[v];
991
992           if (var_is_value_missing (var, case_data (c, var), examine->dep_excl))
993             {
994               es[v].missing += weight;
995               this_case_is_missing = true;
996             }
997         }
998     }
999
1000   if (this_case_is_missing)
1001     return;
1002
1003   for (v = 0; v < examine->n_dep_vars; v++)
1004     {
1005       struct ccase *outcase ;
1006       const struct variable *var = examine->dep_vars[v];
1007       const double x = case_data (c, var)->f;
1008
1009       if (var_is_value_missing (var, case_data (c, var), examine->dep_excl))
1010         {
1011           es[v].missing += weight;
1012           continue;
1013         }
1014
1015       outcase = case_create (examine->ex_proto);
1016
1017       if (x > es[v].maximum)
1018         es[v].maximum = x;
1019
1020       if (x < es[v].minimum)
1021         es[v].minimum =  x;
1022
1023       es[v].non_missing += weight;
1024
1025       moments_pass_one (es[v].mom, x, weight);
1026
1027       /* Save the value and the ID to the writer */
1028       assert (examine->id_idx != -1);
1029       case_data_rw_idx (outcase, EX_VAL)->f = x;
1030       value_copy (case_data_rw_idx (outcase, EX_ID),
1031                   case_data_idx (c, examine->id_idx), examine->id_width);
1032
1033       case_data_rw_idx (outcase, EX_WT)->f = weight;
1034
1035       es[v].cc += weight;
1036
1037       if (es[v].cmin > weight)
1038         es[v].cmin = weight;
1039
1040       casewriter_write (es[v].sorted_writer, outcase);
1041     }
1042 }
1043
1044 static void
1045 calculate_n (const void *aux1, void *aux2 UNUSED, void *user_data)
1046 {
1047   int v;
1048   const struct examine *examine = aux1;
1049   struct exploratory_stats *es = user_data;
1050
1051   for (v = 0; v < examine->n_dep_vars; v++)
1052     {
1053       int i;
1054       casenumber imin = 0;
1055       casenumber imax;
1056       struct casereader *reader;
1057       struct ccase *c;
1058
1059       if (examine->plot & PLOT_HISTOGRAM && es[v].non_missing > 0)
1060         {
1061           /* Sturges Rule */
1062           double bin_width = fabs (es[v].minimum - es[v].maximum)
1063             / (1 + log2 (es[v].cc))
1064             ;
1065
1066           es[v].histogram =
1067             histogram_create (bin_width, es[v].minimum, es[v].maximum);
1068         }
1069
1070       es[v].sorted_reader = casewriter_make_reader (es[v].sorted_writer);
1071       es[v].sorted_writer = NULL;
1072
1073       imax = casereader_get_case_cnt (es[v].sorted_reader);
1074
1075       es[v].maxima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].maxima));
1076       es[v].minima = pool_calloc (examine->pool, examine->calc_extremes, sizeof (*es[v].minima));
1077       for (i = 0; i < examine->calc_extremes; ++i)
1078         {
1079           value_init_pool (examine->pool, &es[v].maxima[i].identity, examine->id_width) ;
1080           value_init_pool (examine->pool, &es[v].minima[i].identity, examine->id_width) ;
1081         }
1082
1083       bool warn = true;
1084       for (reader = casereader_clone (es[v].sorted_reader);
1085            (c = casereader_read (reader)) != NULL; case_unref (c))
1086         {
1087           const double val = case_data_idx (c, EX_VAL)->f;
1088           double wt = case_data_idx (c, EX_WT)->f;
1089           wt = var_force_valid_weight (examine->wv, wt, &warn);
1090
1091           moments_pass_two (es[v].mom, val, wt);
1092
1093           if (es[v].histogram)
1094             histogram_add (es[v].histogram, val, wt);
1095
1096           if (imin < examine->calc_extremes)
1097             {
1098               int x;
1099               for (x = imin; x < examine->calc_extremes; ++x)
1100                 {
1101                   struct extremity *min = &es[v].minima[x];
1102                   min->val = val;
1103                   value_copy (&min->identity, case_data_idx (c, EX_ID), examine->id_width);
1104                 }
1105               imin ++;
1106             }
1107
1108           imax --;
1109           if (imax < examine->calc_extremes)
1110             {
1111               int x;
1112
1113               for (x = imax; x < imax + 1; ++x)
1114                 {
1115                   struct extremity *max;
1116
1117                   if (x >= examine->calc_extremes)
1118                     break;
1119
1120                   max = &es[v].maxima[x];
1121                   max->val = val;
1122                   value_copy (&max->identity, case_data_idx (c, EX_ID), examine->id_width);
1123                 }
1124             }
1125         }
1126       casereader_destroy (reader);
1127
1128       if (examine->calc_extremes > 0 && es[v].non_missing > 0)
1129         {
1130           assert (es[v].minima[0].val == es[v].minimum);
1131           assert (es[v].maxima[0].val == es[v].maximum);
1132         }
1133
1134       {
1135         const int n_os = 5 + examine->n_percentiles;
1136         struct order_stats **os ;
1137         es[v].percentiles = pool_calloc (examine->pool, examine->n_percentiles, sizeof (*es[v].percentiles));
1138
1139         es[v].trimmed_mean = trimmed_mean_create (es[v].cc, 0.05);
1140
1141         os = xcalloc (n_os, sizeof *os);
1142         os[0] = &es[v].trimmed_mean->parent;
1143
1144         es[v].quartiles[0] = percentile_create (0.25, es[v].cc);
1145         es[v].quartiles[1] = percentile_create (0.5,  es[v].cc);
1146         es[v].quartiles[2] = percentile_create (0.75, es[v].cc);
1147
1148         os[1] = &es[v].quartiles[0]->parent;
1149         os[2] = &es[v].quartiles[1]->parent;
1150         os[3] = &es[v].quartiles[2]->parent;
1151
1152         es[v].hinges = tukey_hinges_create (es[v].cc, es[v].cmin);
1153         os[4] = &es[v].hinges->parent;
1154
1155         for (i = 0; i < examine->n_percentiles; ++i)
1156           {
1157             es[v].percentiles[i] = percentile_create (examine->ptiles[i] / 100.00, es[v].cc);
1158             os[5 + i] = &es[v].percentiles[i]->parent;
1159           }
1160
1161         order_stats_accumulate_idx (os, n_os,
1162                                     casereader_clone (es[v].sorted_reader),
1163                                     EX_WT, EX_VAL);
1164
1165         free (os);
1166       }
1167
1168       if (examine->plot & PLOT_BOXPLOT)
1169         {
1170           struct order_stats *os;
1171
1172           es[v].box_whisker = box_whisker_create (es[v].hinges,
1173                                                   EX_ID, examine->id_var);
1174
1175           os = &es[v].box_whisker->parent;
1176           order_stats_accumulate_idx (&os, 1,
1177                                       casereader_clone (es[v].sorted_reader),
1178                                       EX_WT, EX_VAL);
1179         }
1180
1181       if (examine->plot & PLOT_NPPLOT)
1182         {
1183           double n, mean, var;
1184           struct order_stats *os;
1185
1186           moments_calculate (es[v].mom, &n, &mean, &var, NULL, NULL);
1187
1188           es[v].np = np_create (n, mean, var);
1189
1190           os = &es[v].np->parent;
1191
1192           order_stats_accumulate_idx (&os, 1,
1193                                       casereader_clone (es[v].sorted_reader),
1194                                       EX_WT, EX_VAL);
1195         }
1196
1197     }
1198 }
1199
1200 static void
1201 cleanup_exploratory_stats (struct examine *cmd)
1202 {
1203   int i;
1204   for (i = 0; i < cmd->n_iacts; ++i)
1205     {
1206       int v;
1207       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1208
1209       for (v = 0; v < cmd->n_dep_vars; ++v)
1210         {
1211           int grp;
1212           for (grp = 0; grp < n_cats; ++grp)
1213             {
1214               int q;
1215               const struct exploratory_stats *es =
1216                 categoricals_get_user_data_by_category_real (cmd->cats, i, grp);
1217
1218               struct order_stats *os = &es[v].hinges->parent;
1219               struct statistic  *stat = &os->parent;
1220               stat->destroy (stat);
1221
1222               for (q = 0; q < 3 ; q++)
1223                 {
1224                   os = &es[v].quartiles[q]->parent;
1225                   stat = &os->parent;
1226                   stat->destroy (stat);
1227                 }
1228
1229               for (q = 0; q < cmd->n_percentiles ; q++)
1230                 {
1231                   os = &es[v].percentiles[q]->parent;
1232                   stat = &os->parent;
1233                   stat->destroy (stat);
1234                 }
1235
1236               os = &es[v].trimmed_mean->parent;
1237               stat = &os->parent;
1238               stat->destroy (stat);
1239
1240               os = &es[v].np->parent;
1241               if (os)
1242                 {
1243                   stat = &os->parent;
1244                   stat->destroy (stat);
1245                 }
1246
1247               statistic_destroy (&es[v].histogram->parent);
1248               moments_destroy (es[v].mom);
1249
1250               if (es[v].box_whisker)
1251                 {
1252                   stat = &es[v].box_whisker->parent.parent;
1253                   stat->destroy (stat);
1254                 }
1255
1256               casereader_destroy (es[v].sorted_reader);
1257             }
1258         }
1259     }
1260 }
1261
1262
1263 static void
1264 run_examine (struct examine *cmd, struct casereader *input)
1265 {
1266   int i;
1267   struct ccase *c;
1268   struct casereader *reader;
1269
1270   struct payload payload;
1271   payload.create = create_n;
1272   payload.update = update_n;
1273   payload.calculate = calculate_n;
1274   payload.destroy = NULL;
1275
1276   cmd->wv = dict_get_weight (cmd->dict);
1277
1278   cmd->cats
1279     = categoricals_create (cmd->iacts, cmd->n_iacts, cmd->wv, cmd->fctr_excl);
1280
1281   categoricals_set_payload (cmd->cats, &payload, cmd, NULL);
1282
1283   if (cmd->id_var == NULL)
1284     {
1285       struct ccase *c = casereader_peek (input,  0);
1286
1287       cmd->id_idx = case_get_value_cnt (c);
1288       input = casereader_create_arithmetic_sequence (input, 1.0, 1.0);
1289
1290       case_unref (c);
1291     }
1292
1293   for (reader = input;
1294        (c = casereader_read (reader)) != NULL; case_unref (c))
1295     {
1296       categoricals_update (cmd->cats, c);
1297     }
1298   casereader_destroy (reader);
1299   categoricals_done (cmd->cats);
1300
1301   for (i = 0; i < cmd->n_iacts; ++i)
1302     {
1303       summary_report (cmd, i);
1304
1305       const size_t n_cats =  categoricals_n_count (cmd->cats, i);
1306       if (n_cats == 0)
1307         continue;
1308
1309       if (cmd->disp_extremes > 0)
1310         extremes_report (cmd, i);
1311
1312       if (cmd->n_percentiles > 0)
1313         percentiles_report (cmd, i);
1314
1315       if (cmd->plot & PLOT_BOXPLOT)
1316         {
1317           switch (cmd->boxplot_mode)
1318             {
1319             case BP_GROUPS:
1320               show_boxplot_grouped (cmd, i);
1321               break;
1322             case BP_VARIABLES:
1323               show_boxplot_variabled (cmd, i);
1324               break;
1325             default:
1326               NOT_REACHED ();
1327               break;
1328             }
1329         }
1330
1331       if (cmd->plot & PLOT_HISTOGRAM)
1332         show_histogram (cmd, i);
1333
1334       if (cmd->plot & PLOT_NPPLOT)
1335         show_npplot (cmd, i);
1336
1337       if (cmd->plot & PLOT_SPREADLEVEL)
1338         show_spreadlevel (cmd, i);
1339
1340       if (cmd->descriptives)
1341         descriptives_report (cmd, i);
1342     }
1343
1344   cleanup_exploratory_stats (cmd);
1345   categoricals_destroy (cmd->cats);
1346 }
1347
1348
1349 int
1350 cmd_examine (struct lexer *lexer, struct dataset *ds)
1351 {
1352   int i;
1353   bool nototals_seen = false;
1354   bool totals_seen = false;
1355
1356   struct interaction **iacts_mem = NULL;
1357   struct examine examine;
1358   bool percentiles_seen = false;
1359
1360   examine.missing_pw = false;
1361   examine.disp_extremes = 0;
1362   examine.calc_extremes = 0;
1363   examine.descriptives = false;
1364   examine.conf = 0.95;
1365   examine.pc_alg = PC_HAVERAGE;
1366   examine.ptiles = NULL;
1367   examine.n_percentiles = 0;
1368   examine.id_idx = -1;
1369   examine.id_width = 0;
1370   examine.id_var = NULL;
1371   examine.boxplot_mode = BP_GROUPS;
1372
1373   examine.ex_proto = caseproto_create ();
1374
1375   examine.pool = pool_create ();
1376
1377   /* Allocate space for the first interaction.
1378      This is interaction is an empty one (for the totals).
1379      If no totals are requested, we will simply ignore this
1380      interaction.
1381   */
1382   examine.n_iacts = 1;
1383   examine.iacts = iacts_mem = pool_zalloc (examine.pool, sizeof (struct interaction *));
1384   examine.iacts[0] = interaction_create (NULL);
1385
1386   examine.dep_excl = MV_ANY;
1387   examine.fctr_excl = MV_ANY;
1388   examine.plot = 0;
1389   examine.sl_power = 0;
1390   examine.dep_vars = NULL;
1391   examine.n_dep_vars = 0;
1392   examine.dict = dataset_dict (ds);
1393
1394   /* Accept an optional, completely pointless "/VARIABLES=" */
1395   lex_match (lexer, T_SLASH);
1396   if (lex_match_id  (lexer, "VARIABLES"))
1397     {
1398       if (! lex_force_match (lexer, T_EQUALS) )
1399         goto error;
1400     }
1401
1402   if (!parse_variables_const (lexer, examine.dict,
1403                               &examine.dep_vars, &examine.n_dep_vars,
1404                               PV_NO_DUPLICATE | PV_NUMERIC))
1405     goto error;
1406
1407   if (lex_match (lexer, T_BY))
1408     {
1409       struct interaction *iact = NULL;
1410       do
1411         {
1412           iact = parse_interaction (lexer, &examine);
1413           if (iact)
1414             {
1415               examine.n_iacts++;
1416               iacts_mem =
1417                 pool_nrealloc (examine.pool, iacts_mem,
1418                                examine.n_iacts,
1419                                sizeof (*iacts_mem));
1420
1421               iacts_mem[examine.n_iacts - 1] = iact;
1422             }
1423         }
1424       while (iact);
1425     }
1426
1427
1428   while (lex_token (lexer) != T_ENDCMD)
1429     {
1430       lex_match (lexer, T_SLASH);
1431
1432       if (lex_match_id (lexer, "STATISTICS"))
1433         {
1434           lex_match (lexer, T_EQUALS);
1435
1436           while (lex_token (lexer) != T_ENDCMD
1437                  && lex_token (lexer) != T_SLASH)
1438             {
1439               if (lex_match_id (lexer, "DESCRIPTIVES"))
1440                 {
1441                   examine.descriptives = true;
1442                 }
1443               else if (lex_match_id (lexer, "EXTREME"))
1444                 {
1445                   int extr = 5;
1446                   if (lex_match (lexer, T_LPAREN))
1447                     {
1448                       if (!lex_force_int (lexer))
1449                         goto error;
1450                       extr = lex_integer (lexer);
1451
1452                       if (extr < 0)
1453                         {
1454                           msg (MW, _("%s may not be negative. Using default value (%g)."), "EXTREME", 5.0);
1455                           extr = 5;
1456                         }
1457
1458                       lex_get (lexer);
1459                       if (! lex_force_match (lexer, T_RPAREN))
1460                         goto error;
1461                     }
1462                   examine.disp_extremes  = extr;
1463                 }
1464               else if (lex_match_id (lexer, "NONE"))
1465                 {
1466                 }
1467               else if (lex_match (lexer, T_ALL))
1468                 {
1469                   if (examine.disp_extremes == 0)
1470                     examine.disp_extremes = 5;
1471                 }
1472               else
1473                 {
1474                   lex_error (lexer, NULL);
1475                   goto error;
1476                 }
1477             }
1478         }
1479       else if (lex_match_id (lexer, "PERCENTILES"))
1480         {
1481           percentiles_seen = true;
1482           if (lex_match (lexer, T_LPAREN))
1483             {
1484               while (lex_is_number (lexer))
1485                 {
1486                   double p = lex_number (lexer);
1487
1488                   if ( p <= 0 || p >= 100.0)
1489                     {
1490                       lex_error (lexer,
1491                                  _("Percentiles must lie in the range (0, 100)"));
1492                       goto error;
1493                     }
1494
1495                   examine.n_percentiles++;
1496                   examine.ptiles =
1497                     xrealloc (examine.ptiles,
1498                               sizeof (*examine.ptiles) *
1499                               examine.n_percentiles);
1500
1501                   examine.ptiles[examine.n_percentiles - 1] = p;
1502
1503                   lex_get (lexer);
1504                   lex_match (lexer, T_COMMA);
1505                 }
1506               if (!lex_force_match (lexer, T_RPAREN))
1507                 goto error;
1508             }
1509
1510           lex_match (lexer, T_EQUALS);
1511
1512           while (lex_token (lexer) != T_ENDCMD
1513                  && lex_token (lexer) != T_SLASH)
1514             {
1515               if (lex_match_id (lexer, "HAVERAGE"))
1516                 {
1517                   examine.pc_alg = PC_HAVERAGE;
1518                 }
1519               else if (lex_match_id (lexer, "WAVERAGE"))
1520                 {
1521                   examine.pc_alg = PC_WAVERAGE;
1522                 }
1523               else if (lex_match_id (lexer, "ROUND"))
1524                 {
1525                   examine.pc_alg = PC_ROUND;
1526                 }
1527               else if (lex_match_id (lexer, "EMPIRICAL"))
1528                 {
1529                   examine.pc_alg = PC_EMPIRICAL;
1530                 }
1531               else if (lex_match_id (lexer, "AEMPIRICAL"))
1532                 {
1533                   examine.pc_alg = PC_AEMPIRICAL;
1534                 }
1535               else if (lex_match_id (lexer, "NONE"))
1536                 {
1537                   examine.pc_alg = PC_NONE;
1538                 }
1539               else
1540                 {
1541                   lex_error (lexer, NULL);
1542                   goto error;
1543                 }
1544             }
1545         }
1546       else if (lex_match_id (lexer, "TOTAL"))
1547         {
1548           totals_seen = true;
1549         }
1550       else if (lex_match_id (lexer, "NOTOTAL"))
1551         {
1552           nototals_seen = true;
1553         }
1554       else if (lex_match_id (lexer, "MISSING"))
1555         {
1556           lex_match (lexer, T_EQUALS);
1557
1558           while (lex_token (lexer) != T_ENDCMD
1559                  && lex_token (lexer) != T_SLASH)
1560             {
1561               if (lex_match_id (lexer, "LISTWISE"))
1562                 {
1563                   examine.missing_pw = false;
1564                 }
1565               else if (lex_match_id (lexer, "PAIRWISE"))
1566                 {
1567                   examine.missing_pw = true;
1568                 }
1569               else if (lex_match_id (lexer, "EXCLUDE"))
1570                 {
1571                   examine.dep_excl = MV_ANY;
1572                 }
1573               else if (lex_match_id (lexer, "INCLUDE"))
1574                 {
1575                   examine.dep_excl = MV_SYSTEM;
1576                 }
1577               else if (lex_match_id (lexer, "REPORT"))
1578                 {
1579                   examine.fctr_excl = MV_NEVER;
1580                 }
1581               else if (lex_match_id (lexer, "NOREPORT"))
1582                 {
1583                   examine.fctr_excl = MV_ANY;
1584                 }
1585               else
1586                 {
1587                   lex_error (lexer, NULL);
1588                   goto error;
1589                 }
1590             }
1591         }
1592       else if (lex_match_id (lexer, "COMPARE"))
1593         {
1594           lex_match (lexer, T_EQUALS);
1595           if (lex_match_id (lexer, "VARIABLES"))
1596             {
1597               examine.boxplot_mode = BP_VARIABLES;
1598             }
1599           else if (lex_match_id (lexer, "GROUPS"))
1600             {
1601               examine.boxplot_mode = BP_GROUPS;
1602             }
1603           else
1604             {
1605               lex_error (lexer, NULL);
1606               goto error;
1607             }
1608         }
1609       else if (lex_match_id (lexer, "PLOT"))
1610         {
1611           lex_match (lexer, T_EQUALS);
1612
1613           while (lex_token (lexer) != T_ENDCMD
1614                  && lex_token (lexer) != T_SLASH)
1615             {
1616               if (lex_match_id (lexer, "BOXPLOT"))
1617                 {
1618                   examine.plot |= PLOT_BOXPLOT;
1619                 }
1620               else if (lex_match_id (lexer, "NPPLOT"))
1621                 {
1622                   examine.plot |= PLOT_NPPLOT;
1623                 }
1624               else if (lex_match_id (lexer, "HISTOGRAM"))
1625                 {
1626                   examine.plot |= PLOT_HISTOGRAM;
1627                 }
1628               else if (lex_match_id (lexer, "SPREADLEVEL"))
1629                 {
1630                   examine.plot |= PLOT_SPREADLEVEL;
1631                   examine.sl_power = 0;
1632                   if (lex_match (lexer, T_LPAREN) && lex_force_int (lexer))
1633                     {
1634                       examine.sl_power = lex_integer (lexer);
1635
1636                       lex_get (lexer);
1637                       if (! lex_force_match (lexer, T_RPAREN))
1638                         goto error;
1639                     }
1640                 }
1641               else if (lex_match_id (lexer, "NONE"))
1642                 {
1643                   examine.plot = 0;
1644                 }
1645               else if (lex_match (lexer, T_ALL))
1646                 {
1647                   examine.plot = ~0;
1648                 }
1649               else
1650                 {
1651                   lex_error (lexer, NULL);
1652                   goto error;
1653                 }
1654               lex_match (lexer, T_COMMA);
1655             }
1656         }
1657       else if (lex_match_id (lexer, "CINTERVAL"))
1658         {
1659           if ( !lex_force_num (lexer))
1660             goto error;
1661
1662           examine.conf = lex_number (lexer);
1663           lex_get (lexer);
1664         }
1665       else if (lex_match_id (lexer, "ID"))
1666         {
1667           lex_match (lexer, T_EQUALS);
1668
1669           examine.id_var = parse_variable_const (lexer, examine.dict);
1670         }
1671       else
1672         {
1673           lex_error (lexer, NULL);
1674           goto error;
1675         }
1676     }
1677
1678
1679   if ( totals_seen && nototals_seen)
1680     {
1681       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
1682       goto error;
1683     }
1684
1685   /* If totals have been requested or if there are no factors
1686      in this analysis, then the totals need to be included. */
1687   if ( !nototals_seen || examine.n_iacts == 1)
1688     {
1689       examine.iacts = &iacts_mem[0];
1690     }
1691   else
1692     {
1693       examine.n_iacts--;
1694       examine.iacts = &iacts_mem[1];
1695       interaction_destroy (iacts_mem[0]);
1696     }
1697
1698
1699   if ( examine.id_var )
1700     {
1701       examine.id_idx = var_get_case_index (examine.id_var);
1702       examine.id_width = var_get_width (examine.id_var);
1703     }
1704
1705   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* value */
1706   examine.ex_proto = caseproto_add_width (examine.ex_proto, examine.id_width);   /* id */
1707   examine.ex_proto = caseproto_add_width (examine.ex_proto, 0); /* weight */
1708
1709
1710   if (examine.disp_extremes > 0)
1711     {
1712       examine.calc_extremes = examine.disp_extremes;
1713     }
1714
1715   if (examine.descriptives && examine.calc_extremes == 0)
1716     {
1717       /* Descriptives always displays the max and min */
1718       examine.calc_extremes = 1;
1719     }
1720
1721   if (percentiles_seen && examine.n_percentiles == 0)
1722     {
1723       examine.n_percentiles = 7;
1724       examine.ptiles = xcalloc (examine.n_percentiles,
1725                                 sizeof (*examine.ptiles));
1726
1727       examine.ptiles[0] = 5;
1728       examine.ptiles[1] = 10;
1729       examine.ptiles[2] = 25;
1730       examine.ptiles[3] = 50;
1731       examine.ptiles[4] = 75;
1732       examine.ptiles[5] = 90;
1733       examine.ptiles[6] = 95;
1734     }
1735
1736   assert (examine.calc_extremes >= examine.disp_extremes);
1737   {
1738     struct casegrouper *grouper;
1739     struct casereader *group;
1740     bool ok;
1741
1742     grouper = casegrouper_create_splits (proc_open (ds), examine.dict);
1743     while (casegrouper_get_next_group (grouper, &group))
1744       run_examine (&examine, group);
1745     ok = casegrouper_destroy (grouper);
1746     ok = proc_commit (ds) && ok;
1747   }
1748
1749   caseproto_unref (examine.ex_proto);
1750
1751   for (i = 0; i < examine.n_iacts; ++i)
1752     interaction_destroy (examine.iacts[i]);
1753   free (examine.ptiles);
1754   free (examine.dep_vars);
1755   pool_destroy (examine.pool);
1756
1757   return CMD_SUCCESS;
1758
1759  error:
1760   caseproto_unref (examine.ex_proto);
1761   examine.iacts = iacts_mem;
1762   for (i = 0; i < examine.n_iacts; ++i)
1763     interaction_destroy (examine.iacts[i]);
1764   free (examine.dep_vars);
1765   free (examine.ptiles);
1766   pool_destroy (examine.pool);
1767
1768   return CMD_FAILURE;
1769 }