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 <language/lexer/variable-parser.h>
38 #include <libpspp/alloc.h>
39 #include <libpspp/array.h>
40 #include <libpspp/compiler.h>
41 #include <libpspp/magic.h>
42 #include <libpspp/message.h>
43 #include <libpspp/assertion.h>
44 #include <math/moments.h>
45 #include <output/manager.h>
46 #include <output/table.h>
49 #define _(msgid) gettext (msgid)
50 #define N_(msgid) msgid
52 /* DESCRIPTIVES private data. */
56 /* Handling of missing values. */
59 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
60 DSC_LISTWISE /* Discard entire case if any variable is missing. */
63 /* Describes properties of a distribution for the purpose of
64 calculating a Z-score. */
67 int src_idx; /* Source index into case data. */
68 int dst_idx; /* Destination index into case data. */
69 double mean; /* Distribution mean. */
70 double std_dev; /* Distribution standard deviation. */
71 struct variable *v; /* Variable on which z-score is based. */
74 /* DESCRIPTIVES transformation (for calculating Z-scores). */
77 struct dsc_z_score *z_scores; /* Array of Z-scores. */
78 int z_score_cnt; /* Number of Z-scores. */
79 struct variable **vars; /* Variables for listwise missing checks. */
80 size_t var_cnt; /* Number of variables. */
81 enum dsc_missing_type missing_type; /* Treatment of missing values. */
82 int include_user_missing; /* Nonzero to include user-missing values. */
85 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
88 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
89 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
90 DSC_MAX, DSC_SUM, DSC_N_STATS,
92 /* Only valid as sort criteria. */
93 DSC_NAME = -2, /* Sort by name. */
94 DSC_NONE = -1 /* Unsorted. */
97 /* Describes one statistic. */
98 struct dsc_statistic_info
100 const char *identifier; /* Identifier. */
101 const char *name; /* Full name. */
102 enum moment moment; /* Highest moment needed to calculate. */
105 /* Table of statistics, indexed by DSC_*. */
106 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
108 {"MEAN", N_("Mean"), MOMENT_MEAN},
109 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
110 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
111 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
112 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
113 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
114 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
115 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
116 {"RANGE", N_("Range"), MOMENT_NONE},
117 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
118 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
119 {"SUM", N_("Sum"), MOMENT_MEAN},
122 /* Statistics calculated by default if none are explicitly
124 #define DEFAULT_STATS \
125 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
128 /* A variable specified on DESCRIPTIVES. */
131 struct variable *v; /* Variable to calculate on. */
132 char z_name[LONG_NAME_LEN + 1]; /* Name for z-score variable. */
133 double valid, missing; /* Valid, missing counts. */
134 struct moments *moments; /* Moments. */
135 double min, max; /* Maximum and mimimum values. */
136 double stats[DSC_N_STATS]; /* All the stats' values. */
142 DSC_LINE, /* Abbreviated format. */
143 DSC_SERIAL /* Long format. */
146 /* A DESCRIPTIVES procedure. */
149 /* Per-variable info. */
150 struct dsc_var *vars; /* Variables. */
151 size_t var_cnt; /* Number of variables. */
154 enum dsc_missing_type missing_type; /* Treatment of missing values. */
155 int include_user_missing; /* Nonzero to include user-missing values. */
156 int show_var_labels; /* Nonzero to show variable labels. */
157 int show_index; /* Nonzero to show variable index. */
158 enum dsc_format format; /* Output format. */
160 /* Accumulated results. */
161 double missing_listwise; /* Sum of weights of cases missing listwise. */
162 double valid; /* Sum of weights of valid cases. */
163 bool bad_warn; /* Warn if bad weight found. */
164 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
165 int sort_ascending; /* !0: ascending order; 0: descending. */
166 unsigned long show_stats; /* Statistics to display. */
167 unsigned long calc_stats; /* Statistics to calculate. */
168 enum moment max_moment; /* Highest moment needed for stats. */
172 static enum dsc_statistic match_statistic (void);
173 static void free_dsc_proc (struct dsc_proc *);
175 /* Z-score functions. */
176 static bool try_name (struct dsc_proc *dsc, char *name);
177 static bool generate_z_varname (struct dsc_proc *dsc, char *z_name,
178 const char *name, size_t *z_cnt);
179 static void dump_z_table (struct dsc_proc *);
180 static void setup_z_trns (struct dsc_proc *);
182 /* Procedure execution functions. */
183 static bool calc_descriptives (const struct ccase *first,
184 const struct casefile *, void *dsc_);
185 static void display (struct dsc_proc *dsc);
187 /* Parser and outline. */
189 /* Handles DESCRIPTIVES. */
191 cmd_descriptives (void)
193 struct dsc_proc *dsc;
194 struct variable **vars = NULL;
196 int save_z_scores = 0;
201 /* Create and initialize dsc. */
202 dsc = xmalloc (sizeof *dsc);
205 dsc->missing_type = DSC_VARIABLE;
206 dsc->include_user_missing = 0;
207 dsc->show_var_labels = 1;
209 dsc->format = DSC_LINE;
210 dsc->missing_listwise = 0.;
213 dsc->sort_by_stat = DSC_NONE;
214 dsc->sort_ascending = 1;
215 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
217 /* Parse DESCRIPTIVES. */
220 if (lex_match_id ("MISSING"))
223 while (token != '.' && token != '/')
225 if (lex_match_id ("VARIABLE"))
226 dsc->missing_type = DSC_VARIABLE;
227 else if (lex_match_id ("LISTWISE"))
228 dsc->missing_type = DSC_LISTWISE;
229 else if (lex_match_id ("INCLUDE"))
230 dsc->include_user_missing = 1;
239 else if (lex_match_id ("SAVE"))
241 else if (lex_match_id ("FORMAT"))
244 while (token != '.' && token != '/')
246 if (lex_match_id ("LABELS"))
247 dsc->show_var_labels = 1;
248 else if (lex_match_id ("NOLABELS"))
249 dsc->show_var_labels = 0;
250 else if (lex_match_id ("INDEX"))
252 else if (lex_match_id ("NOINDEX"))
254 else if (lex_match_id ("LINE"))
255 dsc->format = DSC_LINE;
256 else if (lex_match_id ("SERIAL"))
257 dsc->format = DSC_SERIAL;
266 else if (lex_match_id ("STATISTICS"))
270 while (token != '.' && token != '/')
272 if (lex_match (T_ALL))
273 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
274 else if (lex_match_id ("DEFAULT"))
275 dsc->show_stats |= DEFAULT_STATS;
277 dsc->show_stats |= 1ul << (match_statistic ());
280 if (dsc->show_stats == 0)
281 dsc->show_stats = DEFAULT_STATS;
283 else if (lex_match_id ("SORT"))
286 if (lex_match_id ("NAME"))
287 dsc->sort_by_stat = DSC_NAME;
290 dsc->sort_by_stat = match_statistic ();
291 if (dsc->sort_by_stat == DSC_NONE )
292 dsc->sort_by_stat = DSC_MEAN;
296 if (lex_match_id ("A"))
297 dsc->sort_ascending = 1;
298 else if (lex_match_id ("D"))
299 dsc->sort_ascending = 0;
302 lex_force_match (')');
305 else if (var_cnt == 0)
307 if (lex_look_ahead () == '=')
309 lex_match_id ("VARIABLES");
313 while (token != '.' && token != '/')
317 if (!parse_variables (default_dict, &vars, &var_cnt,
318 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
321 dsc->vars = xnrealloc (dsc->vars, var_cnt, sizeof *dsc->vars);
322 for (i = dsc->var_cnt; i < var_cnt; i++)
324 struct dsc_var *dv = &dsc->vars[i];
326 dv->z_name[0] = '\0';
329 dsc->var_cnt = var_cnt;
338 if (try_name (dsc, tokid))
340 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
344 msg (SE, _("Z-score variable name %s would be"
345 " a duplicate variable name."), tokid);
347 if (!lex_force_match (')'))
362 msg (SE, _("No variables specified."));
366 /* Construct z-score varnames, show translation table. */
367 if (z_cnt || save_z_scores)
373 for (i = 0; i < dsc->var_cnt; i++)
374 if (dsc->vars[i].z_name[0] == 0)
376 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
377 dsc->vars[i].v->name, &gen_cnt))
385 /* Figure out statistics to display. */
386 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
387 dsc->show_stats |= 1ul << DSC_SESKEW;
388 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
389 dsc->show_stats |= 1ul << DSC_SEKURT;
391 /* Figure out which statistics to calculate. */
392 dsc->calc_stats = dsc->show_stats;
394 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
395 if (dsc->sort_by_stat >= 0)
396 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
397 if (dsc->show_stats & (1ul << DSC_SESKEW))
398 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
399 if (dsc->show_stats & (1ul << DSC_SEKURT))
400 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
402 /* Figure out maximum moment needed and allocate moments for
404 dsc->max_moment = MOMENT_NONE;
405 for (i = 0; i < DSC_N_STATS; i++)
406 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
407 dsc->max_moment = dsc_info[i].moment;
408 if (dsc->max_moment != MOMENT_NONE)
409 for (i = 0; i < dsc->var_cnt; i++)
410 dsc->vars[i].moments = moments_create (dsc->max_moment);
413 ok = multipass_procedure_with_splits (calc_descriptives, dsc);
422 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
430 /* Returns the statistic named by the current token and skips past the token.
431 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
432 specifiers). Emits an error if the current token ID does not name a
434 static enum dsc_statistic
435 match_statistic (void)
439 enum dsc_statistic stat;
441 for (stat = 0; stat < DSC_N_STATS; stat++)
442 if (lex_match_id (dsc_info[stat].identifier))
446 lex_error (_("expecting statistic name: reverting to default"));
454 free_dsc_proc (struct dsc_proc *dsc)
461 for (i = 0; i < dsc->var_cnt; i++)
462 moments_destroy (dsc->vars[i].moments);
469 /* Returns false if NAME is a duplicate of any existing variable name or
470 of any previously-declared z-var name; otherwise returns true. */
472 try_name (struct dsc_proc *dsc, char *name)
476 if (dict_lookup_var (default_dict, name) != NULL)
478 for (i = 0; i < dsc->var_cnt; i++)
479 if (!strcasecmp (dsc->vars[i].z_name, name))
484 /* Generates a name for a Z-score variable based on a variable
485 named VAR_NAME, given that *Z_CNT generated variable names are
486 known to already exist. If successful, returns true and
487 copies the new name into Z_NAME. On failure, returns false. */
489 generate_z_varname (struct dsc_proc *dsc, char *z_name,
490 const char *var_name, size_t *z_cnt)
492 char name[LONG_NAME_LEN + 1];
494 /* Try a name based on the original variable name. */
496 str_copy_trunc (name + 1, sizeof name - 1, var_name);
497 if (try_name (dsc, name))
499 strcpy (z_name, name);
503 /* Generate a synthetic name. */
509 sprintf (name, "ZSC%03d", *z_cnt);
510 else if (*z_cnt <= 108)
511 sprintf (name, "STDZ%02d", *z_cnt - 99);
512 else if (*z_cnt <= 117)
513 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
514 else if (*z_cnt <= 126)
515 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
518 msg (SE, _("Ran out of generic names for Z-score variables. "
519 "There are only 126 generic names: ZSC001-ZSC0999, "
520 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
524 if (try_name (dsc, name))
526 strcpy (z_name, name);
533 /* Outputs a table describing the mapping between source
534 variables and Z-score variables. */
536 dump_z_table (struct dsc_proc *dsc)
544 for (i = 0; i < dsc->var_cnt; i++)
545 if (dsc->vars[i].z_name[0] != '\0')
549 t = tab_create (2, cnt + 1, 0);
550 tab_title (t, _("Mapping of variables to corresponding Z-scores."));
551 tab_columns (t, SOM_COL_DOWN, 1);
552 tab_headers (t, 0, 0, 1, 0);
553 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
554 tab_hline (t, TAL_2, 0, 1, 1);
555 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
556 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
557 tab_dim (t, tab_natural_dimensions);
562 for (i = 0, y = 1; i < dsc->var_cnt; i++)
563 if (dsc->vars[i].z_name[0] != '\0')
565 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
566 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
573 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
574 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
575 SYSMIS 3) score is user missing and they were not included in the original
576 analyis. 4) any of the variables in the original analysis were missing
577 (either system or user-missing values that weren't included).
580 descriptives_trns_proc (void *trns_, struct ccase * c,
581 casenum_t case_idx UNUSED)
583 struct dsc_trns *t = trns_;
584 struct dsc_z_score *z;
585 struct variable **vars;
588 if (t->missing_type == DSC_LISTWISE)
591 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
593 double score = case_num (c, (*vars)->fv);
595 || (!t->include_user_missing
596 && mv_is_num_user_missing (&(*vars)->miss, score)))
604 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
606 double input = case_num (c, z->src_idx);
607 double *output = &case_data_rw (c, z->dst_idx)->f;
609 if (z->mean == SYSMIS || z->std_dev == SYSMIS
610 || all_sysmis || input == SYSMIS
611 || (!t->include_user_missing
612 && mv_is_num_user_missing (&z->v->miss, input)))
615 *output = (input - z->mean) / z->std_dev;
617 return TRNS_CONTINUE;
620 /* Frees a descriptives_trns struct. */
622 descriptives_trns_free (void *trns_)
624 struct dsc_trns *t = trns_;
627 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
632 /* Sets up a transformation to calculate Z scores. */
634 setup_z_trns (struct dsc_proc *dsc)
639 for (cnt = i = 0; i < dsc->var_cnt; i++)
640 if (dsc->vars[i].z_name[0] != '\0')
643 t = xmalloc (sizeof *t);
644 t->z_scores = xnmalloc (cnt, sizeof *t->z_scores);
645 t->z_score_cnt = cnt;
646 t->missing_type = dsc->missing_type;
647 t->include_user_missing = dsc->include_user_missing;
648 if ( t->missing_type == DSC_LISTWISE )
650 t->var_cnt = dsc->var_cnt;
651 t->vars = xnmalloc (t->var_cnt, sizeof *t->vars);
652 for (i = 0; i < t->var_cnt; i++)
653 t->vars[i] = dsc->vars[i].v;
661 for (cnt = i = 0; i < dsc->var_cnt; i++)
663 struct dsc_var *dv = &dsc->vars[i];
664 if (dv->z_name[0] != '\0')
666 struct dsc_z_score *z;
668 struct variable *dst_var;
670 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
673 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
674 cp = stpcpy (dst_var->label, _("Z-score of "));
675 strcpy (cp, dv->v->label);
679 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
680 cp = stpcpy (dst_var->label, _("Z-score of "));
681 strcpy (cp, dv->v->name);
684 z = &t->z_scores[cnt++];
685 z->src_idx = dv->v->fv;
686 z->dst_idx = dst_var->fv;
687 z->mean = dv->stats[DSC_MEAN];
688 z->std_dev = dv->stats[DSC_STDDEV];
693 add_transformation (descriptives_trns_proc, descriptives_trns_free, t);
696 /* Statistical calculation. */
698 static bool listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
700 /* Calculates and displays descriptive statistics for the cases
703 calc_descriptives (const struct ccase *first,
704 const struct casefile *cf, void *dsc_)
706 struct dsc_proc *dsc = dsc_;
707 struct casereader *reader;
711 output_split_file_values (first);
713 for (i = 0; i < dsc->var_cnt; i++)
715 struct dsc_var *dv = &dsc->vars[i];
717 dv->valid = dv->missing = 0.0;
718 if (dv->moments != NULL)
719 moments_clear (dv->moments);
723 dsc->missing_listwise = 0.;
726 /* First pass to handle most of the work. */
727 for (reader = casefile_get_reader (cf);
728 casereader_read (reader, &c);
731 double weight = dict_get_case_weight (default_dict, &c, &dsc->bad_warn);
735 /* Check for missing values. */
736 if (listwise_missing (dsc, &c))
738 dsc->missing_listwise += weight;
739 if (dsc->missing_type == DSC_LISTWISE)
742 dsc->valid += weight;
744 for (i = 0; i < dsc->var_cnt; i++)
746 struct dsc_var *dv = &dsc->vars[i];
747 double x = case_num (&c, dv->v->fv);
749 if (dsc->missing_type != DSC_LISTWISE
751 || (!dsc->include_user_missing
752 && mv_is_num_user_missing (&dv->v->miss, x))))
754 dv->missing += weight;
758 if (dv->moments != NULL)
759 moments_pass_one (dv->moments, x, weight);
767 casereader_destroy (reader);
769 /* Second pass for higher-order moments. */
770 if (dsc->max_moment > MOMENT_MEAN)
772 for (reader = casefile_get_reader (cf);
773 casereader_read (reader, &c);
776 double weight = dict_get_case_weight (default_dict, &c,
781 /* Check for missing values. */
782 if (listwise_missing (dsc, &c)
783 && dsc->missing_type == DSC_LISTWISE)
786 for (i = 0; i < dsc->var_cnt; i++)
788 struct dsc_var *dv = &dsc->vars[i];
789 double x = case_num (&c, dv->v->fv);
791 if (dsc->missing_type != DSC_LISTWISE
793 || (!dsc->include_user_missing
794 && mv_is_num_user_missing (&dv->v->miss, x))))
797 if (dv->moments != NULL)
798 moments_pass_two (dv->moments, x, weight);
801 casereader_destroy (reader);
804 /* Calculate results. */
805 for (i = 0; i < dsc->var_cnt; i++)
807 struct dsc_var *dv = &dsc->vars[i];
811 for (j = 0; j < DSC_N_STATS; j++)
812 dv->stats[j] = SYSMIS;
814 dv->valid = W = dsc->valid - dv->missing;
816 if (dv->moments != NULL)
817 moments_calculate (dv->moments, NULL,
818 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
819 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
820 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
821 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
822 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
823 if (dsc->calc_stats & (1ul << DSC_STDDEV)
824 && dv->stats[DSC_VARIANCE] != SYSMIS)
825 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
826 if (dsc->calc_stats & (1ul << DSC_SEKURT))
827 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
828 dv->stats[DSC_SEKURT] = calc_sekurt (W);
829 if (dsc->calc_stats & (1ul << DSC_SESKEW)
830 && dv->stats[DSC_SKEWNESS] != SYSMIS)
831 dv->stats[DSC_SESKEW] = calc_seskew (W);
832 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
833 ? SYSMIS : dv->max - dv->min);
834 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
835 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
836 if (dsc->calc_stats & (1ul << DSC_SUM))
837 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
840 /* Output results. */
846 /* Returns true if any of the descriptives variables in DSC's
847 variable list have missing values in case C, false otherwise. */
849 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
853 for (i = 0; i < dsc->var_cnt; i++)
855 struct dsc_var *dv = &dsc->vars[i];
856 double x = case_num (c, dv->v->fv);
859 || (!dsc->include_user_missing
860 && mv_is_num_user_missing (&dv->v->miss, x)))
866 /* Statistical display. */
868 static algo_compare_func descriptives_compare_dsc_vars;
870 /* Displays a table of descriptive statistics for DSC. */
872 display (struct dsc_proc *dsc)
878 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
879 for (i = 0; i < DSC_N_STATS; i++)
880 if (dsc->show_stats & (1ul << i))
883 if (dsc->sort_by_stat != DSC_NONE)
884 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
885 descriptives_compare_dsc_vars, dsc);
887 t = tab_create (nc, dsc->var_cnt + 1, 0);
888 tab_headers (t, 1, 0, 1, 0);
889 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
890 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
891 tab_hline (t, TAL_2, 0, nc - 1, 1);
892 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
893 tab_dim (t, tab_natural_dimensions);
896 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
897 if (dsc->format == DSC_SERIAL)
899 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
900 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
903 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
905 for (i = 0; i < DSC_N_STATS; i++)
906 if (dsc->show_stats & (1ul << i))
908 const char *title = gettext (dsc_info[i].name);
909 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
912 for (i = 0; i < dsc->var_cnt; i++)
914 struct dsc_var *dv = &dsc->vars[i];
918 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
919 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
920 if (dsc->format == DSC_SERIAL)
921 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
922 for (j = 0; j < DSC_N_STATS; j++)
923 if (dsc->show_stats & (1ul << j))
924 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
927 tab_title (t, _("Valid cases = %g; cases with missing value(s) = %g."),
928 dsc->valid, dsc->missing_listwise);
933 /* Compares `struct dsc_var's A and B according to the ordering
936 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
938 const struct dsc_var *a = a_;
939 const struct dsc_var *b = b_;
940 struct dsc_proc *dsc = dsc_;
944 if (dsc->sort_by_stat == DSC_NAME)
945 result = strcasecmp (a->v->name, b->v->name);
948 double as = a->stats[dsc->sort_by_stat];
949 double bs = b->stats[dsc->sort_by_stat];
951 result = as < bs ? -1 : as > bs;
954 if (!dsc->sort_ascending)