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"
40 /* DESCRIPTIVES private data. */
42 /* Describes properties of a distribution for the purpose of
43 calculating a Z-score. */
46 int src_idx; /* Source index into case data. */
47 int dst_idx; /* Destination index into case data. */
48 double mean; /* Distribution mean. */
49 double std_dev; /* Distribution standard deviation. */
52 /* DESCRIPTIVES transformation (for calculating Z-scores). */
56 struct dsc_z_score *z_scores; /* Array of Z-scores. */
57 int z_score_cnt; /* Number of Z-scores. */
60 /* Statistics. Used as bit indexes, so must be 32 or fewer. */
63 DSC_MEAN = 0, DSC_SEMEAN, DSC_STDDEV, DSC_VARIANCE, DSC_KURTOSIS,
64 DSC_SEKURT, DSC_SKEWNESS, DSC_SESKEW, DSC_RANGE, DSC_MIN,
65 DSC_MAX, DSC_SUM, DSC_N_STATS,
67 /* Only valid as sort criteria. */
68 DSC_NAME = -2, /* Sort by name. */
69 DSC_NONE = -1 /* Unsorted. */
72 /* Describes one statistic. */
73 struct dsc_statistic_info
75 const char *identifier; /* Identifier. */
76 const char *name; /* Full name. */
77 enum moment moment; /* Highest moment needed to calculate. */
80 /* Table of statistics, indexed by DSC_*. */
81 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
83 {"MEAN", N_("Mean"), MOMENT_MEAN},
84 {"SEMEAN", N_("S E Mean"), MOMENT_VARIANCE},
85 {"STDDEV", N_("Std Dev"), MOMENT_VARIANCE},
86 {"VARIANCE", N_("Variance"), MOMENT_VARIANCE},
87 {"KURTOSIS", N_("Kurtosis"), MOMENT_KURTOSIS},
88 {"SEKURTOSIS", N_("S E Kurt"), MOMENT_NONE},
89 {"SKEWNESS", N_("Skewness"), MOMENT_SKEWNESS},
90 {"SESKEWNESS", N_("S E Skew"), MOMENT_NONE},
91 {"RANGE", N_("Range"), MOMENT_NONE},
92 {"MINIMUM", N_("Minimum"), MOMENT_NONE},
93 {"MAXIMUM", N_("Maximum"), MOMENT_NONE},
94 {"SUM", N_("Sum"), MOMENT_MEAN},
97 /* Statistics calculated by default if none are explicitly
99 #define DEFAULT_STATS \
100 ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN) \
103 /* A variable specified on DESCRIPTIVES. */
106 struct variable *v; /* Variable to calculate on. */
107 char z_name[9]; /* Name for z-score variable. */
108 double valid, missing; /* Valid, missing counts. */
109 struct moments *moments; /* Moments. */
110 double min, max; /* Maximum and mimimum values. */
111 double stats[DSC_N_STATS]; /* All the stats' values. */
114 /* Handling of missing values. */
115 enum dsc_missing_type
117 DSC_VARIABLE, /* Handle missing values on a per-variable basis. */
118 DSC_LISTWISE /* Discard entire case if any variable is missing. */
124 DSC_LINE, /* Abbreviated format. */
125 DSC_SERIAL /* Long format. */
128 /* A DESCRIPTIVES procedure. */
131 /* Per-variable info. */
132 struct dsc_var *vars; /* Variables. */
133 size_t var_cnt; /* Number of variables. */
136 enum dsc_missing_type missing_type; /* Treatment of missing values. */
137 int include_user_missing; /* Nonzero to include user-missing values. */
138 int show_var_labels; /* Nonzero to show variable labels. */
139 int show_index; /* Nonzero to show variable index. */
140 enum dsc_format format; /* Output format. */
142 /* Accumulated results. */
143 double missing_listwise; /* Sum of weights of cases missing listwise. */
144 double valid; /* Sum of weights of valid cases. */
145 int bad_weight; /* Nonzero if a bad weight has been found. */
146 enum dsc_statistic sort_by_stat; /* Statistic to sort by; -1: name. */
147 int sort_ascending; /* !0: ascending order; 0: descending. */
148 unsigned long show_stats; /* Statistics to display. */
149 unsigned long calc_stats; /* Statistics to calculate. */
150 enum moment max_moment; /* Highest moment needed for stats. */
154 static enum dsc_statistic match_statistic (void);
155 static void free_dsc_proc (struct dsc_proc *);
157 /* Z-score functions. */
158 static int try_name (struct dsc_proc *dsc, char *name);
159 static int generate_z_varname (struct dsc_proc *dsc, char *z_name,
160 const char *name, int *z_cnt);
161 static void dump_z_table (struct dsc_proc *);
162 static void setup_z_trns (struct dsc_proc *);
164 /* Procedure execution functions. */
165 static void calc_descriptives (const struct casefile *, void *dsc_);
166 static void display (struct dsc_proc *dsc);
168 /* Parser and outline. */
170 /* Handles DESCRIPTIVES. */
172 cmd_descriptives (void)
174 struct dsc_proc *dsc;
175 struct variable **vars = NULL;
177 int save_z_scores = 0;
181 /* Create and initialize dsc. */
182 dsc = xmalloc (sizeof *dsc);
185 dsc->missing_type = DSC_VARIABLE;
186 dsc->include_user_missing = 0;
187 dsc->show_var_labels = 1;
189 dsc->format = DSC_LINE;
190 dsc->missing_listwise = 0.;
193 dsc->sort_by_stat = DSC_NONE;
194 dsc->sort_ascending = 1;
195 dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
197 /* Parse DESCRIPTIVES. */
200 if (lex_match_id ("MISSING"))
203 while (token != '.' && token != '/')
205 if (lex_match_id ("VARIABLE"))
206 dsc->missing_type = DSC_VARIABLE;
207 else if (lex_match_id ("LISTWISE"))
208 dsc->missing_type = DSC_LISTWISE;
209 else if (lex_match_id ("INCLUDE"))
210 dsc->include_user_missing = 1;
219 else if (lex_match_id ("SAVE"))
221 else if (lex_match_id ("FORMAT"))
224 while (token != '.' && token != '/')
226 if (lex_match_id ("LABELS"))
227 dsc->show_var_labels = 1;
228 else if (lex_match_id ("NOLABELS"))
229 dsc->show_var_labels = 0;
230 else if (lex_match_id ("INDEX"))
232 else if (lex_match_id ("NOINDEX"))
234 else if (lex_match_id ("LINE"))
235 dsc->format = DSC_LINE;
236 else if (lex_match_id ("SERIAL"))
237 dsc->format = DSC_SERIAL;
246 else if (lex_match_id ("STATISTICS"))
250 while (token != '.' && token != '/')
252 if (lex_match (T_ALL))
253 dsc->show_stats |= (1ul << DSC_N_STATS) - 1;
254 else if (lex_match_id ("DEFAULT"))
255 dsc->show_stats |= DEFAULT_STATS;
257 dsc->show_stats |= 1ul << (match_statistic ());
260 if (dsc->show_stats == 0)
261 dsc->show_stats = DEFAULT_STATS;
263 else if (lex_match_id ("SORT"))
266 if (lex_match_id ("NAME"))
267 dsc->sort_by_stat = DSC_NAME;
269 dsc->sort_by_stat = match_statistic ();
272 if (lex_match_id ("A"))
273 dsc->sort_ascending = 1;
274 else if (lex_match_id ("D"))
275 dsc->sort_ascending = 0;
278 lex_force_match (')');
281 else if (var_cnt == 0)
283 if (lex_look_ahead () == '=')
285 lex_match_id ("VARIABLES");
289 while (token != '.' && token != '/')
293 if (!parse_variables (default_dict, &vars, &var_cnt,
294 PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
297 dsc->vars = xrealloc (dsc->vars, sizeof *dsc->vars * var_cnt);
298 for (i = dsc->var_cnt; i < var_cnt; i++)
300 struct dsc_var *dv = &dsc->vars[i];
302 dv->z_name[0] = '\0';
305 dsc->var_cnt = var_cnt;
314 if (try_name (dsc, tokid))
316 strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
320 msg (SE, _("Z-score variable name %s would be"
321 "a duplicate variable name."), tokid);
323 lex_force_match (')');
337 msg (SE, _("No variables specified."));
341 /* Construct z-score varnames, show translation table. */
342 if (z_cnt || save_z_scores)
348 for (i = 0; i < dsc->var_cnt; i++)
349 if (dsc->vars[i].z_name[0] == 0)
351 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
352 dsc->vars[i].v->name, &gen_cnt))
360 /* Figure out statistics to display. */
361 if (dsc->show_stats & (1ul << DSC_SKEWNESS))
362 dsc->show_stats |= 1ul << DSC_SESKEW;
363 if (dsc->show_stats & (1ul << DSC_KURTOSIS))
364 dsc->show_stats |= 1ul << DSC_SEKURT;
366 /* Figure out which statistics to calculate. */
367 dsc->calc_stats = dsc->show_stats;
369 dsc->calc_stats |= (1ul << DSC_MEAN) | (1ul << DSC_STDDEV);
370 if (dsc->sort_by_stat >= 0)
371 dsc->calc_stats |= 1ul << dsc->sort_by_stat;
372 if (dsc->show_stats & (1ul << DSC_SESKEW))
373 dsc->calc_stats |= 1ul << DSC_SKEWNESS;
374 if (dsc->show_stats & (1ul << DSC_SEKURT))
375 dsc->calc_stats |= 1ul << DSC_KURTOSIS;
377 /* Figure out maximum moment needed and allocate moments for
379 dsc->max_moment = MOMENT_NONE;
380 for (i = 0; i < DSC_N_STATS; i++)
381 if (dsc->calc_stats & (1ul << i) && dsc_info[i].moment > dsc->max_moment)
382 dsc->max_moment = dsc_info[i].moment;
383 if (dsc->max_moment != MOMENT_NONE)
384 for (i = 0; i < dsc->var_cnt; i++)
385 dsc->vars[i].moments = moments_create (dsc->max_moment);
388 multipass_procedure_with_splits (calc_descriptives, dsc);
390 msg (SW, _("At least one case in the data file had a weight value "
391 "that was system-missing, zero, or negative. These case(s) "
407 /* Returns the statistic named by the current token and skips
408 past the token. Emits an error if the current token does not
410 static enum dsc_statistic
411 match_statistic (void)
415 enum dsc_statistic stat;
417 for (stat = 0; stat < DSC_N_STATS; stat++)
418 if (lex_match_id (dsc_info[stat].identifier))
425 lex_error (_("expecting statistic name"));
431 free_dsc_proc (struct dsc_proc *dsc)
438 for (i = 0; i < dsc->var_cnt; i++)
439 moments_destroy (dsc->vars[i].moments);
446 /* Returns 0 if NAME is a duplicate of any existing variable name or
447 of any previously-declared z-var name; otherwise returns 1. */
449 try_name (struct dsc_proc *dsc, char *name)
453 if (dict_lookup_var (default_dict, name) != NULL)
455 for (i = 0; i < dsc->var_cnt; i++)
456 if (!strcmp (dsc->vars[i].z_name, name))
461 /* Generates a name for a Z-score variable based on a variable
462 named VAR_NAME, given that *Z_CNT generated variable names are
463 known to already exist. If successful, returns nonzero and
464 copies the new name into Z_NAME. On failure, returns zero. */
466 generate_z_varname (struct dsc_proc *dsc, char *z_name,
467 const char *var_name, int *z_cnt)
471 /* Try a name based on the original variable name. */
473 strcpy (name + 1, var_name);
475 if (try_name (dsc, name))
477 strcpy (z_name, name);
481 /* Generate a synthetic name. */
487 sprintf (name, "ZSC%03d", *z_cnt);
488 else if (*z_cnt <= 108)
489 sprintf (name, "STDZ%02d", *z_cnt - 99);
490 else if (*z_cnt <= 117)
491 sprintf (name, "ZZZZ%02d", *z_cnt - 108);
492 else if (*z_cnt <= 126)
493 sprintf (name, "ZQZQ%02d", *z_cnt - 117);
496 msg (SE, _("Ran out of generic names for Z-score variables. "
497 "There are only 126 generic names: ZSC001-ZSC0999, "
498 "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
502 if (try_name (dsc, name))
504 strcpy (z_name, name);
510 /* Outputs a table describing the mapping between source
511 variables and Z-score variables. */
513 dump_z_table (struct dsc_proc *dsc)
521 for (i = 0; i < dsc->var_cnt; i++)
522 if (dsc->vars[i].z_name[0] != '\0')
526 t = tab_create (2, cnt + 1, 0);
527 tab_title (t, 0, _("Mapping of variables to corresponding Z-scores."));
528 tab_columns (t, SOM_COL_DOWN, 1);
529 tab_headers (t, 0, 0, 1, 0);
530 tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
531 tab_hline (t, TAL_2, 0, 1, 1);
532 tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
533 tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
534 tab_dim (t, tab_natural_dimensions);
539 for (i = 0, y = 1; i < dsc->var_cnt; i++)
540 if (dsc->vars[i].z_name[0] != '\0')
542 tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
543 tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
550 /* Transformation function to calculate Z-scores. */
552 descriptives_trns_proc (struct trns_header *trns, struct ccase * c,
555 struct dsc_trns *t = (struct dsc_trns *) trns;
556 struct dsc_z_score *z;
558 for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
560 double score = c->data[z->src_idx].f;
562 if (z->mean == SYSMIS || score == SYSMIS)
563 c->data[z->dst_idx].f = SYSMIS;
565 c->data[z->dst_idx].f = (score - z->mean) / z->std_dev;
570 /* Frees a descriptives_trns struct. */
572 descriptives_trns_free (struct trns_header * trns)
574 struct dsc_trns *t = (struct dsc_trns *) trns;
579 /* Sets up a transformation to calculate Z scores. */
581 setup_z_trns (struct dsc_proc *dsc)
586 for (cnt = i = 0; i < dsc->var_cnt; i++)
587 if (dsc->vars[i].z_name[0] != '\0')
590 t = xmalloc (sizeof *t);
591 t->h.proc = descriptives_trns_proc;
592 t->h.free = descriptives_trns_free;
593 t->z_scores = xmalloc (cnt * sizeof *t->z_scores);
594 t->z_score_cnt = cnt;
596 for (cnt = i = 0; i < dsc->var_cnt; i++)
598 struct dsc_var *dv = &dsc->vars[i];
599 if (dv->z_name[0] != '\0')
601 struct dsc_z_score *z;
603 struct variable *dst_var;
605 dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
609 dst_var->label = xmalloc (strlen (dv->v->label) + 12);
610 cp = stpcpy (dst_var->label, _("Z-score of "));
611 strcpy (cp, dv->v->label);
615 dst_var->label = xmalloc (strlen (dv->v->name) + 12);
616 cp = stpcpy (dst_var->label, _("Z-score of "));
617 strcpy (cp, dv->v->name);
620 z = &t->z_scores[cnt++];
621 z->src_idx = dv->v->fv;
622 z->dst_idx = dst_var->fv;
623 z->mean = dv->stats[DSC_MEAN];
624 z->std_dev = dv->stats[DSC_STDDEV];
628 add_transformation ((struct trns_header *) t);
631 /* Statistical calculation. */
633 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
635 /* Calculates and displays descriptive statistics for the cases
638 calc_descriptives (const struct casefile *cf, void *dsc_)
640 struct dsc_proc *dsc = dsc_;
641 struct casereader *reader;
642 const struct ccase *c;
645 for (i = 0; i < dsc->var_cnt; i++)
647 struct dsc_var *dv = &dsc->vars[i];
649 dv->valid = dv->missing = 0.0;
650 if (dv->moments != NULL)
651 moments_clear (dv->moments);
655 dsc->missing_listwise = 0.;
658 /* First pass to handle most of the work. */
659 reader = casefile_get_reader (cf);
660 while (casereader_read (reader, &c))
662 double weight = dict_get_case_weight (default_dict, c);
669 /* Check for missing values. */
670 if (listwise_missing (dsc, c))
672 dsc->missing_listwise += weight;
673 if (dsc->missing_type == DSC_LISTWISE)
676 dsc->valid += weight;
678 for (i = 0; i < dsc->var_cnt; i++)
680 struct dsc_var *dv = &dsc->vars[i];
681 double x = c->data[dv->v->fv].f;
683 if (dsc->missing_type != DSC_LISTWISE
685 || (!dsc->include_user_missing
686 && is_num_user_missing (x, dv->v))))
688 dv->missing += weight;
692 if (dv->moments != NULL)
693 moments_pass_one (dv->moments, x, weight);
700 casereader_destroy (reader);
702 /* Second pass for higher-order moments. */
703 if (dsc->max_moment > MOMENT_MEAN)
705 reader = casefile_get_reader (cf);
706 while (casereader_read (reader, &c))
708 double weight = dict_get_case_weight (default_dict, c);
712 /* Check for missing values. */
713 if (listwise_missing (dsc, c)
714 && dsc->missing_type == DSC_LISTWISE)
717 for (i = 0; i < dsc->var_cnt; i++)
719 struct dsc_var *dv = &dsc->vars[i];
720 double x = c->data[dv->v->fv].f;
722 if (dsc->missing_type != DSC_LISTWISE
724 || (!dsc->include_user_missing
725 && is_num_user_missing (x, dv->v))))
728 if (dv->moments != NULL)
729 moments_pass_two (dv->moments, x, weight);
732 casereader_destroy (reader);
735 /* Calculate results. */
736 for (i = 0; i < dsc->var_cnt; i++)
738 struct dsc_var *dv = &dsc->vars[i];
742 for (j = 0; j < DSC_N_STATS; j++)
743 dv->stats[j] = SYSMIS;
745 dv->valid = W = dsc->valid - dv->missing;
747 if (dv->moments != NULL)
748 moments_calculate (dv->moments, NULL,
749 &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
750 &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
751 if (dsc->calc_stats & (1ul << DSC_SEMEAN)
752 && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
753 dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
754 if (dsc->calc_stats & (1ul << DSC_STDDEV)
755 && dv->stats[DSC_VARIANCE] != SYSMIS)
756 dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
757 if (dsc->calc_stats & (1ul << DSC_SEKURT))
758 if (dv->stats[DSC_KURTOSIS] != SYSMIS)
759 dv->stats[DSC_SEKURT] = calc_sekurt (W);
760 if (dsc->calc_stats & (1ul << DSC_SESKEW)
761 && dv->stats[DSC_SKEWNESS] != SYSMIS)
762 dv->stats[DSC_SESKEW] = calc_seskew (W);
763 dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
764 ? SYSMIS : dv->max - dv->min);
765 dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
766 dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
767 if (dsc->calc_stats & (1ul << DSC_SUM))
768 dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
771 /* Output results. */
775 /* Returns nonzero if any of the descriptives variables in DSC's
776 variable list have missing values in case C, zero otherwise. */
778 listwise_missing (struct dsc_proc *dsc, const struct ccase *c)
782 for (i = 0; i < dsc->var_cnt; i++)
784 struct dsc_var *dv = &dsc->vars[i];
785 double x = c->data[dv->v->fv].f;
788 || (!dsc->include_user_missing && is_num_user_missing (x, dv->v)))
794 /* Statistical display. */
796 static algo_compare_func descriptives_compare_dsc_vars;
798 /* Displays a table of descriptive statistics for DSC. */
800 display (struct dsc_proc *dsc)
806 nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
807 for (i = 0; i < DSC_N_STATS; i++)
808 if (dsc->show_stats & (1ul << i))
811 if (dsc->sort_by_stat != DSC_NONE)
812 sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
813 descriptives_compare_dsc_vars, dsc);
815 t = tab_create (nc, dsc->var_cnt + 1, 0);
816 tab_headers (t, 1, 0, 1, 0);
817 tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
818 tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
819 tab_hline (t, TAL_2, 0, nc - 1, 1);
820 tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
821 tab_dim (t, tab_natural_dimensions);
824 tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
825 if (dsc->format == DSC_SERIAL)
827 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
828 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
831 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
833 for (i = 0; i < DSC_N_STATS; i++)
834 if (dsc->show_stats & (1ul << i))
836 const char *title = gettext (dsc_info[i].name);
837 tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
840 for (i = 0; i < dsc->var_cnt; i++)
842 struct dsc_var *dv = &dsc->vars[i];
845 tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
846 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
847 if (dsc->format == DSC_SERIAL)
848 tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
849 for (j = 0; j < DSC_N_STATS; j++)
850 if (dsc->show_stats & (1ul << j))
851 tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
854 tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
855 dsc->valid, dsc->missing_listwise);
860 /* Compares `struct dsc_var's A and B according to the ordering
863 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
865 const struct dsc_var *a = a_;
866 const struct dsc_var *b = b_;
867 struct dsc_proc *dsc = dsc_;
871 if (dsc->sort_by_stat == DSC_NAME)
872 result = strcmp (a->v->name, b->v->name);
875 double as = a->stats[dsc->sort_by_stat];
876 double bs = b->stats[dsc->sort_by_stat];
878 result = as < bs ? -1 : as > bs;
881 if (!dsc->sort_ascending)