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. */
28 #include <data/case.h>
29 #include <data/casefile.h>
30 #include <data/dictionary.h>
31 #include <data/procedure.h>
32 #include <data/transformations.h>
33 #include <data/variable.h>
34 #include <language/command.h>
35 #include <language/dictionary/split-file.h>
36 #include <language/lexer/lexer.h>
37 #include <libpspp/alloc.h>
38 #include <libpspp/array.h>
39 #include <libpspp/compiler.h>
40 #include <libpspp/magic.h>
41 #include <libpspp/message.h>
42 #include <math/moments.h>
43 #include <output/manager.h>
44 #include <output/table.h>
47 #define _(msgid) gettext (msgid)
48 #define N_(msgid) msgid
50 /* DESCRIPTIVES private data. */
54 /* Handling of missing values. */
57 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
58 DSC_LISTWISE /* Discard entire case if any variable is missing. */
61 /* Describes properties of a distribution for the purpose of
62 calculating a Z-score. */
65 int src_idx; /* Source index into case data. */
66 int dst_idx; /* Destination index into case data. */
67 double mean; /* Distribution mean. */
68 double std_dev; /* Distribution standard deviation. */
69 struct variable *v; /* Variable on which z-score is based. */
72 /* DESCRIPTIVES transformation (for calculating Z-scores). */
75 struct dsc_z_score *z_scores; /* Array of Z-scores. */
76 int z_score_cnt; /* Number of Z-scores. */
77 struct variable **vars; /* Variables for listwise missing checks. */
78 size_t var_cnt; /* Number of variables. */
79 enum dsc_missing_type missing_type; /* Treatment of missing values. */
80 int include_user_missing; /* Nonzero to include user-missing values. */
83 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
86 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
87 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
88 DSC_MAX, DSC_SUM, DSC_N_STATS,
90 /* Only valid as sort criteria. */
91 DSC_NAME = -2, /* Sort by name. */
92 DSC_NONE = -1 /* Unsorted. */
95 /* Describes one statistic. */
96 struct dsc_statistic_info
98 const char *identifier; /* Identifier. */
99 const char *name; /* Full name. */
100 enum moment moment; /* Highest moment needed to calculate. */
103 /* Table of statistics, indexed by DSC_*. */
104 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
106 {"MEAN", N_("Mean"), MOMENT_MEAN},
107 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
108 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
109 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
110 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
111 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
112 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
113 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
114 {"RANGE", N_("Range"), MOMENT_NONE},
115 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
116 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
117 {"SUM", N_("Sum"), MOMENT_MEAN},
120 /* Statistics calculated by default if none are explicitly
122 #define DEFAULT_STATS \
123 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
126 /* A variable specified on DESCRIPTIVES. */
129 struct variable *v; /* Variable to calculate on. */
130 char z_name[LONG_NAME_LEN + 1]; /* Name for z-score variable. */
131 double valid, missing; /* Valid, missing counts. */
132 struct moments *moments; /* Moments. */
133 double min, max; /* Maximum and mimimum values. */
134 double stats[DSC_N_STATS]; /* All the stats' values. */
140 DSC_LINE, /* Abbreviated format. */
141 DSC_SERIAL /* Long format. */
144 /* A DESCRIPTIVES procedure. */
147 /* Per-variable info. */
148 struct dsc_var *vars; /* Variables. */
149 size_t var_cnt; /* Number of variables. */
152 enum dsc_missing_type missing_type; /* Treatment of missing values. */
153 int include_user_missing; /* Nonzero to include user-missing values. */
154 int show_var_labels; /* Nonzero to show variable labels. */
155 int show_index; /* Nonzero to show variable index. */
156 enum dsc_format format; /* Output format. */
158 /* Accumulated results. */
159 double missing_listwise; /* Sum of weights of cases missing listwise. */
160 double valid; /* Sum of weights of valid cases. */
161 int bad_warn; /* Warn if bad weight found. */
162 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
163 int sort_ascending; /* !0: ascending order; 0: descending. */
164 unsigned long show_stats; /* Statistics to display. */
165 unsigned long calc_stats; /* Statistics to calculate. */
166 enum moment max_moment; /* Highest moment needed for stats. */
170 static enum dsc_statistic match_statistic (void);
171 static void free_dsc_proc (struct dsc_proc *);
173 /* Z-score functions. */
174 static int try_name (struct dsc_proc *dsc, char *name);
175 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
176 const char *name, size_t *z_cnt);
177 static void dump_z_table (struct dsc_proc *);
178 static void setup_z_trns (struct dsc_proc *);
180 /* Procedure execution functions. */
181 static bool calc_descriptives (const struct ccase *first,
182 const struct casefile *, void *dsc_);
183 static void display (struct dsc_proc *dsc);
185 /* Parser and outline. */
187 /* Handles DESCRIPTIVES. */
189 cmd_descriptives (void)
191 struct dsc_proc *dsc;
192 struct variable **vars = NULL;
194 int save_z_scores = 0;
199 /* Create and initialize dsc. */
200 dsc = xmalloc (sizeof *dsc);
203 dsc->missing_type = DSC_VARIABLE;
204 dsc->include_user_missing = 0;
205 dsc->show_var_labels = 1;
207 dsc->format = DSC_LINE;
208 dsc->missing_listwise = 0.;
211 dsc->sort_by_stat = DSC_NONE;
212 dsc->sort_ascending = 1;
213 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
215 /* Parse DESCRIPTIVES. */
218 if (lex_match_id ("MISSING"))
221 while (token != '.' && token != '/')
223 if (lex_match_id ("VARIABLE"))
224 dsc->missing_type = DSC_VARIABLE;
225 else if (lex_match_id ("LISTWISE"))
226 dsc->missing_type = DSC_LISTWISE;
227 else if (lex_match_id ("INCLUDE"))
228 dsc->include_user_missing = 1;
237 else if (lex_match_id ("SAVE"))
239 else if (lex_match_id ("FORMAT"))
242 while (token != '.' && token != '/')
244 if (lex_match_id ("LABELS"))
245 dsc->show_var_labels = 1;
246 else if (lex_match_id ("NOLABELS"))
247 dsc->show_var_labels = 0;
248 else if (lex_match_id ("INDEX"))
250 else if (lex_match_id ("NOINDEX"))
252 else if (lex_match_id ("LINE"))
253 dsc->format = DSC_LINE;
254 else if (lex_match_id ("SERIAL"))
255 dsc->format = DSC_SERIAL;
264 else if (lex_match_id ("STATISTICS"))
268 while (token != '.' && token != '/')
270 if (lex_match (T_ALL))
271 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
272 else if (lex_match_id ("DEFAULT"))
273 dsc->show_stats |= DEFAULT_STATS;
275 dsc->show_stats |= 1ul << (match_statistic ());
278 if (dsc->show_stats == 0)
279 dsc->show_stats = DEFAULT_STATS;
281 else if (lex_match_id ("SORT"))
284 if (lex_match_id ("NAME"))
285 dsc->sort_by_stat = DSC_NAME;
288 dsc->sort_by_stat = match_statistic ();
289 if (dsc->sort_by_stat == DSC_NONE )
290 dsc->sort_by_stat = DSC_MEAN;
294 if (lex_match_id ("A"))
295 dsc->sort_ascending = 1;
296 else if (lex_match_id ("D"))
297 dsc->sort_ascending = 0;
300 lex_force_match (')');
303 else if (var_cnt == 0)
305 if (lex_look_ahead () == '=')
307 lex_match_id ("VARIABLES");
311 while (token != '.' && token != '/')
315 if (!parse_variables (default_dict, &vars, &var_cnt,
316 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
319 dsc->vars = xnrealloc (dsc->vars, var_cnt, sizeof *dsc->vars);
320 for (i = dsc->var_cnt; i < var_cnt; i++)
322 struct dsc_var *dv = &dsc->vars[i];
324 dv->z_name[0] = '\0';
327 dsc->var_cnt = var_cnt;
336 if (try_name (dsc, tokid))
338 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
342 msg (SE, _("Z-score variable name %s would be"
343 " a duplicate variable name."), tokid);
345 if (!lex_force_match (')'))
360 msg (SE, _("No variables specified."));
364 /* Construct z-score varnames, show translation table. */
365 if (z_cnt || save_z_scores)
371 for (i = 0; i < dsc->var_cnt; i++)
372 if (dsc->vars[i].z_name[0] == 0)
374 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
375 dsc->vars[i].v->name, &gen_cnt))
383 /* Figure out statistics to display. */
384 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
385 dsc->show_stats |= 1ul << DSC_SESKEW;
386 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
387 dsc->show_stats |= 1ul << DSC_SEKURT;
389 /* Figure out which statistics to calculate. */
390 dsc->calc_stats = dsc->show_stats;
392 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
393 if (dsc->sort_by_stat >= 0)
394 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
395 if (dsc->show_stats & (1ul << DSC_SESKEW))
396 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
397 if (dsc->show_stats & (1ul << DSC_SEKURT))
398 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
400 /* Figure out maximum moment needed and allocate moments for
402 dsc->max_moment = MOMENT_NONE;
403 for (i = 0; i < DSC_N_STATS; i++)
404 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
405 dsc->max_moment = dsc_info[i].moment;
406 if (dsc->max_moment != MOMENT_NONE)
407 for (i = 0; i < dsc->var_cnt; i++)
408 dsc->vars[i].moments = moments_create (dsc->max_moment);
411 ok = multipass_procedure_with_splits (calc_descriptives, dsc);
420 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
428 /* Returns the statistic named by the current token and skips past the token.
429 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
430 specifiers). Emits an error if the current token ID does not name a
432 static enum dsc_statistic
433 match_statistic (void)
437 enum dsc_statistic stat;
439 for (stat = 0; stat < DSC_N_STATS; stat++)
440 if (lex_match_id (dsc_info[stat].identifier))
444 lex_error (_("expecting statistic name: reverting to default"));
452 free_dsc_proc (struct dsc_proc *dsc)
459 for (i = 0; i < dsc->var_cnt; i++)
460 moments_destroy (dsc->vars[i].moments);
467 /* Returns 0 if NAME is a duplicate of any existing variable name or
468 of any previously-declared z-var name; otherwise returns 1. */
470 try_name (struct dsc_proc *dsc, char *name)
474 if (dict_lookup_var (default_dict, name) != NULL)
476 for (i = 0; i < dsc->var_cnt; i++)
477 if (!strcasecmp (dsc->vars[i].z_name, name))
482 /* Generates a name for a Z-score variable based on a variable
483 named VAR_NAME, given that *Z_CNT generated variable names are
484 known to already exist. If successful, returns nonzero and
485 copies the new name into Z_NAME. On failure, returns zero. */
487 generate_z_varname (struct dsc_proc *dsc, char *z_name,
488 const char *var_name, size_t *z_cnt)
490 char name[LONG_NAME_LEN + 1];
492 /* Try a name based on the original variable name. */
494 str_copy_trunc (name + 1, sizeof name - 1, var_name);
495 if (try_name (dsc, name))
497 strcpy (z_name, name);
501 /* Generate a synthetic name. */
507 sprintf (name, "ZSC%03d", *z_cnt);
508 else if (*z_cnt <= 108)
509 sprintf (name, "STDZ%02d", *z_cnt - 99);
510 else if (*z_cnt <= 117)
511 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
512 else if (*z_cnt <= 126)
513 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
516 msg (SE, _("Ran out of generic names for Z-score variables. "
517 "There are only 126 generic names: ZSC001-ZSC0999, "
518 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
522 if (try_name (dsc, name))
524 strcpy (z_name, name);
530 /* Outputs a table describing the mapping between source
531 variables and Z-score variables. */
533 dump_z_table (struct dsc_proc *dsc)
541 for (i = 0; i < dsc->var_cnt; i++)
542 if (dsc->vars[i].z_name[0] != '\0')
546 t = tab_create (2, cnt + 1, 0);
547 tab_title (t, _("Mapping of variables to corresponding Z-scores."));
548 tab_columns (t, SOM_COL_DOWN, 1);
549 tab_headers (t, 0, 0, 1, 0);
550 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
551 tab_hline (t, TAL_2, 0, 1, 1);
552 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
553 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
554 tab_dim (t, tab_natural_dimensions);
559 for (i = 0, y = 1; i < dsc->var_cnt; i++)
560 if (dsc->vars[i].z_name[0] != '\0')
562 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
563 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
570 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
571 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
572 SYSMIS 3) score is user missing and they were not included in the original
573 analyis. 4) any of the variables in the original analysis were missing
574 (either system or user-missing values that weren't included).
577 descriptives_trns_proc (void *trns_, struct ccase * c,
580 struct dsc_trns *t = trns_;
581 struct dsc_z_score *z;
582 struct variable **vars;
585 if (t->missing_type == DSC_LISTWISE)
588 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
590 double score = case_num (c, (*vars)->fv);
592 || (!t->include_user_missing
593 && mv_is_num_user_missing (&(*vars)->miss, score)))
601 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
603 double input = case_num (c, z->src_idx);
604 double *output = &case_data_rw (c, z->dst_idx)->f;
606 if (z->mean == SYSMIS || z->std_dev == SYSMIS
607 || all_sysmis || input == SYSMIS
608 || (!t->include_user_missing
609 && mv_is_num_user_missing (&z->v->miss, input)))
612 *output = (input - z->mean) / z->std_dev;
614 return TRNS_CONTINUE;
617 /* Frees a descriptives_trns struct. */
619 descriptives_trns_free (void *trns_)
621 struct dsc_trns *t = trns_;
624 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
629 /* Sets up a transformation to calculate Z scores. */
631 setup_z_trns (struct dsc_proc *dsc)
636 for (cnt = i = 0; i < dsc->var_cnt; i++)
637 if (dsc->vars[i].z_name[0] != '\0')
640 t = xmalloc (sizeof *t);
641 t->z_scores = xnmalloc (cnt, sizeof *t->z_scores);
642 t->z_score_cnt = cnt;
643 t->missing_type = dsc->missing_type;
644 t->include_user_missing = dsc->include_user_missing;
645 if ( t->missing_type == DSC_LISTWISE )
647 t->var_cnt = dsc->var_cnt;
648 t->vars = xnmalloc (t->var_cnt, sizeof *t->vars);
649 for (i = 0; i < t->var_cnt; i++)
650 t->vars[i] = dsc->vars[i].v;
658 for (cnt = i = 0; i < dsc->var_cnt; i++)
660 struct dsc_var *dv = &dsc->vars[i];
661 if (dv->z_name[0] != '\0')
663 struct dsc_z_score *z;
665 struct variable *dst_var;
667 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
670 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
671 cp = stpcpy (dst_var->label, _("Z-score of "));
672 strcpy (cp, dv->v->label);
676 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
677 cp = stpcpy (dst_var->label, _("Z-score of "));
678 strcpy (cp, dv->v->name);
681 z = &t->z_scores[cnt++];
682 z->src_idx = dv->v->fv;
683 z->dst_idx = dst_var->fv;
684 z->mean = dv->stats[DSC_MEAN];
685 z->std_dev = dv->stats[DSC_STDDEV];
690 add_transformation (descriptives_trns_proc, descriptives_trns_free, t);
693 /* Statistical calculation. */
695 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
697 /* Calculates and displays descriptive statistics for the cases
700 calc_descriptives (const struct ccase *first,
701 const struct casefile *cf, void *dsc_)
703 struct dsc_proc *dsc = dsc_;
704 struct casereader *reader;
708 output_split_file_values (first);
710 for (i = 0; i < dsc->var_cnt; i++)
712 struct dsc_var *dv = &dsc->vars[i];
714 dv->valid = dv->missing = 0.0;
715 if (dv->moments != NULL)
716 moments_clear (dv->moments);
720 dsc->missing_listwise = 0.;
723 /* First pass to handle most of the work. */
724 for (reader = casefile_get_reader (cf);
725 casereader_read (reader, &c);
728 double weight = dict_get_case_weight (default_dict, &c, &dsc->bad_warn);
732 /* Check for missing values. */
733 if (listwise_missing (dsc, &c))
735 dsc->missing_listwise += weight;
736 if (dsc->missing_type == DSC_LISTWISE)
739 dsc->valid += weight;
741 for (i = 0; i < dsc->var_cnt; i++)
743 struct dsc_var *dv = &dsc->vars[i];
744 double x = case_num (&c, dv->v->fv);
746 if (dsc->missing_type != DSC_LISTWISE
748 || (!dsc->include_user_missing
749 && mv_is_num_user_missing (&dv->v->miss, x))))
751 dv->missing += weight;
755 if (dv->moments != NULL)
756 moments_pass_one (dv->moments, x, weight);
764 casereader_destroy (reader);
766 /* Second pass for higher-order moments. */
767 if (dsc->max_moment > MOMENT_MEAN)
769 for (reader = casefile_get_reader (cf);
770 casereader_read (reader, &c);
773 double weight = dict_get_case_weight (default_dict, &c,
778 /* Check for missing values. */
779 if (listwise_missing (dsc, &c)
780 && dsc->missing_type == DSC_LISTWISE)
783 for (i = 0; i < dsc->var_cnt; i++)
785 struct dsc_var *dv = &dsc->vars[i];
786 double x = case_num (&c, dv->v->fv);
788 if (dsc->missing_type != DSC_LISTWISE
790 || (!dsc->include_user_missing
791 && mv_is_num_user_missing (&dv->v->miss, x))))
794 if (dv->moments != NULL)
795 moments_pass_two (dv->moments, x, weight);
798 casereader_destroy (reader);
801 /* Calculate results. */
802 for (i = 0; i < dsc->var_cnt; i++)
804 struct dsc_var *dv = &dsc->vars[i];
808 for (j = 0; j < DSC_N_STATS; j++)
809 dv->stats[j] = SYSMIS;
811 dv->valid = W = dsc->valid - dv->missing;
813 if (dv->moments != NULL)
814 moments_calculate (dv->moments, NULL,
815 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
816 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
817 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
818 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
819 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
820 if (dsc->calc_stats & (1ul << DSC_STDDEV)
821 && dv->stats[DSC_VARIANCE] != SYSMIS)
822 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
823 if (dsc->calc_stats & (1ul << DSC_SEKURT))
824 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
825 dv->stats[DSC_SEKURT] = calc_sekurt (W);
826 if (dsc->calc_stats & (1ul << DSC_SESKEW)
827 && dv->stats[DSC_SKEWNESS] != SYSMIS)
828 dv->stats[DSC_SESKEW] = calc_seskew (W);
829 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
830 ? SYSMIS : dv->max - dv->min);
831 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
832 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
833 if (dsc->calc_stats & (1ul << DSC_SUM))
834 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
837 /* Output results. */
843 /* Returns nonzero if any of the descriptives variables in DSC's
844 variable list have missing values in case C, zero otherwise. */
846 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
850 for (i = 0; i < dsc->var_cnt; i++)
852 struct dsc_var *dv = &dsc->vars[i];
853 double x = case_num (c, dv->v->fv);
856 || (!dsc->include_user_missing
857 && mv_is_num_user_missing (&dv->v->miss, x)))
863 /* Statistical display. */
865 static algo_compare_func descriptives_compare_dsc_vars;
867 /* Displays a table of descriptive statistics for DSC. */
869 display (struct dsc_proc *dsc)
875 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
876 for (i = 0; i < DSC_N_STATS; i++)
877 if (dsc->show_stats & (1ul << i))
880 if (dsc->sort_by_stat != DSC_NONE)
881 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
882 descriptives_compare_dsc_vars, dsc);
884 t = tab_create (nc, dsc->var_cnt + 1, 0);
885 tab_headers (t, 1, 0, 1, 0);
886 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
887 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
888 tab_hline (t, TAL_2, 0, nc - 1, 1);
889 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
890 tab_dim (t, tab_natural_dimensions);
893 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
894 if (dsc->format == DSC_SERIAL)
896 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
897 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
900 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
902 for (i = 0; i < DSC_N_STATS; i++)
903 if (dsc->show_stats & (1ul << i))
905 const char *title = gettext (dsc_info[i].name);
906 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
909 for (i = 0; i < dsc->var_cnt; i++)
911 struct dsc_var *dv = &dsc->vars[i];
915 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
916 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
917 if (dsc->format == DSC_SERIAL)
918 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
919 for (j = 0; j < DSC_N_STATS; j++)
920 if (dsc->show_stats & (1ul << j))
921 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
924 tab_title (t, _("Valid cases = %g; cases with missing value(s) = %g."),
925 dsc->valid, dsc->missing_listwise);
930 /* Compares `struct dsc_var's A and B according to the ordering
933 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
935 const struct dsc_var *a = a_;
936 const struct dsc_var *b = b_;
937 struct dsc_proc *dsc = dsc_;
941 if (dsc->sort_by_stat == DSC_NAME)
942 result = strcasecmp (a->v->name, b->v->name);
945 double as = a->stats[dsc->sort_by_stat];
946 double bs = b->stats[dsc->sort_by_stat];
948 result = as < bs ? -1 : as > bs;
951 if (!dsc->sort_ascending)