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., 59 Temple Place - Suite 330, Boston, MA
20 /* FIXME: Many possible optimizations. */
27 #include "algorithm.h"
40 /* DESCRIPTIVES private data. */
44 /* Handling of missing values. */
47 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
48 DSC_LISTWISE /* Discard entire case if any variable is missing. */
51 /* Describes properties of a distribution for the purpose of
52 calculating a Z-score. */
55 int src_idx; /* Source index into case data. */
56 int dst_idx; /* Destination index into case data. */
57 double mean; /* Distribution mean. */
58 double std_dev; /* Distribution standard deviation. */
59 struct variable *v; /* Variable on which z-score is based. */
62 /* DESCRIPTIVES transformation (for calculating Z-scores). */
66 struct dsc_z_score *z_scores; /* Array of Z-scores. */
67 int z_score_cnt; /* Number of Z-scores. */
68 struct variable **vars; /* Variables for listwise missing checks. */
69 size_t var_cnt; /* Number of variables. */
70 enum dsc_missing_type missing_type; /* Treatment of missing values. */
71 int include_user_missing; /* Nonzero to include user-missing values. */
74 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
77 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
78 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
79 DSC_MAX, DSC_SUM, DSC_N_STATS,
81 /* Only valid as sort criteria. */
82 DSC_NAME = -2, /* Sort by name. */
83 DSC_NONE = -1 /* Unsorted. */
86 /* Describes one statistic. */
87 struct dsc_statistic_info
89 const char *identifier; /* Identifier. */
90 const char *name; /* Full name. */
91 enum moment moment; /* Highest moment needed to calculate. */
94 /* Table of statistics, indexed by DSC_*. */
95 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
97 {"MEAN", N_("Mean"), MOMENT_MEAN},
98 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
99 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
100 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
101 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
102 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
103 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
104 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
105 {"RANGE", N_("Range"), MOMENT_NONE},
106 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
107 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
108 {"SUM", N_("Sum"), MOMENT_MEAN},
111 /* Statistics calculated by default if none are explicitly
113 #define DEFAULT_STATS \
114 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
117 /* A variable specified on DESCRIPTIVES. */
120 struct variable *v; /* Variable to calculate on. */
121 char z_name[9]; /* Name for z-score variable. */
122 double valid, missing; /* Valid, missing counts. */
123 struct moments *moments; /* Moments. */
124 double min, max; /* Maximum and mimimum values. */
125 double stats[DSC_N_STATS]; /* All the stats' values. */
131 DSC_LINE, /* Abbreviated format. */
132 DSC_SERIAL /* Long format. */
135 /* A DESCRIPTIVES procedure. */
138 /* Per-variable info. */
139 struct dsc_var *vars; /* Variables. */
140 size_t var_cnt; /* Number of variables. */
143 enum dsc_missing_type missing_type; /* Treatment of missing values. */
144 int include_user_missing; /* Nonzero to include user-missing values. */
145 int show_var_labels; /* Nonzero to show variable labels. */
146 int show_index; /* Nonzero to show variable index. */
147 enum dsc_format format; /* Output format. */
149 /* Accumulated results. */
150 double missing_listwise; /* Sum of weights of cases missing listwise. */
151 double valid; /* Sum of weights of valid cases. */
152 int bad_warn; /* Warn if bad weight found. */
153 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
154 int sort_ascending; /* !0: ascending order; 0: descending. */
155 unsigned long show_stats; /* Statistics to display. */
156 unsigned long calc_stats; /* Statistics to calculate. */
157 enum moment max_moment; /* Highest moment needed for stats. */
161 static enum dsc_statistic match_statistic (void);
162 static void free_dsc_proc (struct dsc_proc *);
164 /* Z-score functions. */
165 static int try_name (struct dsc_proc *dsc, char *name);
166 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
167 const char *name, int *z_cnt);
168 static void dump_z_table (struct dsc_proc *);
169 static void setup_z_trns (struct dsc_proc *);
171 /* Procedure execution functions. */
172 static void calc_descriptives (const struct casefile *, void *dsc_);
173 static void display (struct dsc_proc *dsc);
175 /* Parser and outline. */
177 /* Handles DESCRIPTIVES. */
179 cmd_descriptives (void)
181 struct dsc_proc *dsc;
182 struct variable **vars = NULL;
184 int save_z_scores = 0;
188 /* Create and initialize dsc. */
189 dsc = xmalloc (sizeof *dsc);
192 dsc->missing_type = DSC_VARIABLE;
193 dsc->include_user_missing = 0;
194 dsc->show_var_labels = 1;
196 dsc->format = DSC_LINE;
197 dsc->missing_listwise = 0.;
200 dsc->sort_by_stat = DSC_NONE;
201 dsc->sort_ascending = 1;
202 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
204 /* Parse DESCRIPTIVES. */
207 if (lex_match_id ("MISSING"))
210 while (token != '.' && token != '/')
212 if (lex_match_id ("VARIABLE"))
213 dsc->missing_type = DSC_VARIABLE;
214 else if (lex_match_id ("LISTWISE"))
215 dsc->missing_type = DSC_LISTWISE;
216 else if (lex_match_id ("INCLUDE"))
217 dsc->include_user_missing = 1;
226 else if (lex_match_id ("SAVE"))
228 else if (lex_match_id ("FORMAT"))
231 while (token != '.' && token != '/')
233 if (lex_match_id ("LABELS"))
234 dsc->show_var_labels = 1;
235 else if (lex_match_id ("NOLABELS"))
236 dsc->show_var_labels = 0;
237 else if (lex_match_id ("INDEX"))
239 else if (lex_match_id ("NOINDEX"))
241 else if (lex_match_id ("LINE"))
242 dsc->format = DSC_LINE;
243 else if (lex_match_id ("SERIAL"))
244 dsc->format = DSC_SERIAL;
253 else if (lex_match_id ("STATISTICS"))
257 while (token != '.' && token != '/')
259 if (lex_match (T_ALL))
260 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
261 else if (lex_match_id ("DEFAULT"))
262 dsc->show_stats |= DEFAULT_STATS;
265 dsc->show_stats |= 1ul << (match_statistic ());
266 if (dsc->show_stats == DSC_NONE)
267 dsc->show_stats = DEFAULT_STATS;
271 if (dsc->show_stats == 0)
272 dsc->show_stats = DEFAULT_STATS;
274 else if (lex_match_id ("SORT"))
277 if (lex_match_id ("NAME"))
278 dsc->sort_by_stat = DSC_NAME;
281 dsc->sort_by_stat = match_statistic ();
282 if (dsc->sort_by_stat == DSC_NONE )
283 dsc->sort_by_stat = DSC_MEAN;
287 if (lex_match_id ("A"))
288 dsc->sort_ascending = 1;
289 else if (lex_match_id ("D"))
290 dsc->sort_ascending = 0;
293 lex_force_match (')');
296 else if (var_cnt == 0)
298 if (lex_look_ahead () == '=')
300 lex_match_id ("VARIABLES");
304 while (token != '.' && token != '/')
308 if (!parse_variables (default_dict, &vars, &var_cnt,
309 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
312 dsc->vars = xrealloc (dsc->vars, sizeof *dsc->vars * var_cnt);
313 for (i = dsc->var_cnt; i < var_cnt; i++)
315 struct dsc_var *dv = &dsc->vars[i];
317 dv->z_name[0] = '\0';
320 dsc->var_cnt = var_cnt;
329 if (try_name (dsc, tokid))
331 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
335 msg (SE, _("Z-score variable name %s would be"
336 " a duplicate variable name."), tokid);
338 if (!lex_force_match (')'))
353 msg (SE, _("No variables specified."));
357 /* Construct z-score varnames, show translation table. */
358 if (z_cnt || save_z_scores)
364 for (i = 0; i < dsc->var_cnt; i++)
365 if (dsc->vars[i].z_name[0] == 0)
367 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
368 dsc->vars[i].v->name, &gen_cnt))
376 /* Figure out statistics to display. */
377 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
378 dsc->show_stats |= 1ul << DSC_SESKEW;
379 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
380 dsc->show_stats |= 1ul << DSC_SEKURT;
382 /* Figure out which statistics to calculate. */
383 dsc->calc_stats = dsc->show_stats;
385 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
386 if (dsc->sort_by_stat >= 0)
387 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
388 if (dsc->show_stats & (1ul << DSC_SESKEW))
389 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
390 if (dsc->show_stats & (1ul << DSC_SEKURT))
391 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
393 /* Figure out maximum moment needed and allocate moments for
395 dsc->max_moment = MOMENT_NONE;
396 for (i = 0; i < DSC_N_STATS; i++)
397 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
398 dsc->max_moment = dsc_info[i].moment;
399 if (dsc->max_moment != MOMENT_NONE)
400 for (i = 0; i < dsc->var_cnt; i++)
401 dsc->vars[i].moments = moments_create (dsc->max_moment);
404 multipass_procedure_with_splits (calc_descriptives, dsc);
421 /* Returns the statistic named by the current token and skips past the token.
422 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
423 specifiers). Emits an error if the current token ID does not name a
425 static enum dsc_statistic
426 match_statistic (void)
430 enum dsc_statistic stat;
432 for (stat = 0; stat < DSC_N_STATS; stat++)
433 if (lex_match_id (dsc_info[stat].identifier))
437 lex_error (_("expecting statistic name: reverting to default"));
445 free_dsc_proc (struct dsc_proc *dsc)
452 for (i = 0; i < dsc->var_cnt; i++)
453 moments_destroy (dsc->vars[i].moments);
460 /* Returns 0 if NAME is a duplicate of any existing variable name or
461 of any previously-declared z-var name; otherwise returns 1. */
463 try_name (struct dsc_proc *dsc, char *name)
467 if (dict_lookup_var (default_dict, name) != NULL)
469 for (i = 0; i < dsc->var_cnt; i++)
470 if (!strcmp (dsc->vars[i].z_name, name))
475 /* Generates a name for a Z-score variable based on a variable
476 named VAR_NAME, given that *Z_CNT generated variable names are
477 known to already exist. If successful, returns nonzero and
478 copies the new name into Z_NAME. On failure, returns zero. */
480 generate_z_varname (struct dsc_proc *dsc, char *z_name,
481 const char *var_name, int *z_cnt)
485 /* Try a name based on the original variable name. */
487 strcpy (name + 1, var_name);
489 if (try_name (dsc, name))
491 strcpy (z_name, name);
495 /* Generate a synthetic name. */
501 sprintf (name, "ZSC%03d", *z_cnt);
502 else if (*z_cnt <= 108)
503 sprintf (name, "STDZ%02d", *z_cnt - 99);
504 else if (*z_cnt <= 117)
505 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
506 else if (*z_cnt <= 126)
507 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
510 msg (SE, _("Ran out of generic names for Z-score variables. "
511 "There are only 126 generic names: ZSC001-ZSC0999, "
512 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
516 if (try_name (dsc, name))
518 strcpy (z_name, name);
524 /* Outputs a table describing the mapping between source
525 variables and Z-score variables. */
527 dump_z_table (struct dsc_proc *dsc)
535 for (i = 0; i < dsc->var_cnt; i++)
536 if (dsc->vars[i].z_name[0] != '\0')
540 t = tab_create (2, cnt + 1, 0);
541 tab_title (t, 0, _("Mapping of variables to corresponding Z-scores."));
542 tab_columns (t, SOM_COL_DOWN, 1);
543 tab_headers (t, 0, 0, 1, 0);
544 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
545 tab_hline (t, TAL_2, 0, 1, 1);
546 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
547 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
548 tab_dim (t, tab_natural_dimensions);
553 for (i = 0, y = 1; i < dsc->var_cnt; i++)
554 if (dsc->vars[i].z_name[0] != '\0')
556 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
557 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
564 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
565 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
566 SYSMIS 3) score is user missing and they were not included in the original
567 analyis. 4) any of the variables in the original analysis were missing
568 (either system or user-missing values that weren't included).
571 descriptives_trns_proc (struct trns_header *trns, struct ccase * c,
574 struct dsc_trns *t = (struct dsc_trns *) trns;
575 struct dsc_z_score *z;
576 struct variable **vars;
579 if (t->missing_type == DSC_LISTWISE)
582 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
584 double score = c->data[(*vars)->fv].f;
585 if ( score == SYSMIS || (!t->include_user_missing
586 && is_num_user_missing(score, *vars)) )
594 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
596 double score = c->data[z->src_idx].f;
598 if (z->mean == SYSMIS || z->std_dev == SYSMIS
599 || all_sysmis || score == SYSMIS
600 || (!t->include_user_missing && is_num_user_missing(score, z->v)))
601 c->data[z->dst_idx].f = SYSMIS;
603 c->data[z->dst_idx].f = (score - z->mean) / z->std_dev;
608 /* Frees a descriptives_trns struct. */
610 descriptives_trns_free (struct trns_header * trns)
612 struct dsc_trns *t = (struct dsc_trns *) trns;
615 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
619 /* Sets up a transformation to calculate Z scores. */
621 setup_z_trns (struct dsc_proc *dsc)
626 for (cnt = i = 0; i < dsc->var_cnt; i++)
627 if (dsc->vars[i].z_name[0] != '\0')
630 t = xmalloc (sizeof *t);
631 t->h.proc = descriptives_trns_proc;
632 t->h.free = descriptives_trns_free;
633 t->z_scores = xmalloc (cnt * sizeof *t->z_scores);
634 t->z_score_cnt = cnt;
635 t->missing_type = dsc->missing_type;
636 t->include_user_missing = dsc->include_user_missing;
637 if ( t->missing_type == DSC_LISTWISE )
639 t->var_cnt = dsc->var_cnt;
640 t->vars = xmalloc(t->var_cnt * sizeof *t->vars);
641 for (i = 0; i < t->var_cnt; i++)
642 t->vars[i] = dsc->vars[i].v;
651 for (cnt = i = 0; i < dsc->var_cnt; i++)
653 struct dsc_var *dv = &dsc->vars[i];
654 if (dv->z_name[0] != '\0')
656 struct dsc_z_score *z;
658 struct variable *dst_var;
660 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
664 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
665 cp = stpcpy (dst_var->label, _("Z-score of "));
666 strcpy (cp, dv->v->label);
670 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
671 cp = stpcpy (dst_var->label, _("Z-score of "));
672 strcpy (cp, dv->v->name);
675 z = &t->z_scores[cnt++];
676 z->src_idx = dv->v->fv;
677 z->dst_idx = dst_var->fv;
678 z->mean = dv->stats[DSC_MEAN];
679 z->std_dev = dv->stats[DSC_STDDEV];
684 add_transformation ((struct trns_header *) t);
687 /* Statistical calculation. */
689 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
691 /* Calculates and displays descriptive statistics for the cases
694 calc_descriptives (const struct casefile *cf, void *dsc_)
696 struct dsc_proc *dsc = dsc_;
697 struct casereader *reader;
698 const struct ccase *c;
701 for (i = 0; i < dsc->var_cnt; i++)
703 struct dsc_var *dv = &dsc->vars[i];
705 dv->valid = dv->missing = 0.0;
706 if (dv->moments != NULL)
707 moments_clear (dv->moments);
711 dsc->missing_listwise = 0.;
714 /* First pass to handle most of the work. */
715 reader = casefile_get_reader (cf);
716 while (casereader_read (reader, &c))
718 double weight = dict_get_case_weight (default_dict, c, &dsc->bad_warn);
722 /* Check for missing values. */
723 if (listwise_missing (dsc, c))
725 dsc->missing_listwise += weight;
726 if (dsc->missing_type == DSC_LISTWISE)
729 dsc->valid += weight;
731 for (i = 0; i < dsc->var_cnt; i++)
733 struct dsc_var *dv = &dsc->vars[i];
734 double x = c->data[dv->v->fv].f;
736 if (dsc->missing_type != DSC_LISTWISE
738 || (!dsc->include_user_missing
739 && is_num_user_missing (x, dv->v))))
741 dv->missing += weight;
745 if (dv->moments != NULL)
746 moments_pass_one (dv->moments, x, weight);
754 casereader_destroy (reader);
756 /* Second pass for higher-order moments. */
757 if (dsc->max_moment > MOMENT_MEAN)
759 reader = casefile_get_reader (cf);
760 while (casereader_read (reader, &c))
762 double weight = dict_get_case_weight (default_dict, c,
767 /* Check for missing values. */
768 if (listwise_missing (dsc, c)
769 && dsc->missing_type == DSC_LISTWISE)
772 for (i = 0; i < dsc->var_cnt; i++)
774 struct dsc_var *dv = &dsc->vars[i];
775 double x = c->data[dv->v->fv].f;
777 if (dsc->missing_type != DSC_LISTWISE
779 || (!dsc->include_user_missing
780 && is_num_user_missing (x, dv->v))))
783 if (dv->moments != NULL)
784 moments_pass_two (dv->moments, x, weight);
787 casereader_destroy (reader);
790 /* Calculate results. */
791 for (i = 0; i < dsc->var_cnt; i++)
793 struct dsc_var *dv = &dsc->vars[i];
797 for (j = 0; j < DSC_N_STATS; j++)
798 dv->stats[j] = SYSMIS;
800 dv->valid = W = dsc->valid - dv->missing;
802 if (dv->moments != NULL)
803 moments_calculate (dv->moments, NULL,
804 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
805 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
806 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
807 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
808 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
809 if (dsc->calc_stats & (1ul << DSC_STDDEV)
810 && dv->stats[DSC_VARIANCE] != SYSMIS)
811 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
812 if (dsc->calc_stats & (1ul << DSC_SEKURT))
813 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
814 dv->stats[DSC_SEKURT] = calc_sekurt (W);
815 if (dsc->calc_stats & (1ul << DSC_SESKEW)
816 && dv->stats[DSC_SKEWNESS] != SYSMIS)
817 dv->stats[DSC_SESKEW] = calc_seskew (W);
818 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
819 ? SYSMIS : dv->max - dv->min);
820 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
821 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
822 if (dsc->calc_stats & (1ul << DSC_SUM))
823 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
826 /* Output results. */
830 /* Returns nonzero if any of the descriptives variables in DSC's
831 variable list have missing values in case C, zero otherwise. */
833 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
837 for (i = 0; i < dsc->var_cnt; i++)
839 struct dsc_var *dv = &dsc->vars[i];
840 double x = c->data[dv->v->fv].f;
843 || (!dsc->include_user_missing && is_num_user_missing (x, dv->v)))
849 /* Statistical display. */
851 static algo_compare_func descriptives_compare_dsc_vars;
853 /* Displays a table of descriptive statistics for DSC. */
855 display (struct dsc_proc *dsc)
861 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
862 for (i = 0; i < DSC_N_STATS; i++)
863 if (dsc->show_stats & (1ul << i))
866 if (dsc->sort_by_stat != DSC_NONE)
867 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
868 descriptives_compare_dsc_vars, dsc);
870 t = tab_create (nc, dsc->var_cnt + 1, 0);
871 tab_headers (t, 1, 0, 1, 0);
872 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
873 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
874 tab_hline (t, TAL_2, 0, nc - 1, 1);
875 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
876 tab_dim (t, tab_natural_dimensions);
879 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
880 if (dsc->format == DSC_SERIAL)
882 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
883 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
886 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
888 for (i = 0; i < DSC_N_STATS; i++)
889 if (dsc->show_stats & (1ul << i))
891 const char *title = gettext (dsc_info[i].name);
892 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
895 for (i = 0; i < dsc->var_cnt; i++)
897 struct dsc_var *dv = &dsc->vars[i];
900 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
901 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
902 if (dsc->format == DSC_SERIAL)
903 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
904 for (j = 0; j < DSC_N_STATS; j++)
905 if (dsc->show_stats & (1ul << j))
906 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
909 tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
910 dsc->valid, dsc->missing_listwise);
915 /* Compares `struct dsc_var's A and B according to the ordering
918 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
920 const struct dsc_var *a = a_;
921 const struct dsc_var *b = b_;
922 struct dsc_proc *dsc = dsc_;
926 if (dsc->sort_by_stat == DSC_NAME)
927 result = strcmp (a->v->name, b->v->name);
930 double as = a->stats[dsc->sort_by_stat];
931 double bs = b->stats[dsc->sort_by_stat];
933 result = as < bs ? -1 : as > bs;
936 if (!dsc->sort_ascending)