Fix DESCRIPTIVES memory leak.
[pspp] / src / descript.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
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.
9
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.
14
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
18    02111-1307, USA. */
19
20 /* FIXME: Many possible optimizations. */
21
22 #include <config.h>
23 #include "error.h"
24 #include <limits.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include "algorithm.h"
28 #include "alloc.h"
29 #include "casefile.h"
30 #include "command.h"
31 #include "lexer.h"
32 #include "error.h"
33 #include "magic.h"
34 #include "moments.h"
35 #include "som.h"
36 #include "tab.h"
37 #include "var.h"
38 #include "vfm.h"
39
40 /* DESCRIPTIVES private data. */
41
42 /* Describes properties of a distribution for the purpose of
43    calculating a Z-score. */
44 struct dsc_z_score
45   {
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. */
50   };
51
52 /* DESCRIPTIVES transformation (for calculating Z-scores). */
53 struct dsc_trns
54   {
55     struct trns_header h;
56     struct dsc_z_score *z_scores; /* Array of Z-scores. */
57     int z_score_cnt;            /* Number of Z-scores. */
58   };
59
60 /* Statistics.  Used as bit indexes, so must be 32 or fewer. */
61 enum dsc_statistic
62   {
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,
66
67     /* Only valid as sort criteria. */
68     DSC_NAME = -2,              /* Sort by name. */
69     DSC_NONE = -1               /* Unsorted. */
70   };
71
72 /* Describes one statistic. */
73 struct dsc_statistic_info
74   {
75     const char *identifier;     /* Identifier. */
76     const char *name;           /* Full name. */
77     enum moment moment;         /* Highest moment needed to calculate. */
78   };
79
80 /* Table of statistics, indexed by DSC_*. */
81 static const struct dsc_statistic_info dsc_info[DSC_N_STATS] =
82   {
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},
95   };
96
97 /* Statistics calculated by default if none are explicitly
98    requested. */
99 #define DEFAULT_STATS                                                   \
100         ((1ul << DSC_MEAN) | (1ul << DSC_STDDEV) | (1ul << DSC_MIN)     \
101          | (1ul << DSC_MAX))
102      
103 /* A variable specified on DESCRIPTIVES. */
104 struct dsc_var
105   {
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. */
112   };
113
114 /* Handling of missing values. */
115 enum dsc_missing_type
116   {
117     DSC_VARIABLE,       /* Handle missing values on a per-variable basis. */
118     DSC_LISTWISE        /* Discard entire case if any variable is missing. */
119   };
120
121 /* Output format. */
122 enum dsc_format 
123   {
124     DSC_LINE,           /* Abbreviated format. */
125     DSC_SERIAL          /* Long format. */
126   };
127
128 /* A DESCRIPTIVES procedure. */
129 struct dsc_proc 
130   {
131     /* Per-variable info. */
132     struct dsc_var *vars;       /* Variables. */
133     size_t var_cnt;             /* Number of variables. */
134
135     /* User options. */
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. */
141
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. */
151   };
152
153 /* Parsing. */
154 static enum dsc_statistic match_statistic (void);
155 static void free_dsc_proc (struct dsc_proc *);
156
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 *);
163
164 /* Procedure execution functions. */
165 static void calc_descriptives (const struct casefile *, void *dsc_);
166 static void display (struct dsc_proc *dsc);
167 \f
168 /* Parser and outline. */
169
170 /* Handles DESCRIPTIVES. */
171 int
172 cmd_descriptives (void)
173 {
174   struct dsc_proc *dsc;
175   struct variable **vars = NULL;
176   int var_cnt = 0;
177   int save_z_scores = 0;
178   int z_cnt = 0;
179   int i;
180
181   /* Create and initialize dsc. */
182   dsc = xmalloc (sizeof *dsc);
183   dsc->vars = NULL;
184   dsc->var_cnt = 0;
185   dsc->missing_type = DSC_VARIABLE;
186   dsc->include_user_missing = 0;
187   dsc->show_var_labels = 1;
188   dsc->show_index = 0;
189   dsc->format = DSC_LINE;
190   dsc->missing_listwise = 0.;
191   dsc->valid = 0.;
192   dsc->bad_weight = 0;
193   dsc->sort_by_stat = DSC_NONE;
194   dsc->sort_ascending = 1;
195   dsc->show_stats = dsc->calc_stats = DEFAULT_STATS;
196
197   /* Parse DESCRIPTIVES. */
198   while (token != '.') 
199     {
200       if (lex_match_id ("MISSING"))
201         {
202           lex_match ('=');
203           while (token != '.' && token != '/') 
204             {
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;
211               else
212                 {
213                   lex_error (NULL);
214                   goto error;
215                 }
216               lex_match (',');
217             }
218         }
219       else if (lex_match_id ("SAVE"))
220         save_z_scores = 1;
221       else if (lex_match_id ("FORMAT")) 
222         {
223           lex_match ('=');
224           while (token != '.' && token != '/') 
225             {
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"))
231                 dsc->show_index = 1;
232               else if (lex_match_id ("NOINDEX"))
233                 dsc->show_index = 0;
234               else if (lex_match_id ("LINE"))
235                 dsc->format = DSC_LINE;
236               else if (lex_match_id ("SERIAL"))
237                 dsc->format = DSC_SERIAL;
238               else
239                 {
240                   lex_error (NULL);
241                   goto error;
242                 }
243               lex_match (',');
244             }
245         }
246       else if (lex_match_id ("STATISTICS")) 
247         {
248           lex_match ('=');
249           dsc->show_stats = 0;
250           while (token != '.' && token != '/') 
251             {
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;
256               else
257                 dsc->show_stats |= 1ul << (match_statistic ());
258               lex_match (',');
259             }
260           if (dsc->show_stats == 0)
261             dsc->show_stats = DEFAULT_STATS;
262         }
263       else if (lex_match_id ("SORT")) 
264         {
265           lex_match ('=');
266           if (lex_match_id ("NAME"))
267             dsc->sort_by_stat = DSC_NAME;
268           else
269             dsc->sort_by_stat = match_statistic ();
270           if (lex_match ('(')) 
271             {
272               if (lex_match_id ("A"))
273                 dsc->sort_ascending = 1;
274               else if (lex_match_id ("D"))
275                 dsc->sort_ascending = 0;
276               else
277                 lex_error (NULL);
278               lex_force_match (')');
279             }
280         }
281       else if (var_cnt == 0)
282         {
283           if (lex_look_ahead () == '=') 
284             {
285               lex_match_id ("VARIABLES");
286               lex_match ('=');
287             }
288
289           while (token != '.' && token != '/') 
290             {
291               int i;
292               
293               if (!parse_variables (default_dict, &vars, &var_cnt,
294                                     PV_APPEND | PV_NO_DUPLICATE | PV_NUMERIC))
295                 break;
296
297               dsc->vars = xrealloc (dsc->vars, sizeof *dsc->vars * var_cnt);
298               for (i = dsc->var_cnt; i < var_cnt; i++)
299                 {
300                   struct dsc_var *dv = &dsc->vars[i];
301                   dv->v = vars[i];
302                   dv->z_name[0] = '\0';
303                   dv->moments = NULL;
304                 }
305               dsc->var_cnt = var_cnt;
306
307               if (lex_match ('(')) 
308                 {
309                   if (token != T_ID) 
310                     {
311                       lex_error (NULL);
312                       break;
313                     }
314                   if (try_name (dsc, tokid)) 
315                     {
316                       strcpy (dsc->vars[dsc->var_cnt - 1].z_name, tokid);
317                       z_cnt++;
318                     }
319                   else
320                     msg (SE, _("Z-score variable name %s would be"
321                                "a duplicate variable name."), tokid);
322                   lex_get ();
323                   lex_force_match (')');
324                 }
325             }
326         }
327       else 
328         {
329           lex_error (NULL);
330           break; 
331         }
332
333       lex_match ('/');
334     }
335   if (var_cnt == 0)
336     {
337       msg (SE, _("No variables specified."));
338       goto error;
339     }
340
341   /* Construct z-score varnames, show translation table. */
342   if (z_cnt || save_z_scores)
343     {
344       if (save_z_scores) 
345         {
346           int gen_cnt = 0;
347
348           for (i = 0; i < dsc->var_cnt; i++)
349             if (dsc->vars[i].z_name[0] == 0) 
350               {
351                 if (!generate_z_varname (dsc, dsc->vars[i].z_name,
352                                          dsc->vars[i].v->name, &gen_cnt))
353                   goto error;
354                 z_cnt++;
355               } 
356         }
357       dump_z_table (dsc);
358     }
359
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;
365
366   /* Figure out which statistics to calculate. */
367   dsc->calc_stats = dsc->show_stats;
368   if (z_cnt > 0)
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;
376
377   /* Figure out maximum moment needed and allocate moments for
378      the variables. */
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);
386
387   /* Data pass. */
388   multipass_procedure_with_splits (calc_descriptives, dsc);
389   if (dsc->bad_weight)
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) "
392                "were ignored."));
393
394   /* Z-scoring! */
395   if (z_cnt)
396     setup_z_trns (dsc);
397
398   /* Done. */
399   free (vars);
400   free_dsc_proc (dsc);
401   return CMD_SUCCESS;
402
403  error:
404   free (vars);
405   free_dsc_proc (dsc);
406   return CMD_FAILURE;
407 }
408
409 /* Returns the statistic named by the current token and skips
410    past the token.  Emits an error if the current token does not
411    name a statistic. */
412 static enum dsc_statistic
413 match_statistic (void) 
414 {
415   if (token == T_ID) 
416     {
417       enum dsc_statistic stat;
418
419       for (stat = 0; stat < DSC_N_STATS; stat++)
420         if (lex_match_id (dsc_info[stat].identifier)) 
421           {
422             lex_get ();
423             return stat;
424           }
425     }
426
427   lex_error (_("expecting statistic name"));
428   return DSC_MEAN;
429 }
430
431 /* Frees DSC. */
432 static void
433 free_dsc_proc (struct dsc_proc *dsc)
434 {
435   size_t i;
436
437   if (dsc == NULL)
438     return;
439   
440   for (i = 0; i < dsc->var_cnt; i++)
441     moments_destroy (dsc->vars[i].moments);
442   free (dsc->vars);
443   free (dsc);
444 }
445 \f
446 /* Z scores. */
447
448 /* Returns 0 if NAME is a duplicate of any existing variable name or
449    of any previously-declared z-var name; otherwise returns 1. */
450 static int
451 try_name (struct dsc_proc *dsc, char *name)
452 {
453   int i;
454
455   if (dict_lookup_var (default_dict, name) != NULL)
456     return 0;
457   for (i = 0; i < dsc->var_cnt; i++)
458     if (!strcmp (dsc->vars[i].z_name, name))
459       return 0;
460   return 1;
461 }
462
463 /* Generates a name for a Z-score variable based on a variable
464    named VAR_NAME, given that *Z_CNT generated variable names are
465    known to already exist.  If successful, returns nonzero and
466    copies the new name into Z_NAME.  On failure, returns zero. */
467 static int
468 generate_z_varname (struct dsc_proc *dsc, char *z_name,
469                     const char *var_name, int *z_cnt)
470 {
471   char name[10];
472
473   /* Try a name based on the original variable name. */
474   name[0] = 'Z';
475   strcpy (name + 1, var_name);
476   name[8] = '\0';
477   if (try_name (dsc, name))
478     {
479       strcpy (z_name, name);
480       return 1;
481     }
482
483   /* Generate a synthetic name. */
484   for (;;)
485     {
486       (*z_cnt)++;
487
488       if (*z_cnt <= 99)
489         sprintf (name, "ZSC%03d", *z_cnt);
490       else if (*z_cnt <= 108)
491         sprintf (name, "STDZ%02d", *z_cnt - 99);
492       else if (*z_cnt <= 117)
493         sprintf (name, "ZZZZ%02d", *z_cnt - 108);
494       else if (*z_cnt <= 126)
495         sprintf (name, "ZQZQ%02d", *z_cnt - 117);
496       else
497         {
498           msg (SE, _("Ran out of generic names for Z-score variables.  "
499                      "There are only 126 generic names: ZSC001-ZSC0999, "
500                      "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
501           return 0;
502         }
503       
504       if (try_name (dsc, name))
505         {
506           strcpy (z_name, name);
507           return 1;
508         }
509     }
510 }
511
512 /* Outputs a table describing the mapping between source
513    variables and Z-score variables. */
514 static void
515 dump_z_table (struct dsc_proc *dsc)
516 {
517   int cnt = 0;
518   struct tab_table *t;
519   
520   {
521     int i;
522     
523     for (i = 0; i < dsc->var_cnt; i++)
524       if (dsc->vars[i].z_name[0] != '\0')
525         cnt++;
526   }
527   
528   t = tab_create (2, cnt + 1, 0);
529   tab_title (t, 0, _("Mapping of variables to corresponding Z-scores."));
530   tab_columns (t, SOM_COL_DOWN, 1);
531   tab_headers (t, 0, 0, 1, 0);
532   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
533   tab_hline (t, TAL_2, 0, 1, 1);
534   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
535   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
536   tab_dim (t, tab_natural_dimensions);
537
538   {
539     int i, y;
540     
541     for (i = 0, y = 1; i < dsc->var_cnt; i++)
542       if (dsc->vars[i].z_name[0] != '\0')
543         {
544           tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
545           tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
546         }
547   }
548   
549   tab_submit (t);
550 }
551
552 /* Transformation function to calculate Z-scores. */
553 static int
554 descriptives_trns_proc (struct trns_header *trns, struct ccase * c,
555                         int case_num UNUSED)
556 {
557   struct dsc_trns *t = (struct dsc_trns *) trns;
558   struct dsc_z_score *z;
559
560   for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
561     {
562       double score = c->data[z->src_idx].f;
563
564       if (z->mean == SYSMIS || score == SYSMIS)
565         c->data[z->dst_idx].f = SYSMIS;
566       else
567         c->data[z->dst_idx].f = (score - z->mean) / z->std_dev;
568     }
569   return -1;
570 }
571
572 /* Frees a descriptives_trns struct. */
573 static void
574 descriptives_trns_free (struct trns_header * trns)
575 {
576   struct dsc_trns *t = (struct dsc_trns *) trns;
577
578   free (t->z_scores);
579 }
580
581 /* Sets up a transformation to calculate Z scores. */
582 static void
583 setup_z_trns (struct dsc_proc *dsc)
584 {
585   struct dsc_trns *t;
586   int cnt, i;
587
588   for (cnt = i = 0; i < dsc->var_cnt; i++)
589     if (dsc->vars[i].z_name[0] != '\0')
590       cnt++;
591
592   t = xmalloc (sizeof *t);
593   t->h.proc = descriptives_trns_proc;
594   t->h.free = descriptives_trns_free;
595   t->z_scores = xmalloc (cnt * sizeof *t->z_scores);
596   t->z_score_cnt = cnt;
597
598   for (cnt = i = 0; i < dsc->var_cnt; i++)
599     {
600       struct dsc_var *dv = &dsc->vars[i];
601       if (dv->z_name[0] != '\0')
602         {
603           struct dsc_z_score *z;
604           char *cp;
605           struct variable *dst_var;
606
607           dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
608           dst_var->init = 0;
609           if (dv->v->label)
610             {
611               dst_var->label = xmalloc (strlen (dv->v->label) + 12);
612               cp = stpcpy (dst_var->label, _("Z-score of "));
613               strcpy (cp, dv->v->label);
614             }
615           else
616             {
617               dst_var->label = xmalloc (strlen (dv->v->name) + 12);
618               cp = stpcpy (dst_var->label, _("Z-score of "));
619               strcpy (cp, dv->v->name);
620             }
621
622           z = &t->z_scores[cnt++];
623           z->src_idx = dv->v->fv;
624           z->dst_idx = dst_var->fv;
625           z->mean = dv->stats[DSC_MEAN];
626           z->std_dev = dv->stats[DSC_STDDEV];
627         }
628     }
629
630   add_transformation ((struct trns_header *) t);
631 }
632 \f
633 /* Statistical calculation. */
634
635 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
636
637 /* Calculates and displays descriptive statistics for the cases
638    in CF. */
639 static void
640 calc_descriptives (const struct casefile *cf, void *dsc_) 
641 {
642   struct dsc_proc *dsc = dsc_;
643   struct casereader *reader;
644   const struct ccase *c;
645   int i;
646
647   for (i = 0; i < dsc->var_cnt; i++)
648     {
649       struct dsc_var *dv = &dsc->vars[i];
650       
651       dv->valid = dv->missing = 0.0;
652       if (dv->moments != NULL)
653         moments_clear (dv->moments);
654       dv->min = DBL_MAX;
655       dv->max = -DBL_MAX;
656     }
657   dsc->missing_listwise = 0.;
658   dsc->valid = 0.;
659
660   /* First pass to handle most of the work. */
661   reader = casefile_get_reader (cf);
662   while (casereader_read (reader, &c)) 
663     {
664       double weight = dict_get_case_weight (default_dict, c);
665       if (weight <= 0.0) 
666         {
667           dsc->bad_weight = 1;
668           continue;
669         }
670       
671       /* Check for missing values. */
672       if (listwise_missing (dsc, c)) 
673         {
674           dsc->missing_listwise += weight;
675           if (dsc->missing_type == DSC_LISTWISE)
676             continue; 
677         }
678       dsc->valid += weight;
679
680       for (i = 0; i < dsc->var_cnt; i++) 
681         {
682           struct dsc_var *dv = &dsc->vars[i];
683           double x = c->data[dv->v->fv].f;
684           
685           if (dsc->missing_type != DSC_LISTWISE
686               && (x == SYSMIS
687                   || (!dsc->include_user_missing
688                       && is_num_user_missing (x, dv->v))))
689             {
690               dv->missing += weight;
691               continue;
692             }
693
694           if (dv->moments != NULL)
695             moments_pass_one (dv->moments, x, weight);
696           if (x < dv->min)
697             dv->min = x;
698           if (x > dv->max)
699             dv->max = x;
700         }
701     }
702   casereader_destroy (reader);
703
704   /* Second pass for higher-order moments. */
705   if (dsc->max_moment > MOMENT_MEAN) 
706     {
707       reader = casefile_get_reader (cf);
708       while (casereader_read (reader, &c)) 
709         {
710           double weight = dict_get_case_weight (default_dict, c);
711           if (weight <= 0.0)
712             continue;
713       
714           /* Check for missing values. */
715           if (listwise_missing (dsc, c) 
716               && dsc->missing_type == DSC_LISTWISE)
717             continue; 
718
719           for (i = 0; i < dsc->var_cnt; i++) 
720             {
721               struct dsc_var *dv = &dsc->vars[i];
722               double x = c->data[dv->v->fv].f;
723           
724               if (dsc->missing_type != DSC_LISTWISE
725                   && (x == SYSMIS
726                       || (!dsc->include_user_missing
727                           && is_num_user_missing (x, dv->v))))
728                 continue;
729
730               if (dv->moments != NULL)
731                 moments_pass_two (dv->moments, x, weight);
732             }
733         }
734       casereader_destroy (reader);
735     }
736   
737   /* Calculate results. */
738   for (i = 0; i < dsc->var_cnt; i++)
739     {
740       struct dsc_var *dv = &dsc->vars[i];
741       double W;
742       int j;
743
744       for (j = 0; j < DSC_N_STATS; j++)
745         dv->stats[j] = SYSMIS;
746
747       dv->valid = W = dsc->valid - dv->missing;
748
749       if (dv->moments != NULL)
750         moments_calculate (dv->moments, NULL,
751                            &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
752                            &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
753       if (dsc->calc_stats & (1ul << DSC_SEMEAN)
754           && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
755         dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
756       if (dsc->calc_stats & (1ul << DSC_STDDEV)
757           && dv->stats[DSC_VARIANCE] != SYSMIS)
758         dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
759       if (dsc->calc_stats & (1ul << DSC_SEKURT)) 
760         if (dv->stats[DSC_KURTOSIS] != SYSMIS)
761             dv->stats[DSC_SEKURT] = calc_sekurt (W);
762       if (dsc->calc_stats & (1ul << DSC_SESKEW)
763           && dv->stats[DSC_SKEWNESS] != SYSMIS)
764         dv->stats[DSC_SESKEW] = calc_seskew (W);
765       dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
766                               ? SYSMIS : dv->max - dv->min);
767       dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
768       dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
769       if (dsc->calc_stats & (1ul << DSC_SUM))
770         dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
771     }
772
773   /* Output results. */
774   display (dsc);
775 }
776
777 /* Returns nonzero if any of the descriptives variables in DSC's
778    variable list have missing values in case C, zero otherwise. */
779 static int
780 listwise_missing (struct dsc_proc *dsc, const struct ccase *c) 
781 {
782   int i;
783
784   for (i = 0; i < dsc->var_cnt; i++)
785     {
786       struct dsc_var *dv = &dsc->vars[i];
787       double x = c->data[dv->v->fv].f;
788
789       if (x == SYSMIS
790           || (!dsc->include_user_missing && is_num_user_missing (x, dv->v)))
791         return 1;
792     }
793   return 0;
794 }
795 \f
796 /* Statistical display. */
797
798 static algo_compare_func descriptives_compare_dsc_vars;
799
800 /* Displays a table of descriptive statistics for DSC. */
801 static void
802 display (struct dsc_proc *dsc)
803 {
804   int i, j;
805   int nc;
806   struct tab_table *t;
807
808   nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
809   for (i = 0; i < DSC_N_STATS; i++)
810     if (dsc->show_stats & (1ul << i))
811       nc++;
812
813   if (dsc->sort_by_stat != DSC_NONE)
814     sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
815           descriptives_compare_dsc_vars, dsc);
816
817   t = tab_create (nc, dsc->var_cnt + 1, 0);
818   tab_headers (t, 1, 0, 1, 0);
819   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
820   tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
821   tab_hline (t, TAL_2, 0, nc - 1, 1);
822   tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
823   tab_dim (t, tab_natural_dimensions);
824
825   nc = 0;
826   tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
827   if (dsc->format == DSC_SERIAL)
828     {
829       tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
830       tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
831     }
832   else
833     tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
834
835   for (i = 0; i < DSC_N_STATS; i++)
836     if (dsc->show_stats & (1ul << i))
837       {
838         const char *title = gettext (dsc_info[i].name);
839         tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
840       }
841
842   for (i = 0; i < dsc->var_cnt; i++)
843     {
844       struct dsc_var *dv = &dsc->vars[i];
845
846       nc = 0;
847       tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
848       tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
849       if (dsc->format == DSC_SERIAL)
850         tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
851       for (j = 0; j < DSC_N_STATS; j++)
852         if (dsc->show_stats & (1ul << j))
853           tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
854     }
855
856   tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
857              dsc->valid, dsc->missing_listwise);
858
859   tab_submit (t);
860 }
861
862 /* Compares `struct dsc_var's A and B according to the ordering
863    specified by CMD. */
864 static int
865 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
866 {
867   const struct dsc_var *a = a_;
868   const struct dsc_var *b = b_;
869   struct dsc_proc *dsc = dsc_;
870
871   int result;
872
873   if (dsc->sort_by_stat == DSC_NAME)
874     result = strcmp (a->v->name, b->v->name);
875   else 
876     {
877       double as = a->stats[dsc->sort_by_stat];
878       double bs = b->stats[dsc->sort_by_stat];
879
880       result = as < bs ? -1 : as > bs;
881     }
882
883   if (!dsc->sort_ascending)
884     result = -result;
885
886   return result;
887 }