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/lexer/lexer.h>
36 #include <libpspp/alloc.h>
37 #include <libpspp/array.h>
38 #include <libpspp/compiler.h>
39 #include <libpspp/magic.h>
40 #include <libpspp/message.h>
41 #include <math/moments.h>
42 #include <output/manager.h>
43 #include <output/table.h>
46 #define _(msgid) gettext (msgid)
47 #define N_(msgid) msgid
49 /* DESCRIPTIVES private data. */
53 /* Handling of missing values. */
56 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
57 DSC_LISTWISE /* Discard entire case if any variable is missing. */
60 /* Describes properties of a distribution for the purpose of
61 calculating a Z-score. */
64 int src_idx; /* Source index into case data. */
65 int dst_idx; /* Destination index into case data. */
66 double mean; /* Distribution mean. */
67 double std_dev; /* Distribution standard deviation. */
68 struct variable *v; /* Variable on which z-score is based. */
71 /* DESCRIPTIVES transformation (for calculating Z-scores). */
74 struct dsc_z_score *z_scores; /* Array of Z-scores. */
75 int z_score_cnt; /* Number of Z-scores. */
76 struct variable **vars; /* Variables for listwise missing checks. */
77 size_t var_cnt; /* Number of variables. */
78 enum dsc_missing_type missing_type; /* Treatment of missing values. */
79 int include_user_missing; /* Nonzero to include user-missing values. */
82 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
85 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
86 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
87 DSC_MAX, DSC_SUM, DSC_N_STATS,
89 /* Only valid as sort criteria. */
90 DSC_NAME = -2, /* Sort by name. */
91 DSC_NONE = -1 /* Unsorted. */
94 /* Describes one statistic. */
95 struct dsc_statistic_info
97 const char *identifier; /* Identifier. */
98 const char *name; /* Full name. */
99 enum moment moment; /* Highest moment needed to calculate. */
102 /* Table of statistics, indexed by DSC_*. */
103 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
105 {"MEAN", N_("Mean"), MOMENT_MEAN},
106 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
107 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
108 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
109 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
110 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
111 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
112 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
113 {"RANGE", N_("Range"), MOMENT_NONE},
114 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
115 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
116 {"SUM", N_("Sum"), MOMENT_MEAN},
119 /* Statistics calculated by default if none are explicitly
121 #define DEFAULT_STATS \
122 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
125 /* A variable specified on DESCRIPTIVES. */
128 struct variable *v; /* Variable to calculate on. */
129 char z_name[LONG_NAME_LEN + 1]; /* Name for z-score variable. */
130 double valid, missing; /* Valid, missing counts. */
131 struct moments *moments; /* Moments. */
132 double min, max; /* Maximum and mimimum values. */
133 double stats[DSC_N_STATS]; /* All the stats' values. */
139 DSC_LINE, /* Abbreviated format. */
140 DSC_SERIAL /* Long format. */
143 /* A DESCRIPTIVES procedure. */
146 /* Per-variable info. */
147 struct dsc_var *vars; /* Variables. */
148 size_t var_cnt; /* Number of variables. */
151 enum dsc_missing_type missing_type; /* Treatment of missing values. */
152 int include_user_missing; /* Nonzero to include user-missing values. */
153 int show_var_labels; /* Nonzero to show variable labels. */
154 int show_index; /* Nonzero to show variable index. */
155 enum dsc_format format; /* Output format. */
157 /* Accumulated results. */
158 double missing_listwise; /* Sum of weights of cases missing listwise. */
159 double valid; /* Sum of weights of valid cases. */
160 int bad_warn; /* Warn if bad weight found. */
161 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
162 int sort_ascending; /* !0: ascending order; 0: descending. */
163 unsigned long show_stats; /* Statistics to display. */
164 unsigned long calc_stats; /* Statistics to calculate. */
165 enum moment max_moment; /* Highest moment needed for stats. */
169 static enum dsc_statistic match_statistic (void);
170 static void free_dsc_proc (struct dsc_proc *);
172 /* Z-score functions. */
173 static int try_name (struct dsc_proc *dsc, char *name);
174 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
175 const char *name, size_t *z_cnt);
176 static void dump_z_table (struct dsc_proc *);
177 static void setup_z_trns (struct dsc_proc *);
179 /* Procedure execution functions. */
180 static bool calc_descriptives (const struct casefile *, void *dsc_);
181 static void display (struct dsc_proc *dsc);
183 /* Parser and outline. */
185 /* Handles DESCRIPTIVES. */
187 cmd_descriptives (void)
189 struct dsc_proc *dsc;
190 struct variable **vars = NULL;
192 int save_z_scores = 0;
197 /* Create and initialize dsc. */
198 dsc = xmalloc (sizeof *dsc);
201 dsc->missing_type = DSC_VARIABLE;
202 dsc->include_user_missing = 0;
203 dsc->show_var_labels = 1;
205 dsc->format = DSC_LINE;
206 dsc->missing_listwise = 0.;
209 dsc->sort_by_stat = DSC_NONE;
210 dsc->sort_ascending = 1;
211 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
213 /* Parse DESCRIPTIVES. */
216 if (lex_match_id ("MISSING"))
219 while (token != '.' && token != '/')
221 if (lex_match_id ("VARIABLE"))
222 dsc->missing_type = DSC_VARIABLE;
223 else if (lex_match_id ("LISTWISE"))
224 dsc->missing_type = DSC_LISTWISE;
225 else if (lex_match_id ("INCLUDE"))
226 dsc->include_user_missing = 1;
235 else if (lex_match_id ("SAVE"))
237 else if (lex_match_id ("FORMAT"))
240 while (token != '.' && token != '/')
242 if (lex_match_id ("LABELS"))
243 dsc->show_var_labels = 1;
244 else if (lex_match_id ("NOLABELS"))
245 dsc->show_var_labels = 0;
246 else if (lex_match_id ("INDEX"))
248 else if (lex_match_id ("NOINDEX"))
250 else if (lex_match_id ("LINE"))
251 dsc->format = DSC_LINE;
252 else if (lex_match_id ("SERIAL"))
253 dsc->format = DSC_SERIAL;
262 else if (lex_match_id ("STATISTICS"))
266 while (token != '.' && token != '/')
268 if (lex_match (T_ALL))
269 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
270 else if (lex_match_id ("DEFAULT"))
271 dsc->show_stats |= DEFAULT_STATS;
273 dsc->show_stats |= 1ul << (match_statistic ());
276 if (dsc->show_stats == 0)
277 dsc->show_stats = DEFAULT_STATS;
279 else if (lex_match_id ("SORT"))
282 if (lex_match_id ("NAME"))
283 dsc->sort_by_stat = DSC_NAME;
286 dsc->sort_by_stat = match_statistic ();
287 if (dsc->sort_by_stat == DSC_NONE )
288 dsc->sort_by_stat = DSC_MEAN;
292 if (lex_match_id ("A"))
293 dsc->sort_ascending = 1;
294 else if (lex_match_id ("D"))
295 dsc->sort_ascending = 0;
298 lex_force_match (')');
301 else if (var_cnt == 0)
303 if (lex_look_ahead () == '=')
305 lex_match_id ("VARIABLES");
309 while (token != '.' && token != '/')
313 if (!parse_variables (default_dict, &vars, &var_cnt,
314 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
317 dsc->vars = xnrealloc (dsc->vars, var_cnt, sizeof *dsc->vars);
318 for (i = dsc->var_cnt; i < var_cnt; i++)
320 struct dsc_var *dv = &dsc->vars[i];
322 dv->z_name[0] = '\0';
325 dsc->var_cnt = var_cnt;
334 if (try_name (dsc, tokid))
336 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
340 msg (SE, _("Z-score variable name %s would be"
341 " a duplicate variable name."), tokid);
343 if (!lex_force_match (')'))
358 msg (SE, _("No variables specified."));
362 /* Construct z-score varnames, show translation table. */
363 if (z_cnt || save_z_scores)
369 for (i = 0; i < dsc->var_cnt; i++)
370 if (dsc->vars[i].z_name[0] == 0)
372 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
373 dsc->vars[i].v->name, &gen_cnt))
381 /* Figure out statistics to display. */
382 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
383 dsc->show_stats |= 1ul << DSC_SESKEW;
384 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
385 dsc->show_stats |= 1ul << DSC_SEKURT;
387 /* Figure out which statistics to calculate. */
388 dsc->calc_stats = dsc->show_stats;
390 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
391 if (dsc->sort_by_stat >= 0)
392 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
393 if (dsc->show_stats & (1ul << DSC_SESKEW))
394 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
395 if (dsc->show_stats & (1ul << DSC_SEKURT))
396 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
398 /* Figure out maximum moment needed and allocate moments for
400 dsc->max_moment = MOMENT_NONE;
401 for (i = 0; i < DSC_N_STATS; i++)
402 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
403 dsc->max_moment = dsc_info[i].moment;
404 if (dsc->max_moment != MOMENT_NONE)
405 for (i = 0; i < dsc->var_cnt; i++)
406 dsc->vars[i].moments = moments_create (dsc->max_moment);
409 ok = multipass_procedure_with_splits (calc_descriptives, dsc);
418 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
426 /* Returns the statistic named by the current token and skips past the token.
427 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
428 specifiers). Emits an error if the current token ID does not name a
430 static enum dsc_statistic
431 match_statistic (void)
435 enum dsc_statistic stat;
437 for (stat = 0; stat < DSC_N_STATS; stat++)
438 if (lex_match_id (dsc_info[stat].identifier))
442 lex_error (_("expecting statistic name: reverting to default"));
450 free_dsc_proc (struct dsc_proc *dsc)
457 for (i = 0; i < dsc->var_cnt; i++)
458 moments_destroy (dsc->vars[i].moments);
465 /* Returns 0 if NAME is a duplicate of any existing variable name or
466 of any previously-declared z-var name; otherwise returns 1. */
468 try_name (struct dsc_proc *dsc, char *name)
472 if (dict_lookup_var (default_dict, name) != NULL)
474 for (i = 0; i < dsc->var_cnt; i++)
475 if (!strcasecmp (dsc->vars[i].z_name, name))
480 /* Generates a name for a Z-score variable based on a variable
481 named VAR_NAME, given that *Z_CNT generated variable names are
482 known to already exist. If successful, returns nonzero and
483 copies the new name into Z_NAME. On failure, returns zero. */
485 generate_z_varname (struct dsc_proc *dsc, char *z_name,
486 const char *var_name, size_t *z_cnt)
488 char name[LONG_NAME_LEN + 1];
490 /* Try a name based on the original variable name. */
492 str_copy_trunc (name + 1, sizeof name - 1, var_name);
493 if (try_name (dsc, name))
495 strcpy (z_name, name);
499 /* Generate a synthetic name. */
505 sprintf (name, "ZSC%03d", *z_cnt);
506 else if (*z_cnt <= 108)
507 sprintf (name, "STDZ%02d", *z_cnt - 99);
508 else if (*z_cnt <= 117)
509 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
510 else if (*z_cnt <= 126)
511 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
514 msg (SE, _("Ran out of generic names for Z-score variables. "
515 "There are only 126 generic names: ZSC001-ZSC0999, "
516 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
520 if (try_name (dsc, name))
522 strcpy (z_name, name);
528 /* Outputs a table describing the mapping between source
529 variables and Z-score variables. */
531 dump_z_table (struct dsc_proc *dsc)
539 for (i = 0; i < dsc->var_cnt; i++)
540 if (dsc->vars[i].z_name[0] != '\0')
544 t = tab_create (2, cnt + 1, 0);
545 tab_title (t, _("Mapping of variables to corresponding Z-scores."));
546 tab_columns (t, SOM_COL_DOWN, 1);
547 tab_headers (t, 0, 0, 1, 0);
548 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
549 tab_hline (t, TAL_2, 0, 1, 1);
550 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
551 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
552 tab_dim (t, tab_natural_dimensions);
557 for (i = 0, y = 1; i < dsc->var_cnt; i++)
558 if (dsc->vars[i].z_name[0] != '\0')
560 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
561 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
568 /* Transformation function to calculate Z-scores. Will return SYSMIS if any of
569 the following are true: 1) mean or standard deviation is SYSMIS 2) score is
570 SYSMIS 3) score is user missing and they were not included in the original
571 analyis. 4) any of the variables in the original analysis were missing
572 (either system or user-missing values that weren't included).
575 descriptives_trns_proc (void *trns_, struct ccase * c,
578 struct dsc_trns *t = trns_;
579 struct dsc_z_score *z;
580 struct variable **vars;
583 if (t->missing_type == DSC_LISTWISE)
586 for (vars = t->vars; vars < t->vars + t->var_cnt; vars++)
588 double score = case_num (c, (*vars)->fv);
590 || (!t->include_user_missing
591 && mv_is_num_user_missing (&(*vars)->miss, score)))
599 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
601 double input = case_num (c, z->src_idx);
602 double *output = &case_data_rw (c, z->dst_idx)->f;
604 if (z->mean == SYSMIS || z->std_dev == SYSMIS
605 || all_sysmis || input == SYSMIS
606 || (!t->include_user_missing
607 && mv_is_num_user_missing (&z->v->miss, input)))
610 *output = (input - z->mean) / z->std_dev;
612 return TRNS_CONTINUE;
615 /* Frees a descriptives_trns struct. */
617 descriptives_trns_free (void *trns_)
619 struct dsc_trns *t = trns_;
622 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
627 /* Sets up a transformation to calculate Z scores. */
629 setup_z_trns (struct dsc_proc *dsc)
634 for (cnt = i = 0; i < dsc->var_cnt; i++)
635 if (dsc->vars[i].z_name[0] != '\0')
638 t = xmalloc (sizeof *t);
639 t->z_scores = xnmalloc (cnt, sizeof *t->z_scores);
640 t->z_score_cnt = cnt;
641 t->missing_type = dsc->missing_type;
642 t->include_user_missing = dsc->include_user_missing;
643 if ( t->missing_type == DSC_LISTWISE )
645 t->var_cnt = dsc->var_cnt;
646 t->vars = xnmalloc (t->var_cnt, sizeof *t->vars);
647 for (i = 0; i < t->var_cnt; i++)
648 t->vars[i] = dsc->vars[i].v;
656 for (cnt = i = 0; i < dsc->var_cnt; i++)
658 struct dsc_var *dv = &dsc->vars[i];
659 if (dv->z_name[0] != '\0')
661 struct dsc_z_score *z;
663 struct variable *dst_var;
665 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
668 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
669 cp = stpcpy (dst_var->label, _("Z-score of "));
670 strcpy (cp, dv->v->label);
674 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
675 cp = stpcpy (dst_var->label, _("Z-score of "));
676 strcpy (cp, dv->v->name);
679 z = &t->z_scores[cnt++];
680 z->src_idx = dv->v->fv;
681 z->dst_idx = dst_var->fv;
682 z->mean = dv->stats[DSC_MEAN];
683 z->std_dev = dv->stats[DSC_STDDEV];
688 add_transformation (descriptives_trns_proc, descriptives_trns_free, t);
691 /* Statistical calculation. */
693 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
695 /* Calculates and displays descriptive statistics for the cases
698 calc_descriptives (const struct casefile *cf, void *dsc_)
700 struct dsc_proc *dsc = dsc_;
701 struct casereader *reader;
705 for (i = 0; i < dsc->var_cnt; i++)
707 struct dsc_var *dv = &dsc->vars[i];
709 dv->valid = dv->missing = 0.0;
710 if (dv->moments != NULL)
711 moments_clear (dv->moments);
715 dsc->missing_listwise = 0.;
718 /* First pass to handle most of the work. */
719 for (reader = casefile_get_reader (cf);
720 casereader_read (reader, &c);
723 double weight = dict_get_case_weight (default_dict, &c, &dsc->bad_warn);
727 /* Check for missing values. */
728 if (listwise_missing (dsc, &c))
730 dsc->missing_listwise += weight;
731 if (dsc->missing_type == DSC_LISTWISE)
734 dsc->valid += weight;
736 for (i = 0; i < dsc->var_cnt; i++)
738 struct dsc_var *dv = &dsc->vars[i];
739 double x = case_num (&c, dv->v->fv);
741 if (dsc->missing_type != DSC_LISTWISE
743 || (!dsc->include_user_missing
744 && mv_is_num_user_missing (&dv->v->miss, x))))
746 dv->missing += weight;
750 if (dv->moments != NULL)
751 moments_pass_one (dv->moments, x, weight);
759 casereader_destroy (reader);
761 /* Second pass for higher-order moments. */
762 if (dsc->max_moment > MOMENT_MEAN)
764 for (reader = casefile_get_reader (cf);
765 casereader_read (reader, &c);
768 double weight = dict_get_case_weight (default_dict, &c,
773 /* Check for missing values. */
774 if (listwise_missing (dsc, &c)
775 && dsc->missing_type == DSC_LISTWISE)
778 for (i = 0; i < dsc->var_cnt; i++)
780 struct dsc_var *dv = &dsc->vars[i];
781 double x = case_num (&c, dv->v->fv);
783 if (dsc->missing_type != DSC_LISTWISE
785 || (!dsc->include_user_missing
786 && mv_is_num_user_missing (&dv->v->miss, x))))
789 if (dv->moments != NULL)
790 moments_pass_two (dv->moments, x, weight);
793 casereader_destroy (reader);
796 /* Calculate results. */
797 for (i = 0; i < dsc->var_cnt; i++)
799 struct dsc_var *dv = &dsc->vars[i];
803 for (j = 0; j < DSC_N_STATS; j++)
804 dv->stats[j] = SYSMIS;
806 dv->valid = W = dsc->valid - dv->missing;
808 if (dv->moments != NULL)
809 moments_calculate (dv->moments, NULL,
810 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
811 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
812 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
813 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
814 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
815 if (dsc->calc_stats & (1ul << DSC_STDDEV)
816 && dv->stats[DSC_VARIANCE] != SYSMIS)
817 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
818 if (dsc->calc_stats & (1ul << DSC_SEKURT))
819 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
820 dv->stats[DSC_SEKURT] = calc_sekurt (W);
821 if (dsc->calc_stats & (1ul << DSC_SESKEW)
822 && dv->stats[DSC_SKEWNESS] != SYSMIS)
823 dv->stats[DSC_SESKEW] = calc_seskew (W);
824 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
825 ? SYSMIS : dv->max - dv->min);
826 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
827 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
828 if (dsc->calc_stats & (1ul << DSC_SUM))
829 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
832 /* Output results. */
838 /* Returns nonzero if any of the descriptives variables in DSC's
839 variable list have missing values in case C, zero otherwise. */
841 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
845 for (i = 0; i < dsc->var_cnt; i++)
847 struct dsc_var *dv = &dsc->vars[i];
848 double x = case_num (c, dv->v->fv);
851 || (!dsc->include_user_missing
852 && mv_is_num_user_missing (&dv->v->miss, x)))
858 /* Statistical display. */
860 static algo_compare_func descriptives_compare_dsc_vars;
862 /* Displays a table of descriptive statistics for DSC. */
864 display (struct dsc_proc *dsc)
870 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
871 for (i = 0; i < DSC_N_STATS; i++)
872 if (dsc->show_stats & (1ul << i))
875 if (dsc->sort_by_stat != DSC_NONE)
876 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
877 descriptives_compare_dsc_vars, dsc);
879 t = tab_create (nc, dsc->var_cnt + 1, 0);
880 tab_headers (t, 1, 0, 1, 0);
881 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
882 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
883 tab_hline (t, TAL_2, 0, nc - 1, 1);
884 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
885 tab_dim (t, tab_natural_dimensions);
888 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
889 if (dsc->format == DSC_SERIAL)
891 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
892 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
895 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
897 for (i = 0; i < DSC_N_STATS; i++)
898 if (dsc->show_stats & (1ul << i))
900 const char *title = gettext (dsc_info[i].name);
901 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
904 for (i = 0; i < dsc->var_cnt; i++)
906 struct dsc_var *dv = &dsc->vars[i];
910 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
911 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
912 if (dsc->format == DSC_SERIAL)
913 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
914 for (j = 0; j < DSC_N_STATS; j++)
915 if (dsc->show_stats & (1ul << j))
916 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
919 tab_title (t, _("Valid cases = %g; cases with missing value(s) = %g."),
920 dsc->valid, dsc->missing_listwise);
925 /* Compares `struct dsc_var's A and B according to the ordering
928 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
930 const struct dsc_var *a = a_;
931 const struct dsc_var *b = b_;
932 struct dsc_proc *dsc = dsc_;
936 if (dsc->sort_by_stat == DSC_NAME)
937 result = strcasecmp (a->v->name, b->v->name);
940 double as = a->stats[dsc->sort_by_stat];
941 double bs = b->stats[dsc->sort_by_stat];
943 result = as < bs ? -1 : as > bs;
946 if (!dsc->sort_ascending)