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. */
23 #include <libpspp/message.h>
27 #include <libpspp/array.h>
28 #include <libpspp/alloc.h>
29 #include <data/case.h>
30 #include <data/casefile.h>
31 #include <language/command.h>
32 #include <libpspp/compiler.h>
33 #include <data/dictionary.h>
34 #include <language/lexer/lexer.h>
35 #include <libpspp/message.h>
36 #include <libpspp/magic.h>
37 #include <math/moments.h>
38 #include <output/manager.h>
39 #include <output/table.h>
40 #include <data/variable.h>
41 #include <procedure.h>
44 #define _(msgid) gettext (msgid)
45 #define N_(msgid) msgid
47 /* DESCRIPTIVES private data. */
51 /* Handling of missing values. */
54 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
55 DSC_LISTWISE /* Discard entire case if any variable is missing. */
58 /* Describes properties of a distribution for the purpose of
59 calculating a Z-score. */
62 int src_idx; /* Source index into case data. */
63 int dst_idx; /* Destination index into case data. */
64 double mean; /* Distribution mean. */
65 double std_dev; /* Distribution standard deviation. */
66 struct variable *v; /* Variable on which z-score is based. */
69 /* DESCRIPTIVES transformation (for calculating Z-scores). */
72 struct dsc_z_score *z_scores; /* Array of Z-scores. */
73 int z_score_cnt; /* Number of Z-scores. */
74 struct variable **vars; /* Variables for listwise missing checks. */
75 size_t var_cnt; /* Number of variables. */
76 enum dsc_missing_type missing_type; /* Treatment of missing values. */
77 int include_user_missing; /* Nonzero to include user-missing values. */
80 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
83 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
84 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
85 DSC_MAX, DSC_SUM, DSC_N_STATS,
87 /* Only valid as sort criteria. */
88 DSC_NAME = -2, /* Sort by name. */
89 DSC_NONE = -1 /* Unsorted. */
92 /* Describes one statistic. */
93 struct dsc_statistic_info
95 const char *identifier; /* Identifier. */
96 const char *name; /* Full name. */
97 enum moment moment; /* Highest moment needed to calculate. */
100 /* Table of statistics, indexed by DSC_*. */
101 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
103 {"MEAN", N_("Mean"), MOMENT_MEAN},
104 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
105 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
106 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
107 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
108 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
109 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
110 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
111 {"RANGE", N_("Range"), MOMENT_NONE},
112 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
113 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
114 {"SUM", N_("Sum"), MOMENT_MEAN},
117 /* Statistics calculated by default if none are explicitly
119 #define DEFAULT_STATS \
120 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
123 /* A variable specified on DESCRIPTIVES. */
126 struct variable *v; /* Variable to calculate on. */
127 char z_name[LONG_NAME_LEN + 1]; /* Name for z-score variable. */
128 double valid, missing; /* Valid, missing counts. */
129 struct moments *moments; /* Moments. */
130 double min, max; /* Maximum and mimimum values. */
131 double stats[DSC_N_STATS]; /* All the stats' values. */
137 DSC_LINE, /* Abbreviated format. */
138 DSC_SERIAL /* Long format. */
141 /* A DESCRIPTIVES procedure. */
144 /* Per-variable info. */
145 struct dsc_var *vars; /* Variables. */
146 size_t var_cnt; /* Number of variables. */
149 enum dsc_missing_type missing_type; /* Treatment of missing values. */
150 int include_user_missing; /* Nonzero to include user-missing values. */
151 int show_var_labels; /* Nonzero to show variable labels. */
152 int show_index; /* Nonzero to show variable index. */
153 enum dsc_format format; /* Output format. */
155 /* Accumulated results. */
156 double missing_listwise; /* Sum of weights of cases missing listwise. */
157 double valid; /* Sum of weights of valid cases. */
158 int bad_warn; /* Warn if bad weight found. */
159 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
160 int sort_ascending; /* !0: ascending order; 0: descending. */
161 unsigned long show_stats; /* Statistics to display. */
162 unsigned long calc_stats; /* Statistics to calculate. */
163 enum moment max_moment; /* Highest moment needed for stats. */
167 static enum dsc_statistic match_statistic (void);
168 static void free_dsc_proc (struct dsc_proc *);
170 /* Z-score functions. */
171 static int try_name (struct dsc_proc *dsc, char *name);
172 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
173 const char *name, size_t *z_cnt);
174 static void dump_z_table (struct dsc_proc *);
175 static void setup_z_trns (struct dsc_proc *);
177 /* Procedure execution functions. */
178 static bool calc_descriptives (const struct casefile *, void *dsc_);
179 static void display (struct dsc_proc *dsc);
181 /* Parser and outline. */
183 /* Handles DESCRIPTIVES. */
185 cmd_descriptives (void)
187 struct dsc_proc *dsc;
188 struct variable **vars = NULL;
190 int save_z_scores = 0;
195 /* Create and initialize dsc. */
196 dsc = xmalloc (sizeof *dsc);
199 dsc->missing_type = DSC_VARIABLE;
200 dsc->include_user_missing = 0;
201 dsc->show_var_labels = 1;
203 dsc->format = DSC_LINE;
204 dsc->missing_listwise = 0.;
207 dsc->sort_by_stat = DSC_NONE;
208 dsc->sort_ascending = 1;
209 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
211 /* Parse DESCRIPTIVES. */
214 if (lex_match_id ("MISSING"))
217 while (token != '.' && token != '/')
219 if (lex_match_id ("VARIABLE"))
220 dsc->missing_type = DSC_VARIABLE;
221 else if (lex_match_id ("LISTWISE"))
222 dsc->missing_type = DSC_LISTWISE;
223 else if (lex_match_id ("INCLUDE"))
224 dsc->include_user_missing = 1;
233 else if (lex_match_id ("SAVE"))
235 else if (lex_match_id ("FORMAT"))
238 while (token != '.' && token != '/')
240 if (lex_match_id ("LABELS"))
241 dsc->show_var_labels = 1;
242 else if (lex_match_id ("NOLABELS"))
243 dsc->show_var_labels = 0;
244 else if (lex_match_id ("INDEX"))
246 else if (lex_match_id ("NOINDEX"))
248 else if (lex_match_id ("LINE"))
249 dsc->format = DSC_LINE;
250 else if (lex_match_id ("SERIAL"))
251 dsc->format = DSC_SERIAL;
260 else if (lex_match_id ("STATISTICS"))
264 while (token != '.' && token != '/')
266 if (lex_match (T_ALL))
267 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
268 else if (lex_match_id ("DEFAULT"))
269 dsc->show_stats |= DEFAULT_STATS;
271 dsc->show_stats |= 1ul << (match_statistic ());
274 if (dsc->show_stats == 0)
275 dsc->show_stats = DEFAULT_STATS;
277 else if (lex_match_id ("SORT"))
280 if (lex_match_id ("NAME"))
281 dsc->sort_by_stat = DSC_NAME;
284 dsc->sort_by_stat = match_statistic ();
285 if (dsc->sort_by_stat == DSC_NONE )
286 dsc->sort_by_stat = DSC_MEAN;
290 if (lex_match_id ("A"))
291 dsc->sort_ascending = 1;
292 else if (lex_match_id ("D"))
293 dsc->sort_ascending = 0;
296 lex_force_match (')');
299 else if (var_cnt == 0)
301 if (lex_look_ahead () == '=')
303 lex_match_id ("VARIABLES");
307 while (token != '.' && token != '/')
311 if (!parse_variables (default_dict, &vars, &var_cnt,
312 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
315 dsc->vars = xnrealloc (dsc->vars, var_cnt, sizeof *dsc->vars);
316 for (i = dsc->var_cnt; i < var_cnt; i++)
318 struct dsc_var *dv = &dsc->vars[i];
320 dv->z_name[0] = '\0';
323 dsc->var_cnt = var_cnt;
332 if (try_name (dsc, tokid))
334 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
338 msg (SE, _("Z-score variable name %s would be"
339 " a duplicate variable name."), tokid);
341 if (!lex_force_match (')'))
356 msg (SE, _("No variables specified."));
360 /* Construct z-score varnames, show translation table. */
361 if (z_cnt || save_z_scores)
367 for (i = 0; i < dsc->var_cnt; i++)
368 if (dsc->vars[i].z_name[0] == 0)
370 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
371 dsc->vars[i].v->name, &gen_cnt))
379 /* Figure out statistics to display. */
380 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
381 dsc->show_stats |= 1ul << DSC_SESKEW;
382 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
383 dsc->show_stats |= 1ul << DSC_SEKURT;
385 /* Figure out which statistics to calculate. */
386 dsc->calc_stats = dsc->show_stats;
388 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
389 if (dsc->sort_by_stat >= 0)
390 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
391 if (dsc->show_stats & (1ul << DSC_SESKEW))
392 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
393 if (dsc->show_stats & (1ul << DSC_SEKURT))
394 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
396 /* Figure out maximum moment needed and allocate moments for
398 dsc->max_moment = MOMENT_NONE;
399 for (i = 0; i < DSC_N_STATS; i++)
400 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
401 dsc->max_moment = dsc_info[i].moment;
402 if (dsc->max_moment != MOMENT_NONE)
403 for (i = 0; i < dsc->var_cnt; i++)
404 dsc->vars[i].moments = moments_create (dsc->max_moment);
407 ok = multipass_procedure_with_splits (calc_descriptives, dsc);
416 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
424 /* Returns the statistic named by the current token and skips past the token.
425 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
426 specifiers). Emits an error if the current token ID does not name a
428 static enum dsc_statistic
429 match_statistic (void)
433 enum dsc_statistic stat;
435 for (stat = 0; stat < DSC_N_STATS; stat++)
436 if (lex_match_id (dsc_info[stat].identifier))
440 lex_error (_("expecting statistic name: reverting to default"));
448 free_dsc_proc (struct dsc_proc *dsc)
455 for (i = 0; i < dsc->var_cnt; i++)
456 moments_destroy (dsc->vars[i].moments);
463 /* Returns 0 if NAME is a duplicate of any existing variable name or
464 of any previously-declared z-var name; otherwise returns 1. */
466 try_name (struct dsc_proc *dsc, char *name)
470 if (dict_lookup_var (default_dict, name) != NULL)
472 for (i = 0; i < dsc->var_cnt; i++)
473 if (!strcasecmp (dsc->vars[i].z_name, name))
478 /* Generates a name for a Z-score variable based on a variable
479 named VAR_NAME, given that *Z_CNT generated variable names are
480 known to already exist. If successful, returns nonzero and
481 copies the new name into Z_NAME. On failure, returns zero. */
483 generate_z_varname (struct dsc_proc *dsc, char *z_name,
484 const char *var_name, size_t *z_cnt)
486 char name[LONG_NAME_LEN + 1];
488 /* Try a name based on the original variable name. */
490 str_copy_trunc (name + 1, sizeof name - 1, var_name);
491 if (try_name (dsc, name))
493 strcpy (z_name, name);
497 /* Generate a synthetic name. */
503 sprintf (name, "ZSC%03d", *z_cnt);
504 else if (*z_cnt <= 108)
505 sprintf (name, "STDZ%02d", *z_cnt - 99);
506 else if (*z_cnt <= 117)
507 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
508 else if (*z_cnt <= 126)
509 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
512 msg (SE, _("Ran out of generic names for Z-score variables. "
513 "There are only 126 generic names: ZSC001-ZSC0999, "
514 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
518 if (try_name (dsc, name))
520 strcpy (z_name, name);
526 /* Outputs a table describing the mapping between source
527 variables and Z-score variables. */
529 dump_z_table (struct dsc_proc *dsc)
537 for (i = 0; i < dsc->var_cnt; i++)
538 if (dsc->vars[i].z_name[0] != '\0')
542 t = tab_create (2, cnt + 1, 0);
543 tab_title (t, _("Mapping of variables to corresponding Z-scores."));
544 tab_columns (t, SOM_COL_DOWN, 1);
545 tab_headers (t, 0, 0, 1, 0);
546 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
547 tab_hline (t, TAL_2, 0, 1, 1);
548 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
549 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
550 tab_dim (t, tab_natural_dimensions);
555 for (i = 0, y = 1; i < dsc->var_cnt; i++)
556 if (dsc->vars[i].z_name[0] != '\0')
558 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
559 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
566 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
567 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
568 SYSMIS 3) score is user missing and they were not included in the original
569 analyis. 4) any of the variables in the original analysis were missing
570 (either system or user-missing values that weren't included).
573 descriptives_trns_proc (void *trns_, struct ccase * c,
576 struct dsc_trns *t = trns_;
577 struct dsc_z_score *z;
578 struct variable **vars;
581 if (t->missing_type == DSC_LISTWISE)
584 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
586 double score = case_num (c, (*vars)->fv);
588 || (!t->include_user_missing
589 && mv_is_num_user_missing (&(*vars)->miss, score)))
597 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
599 double input = case_num (c, z->src_idx);
600 double *output = &case_data_rw (c, z->dst_idx)->f;
602 if (z->mean == SYSMIS || z->std_dev == SYSMIS
603 || all_sysmis || input == SYSMIS
604 || (!t->include_user_missing
605 && mv_is_num_user_missing (&z->v->miss, input)))
608 *output = (input - z->mean) / z->std_dev;
610 return TRNS_CONTINUE;
613 /* Frees a descriptives_trns struct. */
615 descriptives_trns_free (void *trns_)
617 struct dsc_trns *t = trns_;
620 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
625 /* Sets up a transformation to calculate Z scores. */
627 setup_z_trns (struct dsc_proc *dsc)
632 for (cnt = i = 0; i < dsc->var_cnt; i++)
633 if (dsc->vars[i].z_name[0] != '\0')
636 t = xmalloc (sizeof *t);
637 t->z_scores = xnmalloc (cnt, sizeof *t->z_scores);
638 t->z_score_cnt = cnt;
639 t->missing_type = dsc->missing_type;
640 t->include_user_missing = dsc->include_user_missing;
641 if ( t->missing_type == DSC_LISTWISE )
643 t->var_cnt = dsc->var_cnt;
644 t->vars = xnmalloc (t->var_cnt, sizeof *t->vars);
645 for (i = 0; i < t->var_cnt; i++)
646 t->vars[i] = dsc->vars[i].v;
654 for (cnt = i = 0; i < dsc->var_cnt; i++)
656 struct dsc_var *dv = &dsc->vars[i];
657 if (dv->z_name[0] != '\0')
659 struct dsc_z_score *z;
661 struct variable *dst_var;
663 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
666 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
667 cp = stpcpy (dst_var->label, _("Z-score of "));
668 strcpy (cp, dv->v->label);
672 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
673 cp = stpcpy (dst_var->label, _("Z-score of "));
674 strcpy (cp, dv->v->name);
677 z = &t->z_scores[cnt++];
678 z->src_idx = dv->v->fv;
679 z->dst_idx = dst_var->fv;
680 z->mean = dv->stats[DSC_MEAN];
681 z->std_dev = dv->stats[DSC_STDDEV];
686 add_transformation (descriptives_trns_proc, descriptives_trns_free, t);
689 /* Statistical calculation. */
691 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
693 /* Calculates and displays descriptive statistics for the cases
696 calc_descriptives (const struct casefile *cf, void *dsc_)
698 struct dsc_proc *dsc = dsc_;
699 struct casereader *reader;
703 for (i = 0; i < dsc->var_cnt; i++)
705 struct dsc_var *dv = &dsc->vars[i];
707 dv->valid = dv->missing = 0.0;
708 if (dv->moments != NULL)
709 moments_clear (dv->moments);
713 dsc->missing_listwise = 0.;
716 /* First pass to handle most of the work. */
717 for (reader = casefile_get_reader (cf);
718 casereader_read (reader, &c);
721 double weight = dict_get_case_weight (default_dict, &c, &dsc->bad_warn);
725 /* Check for missing values. */
726 if (listwise_missing (dsc, &c))
728 dsc->missing_listwise += weight;
729 if (dsc->missing_type == DSC_LISTWISE)
732 dsc->valid += weight;
734 for (i = 0; i < dsc->var_cnt; i++)
736 struct dsc_var *dv = &dsc->vars[i];
737 double x = case_num (&c, dv->v->fv);
739 if (dsc->missing_type != DSC_LISTWISE
741 || (!dsc->include_user_missing
742 && mv_is_num_user_missing (&dv->v->miss, x))))
744 dv->missing += weight;
748 if (dv->moments != NULL)
749 moments_pass_one (dv->moments, x, weight);
757 casereader_destroy (reader);
759 /* Second pass for higher-order moments. */
760 if (dsc->max_moment > MOMENT_MEAN)
762 for (reader = casefile_get_reader (cf);
763 casereader_read (reader, &c);
766 double weight = dict_get_case_weight (default_dict, &c,
771 /* Check for missing values. */
772 if (listwise_missing (dsc, &c)
773 && dsc->missing_type == DSC_LISTWISE)
776 for (i = 0; i < dsc->var_cnt; i++)
778 struct dsc_var *dv = &dsc->vars[i];
779 double x = case_num (&c, dv->v->fv);
781 if (dsc->missing_type != DSC_LISTWISE
783 || (!dsc->include_user_missing
784 && mv_is_num_user_missing (&dv->v->miss, x))))
787 if (dv->moments != NULL)
788 moments_pass_two (dv->moments, x, weight);
791 casereader_destroy (reader);
794 /* Calculate results. */
795 for (i = 0; i < dsc->var_cnt; i++)
797 struct dsc_var *dv = &dsc->vars[i];
801 for (j = 0; j < DSC_N_STATS; j++)
802 dv->stats[j] = SYSMIS;
804 dv->valid = W = dsc->valid - dv->missing;
806 if (dv->moments != NULL)
807 moments_calculate (dv->moments, NULL,
808 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
809 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
810 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
811 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
812 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
813 if (dsc->calc_stats & (1ul << DSC_STDDEV)
814 && dv->stats[DSC_VARIANCE] != SYSMIS)
815 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
816 if (dsc->calc_stats & (1ul << DSC_SEKURT))
817 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
818 dv->stats[DSC_SEKURT] = calc_sekurt (W);
819 if (dsc->calc_stats & (1ul << DSC_SESKEW)
820 && dv->stats[DSC_SKEWNESS] != SYSMIS)
821 dv->stats[DSC_SESKEW] = calc_seskew (W);
822 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
823 ? SYSMIS : dv->max - dv->min);
824 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
825 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
826 if (dsc->calc_stats & (1ul << DSC_SUM))
827 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
830 /* Output results. */
836 /* Returns nonzero if any of the descriptives variables in DSC's
837 variable list have missing values in case C, zero otherwise. */
839 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
843 for (i = 0; i < dsc->var_cnt; i++)
845 struct dsc_var *dv = &dsc->vars[i];
846 double x = case_num (c, dv->v->fv);
849 || (!dsc->include_user_missing
850 && mv_is_num_user_missing (&dv->v->miss, x)))
856 /* Statistical display. */
858 static algo_compare_func descriptives_compare_dsc_vars;
860 /* Displays a table of descriptive statistics for DSC. */
862 display (struct dsc_proc *dsc)
868 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
869 for (i = 0; i < DSC_N_STATS; i++)
870 if (dsc->show_stats & (1ul << i))
873 if (dsc->sort_by_stat != DSC_NONE)
874 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
875 descriptives_compare_dsc_vars, dsc);
877 t = tab_create (nc, dsc->var_cnt + 1, 0);
878 tab_headers (t, 1, 0, 1, 0);
879 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
880 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
881 tab_hline (t, TAL_2, 0, nc - 1, 1);
882 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
883 tab_dim (t, tab_natural_dimensions);
886 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
887 if (dsc->format == DSC_SERIAL)
889 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
890 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
893 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
895 for (i = 0; i < DSC_N_STATS; i++)
896 if (dsc->show_stats & (1ul << i))
898 const char *title = gettext (dsc_info[i].name);
899 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
902 for (i = 0; i < dsc->var_cnt; i++)
904 struct dsc_var *dv = &dsc->vars[i];
908 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
909 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
910 if (dsc->format == DSC_SERIAL)
911 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
912 for (j = 0; j < DSC_N_STATS; j++)
913 if (dsc->show_stats & (1ul << j))
914 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
917 tab_title (t, _("Valid cases = %g; cases with missing value(s) = %g."),
918 dsc->valid, dsc->missing_listwise);
923 /* Compares `struct dsc_var's A and B according to the ordering
926 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
928 const struct dsc_var *a = a_;
929 const struct dsc_var *b = b_;
930 struct dsc_proc *dsc = dsc_;
934 if (dsc->sort_by_stat == DSC_NAME)
935 result = strcasecmp (a->v->name, b->v->name);
938 double as = a->stats[dsc->sort_by_stat];
939 double bs = b->stats[dsc->sort_by_stat];
941 result = as < bs ? -1 : as > bs;
944 if (!dsc->sort_ascending)