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)
747 if (dsc->max_moment > MOMENT_MEAN)
748 moments_pass_one (dv->moments, x, weight);
750 moments_pass_two (dv->moments, x, weight);
759 casereader_destroy (reader);
761 /* Second pass for higher-order moments. */
762 if (dsc->max_moment > MOMENT_MEAN)
764 reader = casefile_get_reader (cf);
765 while (casereader_read (reader, &c))
767 double weight = dict_get_case_weight (default_dict, c,
772 /* Check for missing values. */
773 if (listwise_missing (dsc, c)
774 && dsc->missing_type == DSC_LISTWISE)
777 for (i = 0; i < dsc->var_cnt; i++)
779 struct dsc_var *dv = &dsc->vars[i];
780 double x = c->data[dv->v->fv].f;
782 if (dsc->missing_type != DSC_LISTWISE
784 || (!dsc->include_user_missing
785 && is_num_user_missing (x, dv->v))))
788 if (dv->moments != NULL)
789 moments_pass_two (dv->moments, x, weight);
792 casereader_destroy (reader);
795 /* Calculate results. */
796 for (i = 0; i < dsc->var_cnt; i++)
798 struct dsc_var *dv = &dsc->vars[i];
802 for (j = 0; j < DSC_N_STATS; j++)
803 dv->stats[j] = SYSMIS;
805 dv->valid = W = dsc->valid - dv->missing;
807 if (dv->moments != NULL)
808 moments_calculate (dv->moments, NULL,
809 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
810 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
811 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
812 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
813 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
814 if (dsc->calc_stats & (1ul << DSC_STDDEV)
815 && dv->stats[DSC_VARIANCE] != SYSMIS)
816 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
817 if (dsc->calc_stats & (1ul << DSC_SEKURT))
818 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
819 dv->stats[DSC_SEKURT] = calc_sekurt (W);
820 if (dsc->calc_stats & (1ul << DSC_SESKEW)
821 && dv->stats[DSC_SKEWNESS] != SYSMIS)
822 dv->stats[DSC_SESKEW] = calc_seskew (W);
823 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
824 ? SYSMIS : dv->max - dv->min);
825 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
826 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
827 if (dsc->calc_stats & (1ul << DSC_SUM))
828 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
831 /* Output results. */
835 /* Returns nonzero if any of the descriptives variables in DSC's
836 variable list have missing values in case C, zero otherwise. */
838 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
842 for (i = 0; i < dsc->var_cnt; i++)
844 struct dsc_var *dv = &dsc->vars[i];
845 double x = c->data[dv->v->fv].f;
848 || (!dsc->include_user_missing && is_num_user_missing (x, dv->v)))
854 /* Statistical display. */
856 static algo_compare_func descriptives_compare_dsc_vars;
858 /* Displays a table of descriptive statistics for DSC. */
860 display (struct dsc_proc *dsc)
866 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
867 for (i = 0; i < DSC_N_STATS; i++)
868 if (dsc->show_stats & (1ul << i))
871 if (dsc->sort_by_stat != DSC_NONE)
872 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
873 descriptives_compare_dsc_vars, dsc);
875 t = tab_create (nc, dsc->var_cnt + 1, 0);
876 tab_headers (t, 1, 0, 1, 0);
877 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
878 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
879 tab_hline (t, TAL_2, 0, nc - 1, 1);
880 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
881 tab_dim (t, tab_natural_dimensions);
884 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
885 if (dsc->format == DSC_SERIAL)
887 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
888 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
891 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
893 for (i = 0; i < DSC_N_STATS; i++)
894 if (dsc->show_stats & (1ul << i))
896 const char *title = gettext (dsc_info[i].name);
897 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
900 for (i = 0; i < dsc->var_cnt; i++)
902 struct dsc_var *dv = &dsc->vars[i];
905 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
906 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
907 if (dsc->format == DSC_SERIAL)
908 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
909 for (j = 0; j < DSC_N_STATS; j++)
910 if (dsc->show_stats & (1ul << j))
911 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
914 tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
915 dsc->valid, dsc->missing_listwise);
920 /* Compares `struct dsc_var's A and B according to the ordering
923 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
925 const struct dsc_var *a = a_;
926 const struct dsc_var *b = b_;
927 struct dsc_proc *dsc = dsc_;
931 if (dsc->sort_by_stat == DSC_NAME)
932 result = strcmp (a->v->name, b->v->name);
935 double as = a->stats[dsc->sort_by_stat];
936 double bs = b->stats[dsc->sort_by_stat];
938 result = as < bs ? -1 : as > bs;
941 if (!dsc->sort_ascending)