* Changed dict_get_case_weight() to accept an additional int * flag to complain about...
[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_warn;               /* Warn if bad weight 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_warn = 1;
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
390   /* Z-scoring! */
391   if (z_cnt)
392     setup_z_trns (dsc);
393
394   /* Done. */
395   free (vars);
396   free_dsc_proc (dsc);
397   return CMD_SUCCESS;
398
399  error:
400   free (vars);
401   free_dsc_proc (dsc);
402   return CMD_FAILURE;
403 }
404
405 /* Returns the statistic named by the current token and skips
406    past the token.  Emits an error if the current token does not
407    name a statistic. */
408 static enum dsc_statistic
409 match_statistic (void) 
410 {
411   if (token == T_ID) 
412     {
413       enum dsc_statistic stat;
414
415       for (stat = 0; stat < DSC_N_STATS; stat++)
416         if (lex_match_id (dsc_info[stat].identifier)) 
417           {
418             lex_get ();
419             return stat;
420           }
421     }
422
423   lex_error (_("expecting statistic name"));
424   return DSC_MEAN;
425 }
426
427 /* Frees DSC. */
428 static void
429 free_dsc_proc (struct dsc_proc *dsc)
430 {
431   size_t i;
432
433   if (dsc == NULL)
434     return;
435   
436   for (i = 0; i < dsc->var_cnt; i++)
437     moments_destroy (dsc->vars[i].moments);
438   free (dsc->vars);
439   free (dsc);
440 }
441 \f
442 /* Z scores. */
443
444 /* Returns 0 if NAME is a duplicate of any existing variable name or
445    of any previously-declared z-var name; otherwise returns 1. */
446 static int
447 try_name (struct dsc_proc *dsc, char *name)
448 {
449   int i;
450
451   if (dict_lookup_var (default_dict, name) != NULL)
452     return 0;
453   for (i = 0; i < dsc->var_cnt; i++)
454     if (!strcmp (dsc->vars[i].z_name, name))
455       return 0;
456   return 1;
457 }
458
459 /* Generates a name for a Z-score variable based on a variable
460    named VAR_NAME, given that *Z_CNT generated variable names are
461    known to already exist.  If successful, returns nonzero and
462    copies the new name into Z_NAME.  On failure, returns zero. */
463 static int
464 generate_z_varname (struct dsc_proc *dsc, char *z_name,
465                     const char *var_name, int *z_cnt)
466 {
467   char name[10];
468
469   /* Try a name based on the original variable name. */
470   name[0] = 'Z';
471   strcpy (name + 1, var_name);
472   name[8] = '\0';
473   if (try_name (dsc, name))
474     {
475       strcpy (z_name, name);
476       return 1;
477     }
478
479   /* Generate a synthetic name. */
480   for (;;)
481     {
482       (*z_cnt)++;
483
484       if (*z_cnt <= 99)
485         sprintf (name, "ZSC%03d", *z_cnt);
486       else if (*z_cnt <= 108)
487         sprintf (name, "STDZ%02d", *z_cnt - 99);
488       else if (*z_cnt <= 117)
489         sprintf (name, "ZZZZ%02d", *z_cnt - 108);
490       else if (*z_cnt <= 126)
491         sprintf (name, "ZQZQ%02d", *z_cnt - 117);
492       else
493         {
494           msg (SE, _("Ran out of generic names for Z-score variables.  "
495                      "There are only 126 generic names: ZSC001-ZSC0999, "
496                      "STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."));
497           return 0;
498         }
499       
500       if (try_name (dsc, name))
501         {
502           strcpy (z_name, name);
503           return 1;
504         }
505     }
506 }
507
508 /* Outputs a table describing the mapping between source
509    variables and Z-score variables. */
510 static void
511 dump_z_table (struct dsc_proc *dsc)
512 {
513   int cnt = 0;
514   struct tab_table *t;
515   
516   {
517     int i;
518     
519     for (i = 0; i < dsc->var_cnt; i++)
520       if (dsc->vars[i].z_name[0] != '\0')
521         cnt++;
522   }
523   
524   t = tab_create (2, cnt + 1, 0);
525   tab_title (t, 0, _("Mapping of variables to corresponding Z-scores."));
526   tab_columns (t, SOM_COL_DOWN, 1);
527   tab_headers (t, 0, 0, 1, 0);
528   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
529   tab_hline (t, TAL_2, 0, 1, 1);
530   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
531   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
532   tab_dim (t, tab_natural_dimensions);
533
534   {
535     int i, y;
536     
537     for (i = 0, y = 1; i < dsc->var_cnt; i++)
538       if (dsc->vars[i].z_name[0] != '\0')
539         {
540           tab_text (t, 0, y, TAB_LEFT, dsc->vars[i].v->name);
541           tab_text (t, 1, y++, TAB_LEFT, dsc->vars[i].z_name);
542         }
543   }
544   
545   tab_submit (t);
546 }
547
548 /* Transformation function to calculate Z-scores. */
549 static int
550 descriptives_trns_proc (struct trns_header *trns, struct ccase * c,
551                         int case_num UNUSED)
552 {
553   struct dsc_trns *t = (struct dsc_trns *) trns;
554   struct dsc_z_score *z;
555
556   for (z = t->z_scores; z < t->z_scores + t->z_score_cnt; z++)
557     {
558       double score = c->data[z->src_idx].f;
559
560       if (z->mean == SYSMIS || score == SYSMIS)
561         c->data[z->dst_idx].f = SYSMIS;
562       else
563         c->data[z->dst_idx].f = (score - z->mean) / z->std_dev;
564     }
565   return -1;
566 }
567
568 /* Frees a descriptives_trns struct. */
569 static void
570 descriptives_trns_free (struct trns_header * trns)
571 {
572   struct dsc_trns *t = (struct dsc_trns *) trns;
573
574   free (t->z_scores);
575 }
576
577 /* Sets up a transformation to calculate Z scores. */
578 static void
579 setup_z_trns (struct dsc_proc *dsc)
580 {
581   struct dsc_trns *t;
582   int cnt, i;
583
584   for (cnt = i = 0; i < dsc->var_cnt; i++)
585     if (dsc->vars[i].z_name[0] != '\0')
586       cnt++;
587
588   t = xmalloc (sizeof *t);
589   t->h.proc = descriptives_trns_proc;
590   t->h.free = descriptives_trns_free;
591   t->z_scores = xmalloc (cnt * sizeof *t->z_scores);
592   t->z_score_cnt = cnt;
593
594   for (cnt = i = 0; i < dsc->var_cnt; i++)
595     {
596       struct dsc_var *dv = &dsc->vars[i];
597       if (dv->z_name[0] != '\0')
598         {
599           struct dsc_z_score *z;
600           char *cp;
601           struct variable *dst_var;
602
603           dst_var = dict_create_var_assert (default_dict, dv->z_name, 0);
604           dst_var->init = 0;
605           if (dv->v->label)
606             {
607               dst_var->label = xmalloc (strlen (dv->v->label) + 12);
608               cp = stpcpy (dst_var->label, _("Z-score of "));
609               strcpy (cp, dv->v->label);
610             }
611           else
612             {
613               dst_var->label = xmalloc (strlen (dv->v->name) + 12);
614               cp = stpcpy (dst_var->label, _("Z-score of "));
615               strcpy (cp, dv->v->name);
616             }
617
618           z = &t->z_scores[cnt++];
619           z->src_idx = dv->v->fv;
620           z->dst_idx = dst_var->fv;
621           z->mean = dv->stats[DSC_MEAN];
622           z->std_dev = dv->stats[DSC_STDDEV];
623         }
624     }
625
626   add_transformation ((struct trns_header *) t);
627 }
628 \f
629 /* Statistical calculation. */
630
631 static int listwise_missing (struct dsc_proc *dsc, const struct ccase *c);
632
633 /* Calculates and displays descriptive statistics for the cases
634    in CF. */
635 static void
636 calc_descriptives (const struct casefile *cf, void *dsc_) 
637 {
638   struct dsc_proc *dsc = dsc_;
639   struct casereader *reader;
640   const struct ccase *c;
641   int i;
642
643   for (i = 0; i < dsc->var_cnt; i++)
644     {
645       struct dsc_var *dv = &dsc->vars[i];
646       
647       dv->valid = dv->missing = 0.0;
648       if (dv->moments != NULL)
649         moments_clear (dv->moments);
650       dv->min = DBL_MAX;
651       dv->max = -DBL_MAX;
652     }
653   dsc->missing_listwise = 0.;
654   dsc->valid = 0.;
655
656   /* First pass to handle most of the work. */
657   reader = casefile_get_reader (cf);
658   while (casereader_read (reader, &c)) 
659     {
660       double weight = dict_get_case_weight (default_dict, c, &dsc->bad_warn);
661       if (weight <= 0.0) 
662           continue;
663        
664       /* Check for missing values. */
665       if (listwise_missing (dsc, c)) 
666         {
667           dsc->missing_listwise += weight;
668           if (dsc->missing_type == DSC_LISTWISE)
669             continue; 
670         }
671       dsc->valid += weight;
672
673       for (i = 0; i < dsc->var_cnt; i++) 
674         {
675           struct dsc_var *dv = &dsc->vars[i];
676           double x = c->data[dv->v->fv].f;
677           
678           if (dsc->missing_type != DSC_LISTWISE
679               && (x == SYSMIS
680                   || (!dsc->include_user_missing
681                       && is_num_user_missing (x, dv->v))))
682             {
683               dv->missing += weight;
684               continue;
685             }
686
687           if (dv->moments != NULL)
688             moments_pass_one (dv->moments, x, weight);
689           if (x < dv->min)
690             dv->min = x;
691           if (x > dv->max)
692             dv->max = x;
693         }
694     }
695   casereader_destroy (reader);
696
697   /* Second pass for higher-order moments. */
698   if (dsc->max_moment > MOMENT_MEAN) 
699     {
700       reader = casefile_get_reader (cf);
701       while (casereader_read (reader, &c)) 
702         {
703           double weight = dict_get_case_weight (default_dict, c, 
704                                                 &dsc->bad_warn);
705           if (weight <= 0.0)
706             continue;
707       
708           /* Check for missing values. */
709           if (listwise_missing (dsc, c) 
710               && dsc->missing_type == DSC_LISTWISE)
711             continue; 
712
713           for (i = 0; i < dsc->var_cnt; i++) 
714             {
715               struct dsc_var *dv = &dsc->vars[i];
716               double x = c->data[dv->v->fv].f;
717           
718               if (dsc->missing_type != DSC_LISTWISE
719                   && (x == SYSMIS
720                       || (!dsc->include_user_missing
721                           && is_num_user_missing (x, dv->v))))
722                 continue;
723
724               if (dv->moments != NULL)
725                 moments_pass_two (dv->moments, x, weight);
726             }
727         }
728       casereader_destroy (reader);
729     }
730   
731   /* Calculate results. */
732   for (i = 0; i < dsc->var_cnt; i++)
733     {
734       struct dsc_var *dv = &dsc->vars[i];
735       double W;
736       int j;
737
738       for (j = 0; j < DSC_N_STATS; j++)
739         dv->stats[j] = SYSMIS;
740
741       dv->valid = W = dsc->valid - dv->missing;
742
743       if (dv->moments != NULL)
744         moments_calculate (dv->moments, NULL,
745                            &dv->stats[DSC_MEAN], &dv->stats[DSC_VARIANCE],
746                            &dv->stats[DSC_SKEWNESS], &dv->stats[DSC_KURTOSIS]);
747       if (dsc->calc_stats & (1ul << DSC_SEMEAN)
748           && dv->stats[DSC_VARIANCE] != SYSMIS && W > 0.)
749         dv->stats[DSC_SEMEAN] = sqrt (dv->stats[DSC_VARIANCE]) / sqrt (W);
750       if (dsc->calc_stats & (1ul << DSC_STDDEV)
751           && dv->stats[DSC_VARIANCE] != SYSMIS)
752         dv->stats[DSC_STDDEV] = sqrt (dv->stats[DSC_VARIANCE]);
753       if (dsc->calc_stats & (1ul << DSC_SEKURT)) 
754         if (dv->stats[DSC_KURTOSIS] != SYSMIS)
755             dv->stats[DSC_SEKURT] = calc_sekurt (W);
756       if (dsc->calc_stats & (1ul << DSC_SESKEW)
757           && dv->stats[DSC_SKEWNESS] != SYSMIS)
758         dv->stats[DSC_SESKEW] = calc_seskew (W);
759       dv->stats[DSC_RANGE] = ((dv->min == DBL_MAX || dv->max == -DBL_MAX)
760                               ? SYSMIS : dv->max - dv->min);
761       dv->stats[DSC_MIN] = dv->min == DBL_MAX ? SYSMIS : dv->min;
762       dv->stats[DSC_MAX] = dv->max == -DBL_MAX ? SYSMIS : dv->max;
763       if (dsc->calc_stats & (1ul << DSC_SUM))
764         dv->stats[DSC_SUM] = W * dv->stats[DSC_MEAN];
765     }
766
767   /* Output results. */
768   display (dsc);
769 }
770
771 /* Returns nonzero if any of the descriptives variables in DSC's
772    variable list have missing values in case C, zero otherwise. */
773 static int
774 listwise_missing (struct dsc_proc *dsc, const struct ccase *c) 
775 {
776   int i;
777
778   for (i = 0; i < dsc->var_cnt; i++)
779     {
780       struct dsc_var *dv = &dsc->vars[i];
781       double x = c->data[dv->v->fv].f;
782
783       if (x == SYSMIS
784           || (!dsc->include_user_missing && is_num_user_missing (x, dv->v)))
785         return 1;
786     }
787   return 0;
788 }
789 \f
790 /* Statistical display. */
791
792 static algo_compare_func descriptives_compare_dsc_vars;
793
794 /* Displays a table of descriptive statistics for DSC. */
795 static void
796 display (struct dsc_proc *dsc)
797 {
798   int i, j;
799   int nc;
800   struct tab_table *t;
801
802   nc = 1 + (dsc->format == DSC_SERIAL ? 2 : 1);
803   for (i = 0; i < DSC_N_STATS; i++)
804     if (dsc->show_stats & (1ul << i))
805       nc++;
806
807   if (dsc->sort_by_stat != DSC_NONE)
808     sort (dsc->vars, dsc->var_cnt, sizeof *dsc->vars,
809           descriptives_compare_dsc_vars, dsc);
810
811   t = tab_create (nc, dsc->var_cnt + 1, 0);
812   tab_headers (t, 1, 0, 1, 0);
813   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, dsc->var_cnt);
814   tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
815   tab_hline (t, TAL_2, 0, nc - 1, 1);
816   tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
817   tab_dim (t, tab_natural_dimensions);
818
819   nc = 0;
820   tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
821   if (dsc->format == DSC_SERIAL)
822     {
823       tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Valid N"));
824       tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, _("Missing N"));
825     }
826   else
827     tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, "N");
828
829   for (i = 0; i < DSC_N_STATS; i++)
830     if (dsc->show_stats & (1ul << i))
831       {
832         const char *title = gettext (dsc_info[i].name);
833         tab_text (t, nc++, 0, TAB_CENTER | TAT_TITLE, title);
834       }
835
836   for (i = 0; i < dsc->var_cnt; i++)
837     {
838       struct dsc_var *dv = &dsc->vars[i];
839
840       nc = 0;
841       tab_text (t, nc++, i + 1, TAB_LEFT, dv->v->name);
842       tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->valid);
843       if (dsc->format == DSC_SERIAL)
844         tab_text (t, nc++, i + 1, TAT_PRINTF, "%g", dv->missing);
845       for (j = 0; j < DSC_N_STATS; j++)
846         if (dsc->show_stats & (1ul << j))
847           tab_float (t, nc++, i + 1, TAB_NONE, dv->stats[j], 10, 3);
848     }
849
850   tab_title (t, 1, _("Valid cases = %g; cases with missing value(s) = %g."),
851              dsc->valid, dsc->missing_listwise);
852
853   tab_submit (t);
854 }
855
856 /* Compares `struct dsc_var's A and B according to the ordering
857    specified by CMD. */
858 static int
859 descriptives_compare_dsc_vars (const void *a_, const void *b_, void *dsc_)
860 {
861   const struct dsc_var *a = a_;
862   const struct dsc_var *b = b_;
863   struct dsc_proc *dsc = dsc_;
864
865   int result;
866
867   if (dsc->sort_by_stat == DSC_NAME)
868     result = strcmp (a->v->name, b->v->name);
869   else 
870     {
871       double as = a->stats[dsc->sort_by_stat];
872       double bs = b->stats[dsc->sort_by_stat];
873
874       result = as < bs ? -1 : as > bs;
875     }
876
877   if (!dsc->sort_ascending)
878     result = -result;
879
880   return result;
881 }