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"
41 /* DESCRIPTIVES private data. */
45 /* Handling of missing values. */
48 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
49 DSC_LISTWISE /* Discard entire case if any variable is missing. */
52 /* Describes properties of a distribution for the purpose of
53 calculating a Z-score. */
56 int src_idx; /* Source index into case data. */
57 int dst_idx; /* Destination index into case data. */
58 double mean; /* Distribution mean. */
59 double std_dev; /* Distribution standard deviation. */
60 struct variable *v; /* Variable on which z-score is based. */
63 /* DESCRIPTIVES transformation (for calculating Z-scores). */
67 struct dsc_z_score *z_scores; /* Array of Z-scores. */
68 int z_score_cnt; /* Number of Z-scores. */
69 struct variable **vars; /* Variables for listwise missing checks. */
70 size_t var_cnt; /* Number of variables. */
71 enum dsc_missing_type missing_type; /* Treatment of missing values. */
72 int include_user_missing; /* Nonzero to include user-missing values. */
75 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
78 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
79 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
80 DSC_MAX, DSC_SUM, DSC_N_STATS,
82 /* Only valid as sort criteria. */
83 DSC_NAME = -2, /* Sort by name. */
84 DSC_NONE = -1 /* Unsorted. */
87 /* Describes one statistic. */
88 struct dsc_statistic_info
90 const char *identifier; /* Identifier. */
91 const char *name; /* Full name. */
92 enum moment moment; /* Highest moment needed to calculate. */
95 /* Table of statistics, indexed by DSC_*. */
96 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
98 {"MEAN", N_("Mean"), MOMENT_MEAN},
99 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
100 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
101 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
102 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
103 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
104 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
105 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
106 {"RANGE", N_("Range"), MOMENT_NONE},
107 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
108 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
109 {"SUM", N_("Sum"), MOMENT_MEAN},
112 /* Statistics calculated by default if none are explicitly
114 #define DEFAULT_STATS \
115 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
118 /* A variable specified on DESCRIPTIVES. */
121 struct variable *v; /* Variable to calculate on. */
122 char z_name[9]; /* Name for z-score variable. */
123 double valid, missing; /* Valid, missing counts. */
124 struct moments *moments; /* Moments. */
125 double min, max; /* Maximum and mimimum values. */
126 double stats[DSC_N_STATS]; /* All the stats' values. */
132 DSC_LINE, /* Abbreviated format. */
133 DSC_SERIAL /* Long format. */
136 /* A DESCRIPTIVES procedure. */
139 /* Per-variable info. */
140 struct dsc_var *vars; /* Variables. */
141 size_t var_cnt; /* Number of variables. */
144 enum dsc_missing_type missing_type; /* Treatment of missing values. */
145 int include_user_missing; /* Nonzero to include user-missing values. */
146 int show_var_labels; /* Nonzero to show variable labels. */
147 int show_index; /* Nonzero to show variable index. */
148 enum dsc_format format; /* Output format. */
150 /* Accumulated results. */
151 double missing_listwise; /* Sum of weights of cases missing listwise. */
152 double valid; /* Sum of weights of valid cases. */
153 int bad_warn; /* Warn if bad weight found. */
154 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
155 int sort_ascending; /* !0: ascending order; 0: descending. */
156 unsigned long show_stats; /* Statistics to display. */
157 unsigned long calc_stats; /* Statistics to calculate. */
158 enum moment max_moment; /* Highest moment needed for stats. */
162 static enum dsc_statistic match_statistic (void);
163 static void free_dsc_proc (struct dsc_proc *);
165 /* Z-score functions. */
166 static int try_name (struct dsc_proc *dsc, char *name);
167 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
168 const char *name, int *z_cnt);
169 static void dump_z_table (struct dsc_proc *);
170 static void setup_z_trns (struct dsc_proc *);
172 /* Procedure execution functions. */
173 static void calc_descriptives (const struct casefile *, void *dsc_);
174 static void display (struct dsc_proc *dsc);
176 /* Parser and outline. */
178 /* Handles DESCRIPTIVES. */
180 cmd_descriptives (void)
182 struct dsc_proc *dsc;
183 struct variable **vars = NULL;
185 int save_z_scores = 0;
189 /* Create and initialize dsc. */
190 dsc = xmalloc (sizeof *dsc);
193 dsc->missing_type = DSC_VARIABLE;
194 dsc->include_user_missing = 0;
195 dsc->show_var_labels = 1;
197 dsc->format = DSC_LINE;
198 dsc->missing_listwise = 0.;
201 dsc->sort_by_stat = DSC_NONE;
202 dsc->sort_ascending = 1;
203 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
205 /* Parse DESCRIPTIVES. */
208 if (lex_match_id ("MISSING"))
211 while (token != '.' && token != '/')
213 if (lex_match_id ("VARIABLE"))
214 dsc->missing_type = DSC_VARIABLE;
215 else if (lex_match_id ("LISTWISE"))
216 dsc->missing_type = DSC_LISTWISE;
217 else if (lex_match_id ("INCLUDE"))
218 dsc->include_user_missing = 1;
227 else if (lex_match_id ("SAVE"))
229 else if (lex_match_id ("FORMAT"))
232 while (token != '.' && token != '/')
234 if (lex_match_id ("LABELS"))
235 dsc->show_var_labels = 1;
236 else if (lex_match_id ("NOLABELS"))
237 dsc->show_var_labels = 0;
238 else if (lex_match_id ("INDEX"))
240 else if (lex_match_id ("NOINDEX"))
242 else if (lex_match_id ("LINE"))
243 dsc->format = DSC_LINE;
244 else if (lex_match_id ("SERIAL"))
245 dsc->format = DSC_SERIAL;
254 else if (lex_match_id ("STATISTICS"))
258 while (token != '.' && token != '/')
260 if (lex_match (T_ALL))
261 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
262 else if (lex_match_id ("DEFAULT"))
263 dsc->show_stats |= DEFAULT_STATS;
265 dsc->show_stats |= 1ul << (match_statistic ());
268 if (dsc->show_stats == 0)
269 dsc->show_stats = DEFAULT_STATS;
271 else if (lex_match_id ("SORT"))
274 if (lex_match_id ("NAME"))
275 dsc->sort_by_stat = DSC_NAME;
278 dsc->sort_by_stat = match_statistic ();
279 if (dsc->sort_by_stat == DSC_NONE )
280 dsc->sort_by_stat = DSC_MEAN;
284 if (lex_match_id ("A"))
285 dsc->sort_ascending = 1;
286 else if (lex_match_id ("D"))
287 dsc->sort_ascending = 0;
290 lex_force_match (')');
293 else if (var_cnt == 0)
295 if (lex_look_ahead () == '=')
297 lex_match_id ("VARIABLES");
301 while (token != '.' && token != '/')
305 if (!parse_variables (default_dict, &vars, &var_cnt,
306 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
309 dsc->vars = xrealloc (dsc->vars, sizeof *dsc->vars * var_cnt);
310 for (i = dsc->var_cnt; i < var_cnt; i++)
312 struct dsc_var *dv = &dsc->vars[i];
314 dv->z_name[0] = '\0';
317 dsc->var_cnt = var_cnt;
326 if (try_name (dsc, tokid))
328 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
332 msg (SE, _("Z-score variable name %s would be"
333 " a duplicate variable name."), tokid);
335 if (!lex_force_match (')'))
350 msg (SE, _("No variables specified."));
354 /* Construct z-score varnames, show translation table. */
355 if (z_cnt || save_z_scores)
361 for (i = 0; i < dsc->var_cnt; i++)
362 if (dsc->vars[i].z_name[0] == 0)
364 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
365 dsc->vars[i].v->name, &gen_cnt))
373 /* Figure out statistics to display. */
374 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
375 dsc->show_stats |= 1ul << DSC_SESKEW;
376 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
377 dsc->show_stats |= 1ul << DSC_SEKURT;
379 /* Figure out which statistics to calculate. */
380 dsc->calc_stats = dsc->show_stats;
382 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
383 if (dsc->sort_by_stat >= 0)
384 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
385 if (dsc->show_stats & (1ul << DSC_SESKEW))
386 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
387 if (dsc->show_stats & (1ul << DSC_SEKURT))
388 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
390 /* Figure out maximum moment needed and allocate moments for
392 dsc->max_moment = MOMENT_NONE;
393 for (i = 0; i < DSC_N_STATS; i++)
394 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
395 dsc->max_moment = dsc_info[i].moment;
396 if (dsc->max_moment != MOMENT_NONE)
397 for (i = 0; i < dsc->var_cnt; i++)
398 dsc->vars[i].moments = moments_create (dsc->max_moment);
401 multipass_procedure_with_splits (calc_descriptives, dsc);
418 /* Returns the statistic named by the current token and skips past the token.
419 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
420 specifiers). Emits an error if the current token ID does not name a
422 static enum dsc_statistic
423 match_statistic (void)
427 enum dsc_statistic stat;
429 for (stat = 0; stat < DSC_N_STATS; stat++)
430 if (lex_match_id (dsc_info[stat].identifier))
434 lex_error (_("expecting statistic name: reverting to default"));
442 free_dsc_proc (struct dsc_proc *dsc)
449 for (i = 0; i < dsc->var_cnt; i++)
450 moments_destroy (dsc->vars[i].moments);
457 /* Returns 0 if NAME is a duplicate of any existing variable name or
458 of any previously-declared z-var name; otherwise returns 1. */
460 try_name (struct dsc_proc *dsc, char *name)
464 if (dict_lookup_var (default_dict, name) != NULL)
466 for (i = 0; i < dsc->var_cnt; i++)
467 if (!strcmp (dsc->vars[i].z_name, name))
472 /* Generates a name for a Z-score variable based on a variable
473 named VAR_NAME, given that *Z_CNT generated variable names are
474 known to already exist. If successful, returns nonzero and
475 copies the new name into Z_NAME. On failure, returns zero. */
477 generate_z_varname (struct dsc_proc *dsc, char *z_name,
478 const char *var_name, int *z_cnt)
482 /* Try a name based on the original variable name. */
484 strcpy (name + 1, var_name);
486 if (try_name (dsc, name))
488 strcpy (z_name, name);
492 /* Generate a synthetic name. */
498 sprintf (name, "ZSC%03d", *z_cnt);
499 else if (*z_cnt <= 108)
500 sprintf (name, "STDZ%02d", *z_cnt - 99);
501 else if (*z_cnt <= 117)
502 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
503 else if (*z_cnt <= 126)
504 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
507 msg (SE, _("Ran out of generic names for Z-score variables. "
508 "There are only 126 generic names: ZSC001-ZSC0999, "
509 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
513 if (try_name (dsc, name))
515 strcpy (z_name, name);
521 /* Outputs a table describing the mapping between source
522 variables and Z-score variables. */
524 dump_z_table (struct dsc_proc *dsc)
532 for (i = 0; i < dsc->var_cnt; i++)
533 if (dsc->vars[i].z_name[0] != '\0')
537 t = tab_create (2, cnt + 1, 0);
538 tab_title (t, 0, _("Mapping of variables to corresponding Z-scores."));
539 tab_columns (t, SOM_COL_DOWN, 1);
540 tab_headers (t, 0, 0, 1, 0);
541 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
542 tab_hline (t, TAL_2, 0, 1, 1);
543 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
544 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
545 tab_dim (t, tab_natural_dimensions);
550 for (i = 0, y = 1; i < dsc->var_cnt; i++)
551 if (dsc->vars[i].z_name[0] != '\0')
553 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
554 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
561 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
562 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
563 SYSMIS 3) score is user missing and they were not included in the original
564 analyis. 4) any of the variables in the original analysis were missing
565 (either system or user-missing values that weren't included).
568 descriptives_trns_proc (struct trns_header *trns, struct ccase * c,
571 struct dsc_trns *t = (struct dsc_trns *) trns;
572 struct dsc_z_score *z;
573 struct variable **vars;
576 if (t->missing_type == DSC_LISTWISE)
579 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
581 double score = case_num (c, (*vars)->fv);
582 if ( score == SYSMIS || (!t->include_user_missing
583 && is_num_user_missing(score, *vars)) )
591 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
593 double input = case_num (c, z->src_idx);
594 double *output = &case_data_rw (c, z->dst_idx)->f;
596 if (z->mean == SYSMIS || z->std_dev == SYSMIS
597 || all_sysmis || input == SYSMIS
598 || (!t->include_user_missing && is_num_user_missing(input, z->v)))
601 *output = (input - z->mean) / z->std_dev;
606 /* Frees a descriptives_trns struct. */
608 descriptives_trns_free (struct trns_header * trns)
610 struct dsc_trns *t = (struct dsc_trns *) trns;
613 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
617 /* Sets up a transformation to calculate Z scores. */
619 setup_z_trns (struct dsc_proc *dsc)
624 for (cnt = i = 0; i < dsc->var_cnt; i++)
625 if (dsc->vars[i].z_name[0] != '\0')
628 t = xmalloc (sizeof *t);
629 t->h.proc = descriptives_trns_proc;
630 t->h.free = descriptives_trns_free;
631 t->z_scores = xmalloc (cnt * sizeof *t->z_scores);
632 t->z_score_cnt = cnt;
633 t->missing_type = dsc->missing_type;
634 t->include_user_missing = dsc->include_user_missing;
635 if ( t->missing_type == DSC_LISTWISE )
637 t->var_cnt = dsc->var_cnt;
638 t->vars = xmalloc(t->var_cnt * sizeof *t->vars);
639 for (i = 0; i < t->var_cnt; i++)
640 t->vars[i] = dsc->vars[i].v;
649 for (cnt = i = 0; i < dsc->var_cnt; i++)
651 struct dsc_var *dv = &dsc->vars[i];
652 if (dv->z_name[0] != '\0')
654 struct dsc_z_score *z;
656 struct variable *dst_var;
658 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
662 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
663 cp = stpcpy (dst_var->label, _("Z-score of "));
664 strcpy (cp, dv->v->label);
668 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
669 cp = stpcpy (dst_var->label, _("Z-score of "));
670 strcpy (cp, dv->v->name);
673 z = &t->z_scores[cnt++];
674 z->src_idx = dv->v->fv;
675 z->dst_idx = dst_var->fv;
676 z->mean = dv->stats[DSC_MEAN];
677 z->std_dev = dv->stats[DSC_STDDEV];
682 add_transformation ((struct trns_header *) t);
685 /* Statistical calculation. */
687 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
689 /* Calculates and displays descriptive statistics for the cases
692 calc_descriptives (const struct casefile *cf, void *dsc_)
694 struct dsc_proc *dsc = dsc_;
695 struct casereader *reader;
699 for (i = 0; i < dsc->var_cnt; i++)
701 struct dsc_var *dv = &dsc->vars[i];
703 dv->valid = dv->missing = 0.0;
704 if (dv->moments != NULL)
705 moments_clear (dv->moments);
709 dsc->missing_listwise = 0.;
712 /* First pass to handle most of the work. */
713 for (reader = casefile_get_reader (cf);
714 casereader_read (reader, &c);
717 double weight = dict_get_case_weight (default_dict, &c, &dsc->bad_warn);
721 /* Check for missing values. */
722 if (listwise_missing (dsc, &c))
724 dsc->missing_listwise += weight;
725 if (dsc->missing_type == DSC_LISTWISE)
728 dsc->valid += weight;
730 for (i = 0; i < dsc->var_cnt; i++)
732 struct dsc_var *dv = &dsc->vars[i];
733 double x = case_num (&c, dv->v->fv);
735 if (dsc->missing_type != DSC_LISTWISE
737 || (!dsc->include_user_missing
738 && is_num_user_missing (x, dv->v))))
740 dv->missing += weight;
744 if (dv->moments != NULL)
745 moments_pass_one (dv->moments, x, weight);
753 casereader_destroy (reader);
755 /* Second pass for higher-order moments. */
756 if (dsc->max_moment > MOMENT_MEAN)
758 for (reader = casefile_get_reader (cf);
759 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 = case_num (&c, dv->v->fv);
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 = case_num (c, dv->v->fv);
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)