1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 /* FIXME: Many possible optimizations. */
28 #include <data/case.h>
29 #include <data/casefile.h>
30 #include <data/dictionary.h>
31 #include <data/procedure.h>
32 #include <data/transformations.h>
33 #include <data/variable.h>
34 #include <language/command.h>
35 #include <language/dictionary/split-file.h>
36 #include <language/lexer/lexer.h>
37 #include <language/lexer/variable-parser.h>
38 #include <libpspp/alloc.h>
39 #include <libpspp/array.h>
40 #include <libpspp/compiler.h>
41 #include <libpspp/magic.h>
42 #include <libpspp/message.h>
43 #include <math/moments.h>
44 #include <output/manager.h>
45 #include <output/table.h>
48 #define _(msgid) gettext (msgid)
49 #define N_(msgid) msgid
51 /* DESCRIPTIVES private data. */
55 /* Handling of missing values. */
58 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
59 DSC_LISTWISE /* Discard entire case if any variable is missing. */
62 /* Describes properties of a distribution for the purpose of
63 calculating a Z-score. */
66 int src_idx; /* Source index into case data. */
67 int dst_idx; /* Destination index into case data. */
68 double mean; /* Distribution mean. */
69 double std_dev; /* Distribution standard deviation. */
70 struct variable *v; /* Variable on which z-score is based. */
73 /* DESCRIPTIVES transformation (for calculating Z-scores). */
76 struct dsc_z_score *z_scores; /* Array of Z-scores. */
77 int z_score_cnt; /* Number of Z-scores. */
78 struct variable **vars; /* Variables for listwise missing checks. */
79 size_t var_cnt; /* Number of variables. */
80 enum dsc_missing_type missing_type; /* Treatment of missing values. */
81 int include_user_missing; /* Nonzero to include user-missing values. */
84 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
87 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
88 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
89 DSC_MAX, DSC_SUM, DSC_N_STATS,
91 /* Only valid as sort criteria. */
92 DSC_NAME = -2, /* Sort by name. */
93 DSC_NONE = -1 /* Unsorted. */
96 /* Describes one statistic. */
97 struct dsc_statistic_info
99 const char *identifier; /* Identifier. */
100 const char *name; /* Full name. */
101 enum moment moment; /* Highest moment needed to calculate. */
104 /* Table of statistics, indexed by DSC_*. */
105 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
107 {"MEAN", N_("Mean"), MOMENT_MEAN},
108 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
109 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
110 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
111 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
112 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
113 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
114 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
115 {"RANGE", N_("Range"), MOMENT_NONE},
116 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
117 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
118 {"SUM", N_("Sum"), MOMENT_MEAN},
121 /* Statistics calculated by default if none are explicitly
123 #define DEFAULT_STATS \
124 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
127 /* A variable specified on DESCRIPTIVES. */
130 struct variable *v; /* Variable to calculate on. */
131 char z_name[LONG_NAME_LEN + 1]; /* Name for z-score variable. */
132 double valid, missing; /* Valid, missing counts. */
133 struct moments *moments; /* Moments. */
134 double min, max; /* Maximum and mimimum values. */
135 double stats[DSC_N_STATS]; /* All the stats' values. */
141 DSC_LINE, /* Abbreviated format. */
142 DSC_SERIAL /* Long format. */
145 /* A DESCRIPTIVES procedure. */
148 /* Per-variable info. */
149 struct dsc_var *vars; /* Variables. */
150 size_t var_cnt; /* Number of variables. */
153 enum dsc_missing_type missing_type; /* Treatment of missing values. */
154 int include_user_missing; /* Nonzero to include user-missing values. */
155 int show_var_labels; /* Nonzero to show variable labels. */
156 int show_index; /* Nonzero to show variable index. */
157 enum dsc_format format; /* Output format. */
159 /* Accumulated results. */
160 double missing_listwise; /* Sum of weights of cases missing listwise. */
161 double valid; /* Sum of weights of valid cases. */
162 int bad_warn; /* Warn if bad weight found. */
163 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
164 int sort_ascending; /* !0: ascending order; 0: descending. */
165 unsigned long show_stats; /* Statistics to display. */
166 unsigned long calc_stats; /* Statistics to calculate. */
167 enum moment max_moment; /* Highest moment needed for stats. */
171 static enum dsc_statistic match_statistic (void);
172 static void free_dsc_proc (struct dsc_proc *);
174 /* Z-score functions. */
175 static int try_name (struct dsc_proc *dsc, char *name);
176 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
177 const char *name, size_t *z_cnt);
178 static void dump_z_table (struct dsc_proc *);
179 static void setup_z_trns (struct dsc_proc *);
181 /* Procedure execution functions. */
182 static bool calc_descriptives (const struct ccase *first,
183 const struct casefile *, void *dsc_);
184 static void display (struct dsc_proc *dsc);
186 /* Parser and outline. */
188 /* Handles DESCRIPTIVES. */
190 cmd_descriptives (void)
192 struct dsc_proc *dsc;
193 struct variable **vars = NULL;
195 int save_z_scores = 0;
200 /* Create and initialize dsc. */
201 dsc = xmalloc (sizeof *dsc);
204 dsc->missing_type = DSC_VARIABLE;
205 dsc->include_user_missing = 0;
206 dsc->show_var_labels = 1;
208 dsc->format = DSC_LINE;
209 dsc->missing_listwise = 0.;
212 dsc->sort_by_stat = DSC_NONE;
213 dsc->sort_ascending = 1;
214 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
216 /* Parse DESCRIPTIVES. */
219 if (lex_match_id ("MISSING"))
222 while (token != '.' && token != '/')
224 if (lex_match_id ("VARIABLE"))
225 dsc->missing_type = DSC_VARIABLE;
226 else if (lex_match_id ("LISTWISE"))
227 dsc->missing_type = DSC_LISTWISE;
228 else if (lex_match_id ("INCLUDE"))
229 dsc->include_user_missing = 1;
238 else if (lex_match_id ("SAVE"))
240 else if (lex_match_id ("FORMAT"))
243 while (token != '.' && token != '/')
245 if (lex_match_id ("LABELS"))
246 dsc->show_var_labels = 1;
247 else if (lex_match_id ("NOLABELS"))
248 dsc->show_var_labels = 0;
249 else if (lex_match_id ("INDEX"))
251 else if (lex_match_id ("NOINDEX"))
253 else if (lex_match_id ("LINE"))
254 dsc->format = DSC_LINE;
255 else if (lex_match_id ("SERIAL"))
256 dsc->format = DSC_SERIAL;
265 else if (lex_match_id ("STATISTICS"))
269 while (token != '.' && token != '/')
271 if (lex_match (T_ALL))
272 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
273 else if (lex_match_id ("DEFAULT"))
274 dsc->show_stats |= DEFAULT_STATS;
276 dsc->show_stats |= 1ul << (match_statistic ());
279 if (dsc->show_stats == 0)
280 dsc->show_stats = DEFAULT_STATS;
282 else if (lex_match_id ("SORT"))
285 if (lex_match_id ("NAME"))
286 dsc->sort_by_stat = DSC_NAME;
289 dsc->sort_by_stat = match_statistic ();
290 if (dsc->sort_by_stat == DSC_NONE )
291 dsc->sort_by_stat = DSC_MEAN;
295 if (lex_match_id ("A"))
296 dsc->sort_ascending = 1;
297 else if (lex_match_id ("D"))
298 dsc->sort_ascending = 0;
301 lex_force_match (')');
304 else if (var_cnt == 0)
306 if (lex_look_ahead () == '=')
308 lex_match_id ("VARIABLES");
312 while (token != '.' && token != '/')
316 if (!parse_variables (default_dict, &vars, &var_cnt,
317 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
320 dsc->vars = xnrealloc (dsc->vars, var_cnt, sizeof *dsc->vars);
321 for (i = dsc->var_cnt; i < var_cnt; i++)
323 struct dsc_var *dv = &dsc->vars[i];
325 dv->z_name[0] = '\0';
328 dsc->var_cnt = var_cnt;
337 if (try_name (dsc, tokid))
339 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
343 msg (SE, _("Z-score variable name %s would be"
344 " a duplicate variable name."), tokid);
346 if (!lex_force_match (')'))
361 msg (SE, _("No variables specified."));
365 /* Construct z-score varnames, show translation table. */
366 if (z_cnt || save_z_scores)
372 for (i = 0; i < dsc->var_cnt; i++)
373 if (dsc->vars[i].z_name[0] == 0)
375 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
376 dsc->vars[i].v->name, &gen_cnt))
384 /* Figure out statistics to display. */
385 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
386 dsc->show_stats |= 1ul << DSC_SESKEW;
387 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
388 dsc->show_stats |= 1ul << DSC_SEKURT;
390 /* Figure out which statistics to calculate. */
391 dsc->calc_stats = dsc->show_stats;
393 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
394 if (dsc->sort_by_stat >= 0)
395 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
396 if (dsc->show_stats & (1ul << DSC_SESKEW))
397 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
398 if (dsc->show_stats & (1ul << DSC_SEKURT))
399 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
401 /* Figure out maximum moment needed and allocate moments for
403 dsc->max_moment = MOMENT_NONE;
404 for (i = 0; i < DSC_N_STATS; i++)
405 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
406 dsc->max_moment = dsc_info[i].moment;
407 if (dsc->max_moment != MOMENT_NONE)
408 for (i = 0; i < dsc->var_cnt; i++)
409 dsc->vars[i].moments = moments_create (dsc->max_moment);
412 ok = multipass_procedure_with_splits (calc_descriptives, dsc);
421 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
429 /* Returns the statistic named by the current token and skips past the token.
430 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
431 specifiers). Emits an error if the current token ID does not name a
433 static enum dsc_statistic
434 match_statistic (void)
438 enum dsc_statistic stat;
440 for (stat = 0; stat < DSC_N_STATS; stat++)
441 if (lex_match_id (dsc_info[stat].identifier))
445 lex_error (_("expecting statistic name: reverting to default"));
453 free_dsc_proc (struct dsc_proc *dsc)
460 for (i = 0; i < dsc->var_cnt; i++)
461 moments_destroy (dsc->vars[i].moments);
468 /* Returns 0 if NAME is a duplicate of any existing variable name or
469 of any previously-declared z-var name; otherwise returns 1. */
471 try_name (struct dsc_proc *dsc, char *name)
475 if (dict_lookup_var (default_dict, name) != NULL)
477 for (i = 0; i < dsc->var_cnt; i++)
478 if (!strcasecmp (dsc->vars[i].z_name, name))
483 /* Generates a name for a Z-score variable based on a variable
484 named VAR_NAME, given that *Z_CNT generated variable names are
485 known to already exist. If successful, returns nonzero and
486 copies the new name into Z_NAME. On failure, returns zero. */
488 generate_z_varname (struct dsc_proc *dsc, char *z_name,
489 const char *var_name, size_t *z_cnt)
491 char name[LONG_NAME_LEN + 1];
493 /* Try a name based on the original variable name. */
495 str_copy_trunc (name + 1, sizeof name - 1, var_name);
496 if (try_name (dsc, name))
498 strcpy (z_name, name);
502 /* Generate a synthetic name. */
508 sprintf (name, "ZSC%03d", *z_cnt);
509 else if (*z_cnt <= 108)
510 sprintf (name, "STDZ%02d", *z_cnt - 99);
511 else if (*z_cnt <= 117)
512 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
513 else if (*z_cnt <= 126)
514 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
517 msg (SE, _("Ran out of generic names for Z-score variables. "
518 "There are only 126 generic names: ZSC001-ZSC0999, "
519 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
523 if (try_name (dsc, name))
525 strcpy (z_name, name);
531 /* Outputs a table describing the mapping between source
532 variables and Z-score variables. */
534 dump_z_table (struct dsc_proc *dsc)
542 for (i = 0; i < dsc->var_cnt; i++)
543 if (dsc->vars[i].z_name[0] != '\0')
547 t = tab_create (2, cnt + 1, 0);
548 tab_title (t, _("Mapping of variables to corresponding Z-scores."));
549 tab_columns (t, SOM_COL_DOWN, 1);
550 tab_headers (t, 0, 0, 1, 0);
551 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
552 tab_hline (t, TAL_2, 0, 1, 1);
553 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
554 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
555 tab_dim (t, tab_natural_dimensions);
560 for (i = 0, y = 1; i < dsc->var_cnt; i++)
561 if (dsc->vars[i].z_name[0] != '\0')
563 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
564 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
571 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
572 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
573 SYSMIS 3) score is user missing and they were not included in the original
574 analyis. 4) any of the variables in the original analysis were missing
575 (either system or user-missing values that weren't included).
578 descriptives_trns_proc (void *trns_, struct ccase * c,
581 struct dsc_trns *t = trns_;
582 struct dsc_z_score *z;
583 struct variable **vars;
586 if (t->missing_type == DSC_LISTWISE)
589 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
591 double score = case_num (c, (*vars)->fv);
593 || (!t->include_user_missing
594 && mv_is_num_user_missing (&(*vars)->miss, score)))
602 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
604 double input = case_num (c, z->src_idx);
605 double *output = &case_data_rw (c, z->dst_idx)->f;
607 if (z->mean == SYSMIS || z->std_dev == SYSMIS
608 || all_sysmis || input == SYSMIS
609 || (!t->include_user_missing
610 && mv_is_num_user_missing (&z->v->miss, input)))
613 *output = (input - z->mean) / z->std_dev;
615 return TRNS_CONTINUE;
618 /* Frees a descriptives_trns struct. */
620 descriptives_trns_free (void *trns_)
622 struct dsc_trns *t = trns_;
625 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
630 /* Sets up a transformation to calculate Z scores. */
632 setup_z_trns (struct dsc_proc *dsc)
637 for (cnt = i = 0; i < dsc->var_cnt; i++)
638 if (dsc->vars[i].z_name[0] != '\0')
641 t = xmalloc (sizeof *t);
642 t->z_scores = xnmalloc (cnt, sizeof *t->z_scores);
643 t->z_score_cnt = cnt;
644 t->missing_type = dsc->missing_type;
645 t->include_user_missing = dsc->include_user_missing;
646 if ( t->missing_type == DSC_LISTWISE )
648 t->var_cnt = dsc->var_cnt;
649 t->vars = xnmalloc (t->var_cnt, sizeof *t->vars);
650 for (i = 0; i < t->var_cnt; i++)
651 t->vars[i] = dsc->vars[i].v;
659 for (cnt = i = 0; i < dsc->var_cnt; i++)
661 struct dsc_var *dv = &dsc->vars[i];
662 if (dv->z_name[0] != '\0')
664 struct dsc_z_score *z;
666 struct variable *dst_var;
668 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
671 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
672 cp = stpcpy (dst_var->label, _("Z-score of "));
673 strcpy (cp, dv->v->label);
677 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
678 cp = stpcpy (dst_var->label, _("Z-score of "));
679 strcpy (cp, dv->v->name);
682 z = &t->z_scores[cnt++];
683 z->src_idx = dv->v->fv;
684 z->dst_idx = dst_var->fv;
685 z->mean = dv->stats[DSC_MEAN];
686 z->std_dev = dv->stats[DSC_STDDEV];
691 add_transformation (descriptives_trns_proc, descriptives_trns_free, t);
694 /* Statistical calculation. */
696 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
698 /* Calculates and displays descriptive statistics for the cases
701 calc_descriptives (const struct ccase *first,
702 const struct casefile *cf, void *dsc_)
704 struct dsc_proc *dsc = dsc_;
705 struct casereader *reader;
709 output_split_file_values (first);
711 for (i = 0; i < dsc->var_cnt; i++)
713 struct dsc_var *dv = &dsc->vars[i];
715 dv->valid = dv->missing = 0.0;
716 if (dv->moments != NULL)
717 moments_clear (dv->moments);
721 dsc->missing_listwise = 0.;
724 /* First pass to handle most of the work. */
725 for (reader = casefile_get_reader (cf);
726 casereader_read (reader, &c);
729 double weight = dict_get_case_weight (default_dict, &c, &dsc->bad_warn);
733 /* Check for missing values. */
734 if (listwise_missing (dsc, &c))
736 dsc->missing_listwise += weight;
737 if (dsc->missing_type == DSC_LISTWISE)
740 dsc->valid += weight;
742 for (i = 0; i < dsc->var_cnt; i++)
744 struct dsc_var *dv = &dsc->vars[i];
745 double x = case_num (&c, dv->v->fv);
747 if (dsc->missing_type != DSC_LISTWISE
749 || (!dsc->include_user_missing
750 && mv_is_num_user_missing (&dv->v->miss, x))))
752 dv->missing += weight;
756 if (dv->moments != NULL)
757 moments_pass_one (dv->moments, x, weight);
765 casereader_destroy (reader);
767 /* Second pass for higher-order moments. */
768 if (dsc->max_moment > MOMENT_MEAN)
770 for (reader = casefile_get_reader (cf);
771 casereader_read (reader, &c);
774 double weight = dict_get_case_weight (default_dict, &c,
779 /* Check for missing values. */
780 if (listwise_missing (dsc, &c)
781 && dsc->missing_type == DSC_LISTWISE)
784 for (i = 0; i < dsc->var_cnt; i++)
786 struct dsc_var *dv = &dsc->vars[i];
787 double x = case_num (&c, dv->v->fv);
789 if (dsc->missing_type != DSC_LISTWISE
791 || (!dsc->include_user_missing
792 && mv_is_num_user_missing (&dv->v->miss, x))))
795 if (dv->moments != NULL)
796 moments_pass_two (dv->moments, x, weight);
799 casereader_destroy (reader);
802 /* Calculate results. */
803 for (i = 0; i < dsc->var_cnt; i++)
805 struct dsc_var *dv = &dsc->vars[i];
809 for (j = 0; j < DSC_N_STATS; j++)
810 dv->stats[j] = SYSMIS;
812 dv->valid = W = dsc->valid - dv->missing;
814 if (dv->moments != NULL)
815 moments_calculate (dv->moments, NULL,
816 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
817 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
818 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
819 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
820 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
821 if (dsc->calc_stats & (1ul << DSC_STDDEV)
822 && dv->stats[DSC_VARIANCE] != SYSMIS)
823 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
824 if (dsc->calc_stats & (1ul << DSC_SEKURT))
825 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
826 dv->stats[DSC_SEKURT] = calc_sekurt (W);
827 if (dsc->calc_stats & (1ul << DSC_SESKEW)
828 && dv->stats[DSC_SKEWNESS] != SYSMIS)
829 dv->stats[DSC_SESKEW] = calc_seskew (W);
830 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
831 ? SYSMIS : dv->max - dv->min);
832 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
833 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
834 if (dsc->calc_stats & (1ul << DSC_SUM))
835 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
838 /* Output results. */
844 /* Returns nonzero if any of the descriptives variables in DSC's
845 variable list have missing values in case C, zero otherwise. */
847 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
851 for (i = 0; i < dsc->var_cnt; i++)
853 struct dsc_var *dv = &dsc->vars[i];
854 double x = case_num (c, dv->v->fv);
857 || (!dsc->include_user_missing
858 && mv_is_num_user_missing (&dv->v->miss, x)))
864 /* Statistical display. */
866 static algo_compare_func descriptives_compare_dsc_vars;
868 /* Displays a table of descriptive statistics for DSC. */
870 display (struct dsc_proc *dsc)
876 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
877 for (i = 0; i < DSC_N_STATS; i++)
878 if (dsc->show_stats & (1ul << i))
881 if (dsc->sort_by_stat != DSC_NONE)
882 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
883 descriptives_compare_dsc_vars, dsc);
885 t = tab_create (nc, dsc->var_cnt + 1, 0);
886 tab_headers (t, 1, 0, 1, 0);
887 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
888 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
889 tab_hline (t, TAL_2, 0, nc - 1, 1);
890 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
891 tab_dim (t, tab_natural_dimensions);
894 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
895 if (dsc->format == DSC_SERIAL)
897 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
898 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
901 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
903 for (i = 0; i < DSC_N_STATS; i++)
904 if (dsc->show_stats & (1ul << i))
906 const char *title = gettext (dsc_info[i].name);
907 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
910 for (i = 0; i < dsc->var_cnt; i++)
912 struct dsc_var *dv = &dsc->vars[i];
916 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
917 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
918 if (dsc->format == DSC_SERIAL)
919 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
920 for (j = 0; j < DSC_N_STATS; j++)
921 if (dsc->show_stats & (1ul << j))
922 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
925 tab_title (t, _("Valid cases = %g; cases with missing value(s) = %g."),
926 dsc->valid, dsc->missing_listwise);
931 /* Compares `struct dsc_var's A and B according to the ordering
934 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
936 const struct dsc_var *a = a_;
937 const struct dsc_var *b = b_;
938 struct dsc_proc *dsc = dsc_;
942 if (dsc->sort_by_stat == DSC_NAME)
943 result = strcasecmp (a->v->name, b->v->name);
946 double as = a->stats[dsc->sort_by_stat];
947 double bs = b->stats[dsc->sort_by_stat];
949 result = as < bs ? -1 : as > bs;
952 if (!dsc->sort_ascending)