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. */
27 #include "algorithm.h"
32 #include "dictionary.h"
43 #define _(msgid) gettext (msgid)
44 #define N_(msgid) msgid
46 /* DESCRIPTIVES private data. */
50 /* Handling of missing values. */
53 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
54 DSC_LISTWISE /* Discard entire case if any variable is missing. */
57 /* Describes properties of a distribution for the purpose of
58 calculating a Z-score. */
61 int src_idx; /* Source index into case data. */
62 int dst_idx; /* Destination index into case data. */
63 double mean; /* Distribution mean. */
64 double std_dev; /* Distribution standard deviation. */
65 struct variable *v; /* Variable on which z-score is based. */
68 /* DESCRIPTIVES transformation (for calculating Z-scores). */
72 struct dsc_z_score *z_scores; /* Array of Z-scores. */
73 int z_score_cnt; /* Number of Z-scores. */
74 struct variable **vars; /* Variables for listwise missing checks. */
75 size_t var_cnt; /* Number of variables. */
76 enum dsc_missing_type missing_type; /* Treatment of missing values. */
77 int include_user_missing; /* Nonzero to include user-missing values. */
80 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
83 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
84 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
85 DSC_MAX, DSC_SUM, DSC_N_STATS,
87 /* Only valid as sort criteria. */
88 DSC_NAME = -2, /* Sort by name. */
89 DSC_NONE = -1 /* Unsorted. */
92 /* Describes one statistic. */
93 struct dsc_statistic_info
95 const char *identifier; /* Identifier. */
96 const char *name; /* Full name. */
97 enum moment moment; /* Highest moment needed to calculate. */
100 /* Table of statistics, indexed by DSC_*. */
101 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
103 {"MEAN", N_("Mean"), MOMENT_MEAN},
104 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
105 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
106 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
107 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
108 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
109 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
110 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
111 {"RANGE", N_("Range"), MOMENT_NONE},
112 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
113 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
114 {"SUM", N_("Sum"), MOMENT_MEAN},
117 /* Statistics calculated by default if none are explicitly
119 #define DEFAULT_STATS \
120 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
123 /* A variable specified on DESCRIPTIVES. */
126 struct variable *v; /* Variable to calculate on. */
127 char z_name[LONG_NAME_LEN + 1]; /* Name for z-score variable. */
128 double valid, missing; /* Valid, missing counts. */
129 struct moments *moments; /* Moments. */
130 double min, max; /* Maximum and mimimum values. */
131 double stats[DSC_N_STATS]; /* All the stats' values. */
137 DSC_LINE, /* Abbreviated format. */
138 DSC_SERIAL /* Long format. */
141 /* A DESCRIPTIVES procedure. */
144 /* Per-variable info. */
145 struct dsc_var *vars; /* Variables. */
146 size_t var_cnt; /* Number of variables. */
149 enum dsc_missing_type missing_type; /* Treatment of missing values. */
150 int include_user_missing; /* Nonzero to include user-missing values. */
151 int show_var_labels; /* Nonzero to show variable labels. */
152 int show_index; /* Nonzero to show variable index. */
153 enum dsc_format format; /* Output format. */
155 /* Accumulated results. */
156 double missing_listwise; /* Sum of weights of cases missing listwise. */
157 double valid; /* Sum of weights of valid cases. */
158 int bad_warn; /* Warn if bad weight found. */
159 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
160 int sort_ascending; /* !0: ascending order; 0: descending. */
161 unsigned long show_stats; /* Statistics to display. */
162 unsigned long calc_stats; /* Statistics to calculate. */
163 enum moment max_moment; /* Highest moment needed for stats. */
167 static enum dsc_statistic match_statistic (void);
168 static void free_dsc_proc (struct dsc_proc *);
170 /* Z-score functions. */
171 static int try_name (struct dsc_proc *dsc, char *name);
172 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
173 const char *name, int *z_cnt);
174 static void dump_z_table (struct dsc_proc *);
175 static void setup_z_trns (struct dsc_proc *);
177 /* Procedure execution functions. */
178 static void calc_descriptives (const struct casefile *, void *dsc_);
179 static void display (struct dsc_proc *dsc);
181 /* Parser and outline. */
183 /* Handles DESCRIPTIVES. */
185 cmd_descriptives (void)
187 struct dsc_proc *dsc;
188 struct variable **vars = NULL;
190 int save_z_scores = 0;
194 /* Create and initialize dsc. */
195 dsc = xmalloc (sizeof *dsc);
198 dsc->missing_type = DSC_VARIABLE;
199 dsc->include_user_missing = 0;
200 dsc->show_var_labels = 1;
202 dsc->format = DSC_LINE;
203 dsc->missing_listwise = 0.;
206 dsc->sort_by_stat = DSC_NONE;
207 dsc->sort_ascending = 1;
208 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
210 /* Parse DESCRIPTIVES. */
213 if (lex_match_id ("MISSING"))
216 while (token != '.' && token != '/')
218 if (lex_match_id ("VARIABLE"))
219 dsc->missing_type = DSC_VARIABLE;
220 else if (lex_match_id ("LISTWISE"))
221 dsc->missing_type = DSC_LISTWISE;
222 else if (lex_match_id ("INCLUDE"))
223 dsc->include_user_missing = 1;
232 else if (lex_match_id ("SAVE"))
234 else if (lex_match_id ("FORMAT"))
237 while (token != '.' && token != '/')
239 if (lex_match_id ("LABELS"))
240 dsc->show_var_labels = 1;
241 else if (lex_match_id ("NOLABELS"))
242 dsc->show_var_labels = 0;
243 else if (lex_match_id ("INDEX"))
245 else if (lex_match_id ("NOINDEX"))
247 else if (lex_match_id ("LINE"))
248 dsc->format = DSC_LINE;
249 else if (lex_match_id ("SERIAL"))
250 dsc->format = DSC_SERIAL;
259 else if (lex_match_id ("STATISTICS"))
263 while (token != '.' && token != '/')
265 if (lex_match (T_ALL))
266 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
267 else if (lex_match_id ("DEFAULT"))
268 dsc->show_stats |= DEFAULT_STATS;
270 dsc->show_stats |= 1ul << (match_statistic ());
273 if (dsc->show_stats == 0)
274 dsc->show_stats = DEFAULT_STATS;
276 else if (lex_match_id ("SORT"))
279 if (lex_match_id ("NAME"))
280 dsc->sort_by_stat = DSC_NAME;
283 dsc->sort_by_stat = match_statistic ();
284 if (dsc->sort_by_stat == DSC_NONE )
285 dsc->sort_by_stat = DSC_MEAN;
289 if (lex_match_id ("A"))
290 dsc->sort_ascending = 1;
291 else if (lex_match_id ("D"))
292 dsc->sort_ascending = 0;
295 lex_force_match (')');
298 else if (var_cnt == 0)
300 if (lex_look_ahead () == '=')
302 lex_match_id ("VARIABLES");
306 while (token != '.' && token != '/')
310 if (!parse_variables (default_dict, &vars, &var_cnt,
311 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
314 dsc->vars = xrealloc (dsc->vars, sizeof *dsc->vars * var_cnt);
315 for (i = dsc->var_cnt; i < var_cnt; i++)
317 struct dsc_var *dv = &dsc->vars[i];
319 dv->z_name[0] = '\0';
322 dsc->var_cnt = var_cnt;
331 if (try_name (dsc, tokid))
333 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
337 msg (SE, _("Z-score variable name %s would be"
338 " a duplicate variable name."), tokid);
340 if (!lex_force_match (')'))
355 msg (SE, _("No variables specified."));
359 /* Construct z-score varnames, show translation table. */
360 if (z_cnt || save_z_scores)
366 for (i = 0; i < dsc->var_cnt; i++)
367 if (dsc->vars[i].z_name[0] == 0)
369 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
370 dsc->vars[i].v->name, &gen_cnt))
378 /* Figure out statistics to display. */
379 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
380 dsc->show_stats |= 1ul << DSC_SESKEW;
381 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
382 dsc->show_stats |= 1ul << DSC_SEKURT;
384 /* Figure out which statistics to calculate. */
385 dsc->calc_stats = dsc->show_stats;
387 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
388 if (dsc->sort_by_stat >= 0)
389 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
390 if (dsc->show_stats & (1ul << DSC_SESKEW))
391 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
392 if (dsc->show_stats & (1ul << DSC_SEKURT))
393 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
395 /* Figure out maximum moment needed and allocate moments for
397 dsc->max_moment = MOMENT_NONE;
398 for (i = 0; i < DSC_N_STATS; i++)
399 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
400 dsc->max_moment = dsc_info[i].moment;
401 if (dsc->max_moment != MOMENT_NONE)
402 for (i = 0; i < dsc->var_cnt; i++)
403 dsc->vars[i].moments = moments_create (dsc->max_moment);
406 multipass_procedure_with_splits (calc_descriptives, dsc);
423 /* Returns the statistic named by the current token and skips past the token.
424 Returns DSC_NONE if no statistic is given (e.g., subcommand with no
425 specifiers). Emits an error if the current token ID does not name a
427 static enum dsc_statistic
428 match_statistic (void)
432 enum dsc_statistic stat;
434 for (stat = 0; stat < DSC_N_STATS; stat++)
435 if (lex_match_id (dsc_info[stat].identifier))
439 lex_error (_("expecting statistic name: reverting to default"));
447 free_dsc_proc (struct dsc_proc *dsc)
454 for (i = 0; i < dsc->var_cnt; i++)
455 moments_destroy (dsc->vars[i].moments);
462 /* Returns 0 if NAME is a duplicate of any existing variable name or
463 of any previously-declared z-var name; otherwise returns 1. */
465 try_name (struct dsc_proc *dsc, char *name)
469 if (dict_lookup_var (default_dict, name) != NULL)
471 for (i = 0; i < dsc->var_cnt; i++)
472 if (!strcasecmp (dsc->vars[i].z_name, name))
477 /* Generates a name for a Z-score variable based on a variable
478 named VAR_NAME, given that *Z_CNT generated variable names are
479 known to already exist. If successful, returns nonzero and
480 copies the new name into Z_NAME. On failure, returns zero. */
482 generate_z_varname (struct dsc_proc *dsc, char *z_name,
483 const char *var_name, int *z_cnt)
485 char name[LONG_NAME_LEN + 1];
487 /* Try a name based on the original variable name. */
489 str_copy_trunc (name + 1, sizeof 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);
587 || (!t->include_user_missing
588 && mv_is_num_user_missing (&(*vars)->miss, score)))
596 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
598 double input = case_num (c, z->src_idx);
599 double *output = &case_data_rw (c, z->dst_idx)->f;
601 if (z->mean == SYSMIS || z->std_dev == SYSMIS
602 || all_sysmis || input == SYSMIS
603 || (!t->include_user_missing
604 && mv_is_num_user_missing (&z->v->miss, input)))
607 *output = (input - z->mean) / z->std_dev;
612 /* Frees a descriptives_trns struct. */
614 descriptives_trns_free (struct trns_header * trns)
616 struct dsc_trns *t = (struct dsc_trns *) trns;
619 assert((t->missing_type != DSC_LISTWISE) ^ (t->vars != NULL));
623 /* Sets up a transformation to calculate Z scores. */
625 setup_z_trns (struct dsc_proc *dsc)
630 for (cnt = i = 0; i < dsc->var_cnt; i++)
631 if (dsc->vars[i].z_name[0] != '\0')
634 t = xmalloc (sizeof *t);
635 t->h.proc = descriptives_trns_proc;
636 t->h.free = descriptives_trns_free;
637 t->z_scores = xmalloc (cnt * sizeof *t->z_scores);
638 t->z_score_cnt = cnt;
639 t->missing_type = dsc->missing_type;
640 t->include_user_missing = dsc->include_user_missing;
641 if ( t->missing_type == DSC_LISTWISE )
643 t->var_cnt = dsc->var_cnt;
644 t->vars = xmalloc(t->var_cnt * sizeof *t->vars);
645 for (i = 0; i < t->var_cnt; i++)
646 t->vars[i] = dsc->vars[i].v;
655 for (cnt = i = 0; i < dsc->var_cnt; i++)
657 struct dsc_var *dv = &dsc->vars[i];
658 if (dv->z_name[0] != '\0')
660 struct dsc_z_score *z;
662 struct variable *dst_var;
664 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 ((struct trns_header *) 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. */
836 /* Returns nonzero if any of the descriptives variables in DSC's
837 variable list have missing values in case C, zero otherwise. */
839 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
843 for (i = 0; i < dsc->var_cnt; i++)
845 struct dsc_var *dv = &dsc->vars[i];
846 double x = case_num (c, dv->v->fv);
849 || (!dsc->include_user_missing
850 && mv_is_num_user_missing (&dv->v->miss, x)))
856 /* Statistical display. */
858 static algo_compare_func descriptives_compare_dsc_vars;
860 /* Displays a table of descriptive statistics for DSC. */
862 display (struct dsc_proc *dsc)
868 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
869 for (i = 0; i < DSC_N_STATS; i++)
870 if (dsc->show_stats & (1ul << i))
873 if (dsc->sort_by_stat != DSC_NONE)
874 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
875 descriptives_compare_dsc_vars, dsc);
877 t = tab_create (nc, dsc->var_cnt + 1, 0);
878 tab_headers (t, 1, 0, 1, 0);
879 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
880 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
881 tab_hline (t, TAL_2, 0, nc - 1, 1);
882 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
883 tab_dim (t, tab_natural_dimensions);
886 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
887 if (dsc->format == DSC_SERIAL)
889 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
890 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
893 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
895 for (i = 0; i < DSC_N_STATS; i++)
896 if (dsc->show_stats & (1ul << i))
898 const char *title = gettext (dsc_info[i].name);
899 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
902 for (i = 0; i < dsc->var_cnt; i++)
904 struct dsc_var *dv = &dsc->vars[i];
907 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
908 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
909 if (dsc->format == DSC_SERIAL)
910 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
911 for (j = 0; j < DSC_N_STATS; j++)
912 if (dsc->show_stats & (1ul << j))
913 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
916 tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
917 dsc->valid, dsc->missing_listwise);
922 /* Compares `struct dsc_var's A and B according to the ordering
925 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
927 const struct dsc_var *a = a_;
928 const struct dsc_var *b = b_;
929 struct dsc_proc *dsc = dsc_;
933 if (dsc->sort_by_stat == DSC_NAME)
934 result = strcasecmp (a->v->name, b->v->name);
937 double as = a->stats[dsc->sort_by_stat];
938 double bs = b->stats[dsc->sort_by_stat];
940 result = as < bs ? -1 : as > bs;
943 if (!dsc->sort_ascending)