e39dc8d3f74d200046d3c4e287ced638703e88c5
[pspp-builds.git] / src / language / stats / examine.q
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2008, 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <gsl/gsl_cdf.h>
20 #include <libpspp/message.h>
21 #include <math.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <math/sort.h>
26 #include <math/order-stats.h>
27 #include <math/percentiles.h>
28 #include <math/tukey-hinges.h>
29 #include <math/box-whisker.h>
30 #include <math/trimmed-mean.h>
31 #include <math/extrema.h>
32 #include <math/np.h>
33 #include <data/case.h>
34 #include <data/casegrouper.h>
35 #include <data/casereader.h>
36 #include <data/casewriter.h>
37 #include <data/dictionary.h>
38 #include <data/procedure.h>
39 #include <data/subcase.h>
40 #include <data/value-labels.h>
41 #include <data/variable.h>
42 #include <language/command.h>
43 #include <language/dictionary/split-file.h>
44 #include <language/lexer/lexer.h>
45 #include <libpspp/compiler.h>
46 #include <libpspp/hash.h>
47 #include <libpspp/message.h>
48 #include <libpspp/misc.h>
49 #include <libpspp/str.h>
50 #include <math/moments.h>
51 #include <output/chart-provider.h>
52 #include <output/charts/box-whisker.h>
53 #include <output/charts/cartesian.h>
54 #include <output/manager.h>
55 #include <output/table.h>
56
57 #include "minmax.h"
58 #include "xalloc.h"
59
60 #include "gettext.h"
61 #define _(msgid) gettext (msgid)
62 #define N_(msgid) msgid
63
64 /* (headers) */
65 #include <output/chart.h>
66 #include <output/charts/plot-hist.h>
67 #include <output/charts/plot-chart.h>
68 #include <math/histogram.h>
69
70 /* (specification)
71    "EXAMINE" (xmn_):
72    *^variables=custom;
73    +total=custom;
74    +nototal=custom;
75    missing=miss:pairwise/!listwise,
76    rep:report/!noreport,
77    incl:include/!exclude;
78    +compare=cmp:variables/!groups;
79    +percentiles=custom;
80    +id=var;
81    +plot[plt_]=stemleaf,boxplot,npplot,:spreadlevel(*d:n),histogram,all,none;
82    +cinterval=double;
83    +statistics[st_]=descriptives,:extreme(*d:n),all,none.
84 */
85
86 /* (declarations) */
87
88 /* (functions) */
89
90
91 static struct cmd_examine cmd;
92
93 static const struct variable **dependent_vars;
94 static size_t n_dependent_vars;
95
96 /* PERCENTILES */
97
98 static subc_list_double percentile_list;
99 static enum pc_alg percentile_algorithm;
100
101 struct factor_metrics
102 {
103   struct moments1 *moments;
104
105   struct percentile **ptl;
106   size_t n_ptiles;
107
108   struct statistic *tukey_hinges;
109   struct statistic *box_whisker;
110   struct statistic *trimmed_mean;
111   struct statistic *histogram;
112   struct order_stats *np;
113
114   /* Three quartiles indexing into PTL */
115   struct percentile **quartiles;
116
117   /* A reader sorted in ASCENDING order */
118   struct casereader *up_reader;
119
120   /* The minimum value of all the weights */
121   double cmin;
122
123   /* Sum of all weights, including those for missing values */
124   double n;
125
126   /* Sum of weights of non_missing values */
127   double n_valid;
128
129   double mean;
130
131   double variance;
132
133   double skewness;
134
135   double kurtosis;
136
137   double se_mean;
138
139   struct extrema *minima;
140   struct extrema *maxima;
141 };
142
143 struct factor_result
144 {
145   struct ll ll;
146
147   union value value[2];
148
149   /* An array of factor metrics, one for each variable */
150   struct factor_metrics *metrics;
151 };
152
153 struct xfactor
154 {
155   /* We need to make a list of this structure */
156   struct ll ll;
157
158   /* The independent variable */
159   const struct variable const* indep_var[2];
160
161   /* A list of results for this factor */
162   struct ll_list result_list ;
163 };
164
165
166 static void
167 factor_destroy (struct xfactor *fctr)
168 {
169   struct ll *ll = ll_head (&fctr->result_list);
170   while (ll != ll_null (&fctr->result_list))
171     {
172       int v;
173       struct factor_result *result =
174         ll_data (ll, struct factor_result, ll);
175       int i;
176
177       for (v = 0; v < n_dependent_vars; ++v)
178         {
179           int i;
180           moments1_destroy (result->metrics[v].moments);
181           extrema_destroy (result->metrics[v].minima);
182           extrema_destroy (result->metrics[v].maxima);
183           statistic_destroy (result->metrics[v].trimmed_mean);
184           statistic_destroy (result->metrics[v].tukey_hinges);
185           statistic_destroy (result->metrics[v].box_whisker);
186           statistic_destroy (result->metrics[v].histogram);
187           for (i = 0 ; i < result->metrics[v].n_ptiles; ++i)
188             statistic_destroy ((struct statistic *) result->metrics[v].ptl[i]);
189           free (result->metrics[v].ptl);
190           free (result->metrics[v].quartiles);
191           casereader_destroy (result->metrics[v].up_reader);
192         }
193
194       for (i = 0; i < 2; i++)
195         if (fctr->indep_var[i])
196           value_destroy (&result->value[i],
197                          var_get_width (fctr->indep_var[i]));
198       free (result->metrics);
199       ll = ll_next (ll);
200       free (result);
201     }
202 }
203
204 static struct xfactor level0_factor;
205 static struct ll_list factor_list;
206
207 /* Parse the clause specifying the factors */
208 static int examine_parse_independent_vars (struct lexer *lexer,
209                                            const struct dictionary *dict,
210                                            struct cmd_examine *cmd);
211
212 /* Output functions */
213 static void show_summary (const struct variable **dependent_var, int n_dep_var,
214                           const struct dictionary *dict,
215                           const struct xfactor *f);
216
217
218 static void show_descriptives (const struct variable **dependent_var,
219                                int n_dep_var,
220                                const struct xfactor *f);
221
222
223 static void show_percentiles (const struct variable **dependent_var,
224                                int n_dep_var,
225                                const struct xfactor *f);
226
227
228 static void show_extremes (const struct variable **dependent_var,
229                            int n_dep_var,
230                            const struct xfactor *f);
231
232
233
234
235 /* Per Split function */
236 static void run_examine (struct cmd_examine *, struct casereader *,
237                          struct dataset *);
238
239 static void output_examine (const struct dictionary *dict);
240
241
242 void factor_calc (const struct ccase *c, int case_no,
243                   double weight, bool case_missing);
244
245
246 /* Represent a factor as a string, so it can be
247    printed in a human readable fashion */
248 static void factor_to_string (const struct xfactor *fctr,
249                               const struct factor_result *result,
250                               struct string *str);
251
252 /* Represent a factor as a string, so it can be
253    printed in a human readable fashion,
254    but sacrificing some readablility for the sake of brevity */
255 static void
256 factor_to_string_concise (const struct xfactor *fctr,
257                           const struct factor_result *result,
258                           struct string *str
259                           );
260
261
262
263 /* Categories of missing values to exclude. */
264 static enum mv_class exclude_values;
265
266 int
267 cmd_examine (struct lexer *lexer, struct dataset *ds)
268 {
269   struct casegrouper *grouper;
270   struct casereader *group;
271   bool ok;
272
273   subc_list_double_create (&percentile_list);
274   percentile_algorithm = PC_HAVERAGE;
275
276   ll_init (&factor_list);
277
278   if ( !parse_examine (lexer, ds, &cmd, NULL) )
279     {
280       subc_list_double_destroy (&percentile_list);
281       return CMD_FAILURE;
282     }
283
284   /* If /MISSING=INCLUDE is set, then user missing values are ignored */
285   exclude_values = cmd.incl == XMN_INCLUDE ? MV_SYSTEM : MV_ANY;
286
287   if ( cmd.st_n == SYSMIS )
288     cmd.st_n = 5;
289
290   if ( ! cmd.sbc_cinterval)
291     cmd.n_cinterval[0] = 95.0;
292
293   /* If descriptives have been requested, make sure the
294      quartiles are calculated */
295   if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
296     {
297       subc_list_double_push (&percentile_list, 25);
298       subc_list_double_push (&percentile_list, 50);
299       subc_list_double_push (&percentile_list, 75);
300     }
301
302   grouper = casegrouper_create_splits (proc_open (ds), dataset_dict (ds));
303
304   while (casegrouper_get_next_group (grouper, &group))
305     {
306       struct casereader *reader =
307         casereader_create_arithmetic_sequence (group, 1, 1);
308
309       run_examine (&cmd, reader, ds);
310     }
311
312   ok = casegrouper_destroy (grouper);
313   ok = proc_commit (ds) && ok;
314
315   if ( dependent_vars )
316     free (dependent_vars);
317
318   subc_list_double_destroy (&percentile_list);
319
320   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
321 };
322
323
324 struct np_plot_chart
325   {
326     struct chart chart;
327     char *label;
328     struct casereader *data;
329
330     /* Copied directly from struct np. */
331     double y_min, y_max;
332     double dns_min, dns_max;
333
334     /* Calculated. */
335     double slope, intercept;
336     double y_first, y_last;
337     double x_lower, x_upper;
338     double slack;
339   };
340
341 static const struct chart_class np_plot_chart_class;
342 static const struct chart_class dnp_plot_chart_class;
343
344 /* Plot the normal and detrended normal plots for RESULT.
345    Label the plots with LABEL */
346 static void
347 np_plot (struct np *np, const char *label)
348 {
349   struct np_plot_chart *np_plot, *dnp_plot;
350
351   if ( np->n < 1.0 )
352     {
353       msg (MW, _("Not creating plot because data set is empty."));
354       return ;
355     }
356
357   np_plot = xmalloc (sizeof *np_plot);
358   chart_init (&np_plot->chart, &np_plot_chart_class);
359   np_plot->label = xstrdup (label);
360   np_plot->data = casewriter_make_reader (np->writer);
361   np_plot->y_min = np->y_min;
362   np_plot->y_max = np->y_max;
363   np_plot->dns_min = np->dns_min;
364   np_plot->dns_max = np->dns_max;
365
366   /* Slope and intercept of the ideal normal probability line. */
367   np_plot->slope = 1.0 / np->stddev;
368   np_plot->intercept = -np->mean / np->stddev;
369
370   np_plot->y_first = gsl_cdf_ugaussian_Pinv (1 / (np->n + 1));
371   np_plot->y_last = gsl_cdf_ugaussian_Pinv (np->n / (np->n + 1));
372
373   /* Need to make sure that both the scatter plot and the ideal fit into the
374      plot */
375   np_plot->x_lower = MIN (
376     np->y_min, (np_plot->y_first - np_plot->intercept) / np_plot->slope);
377   np_plot->x_upper = MAX (
378     np->y_max, (np_plot->y_last  - np_plot->intercept) / np_plot->slope) ;
379   np_plot->slack = (np_plot->x_upper - np_plot->x_lower) * 0.05 ;
380
381   dnp_plot = xmemdup (np_plot, sizeof *np_plot);
382   chart_init (&dnp_plot->chart, &dnp_plot_chart_class);
383   dnp_plot->label = xstrdup (dnp_plot->label);
384   dnp_plot->data = casereader_clone (dnp_plot->data);
385
386   chart_submit (&np_plot->chart);
387   chart_submit (&dnp_plot->chart);
388 }
389
390 static void
391 np_plot_chart_draw (const struct chart *chart, plPlotter *lp)
392 {
393   const struct np_plot_chart *plot = (struct np_plot_chart *) chart;
394   struct chart_geometry geom;
395   struct casereader *data;
396   struct ccase *c;
397
398   chart_geometry_init (lp, &geom);
399   chart_write_title (lp, &geom, _("Normal Q-Q Plot of %s"), plot->label);
400   chart_write_xlabel (lp, &geom, _("Observed Value"));
401   chart_write_ylabel (lp, &geom, _("Expected Normal"));
402   chart_write_xscale (lp, &geom,
403                       plot->x_lower - plot->slack,
404                       plot->x_upper + plot->slack, 5);
405   chart_write_yscale (lp, &geom, plot->y_first, plot->y_last, 5);
406
407   data = casereader_clone (plot->data);
408   for (; (c = casereader_read (data)) != NULL; case_unref (c))
409     chart_datum (lp, &geom, 0,
410                  case_data_idx (c, NP_IDX_Y)->f,
411                  case_data_idx (c, NP_IDX_NS)->f);
412   casereader_destroy (data);
413
414   chart_line (lp, &geom, plot->slope, plot->intercept,
415               plot->y_first, plot->y_last, CHART_DIM_Y);
416
417   chart_geometry_free (lp);
418 }
419
420 static void
421 dnp_plot_chart_draw (const struct chart *chart, plPlotter *lp)
422 {
423   const struct np_plot_chart *plot = (struct np_plot_chart *) chart;
424   struct chart_geometry geom;
425   struct casereader *data;
426   struct ccase *c;
427
428   chart_geometry_init (lp, &geom);
429   chart_write_title (lp, &geom, _("Detrended Normal Q-Q Plot of %s"),
430                      plot->label);
431   chart_write_xlabel (lp, &geom, _("Observed Value"));
432   chart_write_ylabel (lp, &geom, _("Dev from Normal"));
433   chart_write_xscale (lp, &geom, plot->y_min, plot->y_max, 5);
434   chart_write_yscale (lp, &geom, plot->dns_min, plot->dns_max, 5);
435
436   data = casereader_clone (plot->data);
437   for (; (c = casereader_read (data)) != NULL; case_unref (c))
438     chart_datum (lp, &geom, 0, case_data_idx (c, NP_IDX_Y)->f,
439                  case_data_idx (c, NP_IDX_DNS)->f);
440   casereader_destroy (data);
441
442   chart_line (lp, &geom, 0, 0, plot->y_min, plot->y_max, CHART_DIM_X);
443
444   chart_geometry_free (lp);
445 }
446
447 static void
448 np_plot_chart_destroy (struct chart *chart)
449 {
450   struct np_plot_chart *plot = (struct np_plot_chart *) chart;
451
452   casereader_destroy (plot->data);
453   free (plot->label);
454   free (plot);
455 }
456
457 static const struct chart_class np_plot_chart_class =
458   {
459     np_plot_chart_draw,
460     np_plot_chart_destroy
461   };
462
463 static const struct chart_class dnp_plot_chart_class =
464   {
465     dnp_plot_chart_draw,
466     np_plot_chart_destroy
467   };
468
469
470 static void
471 show_npplot (const struct variable **dependent_var,
472              int n_dep_var,
473              const struct xfactor *fctr)
474 {
475   int v;
476
477   for (v = 0; v < n_dep_var; ++v)
478     {
479       struct ll *ll;
480       for (ll = ll_head (&fctr->result_list);
481            ll != ll_null (&fctr->result_list);
482            ll = ll_next (ll))
483         {
484           struct string str;
485           const struct factor_result *result =
486             ll_data (ll, struct factor_result, ll);
487
488           ds_init_empty (&str);
489           ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
490
491           factor_to_string (fctr, result, &str);
492
493           np_plot ((struct np*) result->metrics[v].np, ds_cstr(&str));
494
495           statistic_destroy ((struct statistic *)result->metrics[v].np);
496
497           ds_destroy (&str);
498         }
499     }
500 }
501
502
503 static void
504 show_histogram (const struct variable **dependent_var,
505                 int n_dep_var,
506                 const struct xfactor *fctr)
507 {
508   int v;
509
510   for (v = 0; v < n_dep_var; ++v)
511     {
512       struct ll *ll;
513       for (ll = ll_head (&fctr->result_list);
514            ll != ll_null (&fctr->result_list);
515            ll = ll_next (ll))
516         {
517           struct string str;
518           const struct factor_result *result =
519             ll_data (ll, struct factor_result, ll);
520           struct histogram *histogram;
521           double mean, var, n;
522
523           histogram = (struct histogram *) result->metrics[v].histogram;
524           if (histogram == NULL)
525             {
526               /* Probably all values are SYSMIS. */
527               continue;
528             }
529
530           ds_init_empty (&str);
531           ds_put_format (&str, "%s ", var_get_name (dependent_var[v]));
532
533           factor_to_string (fctr, result, &str);
534
535           moments1_calculate ((struct moments1 *) result->metrics[v].moments,
536                               &n, &mean, &var, NULL,  NULL);
537           chart_submit (histogram_chart_create (histogram, ds_cstr (&str),
538                                                 n, mean, sqrt (var), false));
539
540           ds_destroy (&str);
541         }
542     }
543 }
544
545
546
547 static void
548 show_boxplot_groups (const struct variable **dependent_var,
549                      int n_dep_var,
550                      const struct xfactor *fctr)
551 {
552   int v;
553
554   for (v = 0; v < n_dep_var; ++v)
555     {
556       const struct factor_result *result;
557       struct boxplot *boxplot;
558       double y_min = DBL_MAX;
559       double y_max = -DBL_MAX;
560       char *title;
561
562       ll_for_each (result, struct factor_result, ll, &fctr->result_list)
563         {
564           struct factor_metrics *metrics = &result->metrics[v];
565           const struct ll_list *max_list = extrema_list (metrics->maxima);
566           const struct ll_list *min_list = extrema_list (metrics->minima);
567           const struct extremum  *max, *min;
568
569           if ( ll_is_empty (max_list))
570             {
571               msg (MW, _("Not creating plot because data set is empty."));
572               continue;
573             }
574
575           max = ll_data (ll_head(max_list), struct extremum, ll);
576           min = ll_data (ll_head (min_list), struct extremum, ll);
577
578           y_max = MAX (y_max, max->value);
579           y_min = MIN (y_min, min->value);
580         }
581
582       if (fctr->indep_var[0])
583         title = xasprintf (_("Boxplot of %s vs. %s"),
584                            var_to_string (dependent_var[v]),
585                            var_to_string (fctr->indep_var[0]));
586       else
587         title = xasprintf (_("Boxplot of %s"),
588                            var_to_string (dependent_var[v]));
589       boxplot = boxplot_create (y_min, y_max, title);
590       free (title);
591
592       ll_for_each (result, struct factor_result, ll, &fctr->result_list)
593         {
594           struct factor_metrics *metrics = &result->metrics[v];
595           struct string str = DS_EMPTY_INITIALIZER;
596           factor_to_string_concise (fctr, result, &str);
597           boxplot_add_box (boxplot,
598                            (struct box_whisker *) metrics->box_whisker,
599                            ds_cstr (&str));
600           metrics->box_whisker = NULL;
601           ds_destroy (&str);
602         }
603
604       chart_submit (boxplot_get_chart (boxplot));
605     }
606 }
607
608
609
610 static void
611 show_boxplot_variables (const struct variable **dependent_var,
612                         int n_dep_var,
613                         const struct xfactor *fctr
614                         )
615
616 {
617   const struct factor_result *result;
618   int v;
619
620   ll_for_each (result, struct factor_result, ll, &fctr->result_list)
621     {
622       struct string title;
623       double y_min = DBL_MAX;
624       double y_max = -DBL_MAX;
625       struct boxplot *boxplot;
626
627       for (v = 0; v < n_dep_var; ++v)
628         {
629           const struct factor_metrics *metrics = &result->metrics[v];
630           const struct ll *max_ll = ll_head (extrema_list (metrics->maxima));
631           const struct ll *min_ll = ll_head (extrema_list (metrics->minima));
632           const struct extremum *max = ll_data (max_ll, struct extremum, ll);
633           const struct extremum *min = ll_data (min_ll, struct extremum, ll);
634
635           y_max = MAX (y_max, max->value);
636           y_min = MIN (y_min, min->value);
637         }
638
639       ds_init_empty (&title);
640       factor_to_string (fctr, result, &title);
641       boxplot = boxplot_create (y_min, y_max, ds_cstr (&title));
642       ds_destroy (&title);
643
644       for (v = 0; v < n_dep_var; ++v)
645         {
646           struct factor_metrics *metrics = &result->metrics[v];
647           boxplot_add_box (boxplot,
648                            (struct box_whisker *) metrics->box_whisker,
649                            var_get_name (dependent_var[v]));
650           metrics->box_whisker = NULL;
651         }
652
653       chart_submit (boxplot_get_chart (boxplot));
654     }
655 }
656
657
658 /* Show all the appropriate tables */
659 static void
660 output_examine (const struct dictionary *dict)
661 {
662   struct ll *ll;
663
664   show_summary (dependent_vars, n_dependent_vars, dict, &level0_factor);
665
666   if ( cmd.a_statistics[XMN_ST_EXTREME] )
667     show_extremes (dependent_vars, n_dependent_vars, &level0_factor);
668
669   if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
670     show_descriptives (dependent_vars, n_dependent_vars, &level0_factor);
671
672   if ( cmd.sbc_percentiles)
673     show_percentiles (dependent_vars, n_dependent_vars, &level0_factor);
674
675   if ( cmd.sbc_plot)
676     {
677       if (cmd.a_plot[XMN_PLT_BOXPLOT])
678         show_boxplot_groups (dependent_vars, n_dependent_vars, &level0_factor);
679
680       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
681         show_histogram (dependent_vars, n_dependent_vars, &level0_factor);
682
683       if (cmd.a_plot[XMN_PLT_NPPLOT])
684         show_npplot (dependent_vars, n_dependent_vars, &level0_factor);
685     }
686
687   for (ll = ll_head (&factor_list);
688        ll != ll_null (&factor_list); ll = ll_next (ll))
689     {
690       struct xfactor *factor = ll_data (ll, struct xfactor, ll);
691       show_summary (dependent_vars, n_dependent_vars, dict, factor);
692
693       if ( cmd.a_statistics[XMN_ST_EXTREME] )
694         show_extremes (dependent_vars, n_dependent_vars, factor);
695
696       if ( cmd.a_statistics[XMN_ST_DESCRIPTIVES] )
697         show_descriptives (dependent_vars, n_dependent_vars, factor);
698
699       if ( cmd.sbc_percentiles)
700         show_percentiles (dependent_vars, n_dependent_vars, factor);
701
702       if (cmd.a_plot[XMN_PLT_BOXPLOT])
703         {
704           if (cmd.cmp == XMN_GROUPS)
705             show_boxplot_groups (dependent_vars, n_dependent_vars, factor);
706           else if (cmd.cmp == XMN_VARIABLES)
707             show_boxplot_variables (dependent_vars, n_dependent_vars, factor);
708         }
709       
710       if (cmd.a_plot[XMN_PLT_HISTOGRAM])
711         show_histogram (dependent_vars, n_dependent_vars, factor);
712
713       if (cmd.a_plot[XMN_PLT_NPPLOT])
714         show_npplot (dependent_vars, n_dependent_vars, factor);
715     }
716 }
717
718 /* Parse the PERCENTILES subcommand */
719 static int
720 xmn_custom_percentiles (struct lexer *lexer, struct dataset *ds UNUSED,
721                         struct cmd_examine *p UNUSED, void *aux UNUSED)
722 {
723   lex_match (lexer, '=');
724
725   lex_match (lexer, '(');
726
727   while ( lex_is_number (lexer) )
728     {
729       subc_list_double_push (&percentile_list, lex_number (lexer));
730
731       lex_get (lexer);
732
733       lex_match (lexer, ',') ;
734     }
735   lex_match (lexer, ')');
736
737   lex_match (lexer, '=');
738
739   if ( lex_match_id (lexer, "HAVERAGE"))
740     percentile_algorithm = PC_HAVERAGE;
741
742   else if ( lex_match_id (lexer, "WAVERAGE"))
743     percentile_algorithm = PC_WAVERAGE;
744
745   else if ( lex_match_id (lexer, "ROUND"))
746     percentile_algorithm = PC_ROUND;
747
748   else if ( lex_match_id (lexer, "EMPIRICAL"))
749     percentile_algorithm = PC_EMPIRICAL;
750
751   else if ( lex_match_id (lexer, "AEMPIRICAL"))
752     percentile_algorithm = PC_AEMPIRICAL;
753
754   else if ( lex_match_id (lexer, "NONE"))
755     percentile_algorithm = PC_NONE;
756
757
758   if ( 0 == subc_list_double_count (&percentile_list))
759     {
760       subc_list_double_push (&percentile_list, 5);
761       subc_list_double_push (&percentile_list, 10);
762       subc_list_double_push (&percentile_list, 25);
763       subc_list_double_push (&percentile_list, 50);
764       subc_list_double_push (&percentile_list, 75);
765       subc_list_double_push (&percentile_list, 90);
766       subc_list_double_push (&percentile_list, 95);
767     }
768
769   return 1;
770 }
771
772 /* TOTAL and NOTOTAL are simple, mutually exclusive flags */
773 static int
774 xmn_custom_total (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
775                   struct cmd_examine *p, void *aux UNUSED)
776 {
777   if ( p->sbc_nototal )
778     {
779       msg (SE, _("%s and %s are mutually exclusive"),"TOTAL","NOTOTAL");
780       return 0;
781     }
782
783   return 1;
784 }
785
786 static int
787 xmn_custom_nototal (struct lexer *lexer UNUSED, struct dataset *ds UNUSED,
788                     struct cmd_examine *p, void *aux UNUSED)
789 {
790   if ( p->sbc_total )
791     {
792       msg (SE, _("%s and %s are mutually exclusive"), "TOTAL", "NOTOTAL");
793       return 0;
794     }
795
796   return 1;
797 }
798
799
800
801 /* Parser for the variables sub command
802    Returns 1 on success */
803 static int
804 xmn_custom_variables (struct lexer *lexer, struct dataset *ds,
805                       struct cmd_examine *cmd,
806                       void *aux UNUSED)
807 {
808   const struct dictionary *dict = dataset_dict (ds);
809   lex_match (lexer, '=');
810
811   if ( (lex_token (lexer) != T_ID || dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
812        && lex_token (lexer) != T_ALL)
813     {
814       return 2;
815     }
816
817   if (!parse_variables_const (lexer, dict, &dependent_vars, &n_dependent_vars,
818                               PV_NO_DUPLICATE | PV_NUMERIC | PV_NO_SCRATCH) )
819     {
820       free (dependent_vars);
821       return 0;
822     }
823
824   assert (n_dependent_vars);
825
826
827   if ( lex_match (lexer, T_BY))
828     {
829       int success ;
830       success =  examine_parse_independent_vars (lexer, dict, cmd);
831       if ( success != 1 )
832         {
833           free (dependent_vars);
834         }
835       return success;
836     }
837
838   return 1;
839 }
840
841
842
843 /* Parse the clause specifying the factors */
844 static int
845 examine_parse_independent_vars (struct lexer *lexer,
846                                 const struct dictionary *dict,
847                                 struct cmd_examine *cmd)
848 {
849   int success;
850   struct xfactor *sf = xmalloc (sizeof *sf);
851
852   ll_init (&sf->result_list);
853
854   if ( (lex_token (lexer) != T_ID ||
855         dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
856        && lex_token (lexer) != T_ALL)
857     {
858       free ( sf ) ;
859       return 2;
860     }
861
862   sf->indep_var[0] = parse_variable (lexer, dict);
863   sf->indep_var[1] = NULL;
864
865   if ( lex_token (lexer) == T_BY )
866     {
867       lex_match (lexer, T_BY);
868
869       if ( (lex_token (lexer) != T_ID ||
870             dict_lookup_var (dict, lex_tokid (lexer)) == NULL)
871            && lex_token (lexer) != T_ALL)
872         {
873           free (sf);
874           return 2;
875         }
876
877       sf->indep_var[1] = parse_variable (lexer, dict);
878
879       ll_push_tail (&factor_list, &sf->ll);
880     }
881   else
882     ll_push_tail (&factor_list, &sf->ll);
883
884   lex_match (lexer, ',');
885
886   if ( lex_token (lexer) == '.' || lex_token (lexer) == '/' )
887     return 1;
888
889   success =  examine_parse_independent_vars (lexer, dict, cmd);
890
891   if ( success != 1 )
892     free ( sf ) ;
893
894   return success;
895 }
896
897 static void
898 examine_group (struct cmd_examine *cmd, struct casereader *reader, int level,
899                const struct dictionary *dict, struct xfactor *factor)
900 {
901   struct ccase *c;
902   const struct variable *wv = dict_get_weight (dict);
903   int v;
904   int n_extrema = 1;
905   struct factor_result *result = xzalloc (sizeof (*result));
906   int i;
907
908   for (i = 0; i < 2; i++)
909     if (factor->indep_var[i])
910       value_init (&result->value[i], var_get_width (factor->indep_var[i]));
911
912   result->metrics = xcalloc (n_dependent_vars, sizeof (*result->metrics));
913
914   if ( cmd->a_statistics[XMN_ST_EXTREME] )
915     n_extrema = cmd->st_n;
916
917
918   c = casereader_peek (reader, 0);
919   if (c != NULL)
920     {
921       if ( level > 0)
922         for (i = 0; i < 2; i++)
923           if (factor->indep_var[i])
924             value_copy (&result->value[i], case_data (c, factor->indep_var[i]),
925                         var_get_width (factor->indep_var[i]));
926       case_unref (c);
927     }
928
929   for (v = 0; v < n_dependent_vars; ++v)
930     {
931       struct casewriter *writer;
932       struct casereader *input = casereader_clone (reader);
933
934       result->metrics[v].moments = moments1_create (MOMENT_KURTOSIS);
935       result->metrics[v].minima = extrema_create (n_extrema, EXTREME_MINIMA);
936       result->metrics[v].maxima = extrema_create (n_extrema, EXTREME_MAXIMA);
937       result->metrics[v].cmin = DBL_MAX;
938
939       if (cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
940           cmd->a_plot[XMN_PLT_BOXPLOT] ||
941           cmd->a_plot[XMN_PLT_NPPLOT] ||
942           cmd->sbc_percentiles)
943         {
944           /* In this case, we need to sort the data, so we create a sorting
945              casewriter */
946           struct subcase up_ordering;
947           subcase_init_var (&up_ordering, dependent_vars[v], SC_ASCEND);
948           writer = sort_create_writer (&up_ordering,
949                                        casereader_get_proto (reader));
950           subcase_destroy (&up_ordering);
951         }
952       else
953         {
954           /* but in this case, sorting is unnecessary, so an ordinary
955              casewriter is sufficient */
956           writer =
957             autopaging_writer_create (casereader_get_proto (reader));
958         }
959
960
961       /* Sort or just iterate, whilst calculating moments etc */
962       while ((c = casereader_read (input)) != NULL)
963         {
964           int n_vals = caseproto_get_n_widths (casereader_get_proto (reader));
965           const casenumber loc = case_data_idx (c, n_vals - 1)->f;
966
967           const double weight = wv ? case_data (c, wv)->f : 1.0;
968           const union value *value = case_data (c, dependent_vars[v]);
969
970           if (weight != SYSMIS)
971             minimize (&result->metrics[v].cmin, weight);
972
973           moments1_add (result->metrics[v].moments,
974                         value->f,
975                         weight);
976
977           result->metrics[v].n += weight;
978
979           if ( ! var_is_value_missing (dependent_vars[v], value, MV_ANY) )
980             result->metrics[v].n_valid += weight;
981
982           extrema_add (result->metrics[v].maxima,
983                        value->f,
984                        weight,
985                        loc);
986
987           extrema_add (result->metrics[v].minima,
988                        value->f,
989                        weight,
990                        loc);
991
992           casewriter_write (writer, c);
993         }
994       casereader_destroy (input);
995       result->metrics[v].up_reader = casewriter_make_reader (writer);
996     }
997
998   /* If percentiles or descriptives have been requested, then a
999      second pass through the data (which has now been sorted)
1000      is necessary */
1001   if ( cmd->a_statistics[XMN_ST_DESCRIPTIVES] ||
1002        cmd->a_plot[XMN_PLT_BOXPLOT] ||
1003        cmd->a_plot[XMN_PLT_NPPLOT] ||
1004        cmd->sbc_percentiles)
1005     {
1006       for (v = 0; v < n_dependent_vars; ++v)
1007         {
1008           int i;
1009           int n_os;
1010           struct order_stats **os ;
1011           struct factor_metrics *metric = &result->metrics[v];
1012
1013           metric->n_ptiles = percentile_list.n_data;
1014
1015           metric->ptl = xcalloc (metric->n_ptiles,
1016                                  sizeof (struct percentile *));
1017
1018           metric->quartiles = xcalloc (3, sizeof (*metric->quartiles));
1019
1020           for (i = 0 ; i < metric->n_ptiles; ++i)
1021             {
1022               metric->ptl[i] = (struct percentile *)
1023                 percentile_create (percentile_list.data[i] / 100.0, metric->n_valid);
1024
1025               if ( percentile_list.data[i] == 25)
1026                 metric->quartiles[0] = metric->ptl[i];
1027               else if ( percentile_list.data[i] == 50)
1028                 metric->quartiles[1] = metric->ptl[i];
1029               else if ( percentile_list.data[i] == 75)
1030                 metric->quartiles[2] = metric->ptl[i];
1031             }
1032
1033           metric->tukey_hinges = tukey_hinges_create (metric->n_valid, metric->cmin);
1034           metric->trimmed_mean = trimmed_mean_create (metric->n_valid, 0.05);
1035
1036           n_os = metric->n_ptiles + 2;
1037
1038          if ( cmd->a_plot[XMN_PLT_NPPLOT] )
1039             {
1040               metric->np = np_create (metric->moments);
1041               n_os ++;
1042             }
1043
1044           os = xcalloc (sizeof (struct order_stats *), n_os);
1045
1046           for (i = 0 ; i < metric->n_ptiles ; ++i )
1047             {
1048               os[i] = (struct order_stats *) metric->ptl[i];
1049             }
1050
1051           os[i] = (struct order_stats *) metric->tukey_hinges;
1052           os[i+1] = (struct order_stats *) metric->trimmed_mean;
1053
1054           if (cmd->a_plot[XMN_PLT_NPPLOT])
1055             os[i+2] = metric->np;
1056
1057           order_stats_accumulate (os, n_os,
1058                                   casereader_clone (metric->up_reader),
1059                                   wv, dependent_vars[v], MV_ANY);
1060           free (os);
1061         }
1062     }
1063
1064   /* FIXME: Do this in the above loop */
1065   if ( cmd->a_plot[XMN_PLT_HISTOGRAM] )
1066     {
1067       struct ccase *c;
1068       struct casereader *input = casereader_clone (reader);
1069
1070       for (v = 0; v < n_dependent_vars; ++v)
1071         {
1072           const struct extremum  *max, *min;
1073           struct factor_metrics *metric = &result->metrics[v];
1074
1075           const struct ll_list *max_list =
1076             extrema_list (result->metrics[v].maxima);
1077
1078           const struct ll_list *min_list =
1079             extrema_list (result->metrics[v].minima);
1080
1081           if ( ll_is_empty (max_list))
1082             {
1083               msg (MW, _("Not creating plot because data set is empty."));
1084               continue;
1085             }
1086
1087           assert (! ll_is_empty (min_list));
1088
1089           max = (const struct extremum *)
1090             ll_data (ll_head(max_list), struct extremum, ll);
1091
1092           min = (const struct extremum *)
1093             ll_data (ll_head (min_list), struct extremum, ll);
1094
1095           metric->histogram = histogram_create (10, min->value, max->value);
1096         }
1097
1098       while ((c = casereader_read (input)) != NULL)
1099         {
1100           const double weight = wv ? case_data (c, wv)->f : 1.0;
1101
1102           for (v = 0; v < n_dependent_vars; ++v)
1103             {
1104               struct factor_metrics *metric = &result->metrics[v];
1105               if ( metric->histogram)
1106                 histogram_add ((struct histogram *) metric->histogram,
1107                                case_data (c, dependent_vars[v])->f, weight);
1108             }
1109           case_unref (c);
1110         }
1111       casereader_destroy (input);
1112     }
1113
1114   /* In this case, a third iteration is required */
1115   if (cmd->a_plot[XMN_PLT_BOXPLOT])
1116     {
1117       for (v = 0; v < n_dependent_vars; ++v)
1118         {
1119           struct factor_metrics *metric = &result->metrics[v];
1120           int n_vals = caseproto_get_n_widths (casereader_get_proto (
1121                                                  metric->up_reader));
1122
1123           metric->box_whisker =
1124             box_whisker_create ((struct tukey_hinges *) metric->tukey_hinges,
1125                                 cmd->v_id, n_vals - 1);
1126
1127           order_stats_accumulate ((struct order_stats **) &metric->box_whisker,
1128                                   1,
1129                                   casereader_clone (metric->up_reader),
1130                                   wv, dependent_vars[v], MV_ANY);
1131         }
1132     }
1133
1134   ll_push_tail (&factor->result_list, &result->ll);
1135   casereader_destroy (reader);
1136 }
1137
1138
1139 static void
1140 run_examine (struct cmd_examine *cmd, struct casereader *input,
1141              struct dataset *ds)
1142 {
1143   struct ll *ll;
1144   const struct dictionary *dict = dataset_dict (ds);
1145   struct ccase *c;
1146   struct casereader *level0 = casereader_clone (input);
1147
1148   c = casereader_peek (input, 0);
1149   if (c == NULL)
1150     {
1151       casereader_destroy (input);
1152       return;
1153     }
1154
1155   output_split_file_values (ds, c);
1156   case_unref (c);
1157
1158   ll_init (&level0_factor.result_list);
1159
1160   examine_group (cmd, level0, 0, dict, &level0_factor);
1161
1162   for (ll = ll_head (&factor_list);
1163        ll != ll_null (&factor_list);
1164        ll = ll_next (ll))
1165     {
1166       struct xfactor *factor = ll_data (ll, struct xfactor, ll);
1167
1168       struct casereader *group = NULL;
1169       struct casereader *level1;
1170       struct casegrouper *grouper1 = NULL;
1171
1172       level1 = casereader_clone (input);
1173       level1 = sort_execute_1var (level1, factor->indep_var[0]);
1174       grouper1 = casegrouper_create_vars (level1, &factor->indep_var[0], 1);
1175
1176       while (casegrouper_get_next_group (grouper1, &group))
1177         {
1178           struct casereader *group_copy = casereader_clone (group);
1179
1180           if ( !factor->indep_var[1])
1181             examine_group (cmd, group_copy, 1, dict, factor);
1182           else
1183             {
1184               int n_groups = 0;
1185               struct casereader *group2 = NULL;
1186               struct casegrouper *grouper2 = NULL;
1187
1188               group_copy = sort_execute_1var (group_copy,
1189                                               factor->indep_var[1]);
1190
1191               grouper2 = casegrouper_create_vars (group_copy,
1192                                                   &factor->indep_var[1], 1);
1193
1194               while (casegrouper_get_next_group (grouper2, &group2))
1195                 {
1196                   examine_group (cmd, group2, 2, dict, factor);
1197                   n_groups++;
1198                 }
1199               casegrouper_destroy (grouper2);
1200             }
1201
1202           casereader_destroy (group);
1203         }
1204       casegrouper_destroy (grouper1);
1205     }
1206
1207   casereader_destroy (input);
1208
1209   output_examine (dict);
1210
1211   factor_destroy (&level0_factor);
1212
1213   {
1214     struct ll *ll;
1215     for (ll = ll_head (&factor_list);
1216          ll != ll_null (&factor_list);
1217          ll = ll_next (ll))
1218       {
1219         struct xfactor *f = ll_data (ll, struct xfactor, ll);
1220         factor_destroy (f);
1221       }
1222   }
1223
1224 }
1225
1226
1227 static void
1228 show_summary (const struct variable **dependent_var, int n_dep_var,
1229               const struct dictionary *dict,
1230               const struct xfactor *fctr)
1231 {
1232   const struct variable *wv = dict_get_weight (dict);
1233   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
1234
1235   static const char *subtitle[]=
1236     {
1237       N_("Valid"),
1238       N_("Missing"),
1239       N_("Total")
1240     };
1241
1242   int v, j;
1243   int heading_columns = 1;
1244   int n_cols;
1245   const int heading_rows = 3;
1246   struct tab_table *tbl;
1247
1248   int n_rows ;
1249   n_rows = n_dep_var;
1250
1251   assert (fctr);
1252
1253   if ( fctr->indep_var[0] )
1254     {
1255       heading_columns = 2;
1256
1257       if ( fctr->indep_var[1] )
1258         {
1259           heading_columns = 3;
1260         }
1261     }
1262
1263   n_rows *= ll_count (&fctr->result_list);
1264   n_rows += heading_rows;
1265
1266   n_cols = heading_columns + 6;
1267
1268   tbl = tab_create (n_cols, n_rows, 0);
1269   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1270
1271   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
1272
1273   /* Outline the box */
1274   tab_box (tbl,
1275            TAL_2, TAL_2,
1276            -1, -1,
1277            0, 0,
1278            n_cols - 1, n_rows - 1);
1279
1280   /* Vertical lines for the data only */
1281   tab_box (tbl,
1282            -1, -1,
1283            -1, TAL_1,
1284            heading_columns, 0,
1285            n_cols - 1, n_rows - 1);
1286
1287
1288   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1289   tab_hline (tbl, TAL_1, heading_columns, n_cols - 1, 1 );
1290   tab_hline (tbl, TAL_1, heading_columns, n_cols - 1, heading_rows -1 );
1291
1292   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1293
1294
1295   tab_title (tbl, _("Case Processing Summary"));
1296
1297   tab_joint_text (tbl, heading_columns, 0,
1298                   n_cols -1, 0,
1299                   TAB_CENTER | TAT_TITLE,
1300                   _("Cases"));
1301
1302   /* Remove lines ... */
1303   tab_box (tbl,
1304            -1, -1,
1305            TAL_0, TAL_0,
1306            heading_columns, 0,
1307            n_cols - 1, 0);
1308
1309   for (j = 0 ; j < 3 ; ++j)
1310     {
1311       tab_text (tbl, heading_columns + j * 2 , 2, TAB_CENTER | TAT_TITLE,
1312                 _("N"));
1313
1314       tab_text (tbl, heading_columns + j * 2 + 1, 2, TAB_CENTER | TAT_TITLE,
1315                 _("Percent"));
1316
1317       tab_joint_text (tbl, heading_columns + j * 2 , 1,
1318                       heading_columns + j * 2 + 1, 1,
1319                       TAB_CENTER | TAT_TITLE,
1320                       subtitle[j]);
1321
1322       tab_box (tbl, -1, -1,
1323                TAL_0, TAL_0,
1324                heading_columns + j * 2, 1,
1325                heading_columns + j * 2 + 1, 1);
1326     }
1327
1328
1329   /* Titles for the independent variables */
1330   if ( fctr->indep_var[0] )
1331     {
1332       tab_text (tbl, 1, heading_rows - 1, TAB_CENTER | TAT_TITLE,
1333                 var_to_string (fctr->indep_var[0]));
1334
1335       if ( fctr->indep_var[1] )
1336         {
1337           tab_text (tbl, 2, heading_rows - 1, TAB_CENTER | TAT_TITLE,
1338                     var_to_string (fctr->indep_var[1]));
1339         }
1340     }
1341
1342   for (v = 0 ; v < n_dep_var ; ++v)
1343     {
1344       int j = 0;
1345       struct ll *ll;
1346       const union value *last_value = NULL;
1347
1348       if ( v > 0 )
1349         tab_hline (tbl, TAL_1, 0, n_cols -1 ,
1350                    v * ll_count (&fctr->result_list)
1351                    + heading_rows);
1352
1353       tab_text (tbl,
1354                 0,
1355                 v * ll_count (&fctr->result_list) + heading_rows,
1356                 TAB_LEFT | TAT_TITLE,
1357                 var_to_string (dependent_var[v])
1358                 );
1359
1360
1361       for (ll = ll_head (&fctr->result_list);
1362            ll != ll_null (&fctr->result_list); ll = ll_next (ll))
1363         {
1364           double n;
1365           const struct factor_result *result =
1366             ll_data (ll, struct factor_result, ll);
1367
1368           if ( fctr->indep_var[0] )
1369             {
1370
1371               if ( last_value == NULL ||
1372                    !value_equal (last_value, &result->value[0],
1373                                  var_get_width (fctr->indep_var[0])))
1374                 {
1375                   struct string str;
1376
1377                   last_value = &result->value[0];
1378                   ds_init_empty (&str);
1379
1380                   var_append_value_name (fctr->indep_var[0], &result->value[0],
1381                                          &str);
1382
1383                   tab_text (tbl, 1,
1384                             heading_rows + j +
1385                             v * ll_count (&fctr->result_list),
1386                             TAB_LEFT | TAT_TITLE,
1387                             ds_cstr (&str));
1388
1389                   ds_destroy (&str);
1390
1391                   if ( fctr->indep_var[1] && j > 0)
1392                     tab_hline (tbl, TAL_1, 1, n_cols - 1,
1393                                heading_rows + j +
1394                                v * ll_count (&fctr->result_list));
1395                 }
1396
1397               if ( fctr->indep_var[1])
1398                 {
1399                   struct string str;
1400
1401                   ds_init_empty (&str);
1402
1403                   var_append_value_name (fctr->indep_var[1],
1404                                          &result->value[1], &str);
1405
1406                   tab_text (tbl, 2,
1407                             heading_rows + j +
1408                             v * ll_count (&fctr->result_list),
1409                             TAB_LEFT | TAT_TITLE,
1410                             ds_cstr (&str));
1411
1412                   ds_destroy (&str);
1413                 }
1414             }
1415
1416
1417           moments1_calculate (result->metrics[v].moments,
1418                               &n, &result->metrics[v].mean,
1419                               &result->metrics[v].variance,
1420                               &result->metrics[v].skewness,
1421                               &result->metrics[v].kurtosis);
1422
1423           result->metrics[v].se_mean = sqrt (result->metrics[v].variance / n) ;
1424
1425           /* Total Valid */
1426           tab_double (tbl, heading_columns,
1427                      heading_rows + j + v * ll_count (&fctr->result_list),
1428                      TAB_LEFT,
1429                      n, wfmt);
1430
1431           tab_text (tbl, heading_columns + 1,
1432                     heading_rows + j + v * ll_count (&fctr->result_list),
1433                     TAB_RIGHT | TAT_PRINTF,
1434                     "%g%%", n * 100.0 / result->metrics[v].n);
1435
1436           /* Total Missing */
1437           tab_double (tbl, heading_columns + 2,
1438                      heading_rows + j + v * ll_count (&fctr->result_list),
1439                      TAB_LEFT,
1440                      result->metrics[v].n - n,
1441                      wfmt);
1442
1443           tab_text (tbl, heading_columns + 3,
1444                     heading_rows + j + v * ll_count (&fctr->result_list),
1445                     TAB_RIGHT | TAT_PRINTF,
1446                     "%g%%",
1447                     (result->metrics[v].n - n) * 100.0 / result->metrics[v].n
1448                     );
1449
1450           /* Total Valid + Missing */
1451           tab_double (tbl, heading_columns + 4,
1452                      heading_rows + j + v * ll_count (&fctr->result_list),
1453                      TAB_LEFT,
1454                      result->metrics[v].n,
1455                      wfmt);
1456
1457           tab_text (tbl, heading_columns + 5,
1458                     heading_rows + j + v * ll_count (&fctr->result_list),
1459                     TAB_RIGHT | TAT_PRINTF,
1460                     "%g%%",
1461                     (result->metrics[v].n) * 100.0 / result->metrics[v].n
1462                     );
1463
1464           ++j;
1465         }
1466     }
1467
1468
1469   tab_submit (tbl);
1470 }
1471
1472 #define DESCRIPTIVE_ROWS 13
1473
1474 static void
1475 show_descriptives (const struct variable **dependent_var,
1476                    int n_dep_var,
1477                    const struct xfactor *fctr)
1478 {
1479   int v;
1480   int heading_columns = 3;
1481   int n_cols;
1482   const int heading_rows = 1;
1483   struct tab_table *tbl;
1484
1485   int n_rows ;
1486   n_rows = n_dep_var;
1487
1488   assert (fctr);
1489
1490   if ( fctr->indep_var[0] )
1491     {
1492       heading_columns = 4;
1493
1494       if ( fctr->indep_var[1] )
1495         {
1496           heading_columns = 5;
1497         }
1498     }
1499
1500   n_rows *= ll_count (&fctr->result_list) * DESCRIPTIVE_ROWS;
1501   n_rows += heading_rows;
1502
1503   n_cols = heading_columns + 2;
1504
1505   tbl = tab_create (n_cols, n_rows, 0);
1506   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1507
1508   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
1509
1510   /* Outline the box */
1511   tab_box (tbl,
1512            TAL_2, TAL_2,
1513            -1, -1,
1514            0, 0,
1515            n_cols - 1, n_rows - 1);
1516
1517
1518   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1519   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
1520
1521   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
1522
1523
1524   if ( fctr->indep_var[0])
1525     tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
1526
1527   if ( fctr->indep_var[1])
1528     tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
1529
1530   for (v = 0 ; v < n_dep_var ; ++v )
1531     {
1532       struct ll *ll;
1533       int i = 0;
1534
1535       const int row_var_start =
1536         v * DESCRIPTIVE_ROWS * ll_count(&fctr->result_list);
1537
1538       tab_text (tbl,
1539                 0,
1540                 heading_rows + row_var_start,
1541                 TAB_LEFT | TAT_TITLE,
1542                 var_to_string (dependent_var[v])
1543                 );
1544
1545       for (ll = ll_head (&fctr->result_list);
1546            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
1547         {
1548           const struct factor_result *result =
1549             ll_data (ll, struct factor_result, ll);
1550
1551           const double t =
1552             gsl_cdf_tdist_Qinv ((1 - cmd.n_cinterval[0] / 100.0) / 2.0,
1553                                       result->metrics[v].n - 1);
1554
1555           if ( i > 0 || v > 0 )
1556             {
1557               const int left_col = (i == 0) ? 0 : 1;
1558               tab_hline (tbl, TAL_1, left_col, n_cols - 1,
1559                          heading_rows + row_var_start + i * DESCRIPTIVE_ROWS);
1560             }
1561
1562           if ( fctr->indep_var[0])
1563             {
1564               struct string vstr;
1565               ds_init_empty (&vstr);
1566               var_append_value_name (fctr->indep_var[0],
1567                                      &result->value[0], &vstr);
1568
1569               tab_text (tbl, 1,
1570                         heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1571                         TAB_LEFT,
1572                         ds_cstr (&vstr)
1573                         );
1574
1575               ds_destroy (&vstr);
1576             }
1577
1578
1579           tab_text (tbl, n_cols - 4,
1580                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1581                     TAB_LEFT,
1582                     _("Mean"));
1583
1584           tab_text (tbl, n_cols - 4,
1585                     heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1586                     TAB_LEFT | TAT_PRINTF,
1587                     _("%g%% Confidence Interval for Mean"),
1588                     cmd.n_cinterval[0]);
1589
1590           tab_text (tbl, n_cols - 3,
1591                     heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1592                     TAB_LEFT,
1593                     _("Lower Bound"));
1594
1595           tab_text (tbl, n_cols - 3,
1596                     heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
1597                     TAB_LEFT,
1598                     _("Upper Bound"));
1599
1600           tab_text (tbl, n_cols - 4,
1601                     heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
1602                     TAB_LEFT | TAT_PRINTF,
1603                     _("5%% Trimmed Mean"));
1604
1605           tab_text (tbl, n_cols - 4,
1606                     heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
1607                     TAB_LEFT,
1608                     _("Median"));
1609
1610           tab_text (tbl, n_cols - 4,
1611                     heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
1612                     TAB_LEFT,
1613                     _("Variance"));
1614
1615           tab_text (tbl, n_cols - 4,
1616                     heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
1617                     TAB_LEFT,
1618                     _("Std. Deviation"));
1619
1620           tab_text (tbl, n_cols - 4,
1621                     heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
1622                     TAB_LEFT,
1623                     _("Minimum"));
1624
1625           tab_text (tbl, n_cols - 4,
1626                     heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
1627                     TAB_LEFT,
1628                     _("Maximum"));
1629
1630           tab_text (tbl, n_cols - 4,
1631                     heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
1632                     TAB_LEFT,
1633                     _("Range"));
1634
1635           tab_text (tbl, n_cols - 4,
1636                     heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
1637                     TAB_LEFT,
1638                     _("Interquartile Range"));
1639
1640
1641           tab_text (tbl, n_cols - 4,
1642                     heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1643                     TAB_LEFT,
1644                     _("Skewness"));
1645
1646           tab_text (tbl, n_cols - 4,
1647                     heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1648                     TAB_LEFT,
1649                     _("Kurtosis"));
1650
1651
1652           /* Now the statistics ... */
1653
1654           tab_double (tbl, n_cols - 2,
1655                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1656                      TAB_CENTER,
1657                      result->metrics[v].mean,
1658                      NULL);
1659
1660           tab_double (tbl, n_cols - 1,
1661                     heading_rows + row_var_start + i * DESCRIPTIVE_ROWS,
1662                      TAB_CENTER,
1663                      result->metrics[v].se_mean,
1664                      NULL);
1665
1666
1667           tab_double (tbl, n_cols - 2,
1668                      heading_rows + row_var_start + 1 + i * DESCRIPTIVE_ROWS,
1669                      TAB_CENTER,
1670                      result->metrics[v].mean - t *
1671                       result->metrics[v].se_mean,
1672                      NULL);
1673
1674           tab_double (tbl, n_cols - 2,
1675                      heading_rows + row_var_start + 2 + i * DESCRIPTIVE_ROWS,
1676                      TAB_CENTER,
1677                      result->metrics[v].mean + t *
1678                       result->metrics[v].se_mean,
1679                      NULL);
1680
1681
1682           tab_double (tbl, n_cols - 2,
1683                      heading_rows + row_var_start + 3 + i * DESCRIPTIVE_ROWS,
1684                      TAB_CENTER,
1685                      trimmed_mean_calculate ((struct trimmed_mean *) result->metrics[v].trimmed_mean),
1686                      NULL);
1687
1688
1689           tab_double (tbl, n_cols - 2,
1690                      heading_rows + row_var_start + 4 + i * DESCRIPTIVE_ROWS,
1691                      TAB_CENTER,
1692                      percentile_calculate (result->metrics[v].quartiles[1], percentile_algorithm),
1693                      NULL);
1694
1695
1696           tab_double (tbl, n_cols - 2,
1697                      heading_rows + row_var_start + 5 + i * DESCRIPTIVE_ROWS,
1698                      TAB_CENTER,
1699                      result->metrics[v].variance,
1700                      NULL);
1701
1702           tab_double (tbl, n_cols - 2,
1703                      heading_rows + row_var_start + 6 + i * DESCRIPTIVE_ROWS,
1704                      TAB_CENTER,
1705                      sqrt (result->metrics[v].variance),
1706                      NULL);
1707
1708           tab_double (tbl, n_cols - 2,
1709                      heading_rows + row_var_start + 10 + i * DESCRIPTIVE_ROWS,
1710                      TAB_CENTER,
1711                      percentile_calculate (result->metrics[v].quartiles[2],
1712                                            percentile_algorithm) -
1713                      percentile_calculate (result->metrics[v].quartiles[0],
1714                                            percentile_algorithm),
1715                      NULL);
1716
1717
1718           tab_double (tbl, n_cols - 2,
1719                      heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1720                      TAB_CENTER,
1721                      result->metrics[v].skewness,
1722                      NULL);
1723
1724           tab_double (tbl, n_cols - 2,
1725                      heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1726                      TAB_CENTER,
1727                      result->metrics[v].kurtosis,
1728                      NULL);
1729
1730           tab_double (tbl, n_cols - 1,
1731                      heading_rows + row_var_start + 11 + i * DESCRIPTIVE_ROWS,
1732                      TAB_CENTER,
1733                      calc_seskew (result->metrics[v].n),
1734                      NULL);
1735
1736           tab_double (tbl, n_cols - 1,
1737                      heading_rows + row_var_start + 12 + i * DESCRIPTIVE_ROWS,
1738                      TAB_CENTER,
1739                      calc_sekurt (result->metrics[v].n),
1740                      NULL);
1741
1742           {
1743             struct extremum *minimum, *maximum ;
1744
1745             struct ll *max_ll = ll_head (extrema_list (result->metrics[v].maxima));
1746             struct ll *min_ll = ll_head (extrema_list (result->metrics[v].minima));
1747
1748             maximum = ll_data (max_ll, struct extremum, ll);
1749             minimum = ll_data (min_ll, struct extremum, ll);
1750
1751             tab_double (tbl, n_cols - 2,
1752                        heading_rows + row_var_start + 7 + i * DESCRIPTIVE_ROWS,
1753                        TAB_CENTER,
1754                        minimum->value,
1755                        NULL);
1756
1757             tab_double (tbl, n_cols - 2,
1758                        heading_rows + row_var_start + 8 + i * DESCRIPTIVE_ROWS,
1759                        TAB_CENTER,
1760                        maximum->value,
1761                        NULL);
1762
1763             tab_double (tbl, n_cols - 2,
1764                        heading_rows + row_var_start + 9 + i * DESCRIPTIVE_ROWS,
1765                        TAB_CENTER,
1766                        maximum->value - minimum->value,
1767                        NULL);
1768           }
1769         }
1770     }
1771
1772   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1773
1774   tab_title (tbl, _("Descriptives"));
1775
1776   tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
1777             _("Statistic"));
1778
1779   tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
1780             _("Std. Error"));
1781
1782   tab_submit (tbl);
1783 }
1784
1785
1786
1787 static void
1788 show_extremes (const struct variable **dependent_var,
1789                int n_dep_var,
1790                const struct xfactor *fctr)
1791 {
1792   int v;
1793   int heading_columns = 3;
1794   int n_cols;
1795   const int heading_rows = 1;
1796   struct tab_table *tbl;
1797
1798   int n_rows ;
1799   n_rows = n_dep_var;
1800
1801   assert (fctr);
1802
1803   if ( fctr->indep_var[0] )
1804     {
1805       heading_columns = 4;
1806
1807       if ( fctr->indep_var[1] )
1808         {
1809           heading_columns = 5;
1810         }
1811     }
1812
1813   n_rows *= ll_count (&fctr->result_list) * cmd.st_n * 2;
1814   n_rows += heading_rows;
1815
1816   n_cols = heading_columns + 2;
1817
1818   tbl = tab_create (n_cols, n_rows, 0);
1819   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
1820
1821   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
1822
1823   /* Outline the box */
1824   tab_box (tbl,
1825            TAL_2, TAL_2,
1826            -1, -1,
1827            0, 0,
1828            n_cols - 1, n_rows - 1);
1829
1830
1831   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
1832   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
1833   tab_vline (tbl, TAL_1, n_cols - 1, 0, n_rows - 1);
1834
1835   if ( fctr->indep_var[0])
1836     tab_text (tbl, 1, 0, TAT_TITLE, var_to_string (fctr->indep_var[0]));
1837
1838   if ( fctr->indep_var[1])
1839     tab_text (tbl, 2, 0, TAT_TITLE, var_to_string (fctr->indep_var[1]));
1840
1841   for (v = 0 ; v < n_dep_var ; ++v )
1842     {
1843       struct ll *ll;
1844       int i = 0;
1845       const int row_var_start = v * cmd.st_n * 2 * ll_count(&fctr->result_list);
1846
1847       tab_text (tbl,
1848                 0,
1849                 heading_rows + row_var_start,
1850                 TAB_LEFT | TAT_TITLE,
1851                 var_to_string (dependent_var[v])
1852                 );
1853
1854       for (ll = ll_head (&fctr->result_list);
1855            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
1856         {
1857           int e ;
1858           struct ll *min_ll;
1859           struct ll *max_ll;
1860           const int row_result_start = i * cmd.st_n * 2;
1861
1862           const struct factor_result *result =
1863             ll_data (ll, struct factor_result, ll);
1864
1865           if (i > 0 || v > 0)
1866             tab_hline (tbl, TAL_1, 1, n_cols - 1,
1867                        heading_rows + row_var_start + row_result_start);
1868
1869           tab_hline (tbl, TAL_1, heading_columns - 2, n_cols - 1,
1870                      heading_rows + row_var_start + row_result_start + cmd.st_n);
1871
1872           for ( e = 1; e <= cmd.st_n; ++e )
1873             {
1874               tab_text (tbl, n_cols - 3,
1875                         heading_rows + row_var_start + row_result_start + e - 1,
1876                         TAB_RIGHT | TAT_PRINTF,
1877                         _("%d"), e);
1878
1879               tab_text (tbl, n_cols - 3,
1880                         heading_rows + row_var_start + row_result_start + cmd.st_n + e - 1,
1881                         TAB_RIGHT | TAT_PRINTF,
1882                         _("%d"), e);
1883             }
1884
1885
1886           min_ll = ll_head (extrema_list (result->metrics[v].minima));
1887           for (e = 0; e < cmd.st_n;)
1888             {
1889               struct extremum *minimum = ll_data (min_ll, struct extremum, ll);
1890               double weight = minimum->weight;
1891
1892               while (weight-- > 0 && e < cmd.st_n)
1893                 {
1894                   tab_double (tbl, n_cols - 1,
1895                              heading_rows + row_var_start + row_result_start + cmd.st_n + e,
1896                              TAB_RIGHT,
1897                              minimum->value,
1898                              NULL);
1899
1900
1901                   tab_fixed (tbl, n_cols - 2,
1902                              heading_rows + row_var_start +
1903                              row_result_start + cmd.st_n + e,
1904                              TAB_RIGHT,
1905                              minimum->location,
1906                              10, 0);
1907                   ++e;
1908                 }
1909
1910               min_ll = ll_next (min_ll);
1911             }
1912
1913
1914           max_ll = ll_head (extrema_list (result->metrics[v].maxima));
1915           for (e = 0; e < cmd.st_n;)
1916             {
1917               struct extremum *maximum = ll_data (max_ll, struct extremum, ll);
1918               double weight = maximum->weight;
1919
1920               while (weight-- > 0 && e < cmd.st_n)
1921                 {
1922                   tab_double (tbl, n_cols - 1,
1923                              heading_rows + row_var_start +
1924                               row_result_start + e,
1925                              TAB_RIGHT,
1926                              maximum->value,
1927                              NULL);
1928
1929
1930                   tab_fixed (tbl, n_cols - 2,
1931                              heading_rows + row_var_start +
1932                              row_result_start + e,
1933                              TAB_RIGHT,
1934                              maximum->location,
1935                              10, 0);
1936                   ++e;
1937                 }
1938
1939               max_ll = ll_next (max_ll);
1940             }
1941
1942
1943           if ( fctr->indep_var[0])
1944             {
1945               struct string vstr;
1946               ds_init_empty (&vstr);
1947               var_append_value_name (fctr->indep_var[0],
1948                                      &result->value[0], &vstr);
1949
1950               tab_text (tbl, 1,
1951                         heading_rows + row_var_start + row_result_start,
1952                         TAB_LEFT,
1953                         ds_cstr (&vstr)
1954                         );
1955
1956               ds_destroy (&vstr);
1957             }
1958
1959
1960           tab_text (tbl, n_cols - 4,
1961                     heading_rows + row_var_start + row_result_start,
1962                     TAB_RIGHT,
1963                     _("Highest"));
1964
1965           tab_text (tbl, n_cols - 4,
1966                     heading_rows + row_var_start + row_result_start + cmd.st_n,
1967                     TAB_RIGHT,
1968                     _("Lowest"));
1969         }
1970     }
1971
1972   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
1973
1974
1975   tab_title (tbl, _("Extreme Values"));
1976
1977
1978   tab_text (tbl, n_cols - 2, 0, TAB_CENTER | TAT_TITLE,
1979             _("Case Number"));
1980
1981
1982   tab_text (tbl, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
1983             _("Value"));
1984
1985   tab_submit (tbl);
1986 }
1987
1988 #define PERCENTILE_ROWS 2
1989
1990 static void
1991 show_percentiles (const struct variable **dependent_var,
1992                   int n_dep_var,
1993                   const struct xfactor *fctr)
1994 {
1995   int i;
1996   int v;
1997   int heading_columns = 2;
1998   int n_cols;
1999   const int n_percentiles = subc_list_double_count (&percentile_list);
2000   const int heading_rows = 2;
2001   struct tab_table *tbl;
2002
2003   int n_rows ;
2004   n_rows = n_dep_var;
2005
2006   assert (fctr);
2007
2008   if ( fctr->indep_var[0] )
2009     {
2010       heading_columns = 3;
2011
2012       if ( fctr->indep_var[1] )
2013         {
2014           heading_columns = 4;
2015         }
2016     }
2017
2018   n_rows *= ll_count (&fctr->result_list) * PERCENTILE_ROWS;
2019   n_rows += heading_rows;
2020
2021   n_cols = heading_columns + n_percentiles;
2022
2023   tbl = tab_create (n_cols, n_rows, 0);
2024   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
2025
2026   tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
2027
2028   /* Outline the box */
2029   tab_box (tbl,
2030            TAL_2, TAL_2,
2031            -1, -1,
2032            0, 0,
2033            n_cols - 1, n_rows - 1);
2034
2035
2036   tab_hline (tbl, TAL_2, 0, n_cols - 1, heading_rows );
2037   tab_hline (tbl, TAL_2, 1, n_cols - 1, heading_rows );
2038
2039   if ( fctr->indep_var[0])
2040     tab_text (tbl, 1, 1, TAT_TITLE, var_to_string (fctr->indep_var[0]));
2041
2042   if ( fctr->indep_var[1])
2043     tab_text (tbl, 2, 1, TAT_TITLE, var_to_string (fctr->indep_var[1]));
2044
2045   for (v = 0 ; v < n_dep_var ; ++v )
2046     {
2047       double hinges[3];
2048       struct ll *ll;
2049       int i = 0;
2050
2051       const int row_var_start =
2052         v * PERCENTILE_ROWS * ll_count(&fctr->result_list);
2053
2054       tab_text (tbl,
2055                 0,
2056                 heading_rows + row_var_start,
2057                 TAB_LEFT | TAT_TITLE,
2058                 var_to_string (dependent_var[v])
2059                 );
2060
2061       for (ll = ll_head (&fctr->result_list);
2062            ll != ll_null (&fctr->result_list); i++, ll = ll_next (ll))
2063         {
2064           int j;
2065           const struct factor_result *result =
2066             ll_data (ll, struct factor_result, ll);
2067
2068           if ( i > 0 || v > 0 )
2069             {
2070               const int left_col = (i == 0) ? 0 : 1;
2071               tab_hline (tbl, TAL_1, left_col, n_cols - 1,
2072                          heading_rows + row_var_start + i * PERCENTILE_ROWS);
2073             }
2074
2075           if ( fctr->indep_var[0])
2076             {
2077               struct string vstr;
2078               ds_init_empty (&vstr);
2079               var_append_value_name (fctr->indep_var[0],
2080                                      &result->value[0], &vstr);
2081
2082               tab_text (tbl, 1,
2083                         heading_rows + row_var_start + i * PERCENTILE_ROWS,
2084                         TAB_LEFT,
2085                         ds_cstr (&vstr)
2086                         );
2087
2088               ds_destroy (&vstr);
2089             }
2090
2091
2092           tab_text (tbl, n_cols - n_percentiles - 1,
2093                     heading_rows + row_var_start + i * PERCENTILE_ROWS,
2094                     TAB_LEFT,
2095                     ptile_alg_desc [percentile_algorithm]);
2096
2097
2098           tab_text (tbl, n_cols - n_percentiles - 1,
2099                     heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
2100                     TAB_LEFT,
2101                     _("Tukey's Hinges"));
2102
2103
2104           tab_vline (tbl, TAL_1, n_cols - n_percentiles -1, heading_rows, n_rows - 1);
2105
2106           tukey_hinges_calculate ((struct tukey_hinges *) result->metrics[v].tukey_hinges,
2107                                   hinges);
2108
2109           for (j = 0; j < n_percentiles; ++j)
2110             {
2111               double hinge = SYSMIS;
2112               tab_double (tbl, n_cols - n_percentiles + j,
2113                          heading_rows + row_var_start + i * PERCENTILE_ROWS,
2114                          TAB_CENTER,
2115                          percentile_calculate (result->metrics[v].ptl[j],
2116                                                percentile_algorithm),
2117                          NULL
2118                          );
2119
2120               if ( result->metrics[v].ptl[j]->ptile == 0.5)
2121                 hinge = hinges[1];
2122               else if ( result->metrics[v].ptl[j]->ptile == 0.25)
2123                 hinge = hinges[0];
2124               else if ( result->metrics[v].ptl[j]->ptile == 0.75)
2125                 hinge = hinges[2];
2126
2127               if ( hinge != SYSMIS)
2128                 tab_double (tbl, n_cols - n_percentiles + j,
2129                            heading_rows + row_var_start + 1 + i * PERCENTILE_ROWS,
2130                            TAB_CENTER,
2131                            hinge,
2132                            NULL
2133                            );
2134
2135             }
2136         }
2137     }
2138
2139   tab_vline (tbl, TAL_2, heading_columns, 0, n_rows - 1);
2140
2141   tab_title (tbl, _("Percentiles"));
2142
2143
2144   for (i = 0 ; i < n_percentiles; ++i )
2145     {
2146       tab_text (tbl, n_cols - n_percentiles + i, 1,
2147                 TAB_CENTER | TAT_TITLE | TAT_PRINTF,
2148                 _("%g"),
2149                 subc_list_double_at (&percentile_list, i)
2150                 );
2151
2152
2153     }
2154
2155   tab_joint_text (tbl,
2156                   n_cols - n_percentiles, 0,
2157                   n_cols - 1, 0,
2158                   TAB_CENTER | TAT_TITLE,
2159                   _("Percentiles"));
2160
2161   /* Vertical lines for the data only */
2162   tab_box (tbl,
2163            -1, -1,
2164            -1, TAL_1,
2165            n_cols - n_percentiles, 1,
2166            n_cols - 1, n_rows - 1);
2167
2168   tab_hline (tbl, TAL_1, n_cols - n_percentiles, n_cols - 1, 1);
2169
2170
2171   tab_submit (tbl);
2172 }
2173
2174
2175 static void
2176 factor_to_string_concise (const struct xfactor *fctr,
2177                           const struct factor_result *result,
2178                           struct string *str
2179                           )
2180 {
2181   if (fctr->indep_var[0])
2182     {
2183       var_append_value_name (fctr->indep_var[0], &result->value[0], str);
2184
2185       if ( fctr->indep_var[1] )
2186         {
2187           ds_put_cstr (str, ",");
2188
2189           var_append_value_name (fctr->indep_var[1], &result->value[1], str);
2190
2191           ds_put_cstr (str, ")");
2192         }
2193     }
2194 }
2195
2196
2197 static void
2198 factor_to_string (const struct xfactor *fctr,
2199                   const struct factor_result *result,
2200                   struct string *str
2201                   )
2202 {
2203   if (fctr->indep_var[0])
2204     {
2205       ds_put_format (str, "(%s = ", var_get_name (fctr->indep_var[0]));
2206
2207       var_append_value_name (fctr->indep_var[0], &result->value[0], str);
2208
2209       if ( fctr->indep_var[1] )
2210         {
2211           ds_put_cstr (str, ",");
2212           ds_put_format (str, "%s = ", var_get_name (fctr->indep_var[1]));
2213
2214           var_append_value_name (fctr->indep_var[1], &result->value[1], str);
2215         }
2216       ds_put_cstr (str, ")");
2217     }
2218 }
2219
2220
2221
2222
2223 /*
2224   Local Variables:
2225   mode: c
2226   End:
2227 */