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;
266 dsc->show_stats |= 1ul << (match_statistic ());
267 if (dsc->show_stats == DSC_NONE)
268 dsc->show_stats = DEFAULT_STATS;
272 if (dsc->show_stats == 0)
273 dsc->show_stats = DEFAULT_STATS;
275 else if (lex_match_id ("SORT"))
278 if (lex_match_id ("NAME"))
279 dsc->sort_by_stat = DSC_NAME;
282 dsc->sort_by_stat = match_statistic ();
283 if (dsc->sort_by_stat == DSC_NONE )
284 dsc->sort_by_stat = DSC_MEAN;
288 if (lex_match_id ("A"))
289 dsc->sort_ascending = 1;
290 else if (lex_match_id ("D"))
291 dsc->sort_ascending = 0;
294 lex_force_match (')');
297 else if (var_cnt == 0)
299 if (lex_look_ahead () == '=')
301 lex_match_id ("VARIABLES");
305 while (token != '.' && token != '/')
309 if (!parse_variables (default_dict, &vars, &var_cnt,
310 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
313 dsc->vars = xrealloc (dsc->vars, sizeof *dsc->vars * var_cnt);
314 for (i = dsc->var_cnt; i < var_cnt; i++)
316 struct dsc_var *dv = &dsc->vars[i];
318 dv->z_name[0] = '\0';
321 dsc->var_cnt = var_cnt;
330 if (try_name (dsc, tokid))
332 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
336 msg (SE, _("Z-score variable name %s would be"
337 " a duplicate variable name."), tokid);
339 if (!lex_force_match (')'))
354 msg (SE, _("No variables specified."));
358 /* Construct z-score varnames, show translation table. */
359 if (z_cnt || save_z_scores)
365 for (i = 0; i < dsc->var_cnt; i++)
366 if (dsc->vars[i].z_name[0] == 0)
368 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
369 dsc->vars[i].v->name, &gen_cnt))
377 /* Figure out statistics to display. */
378 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
379 dsc->show_stats |= 1ul << DSC_SESKEW;
380 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
381 dsc->show_stats |= 1ul << DSC_SEKURT;
383 /* Figure out which statistics to calculate. */
384 dsc->calc_stats = dsc->show_stats;
386 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
387 if (dsc->sort_by_stat >= 0)
388 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
389 if (dsc->show_stats & (1ul << DSC_SESKEW))
390 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
391 if (dsc->show_stats & (1ul << DSC_SEKURT))
392 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
394 /* Figure out maximum moment needed and allocate moments for
396 dsc->max_moment = MOMENT_NONE;
397 for (i = 0; i < DSC_N_STATS; i++)
398 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
399 dsc->max_moment = dsc_info[i].moment;
400 if (dsc->max_moment != MOMENT_NONE)
401 for (i = 0; i < dsc->var_cnt; i++)
402 dsc->vars[i].moments = moments_create (dsc->max_moment);
405 multipass_procedure_with_splits (calc_descriptives, dsc);
422 /* Returns the statistic named by the current token and skips past the token.
423 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
424 specifiers). Emits an error if the current token ID does not name a
426 static enum dsc_statistic
427 match_statistic (void)
431 enum dsc_statistic stat;
433 for (stat = 0; stat < DSC_N_STATS; stat++)
434 if (lex_match_id (dsc_info[stat].identifier))
438 lex_error (_("expecting statistic name: reverting to default"));
446 free_dsc_proc (struct dsc_proc *dsc)
453 for (i = 0; i < dsc->var_cnt; i++)
454 moments_destroy (dsc->vars[i].moments);
461 /* Returns 0 if NAME is a duplicate of any existing variable name or
462 of any previously-declared z-var name; otherwise returns 1. */
464 try_name (struct dsc_proc *dsc, char *name)
468 if (dict_lookup_var (default_dict, name) != NULL)
470 for (i = 0; i < dsc->var_cnt; i++)
471 if (!strcmp (dsc->vars[i].z_name, name))
476 /* Generates a name for a Z-score variable based on a variable
477 named VAR_NAME, given that *Z_CNT generated variable names are
478 known to already exist. If successful, returns nonzero and
479 copies the new name into Z_NAME. On failure, returns zero. */
481 generate_z_varname (struct dsc_proc *dsc, char *z_name,
482 const char *var_name, int *z_cnt)
486 /* Try a name based on the original variable name. */
488 strcpy (name + 1, var_name);
490 if (try_name (dsc, name))
492 strcpy (z_name, name);
496 /* Generate a synthetic name. */
502 sprintf (name, "ZSC%03d", *z_cnt);
503 else if (*z_cnt <= 108)
504 sprintf (name, "STDZ%02d", *z_cnt - 99);
505 else if (*z_cnt <= 117)
506 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
507 else if (*z_cnt <= 126)
508 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
511 msg (SE, _("Ran out of generic names for Z-score variables. "
512 "There are only 126 generic names: ZSC001-ZSC0999, "
513 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
517 if (try_name (dsc, name))
519 strcpy (z_name, name);
525 /* Outputs a table describing the mapping between source
526 variables and Z-score variables. */
528 dump_z_table (struct dsc_proc *dsc)
536 for (i = 0; i < dsc->var_cnt; i++)
537 if (dsc->vars[i].z_name[0] != '\0')
541 t = tab_create (2, cnt + 1, 0);
542 tab_title (t, 0, _("Mapping of variables to corresponding Z-scores."));
543 tab_columns (t, SOM_COL_DOWN, 1);
544 tab_headers (t, 0, 0, 1, 0);
545 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
546 tab_hline (t, TAL_2, 0, 1, 1);
547 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
548 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
549 tab_dim (t, tab_natural_dimensions);
554 for (i = 0, y = 1; i < dsc->var_cnt; i++)
555 if (dsc->vars[i].z_name[0] != '\0')
557 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
558 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
565 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
566 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
567 SYSMIS 3) score is user missing and they were not included in the original
568 analyis. 4) any of the variables in the original analysis were missing
569 (either system or user-missing values that weren't included).
572 descriptives_trns_proc (struct trns_header *trns, struct ccase * c,
575 struct dsc_trns *t = (struct dsc_trns *) trns;
576 struct dsc_z_score *z;
577 struct variable **vars;
580 if (t->missing_type == DSC_LISTWISE)
583 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
585 double score = case_num (c, (*vars)->fv);
586 if ( score == SYSMIS || (!t->include_user_missing
587 && is_num_user_missing(score, *vars)) )
595 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
597 double input = case_num (c, z->src_idx);
598 double *output = &case_data_rw (c, z->dst_idx)->f;
600 if (z->mean == SYSMIS || z->std_dev == SYSMIS
601 || all_sysmis || input == SYSMIS
602 || (!t->include_user_missing && is_num_user_missing(input, z->v)))
605 *output = (input - z->mean) / z->std_dev;
610 /* Frees a descriptives_trns struct. */
612 descriptives_trns_free (struct trns_header * trns)
614 struct dsc_trns *t = (struct dsc_trns *) trns;
617 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
621 /* Sets up a transformation to calculate Z scores. */
623 setup_z_trns (struct dsc_proc *dsc)
628 for (cnt = i = 0; i < dsc->var_cnt; i++)
629 if (dsc->vars[i].z_name[0] != '\0')
632 t = xmalloc (sizeof *t);
633 t->h.proc = descriptives_trns_proc;
634 t->h.free = descriptives_trns_free;
635 t->z_scores = xmalloc (cnt * sizeof *t->z_scores);
636 t->z_score_cnt = cnt;
637 t->missing_type = dsc->missing_type;
638 t->include_user_missing = dsc->include_user_missing;
639 if ( t->missing_type == DSC_LISTWISE )
641 t->var_cnt = dsc->var_cnt;
642 t->vars = xmalloc(t->var_cnt * sizeof *t->vars);
643 for (i = 0; i < t->var_cnt; i++)
644 t->vars[i] = dsc->vars[i].v;
653 for (cnt = i = 0; i < dsc->var_cnt; i++)
655 struct dsc_var *dv = &dsc->vars[i];
656 if (dv->z_name[0] != '\0')
658 struct dsc_z_score *z;
660 struct variable *dst_var;
662 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 ((struct trns_header *) 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 && is_num_user_missing (x, dv->v))))
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 && is_num_user_missing (x, dv->v))))
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. */
834 /* Returns nonzero if any of the descriptives variables in DSC's
835 variable list have missing values in case C, zero otherwise. */
837 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
841 for (i = 0; i < dsc->var_cnt; i++)
843 struct dsc_var *dv = &dsc->vars[i];
844 double x = case_num (c, dv->v->fv);
847 || (!dsc->include_user_missing && is_num_user_missing (x, dv->v)))
853 /* Statistical display. */
855 static algo_compare_func descriptives_compare_dsc_vars;
857 /* Displays a table of descriptive statistics for DSC. */
859 display (struct dsc_proc *dsc)
865 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
866 for (i = 0; i < DSC_N_STATS; i++)
867 if (dsc->show_stats & (1ul << i))
870 if (dsc->sort_by_stat != DSC_NONE)
871 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
872 descriptives_compare_dsc_vars, dsc);
874 t = tab_create (nc, dsc->var_cnt + 1, 0);
875 tab_headers (t, 1, 0, 1, 0);
876 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
877 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
878 tab_hline (t, TAL_2, 0, nc - 1, 1);
879 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
880 tab_dim (t, tab_natural_dimensions);
883 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
884 if (dsc->format == DSC_SERIAL)
886 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
887 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
890 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
892 for (i = 0; i < DSC_N_STATS; i++)
893 if (dsc->show_stats & (1ul << i))
895 const char *title = gettext (dsc_info[i].name);
896 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
899 for (i = 0; i < dsc->var_cnt; i++)
901 struct dsc_var *dv = &dsc->vars[i];
904 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
905 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
906 if (dsc->format == DSC_SERIAL)
907 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
908 for (j = 0; j < DSC_N_STATS; j++)
909 if (dsc->show_stats & (1ul << j))
910 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
913 tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
914 dsc->valid, dsc->missing_listwise);
919 /* Compares `struct dsc_var's A and B according to the ordering
922 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
924 const struct dsc_var *a = a_;
925 const struct dsc_var *b = b_;
926 struct dsc_proc *dsc = dsc_;
930 if (dsc->sort_by_stat == DSC_NAME)
931 result = strcmp (a->v->name, b->v->name);
934 double as = a->stats[dsc->sort_by_stat];
935 double bs = b->stats[dsc->sort_by_stat];
937 result = as < bs ? -1 : as > bs;
940 if (!dsc->sort_ascending)