Oneway: Use covariance matrix and sweep operator
[pspp] / src / language / stats / oneway.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2007, 2009, 2010 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <data/case.h>
20 #include <data/casegrouper.h>
21 #include <data/casereader.h>
22
23 #include <math/covariance.h>
24 #include <math/categoricals.h>
25 #include <gsl/gsl_matrix.h>
26 #include <linreg/sweep.h>
27
28 #include <libpspp/ll.h>
29
30 #include <language/lexer/lexer.h>
31 #include <language/lexer/variable-parser.h>
32 #include <language/lexer/value-parser.h>
33 #include <language/command.h>
34
35 #include <data/procedure.h>
36 #include <data/dictionary.h>
37
38
39 #include <language/dictionary/split-file.h>
40 #include <libpspp/hash.h>
41 #include <libpspp/taint.h>
42 #include <math/group-proc.h>
43 #include <math/levene.h>
44 #include <libpspp/misc.h>
45
46 #include <output/tab.h>
47
48 #include <gsl/gsl_cdf.h>
49 #include <math.h>
50 #include <data/format.h>
51
52 #include <libpspp/message.h>
53
54 #include "gettext.h"
55 #define _(msgid) gettext (msgid)
56
57 enum missing_type
58   {
59     MISS_LISTWISE,
60     MISS_ANALYSIS,
61   };
62
63 enum statistics
64   {
65     STATS_DESCRIPTIVES = 0x0001,
66     STATS_HOMOGENEITY = 0x0002
67   };
68
69 struct coeff_node
70 {
71   struct ll ll; 
72   double coeff; 
73 };
74
75
76 struct contrasts_node
77 {
78   struct ll ll; 
79   struct ll_list coefficient_list;
80
81   bool bad_count; /* True if the number of coefficients does not equal the number of groups */
82 };
83
84 struct oneway_spec
85 {
86   size_t n_vars;
87   const struct variable **vars;
88
89   const struct dictionary *dict;
90
91   const struct variable *indep_var;
92
93   enum statistics stats;
94
95   enum missing_type missing_type;
96   enum mv_class exclude;
97
98   /* List of contrasts */
99   struct ll_list contrast_list;
100 };
101
102
103 /* Workspace variable for each dependent variable */
104 struct per_var_ws
105 {
106   struct covariance *cov;
107
108   double sst;
109   double sse;
110   double ssa;
111
112   int n_groups;
113
114   double cc;
115 };
116
117 struct oneway_workspace
118 {
119   /* The number of distinct values of the independent variable, when all
120      missing values are disregarded */
121   int actual_number_of_groups;
122
123   /* A  hash table containing all the distinct values of the independent
124      variable */
125   struct hsh_table *group_hash;
126
127   struct per_var_ws *vws;
128 };
129
130 /* Routines to show the output tables */
131 static void show_anova_table (const struct oneway_spec *, const struct oneway_workspace *);
132 static void show_descriptives (const struct oneway_spec *, const struct dictionary *dict);
133 static void show_homogeneity (const struct oneway_spec *);
134
135 static void output_oneway (const struct oneway_spec *, struct oneway_workspace *ws);
136 static void run_oneway (const struct oneway_spec *cmd, struct casereader *input, const struct dataset *ds);
137
138 int
139 cmd_oneway (struct lexer *lexer, struct dataset *ds)
140 {
141   struct oneway_spec oneway ;
142   oneway.n_vars = 0;
143   oneway.vars = NULL;
144   oneway.indep_var = NULL;
145   oneway.stats = 0;
146   oneway.missing_type = MISS_ANALYSIS;
147   oneway.exclude = MV_ANY;
148   oneway.dict = dataset_dict (ds);  
149
150   ll_init (&oneway.contrast_list);
151
152   
153   if ( lex_match (lexer, '/'))
154     {
155       if (!lex_force_match_id (lexer, "VARIABLES"))
156         {
157           goto error;
158         }
159       lex_match (lexer, '=');
160     }
161
162   if (!parse_variables_const (lexer, oneway.dict,
163                               &oneway.vars, &oneway.n_vars,
164                               PV_NO_DUPLICATE | PV_NUMERIC))
165     goto error;
166
167   lex_force_match (lexer, T_BY);
168
169   oneway.indep_var = parse_variable_const (lexer, oneway.dict);
170
171   while (lex_token (lexer) != '.')
172     {
173       lex_match (lexer, '/');
174
175       if (lex_match_id (lexer, "STATISTICS"))
176         {
177           lex_match (lexer, '=');
178           while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
179             {
180               if (lex_match_id (lexer, "DESCRIPTIVES"))
181                 {
182                   oneway.stats |= STATS_DESCRIPTIVES;
183                 }
184               else if (lex_match_id (lexer, "HOMOGENEITY"))
185                 {
186                   oneway.stats |= STATS_HOMOGENEITY;
187                 }
188               else
189                 {
190                   lex_error (lexer, NULL);
191                   goto error;
192                 }
193             }
194         }
195       else if (lex_match_id (lexer, "CONTRAST"))
196         {
197           struct contrasts_node *cl = xzalloc (sizeof *cl);
198
199           struct ll_list *coefficient_list = &cl->coefficient_list;
200           lex_match (lexer, '=');
201
202           ll_init (coefficient_list);
203
204           while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
205             {
206               if ( lex_is_number (lexer))
207                 {
208                   struct coeff_node *cc = xmalloc (sizeof *cc);
209                   cc->coeff = lex_number (lexer);
210
211                   ll_push_tail (coefficient_list, &cc->ll);
212                   lex_get (lexer);
213                 }
214               else
215                 {
216                   lex_error (lexer, NULL);
217                   goto error;
218                 }
219             }
220
221           ll_push_tail (&oneway.contrast_list, &cl->ll);
222         }
223       else if (lex_match_id (lexer, "MISSING"))
224         {
225           lex_match (lexer, '=');
226           while (lex_token (lexer) != '.' && lex_token (lexer) != '/')
227             {
228               if (lex_match_id (lexer, "INCLUDE"))
229                 {
230                   oneway.exclude = MV_SYSTEM;
231                 }
232               else if (lex_match_id (lexer, "EXCLUDE"))
233                 {
234                   oneway.exclude = MV_ANY;
235                 }
236               else if (lex_match_id (lexer, "LISTWISE"))
237                 {
238                   oneway.missing_type = MISS_LISTWISE;
239                 }
240               else if (lex_match_id (lexer, "ANALYSIS"))
241                 {
242                   oneway.missing_type = MISS_ANALYSIS;
243                 }
244               else
245                 {
246                   lex_error (lexer, NULL);
247                   goto error;
248                 }
249             }
250         }
251       else
252         {
253           lex_error (lexer, NULL);
254           goto error;
255         }
256     }
257
258
259   {
260     struct casegrouper *grouper;
261     struct casereader *group;
262     bool ok;
263
264     grouper = casegrouper_create_splits (proc_open (ds), oneway.dict);
265     while (casegrouper_get_next_group (grouper, &group))
266       run_oneway (&oneway, group, ds);
267     ok = casegrouper_destroy (grouper);
268     ok = proc_commit (ds) && ok;
269   }
270
271   free (oneway.vars);
272   return CMD_SUCCESS;
273
274  error:
275   free (oneway.vars);
276   return CMD_FAILURE;
277 }
278
279
280 \f
281
282 static int
283 compare_double_3way (const void *a_, const void *b_, const void *aux UNUSED)
284 {
285   const double *a = a_;
286   const double *b = b_;
287   return *a < *b ? -1 : *a > *b;
288 }
289
290 static unsigned
291 do_hash_double (const void *value_, const void *aux UNUSED)
292 {
293   const double *value = value_;
294   return hash_double (*value, 0);
295 }
296
297 static void
298 free_double (void *value_, const void *aux UNUSED)
299 {
300   double *value = value_;
301   free (value);
302 }
303
304 static void postcalc (const struct oneway_spec *cmd);
305 static void  precalc (const struct oneway_spec *cmd);
306
307
308 static void
309 run_oneway (const struct oneway_spec *cmd,
310             struct casereader *input,
311             const struct dataset *ds)
312 {
313   int v;
314   struct taint *taint;
315   struct dictionary *dict = dataset_dict (ds);
316   struct casereader *reader;
317   struct ccase *c;
318   const struct variable *wv = dict_get_weight (dict);
319
320   struct oneway_workspace ws;
321
322   ws.vws = xmalloc (cmd->n_vars * sizeof (*ws.vws));
323
324   for (v = 0; v < cmd->n_vars; ++v)
325     {
326       ws.vws[v].cov = covariance_2pass_create (1, &cmd->vars[v],
327                                                1, &cmd->indep_var,
328                                                wv, cmd->exclude);
329       ws.vws[v].cc = 0;
330     }
331
332   c = casereader_peek (input, 0);
333   if (c == NULL)
334     {
335       casereader_destroy (input);
336       return;
337     }
338   output_split_file_values (ds, c);
339   case_unref (c);
340
341   taint = taint_clone (casereader_get_taint (input));
342
343   ws.group_hash = hsh_create (4,
344                                   compare_double_3way,
345                                   do_hash_double,
346                                   free_double,
347                                   cmd->indep_var);
348
349   precalc (cmd);
350
351   input = casereader_create_filter_missing (input, &cmd->indep_var, 1,
352                                             cmd->exclude, NULL, NULL);
353   if (cmd->missing_type == MISS_LISTWISE)
354     input = casereader_create_filter_missing (input, cmd->vars, cmd->n_vars,
355                                               cmd->exclude, NULL, NULL);
356   input = casereader_create_filter_weight (input, dict, NULL, NULL);
357
358   reader = casereader_clone (input);
359
360   for (; (c = casereader_read (reader)) != NULL; case_unref (c))
361     {
362       size_t i;
363
364       const double weight = dict_get_case_weight (dict, c, NULL);
365
366       const union value *indep_val = case_data (c, cmd->indep_var);
367       void **p = hsh_probe (ws.group_hash, &indep_val->f);
368       if (*p == NULL)
369         {
370           double *value = *p = xmalloc (sizeof *value);
371           *value = indep_val->f;
372         }
373
374       for (i = 0; i < cmd->n_vars; ++i)
375         {
376           {
377             struct per_var_ws *pvw = &ws.vws[i];
378
379             pvw->cc += weight;
380             covariance_accumulate_pass1 (pvw->cov, c);
381           }
382
383           const struct variable *v = cmd->vars[i];
384
385           const union value *val = case_data (c, v);
386
387           struct group_proc *gp = group_proc_get (cmd->vars[i]);
388           struct hsh_table *group_hash = gp->group_hash;
389
390           struct group_statistics *gs;
391
392           gs = hsh_find (group_hash, indep_val );
393
394           if ( ! gs )
395             {
396               gs = xmalloc (sizeof *gs);
397               gs->id = *indep_val;
398               gs->sum = 0;
399               gs->n = 0;
400               gs->ssq = 0;
401               gs->sum_diff = 0;
402               gs->minimum = DBL_MAX;
403               gs->maximum = -DBL_MAX;
404
405               hsh_insert ( group_hash, gs );
406             }
407
408           if (!var_is_value_missing (v, val, cmd->exclude))
409             {
410               struct group_statistics *totals = &gp->ugs;
411
412               totals->n += weight;
413               totals->sum += weight * val->f;
414               totals->ssq += weight * pow2 (val->f);
415
416               if ( val->f * weight  < totals->minimum )
417                 totals->minimum = val->f * weight;
418
419               if ( val->f * weight  > totals->maximum )
420                 totals->maximum = val->f * weight;
421
422               gs->n += weight;
423               gs->sum += weight * val->f;
424               gs->ssq += weight * pow2 (val->f);
425
426               if ( val->f * weight  < gs->minimum )
427                 gs->minimum = val->f * weight;
428
429               if ( val->f * weight  > gs->maximum )
430                 gs->maximum = val->f * weight;
431             }
432
433           gp->n_groups = hsh_count (group_hash );
434         }
435
436     }
437   casereader_destroy (reader);
438   reader = casereader_clone (input);
439   for ( ; (c = casereader_read (reader) ); case_unref (c))
440     {
441       int i;
442       for (i = 0; i < cmd->n_vars; ++i)
443         {
444           struct per_var_ws *pvw = &ws.vws[i];
445           covariance_accumulate_pass2 (pvw->cov, c);
446         }
447     }
448   casereader_destroy (reader);
449
450   for (v = 0; v < cmd->n_vars; ++v)
451     {
452       struct per_var_ws *pvw = &ws.vws[v];
453       gsl_matrix *cm = covariance_calculate_unnormalized (pvw->cov);
454       const struct categoricals *cats = covariance_get_categoricals (pvw->cov);
455
456       pvw->sst = gsl_matrix_get (cm, 0, 0);
457
458       reg_sweep (cm, 0);
459
460       pvw->sse = gsl_matrix_get (cm, 0, 0);
461
462       pvw->ssa = pvw->sst - pvw->sse;
463
464       pvw->n_groups = categoricals_total (cats);
465     }
466
467   postcalc (cmd);
468
469   if ( cmd->stats & STATS_HOMOGENEITY )
470     levene (dict, casereader_clone (input), cmd->indep_var,
471             cmd->n_vars, cmd->vars, cmd->exclude);
472
473   casereader_destroy (input);
474
475   ws.actual_number_of_groups = hsh_count (ws.group_hash);
476
477   if (!taint_has_tainted_successor (taint))
478     output_oneway (cmd, &ws);
479
480   taint_destroy (taint);
481 }
482
483 /* Pre calculations */
484 static void
485 precalc (const struct oneway_spec *cmd)
486 {
487   size_t i = 0;
488
489   for (i = 0; i < cmd->n_vars; ++i)
490     {
491       struct group_proc *gp = group_proc_get (cmd->vars[i]);
492       struct group_statistics *totals = &gp->ugs;
493
494       /* Create a hash for each of the dependent variables.
495          The hash contains a group_statistics structure,
496          and is keyed by value of the independent variable */
497
498       gp->group_hash = hsh_create (4, compare_group, hash_group,
499                                    (hsh_free_func *) free_group,
500                                    cmd->indep_var);
501
502       totals->sum = 0;
503       totals->n = 0;
504       totals->ssq = 0;
505       totals->sum_diff = 0;
506       totals->maximum = -DBL_MAX;
507       totals->minimum = DBL_MAX;
508     }
509 }
510
511 /* Post calculations for the ONEWAY command */
512 static void
513 postcalc (const struct oneway_spec *cmd)
514 {
515   size_t i = 0;
516
517   for (i = 0; i < cmd->n_vars; ++i)
518     {
519       struct group_proc *gp = group_proc_get (cmd->vars[i]);
520       struct hsh_table *group_hash = gp->group_hash;
521       struct group_statistics *totals = &gp->ugs;
522
523       struct hsh_iterator g;
524       struct group_statistics *gs;
525
526       for (gs =  hsh_first (group_hash, &g);
527            gs != 0;
528            gs = hsh_next (group_hash, &g))
529         {
530           gs->mean = gs->sum / gs->n;
531           gs->s_std_dev = sqrt (gs->ssq / gs->n - pow2 (gs->mean));
532
533           gs->std_dev = sqrt (
534                               gs->n / (gs->n - 1) *
535                               ( gs->ssq / gs->n - pow2 (gs->mean))
536                               );
537
538           gs->se_mean = gs->std_dev / sqrt (gs->n);
539           gs->mean_diff = gs->sum_diff / gs->n;
540         }
541
542       totals->mean = totals->sum / totals->n;
543       totals->std_dev = sqrt (
544                               totals->n / (totals->n - 1) *
545                               (totals->ssq / totals->n - pow2 (totals->mean))
546                               );
547
548       totals->se_mean = totals->std_dev / sqrt (totals->n);
549     }
550 }
551
552 static void show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws);
553 static void show_contrast_tests (const struct oneway_spec *cmd, struct oneway_workspace *ws);
554
555 static void
556 output_oneway (const struct oneway_spec *cmd, struct oneway_workspace *ws)
557 {
558   size_t i = 0;
559
560   /* Check the sanity of the given contrast values */
561   struct contrasts_node *coeff_list  = NULL;
562   ll_for_each (coeff_list, struct contrasts_node, ll, &cmd->contrast_list)
563     {
564       struct coeff_node *cn = NULL;
565       double sum = 0;
566       struct ll_list *cl = &coeff_list->coefficient_list;
567       ++i;
568
569       if (ll_count (cl) != ws->actual_number_of_groups)
570         {
571           msg (SW,
572                _("Number of contrast coefficients must equal the number of groups"));
573           coeff_list->bad_count = true;
574           continue;
575         }
576
577       ll_for_each (cn, struct coeff_node, ll, cl)
578         sum += cn->coeff;
579
580       if ( sum != 0.0 )
581         msg (SW, _("Coefficients for contrast %zu do not total zero"), i);
582     }
583
584   if (cmd->stats & STATS_DESCRIPTIVES)
585     show_descriptives (cmd, cmd->dict);
586
587   if (cmd->stats & STATS_HOMOGENEITY)
588     show_homogeneity (cmd);
589
590   show_anova_table (cmd, ws);
591
592
593   if (ll_count (&cmd->contrast_list) > 0)
594     {
595       show_contrast_coeffs (cmd, ws);
596       show_contrast_tests (cmd, ws);
597     }
598
599
600   /* Clean up */
601   for (i = 0; i < cmd->n_vars; ++i )
602     {
603       struct hsh_table *group_hash = group_proc_get (cmd->vars[i])->group_hash;
604
605       hsh_destroy (group_hash);
606     }
607
608   hsh_destroy (ws->group_hash);
609 }
610
611
612 /* Show the ANOVA table */
613 static void
614 show_anova_table (const struct oneway_spec *cmd, const struct oneway_workspace *ws)
615 {
616   size_t i;
617   int n_cols =7;
618   size_t n_rows = cmd->n_vars * 3 + 1;
619
620   struct tab_table *t = tab_create (n_cols, n_rows);
621
622   tab_headers (t, 2, 0, 1, 0);
623
624   tab_box (t,
625            TAL_2, TAL_2,
626            -1, TAL_1,
627            0, 0,
628            n_cols - 1, n_rows - 1);
629
630   tab_hline (t, TAL_2, 0, n_cols - 1, 1 );
631   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
632   tab_vline (t, TAL_0, 1, 0, 0);
633
634   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Sum of Squares"));
635   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("df"));
636   tab_text (t, 4, 0, TAB_CENTER | TAT_TITLE, _("Mean Square"));
637   tab_text (t, 5, 0, TAB_CENTER | TAT_TITLE, _("F"));
638   tab_text (t, 6, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
639
640
641   for (i = 0; i < cmd->n_vars; ++i)
642     {
643       const struct per_var_ws *pvw = &ws->vws[i];
644       struct group_proc *gp = group_proc_get (cmd->vars[i]);
645       const double df1 = pvw->n_groups - 1;
646       const double df2 = pvw->cc - pvw->n_groups;
647       const double msa = pvw->ssa / df1;
648
649       const char *s = var_to_string (cmd->vars[i]);
650
651       tab_text (t, 0, i * 3 + 1, TAB_LEFT | TAT_TITLE, s);
652       tab_text (t, 1, i * 3 + 1, TAB_LEFT | TAT_TITLE, _("Between Groups"));
653       tab_text (t, 1, i * 3 + 2, TAB_LEFT | TAT_TITLE, _("Within Groups"));
654       tab_text (t, 1, i * 3 + 3, TAB_LEFT | TAT_TITLE, _("Total"));
655
656       if (i > 0)
657         tab_hline (t, TAL_1, 0, n_cols - 1, i * 3 + 1);
658
659
660       gp->mse  = (pvw->sst - pvw->ssa) / df2;
661
662       /* Sums of Squares */
663       tab_double (t, 2, i * 3 + 1, 0, pvw->ssa, NULL);
664       tab_double (t, 2, i * 3 + 3, 0, pvw->sst, NULL);
665       tab_double (t, 2, i * 3 + 2, 0, pvw->sse, NULL);
666
667
668       /* Degrees of freedom */
669       tab_fixed (t, 3, i * 3 + 1, 0, df1, 4, 0);
670       tab_fixed (t, 3, i * 3 + 2, 0, df2, 4, 0);
671       tab_fixed (t, 3, i * 3 + 3, 0, pvw->cc - 1, 4, 0);
672
673       /* Mean Squares */
674       tab_double (t, 4, i * 3 + 1, TAB_RIGHT, msa, NULL);
675       tab_double (t, 4, i * 3 + 2, TAB_RIGHT, gp->mse, NULL);
676
677       {
678         const double F = msa / gp->mse ;
679
680         /* The F value */
681         tab_double (t, 5, i * 3 + 1, 0,  F, NULL);
682
683         /* The significance */
684         tab_double (t, 6, i * 3 + 1, 0, gsl_cdf_fdist_Q (F, df1, df2), NULL);
685       }
686     }
687
688   tab_title (t, _("ANOVA"));
689   tab_submit (t);
690 }
691
692
693 /* Show the descriptives table */
694 static void
695 show_descriptives (const struct oneway_spec *cmd, const struct dictionary *dict)
696 {
697   size_t v;
698   int n_cols = 10;
699   struct tab_table *t;
700   int row;
701
702   const double confidence = 0.95;
703   const double q = (1.0 - confidence) / 2.0;
704
705   const struct variable *wv = dict_get_weight (dict);
706   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
707
708   int n_rows = 2;
709
710   for ( v = 0; v < cmd->n_vars; ++v )
711     n_rows += group_proc_get (cmd->vars[v])->n_groups + 1;
712
713   t = tab_create (n_cols, n_rows);
714   tab_headers (t, 2, 0, 2, 0);
715
716
717   /* Put a frame around the entire box, and vertical lines inside */
718   tab_box (t,
719            TAL_2, TAL_2,
720            -1, TAL_1,
721            0, 0,
722            n_cols - 1, n_rows - 1);
723
724   /* Underline headers */
725   tab_hline (t, TAL_2, 0, n_cols - 1, 2);
726   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
727
728   tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("N"));
729   tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Mean"));
730   tab_text (t, 4, 1, TAB_CENTER | TAT_TITLE, _("Std. Deviation"));
731   tab_text (t, 5, 1, TAB_CENTER | TAT_TITLE, _("Std. Error"));
732
733
734   tab_vline (t, TAL_0, 7, 0, 0);
735   tab_hline (t, TAL_1, 6, 7, 1);
736   tab_joint_text_format (t, 6, 0, 7, 0, TAB_CENTER | TAT_TITLE,
737                          _("%g%% Confidence Interval for Mean"),
738                          confidence*100.0);
739
740   tab_text (t, 6, 1, TAB_CENTER | TAT_TITLE, _("Lower Bound"));
741   tab_text (t, 7, 1, TAB_CENTER | TAT_TITLE, _("Upper Bound"));
742
743   tab_text (t, 8, 1, TAB_CENTER | TAT_TITLE, _("Minimum"));
744   tab_text (t, 9, 1, TAB_CENTER | TAT_TITLE, _("Maximum"));
745
746
747   tab_title (t, _("Descriptives"));
748
749
750   row = 2;
751   for (v = 0; v < cmd->n_vars; ++v)
752     {
753       double T;
754       double std_error;
755
756       struct group_proc *gp = group_proc_get (cmd->vars[v]);
757
758       struct group_statistics *gs;
759       struct group_statistics *totals = &gp->ugs;
760
761       const char *s = var_to_string (cmd->vars[v]);
762       const struct fmt_spec *fmt = var_get_print_format (cmd->vars[v]);
763
764       struct group_statistics *const *gs_array =
765         (struct group_statistics *const *) hsh_sort (gp->group_hash);
766       int count = 0;
767
768       tab_text (t, 0, row, TAB_LEFT | TAT_TITLE, s);
769       if ( v > 0)
770         tab_hline (t, TAL_1, 0, n_cols - 1, row);
771
772       for (count = 0; count < hsh_count (gp->group_hash); ++count)
773         {
774           struct string vstr;
775           ds_init_empty (&vstr);
776           gs = gs_array[count];
777
778           var_append_value_name (cmd->indep_var, &gs->id, &vstr);
779
780           tab_text (t, 1, row + count,
781                     TAB_LEFT | TAT_TITLE,
782                     ds_cstr (&vstr));
783
784           ds_destroy (&vstr);
785
786           /* Now fill in the numbers ... */
787
788           tab_fixed (t, 2, row + count, 0, gs->n, 8, 0);
789
790           tab_double (t, 3, row + count, 0, gs->mean, NULL);
791
792           tab_double (t, 4, row + count, 0, gs->std_dev, NULL);
793
794           std_error = gs->std_dev / sqrt (gs->n) ;
795           tab_double (t, 5, row + count, 0,
796                       std_error, NULL);
797
798           /* Now the confidence interval */
799
800           T = gsl_cdf_tdist_Qinv (q, gs->n - 1);
801
802           tab_double (t, 6, row + count, 0,
803                       gs->mean - T * std_error, NULL);
804
805           tab_double (t, 7, row + count, 0,
806                       gs->mean + T * std_error, NULL);
807
808           /* Min and Max */
809
810           tab_double (t, 8, row + count, 0,  gs->minimum, fmt);
811           tab_double (t, 9, row + count, 0,  gs->maximum, fmt);
812         }
813
814       tab_text (t, 1, row + count,
815                 TAB_LEFT | TAT_TITLE, _("Total"));
816
817       tab_double (t, 2, row + count, 0, totals->n, wfmt);
818
819       tab_double (t, 3, row + count, 0, totals->mean, NULL);
820
821       tab_double (t, 4, row + count, 0, totals->std_dev, NULL);
822
823       std_error = totals->std_dev / sqrt (totals->n) ;
824
825       tab_double (t, 5, row + count, 0, std_error, NULL);
826
827       /* Now the confidence interval */
828
829       T = gsl_cdf_tdist_Qinv (q, totals->n - 1);
830
831       tab_double (t, 6, row + count, 0,
832                   totals->mean - T * std_error, NULL);
833
834       tab_double (t, 7, row + count, 0,
835                   totals->mean + T * std_error, NULL);
836
837       /* Min and Max */
838
839       tab_double (t, 8, row + count, 0,  totals->minimum, fmt);
840       tab_double (t, 9, row + count, 0,  totals->maximum, fmt);
841
842       row += gp->n_groups + 1;
843     }
844
845   tab_submit (t);
846 }
847
848 /* Show the homogeneity table */
849 static void
850 show_homogeneity (const struct oneway_spec *cmd)
851 {
852   size_t v;
853   int n_cols = 5;
854   size_t n_rows = cmd->n_vars + 1;
855
856   struct tab_table *t;
857
858
859   t = tab_create (n_cols, n_rows);
860   tab_headers (t, 1, 0, 1, 0);
861
862
863   /* Put a frame around the entire box, and vertical lines inside */
864   tab_box (t,
865            TAL_2, TAL_2,
866            -1, TAL_1,
867            0, 0,
868            n_cols - 1, n_rows - 1);
869
870
871   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
872   tab_vline (t, TAL_2, 1, 0, n_rows - 1);
873
874
875   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Levene Statistic"));
876   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("df1"));
877   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("df2"));
878   tab_text (t, 4, 0, TAB_CENTER | TAT_TITLE, _("Significance"));
879
880   tab_title (t, _("Test of Homogeneity of Variances"));
881
882   for (v = 0; v < cmd->n_vars; ++v)
883     {
884       double F;
885       const struct variable *var = cmd->vars[v];
886       const struct group_proc *gp = group_proc_get (cmd->vars[v]);
887       const char *s = var_to_string (var);
888       const struct group_statistics *totals = &gp->ugs;
889
890       const double df1 = gp->n_groups - 1;
891       const double df2 = totals->n - gp->n_groups;
892
893       tab_text (t, 0, v + 1, TAB_LEFT | TAT_TITLE, s);
894
895       F = gp->levene;
896       tab_double (t, 1, v + 1, TAB_RIGHT, F, NULL);
897       tab_fixed (t, 2, v + 1, TAB_RIGHT, df1, 8, 0);
898       tab_fixed (t, 3, v + 1, TAB_RIGHT, df2, 8, 0);
899
900       /* Now the significance */
901       tab_double (t, 4, v + 1, TAB_RIGHT,gsl_cdf_fdist_Q (F, df1, df2), NULL);
902     }
903
904   tab_submit (t);
905 }
906
907
908 /* Show the contrast coefficients table */
909 static void
910 show_contrast_coeffs (const struct oneway_spec *cmd, struct oneway_workspace *ws)
911 {
912   int c_num = 0;
913   struct ll *cli;
914
915   int n_contrasts = ll_count (&cmd->contrast_list);
916   int n_cols = 2 + ws->actual_number_of_groups;
917   int n_rows = 2 + n_contrasts;
918
919   void *const *group_values;
920
921   struct tab_table *t;
922
923   t = tab_create (n_cols, n_rows);
924   tab_headers (t, 2, 0, 2, 0);
925
926   /* Put a frame around the entire box, and vertical lines inside */
927   tab_box (t,
928            TAL_2, TAL_2,
929            -1, TAL_1,
930            0, 0,
931            n_cols - 1, n_rows - 1);
932
933   tab_box (t,
934            -1, -1,
935            TAL_0, TAL_0,
936            2, 0,
937            n_cols - 1, 0);
938
939   tab_box (t,
940            -1, -1,
941            TAL_0, TAL_0,
942            0, 0,
943            1, 1);
944
945   tab_hline (t, TAL_1, 2, n_cols - 1, 1);
946   tab_hline (t, TAL_2, 0, n_cols - 1, 2);
947
948   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
949
950   tab_title (t, _("Contrast Coefficients"));
951
952   tab_text (t,  0, 2, TAB_LEFT | TAT_TITLE, _("Contrast"));
953
954
955   tab_joint_text (t, 2, 0, n_cols - 1, 0, TAB_CENTER | TAT_TITLE,
956                   var_to_string (cmd->indep_var));
957
958   group_values = hsh_sort (ws->group_hash);
959
960   for ( cli = ll_head (&cmd->contrast_list);
961         cli != ll_null (&cmd->contrast_list);
962         cli = ll_next (cli))
963     {
964       int count = 0;
965       struct contrasts_node *cn = ll_data (cli, struct contrasts_node, ll);
966       struct ll *coeffi = ll_head (&cn->coefficient_list);
967
968       tab_text_format (t, 1, c_num + 2, TAB_CENTER, "%d", c_num + 1);
969
970       for (count = 0;
971            count < hsh_count (ws->group_hash) && coeffi != ll_null (&cn->coefficient_list);
972            ++count)
973         {
974           double *group_value_p;
975           union value group_value;
976           struct string vstr;
977
978           ds_init_empty (&vstr);
979
980           group_value_p = group_values[count];
981           group_value.f = *group_value_p;
982           var_append_value_name (cmd->indep_var, &group_value, &vstr);
983
984           tab_text (t, count + 2, 1, TAB_CENTER | TAT_TITLE, ds_cstr (&vstr));
985
986           ds_destroy (&vstr);
987
988           if (cn->bad_count)
989             tab_text (t, count + 2, c_num + 2, TAB_RIGHT, "?" );
990           else
991             {
992               struct coeff_node *coeffn = ll_data (coeffi, struct coeff_node, ll);
993
994               tab_text_format (t, count + 2, c_num + 2, TAB_RIGHT, "%g", coeffn->coeff);
995             }
996
997           coeffi = ll_next (coeffi);
998         }
999       ++c_num;
1000     }
1001
1002   tab_submit (t);
1003 }
1004
1005
1006 /* Show the results of the contrast tests */
1007 static void
1008 show_contrast_tests (const struct oneway_spec *cmd, struct oneway_workspace *ws)
1009 {
1010   int n_contrasts = ll_count (&cmd->contrast_list);
1011   size_t v;
1012   int n_cols = 8;
1013   size_t n_rows = 1 + cmd->n_vars * 2 * n_contrasts;
1014
1015   struct tab_table *t;
1016
1017   t = tab_create (n_cols, n_rows);
1018   tab_headers (t, 3, 0, 1, 0);
1019
1020   /* Put a frame around the entire box, and vertical lines inside */
1021   tab_box (t,
1022            TAL_2, TAL_2,
1023            -1, TAL_1,
1024            0, 0,
1025            n_cols - 1, n_rows - 1);
1026
1027   tab_box (t,
1028            -1, -1,
1029            TAL_0, TAL_0,
1030            0, 0,
1031            2, 0);
1032
1033   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
1034   tab_vline (t, TAL_2, 3, 0, n_rows - 1);
1035
1036
1037   tab_title (t, _("Contrast Tests"));
1038
1039   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Contrast"));
1040   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Value of Contrast"));
1041   tab_text (t,  4, 0, TAB_CENTER | TAT_TITLE, _("Std. Error"));
1042   tab_text (t,  5, 0, TAB_CENTER | TAT_TITLE, _("t"));
1043   tab_text (t,  6, 0, TAB_CENTER | TAT_TITLE, _("df"));
1044   tab_text (t,  7, 0, TAB_CENTER | TAT_TITLE, _("Sig. (2-tailed)"));
1045
1046   for (v = 0; v < cmd->n_vars; ++v)
1047     {
1048       struct ll *cli;
1049       int i = 0;
1050       int lines_per_variable = 2 * n_contrasts;
1051
1052       tab_text (t,  0, (v * lines_per_variable) + 1, TAB_LEFT | TAT_TITLE,
1053                 var_to_string (cmd->vars[v]));
1054
1055       for ( cli = ll_head (&cmd->contrast_list);
1056             cli != ll_null (&cmd->contrast_list);
1057             ++i, cli = ll_next (cli))
1058         {
1059           struct contrasts_node *cn = ll_data (cli, struct contrasts_node, ll);
1060           struct ll *coeffi = ll_head (&cn->coefficient_list);
1061           int ci;
1062           double contrast_value = 0.0;
1063           double coef_msq = 0.0;
1064           struct group_proc *grp_data = group_proc_get (cmd->vars[v]);
1065           struct hsh_table *group_hash = grp_data->group_hash;
1066
1067           void *const *group_stat_array;
1068
1069           double T;
1070           double std_error_contrast;
1071           double df;
1072           double sec_vneq = 0.0;
1073
1074           /* Note: The calculation of the degrees of freedom in the
1075              "variances not equal" case is painfull!!
1076              The following formula may help to understand it:
1077              \frac{\left (\sum_{i=1}^k{c_i^2\frac{s_i^2}{n_i}}\right)^2}
1078              {
1079              \sum_{i=1}^k\left (
1080              \frac{\left (c_i^2\frac{s_i^2}{n_i}\right)^2}  {n_i-1}
1081              \right)
1082              }
1083           */
1084
1085           double df_denominator = 0.0;
1086           double df_numerator = 0.0;
1087           if ( i == 0 )
1088             {
1089               tab_text (t,  1, (v * lines_per_variable) + i + 1,
1090                         TAB_LEFT | TAT_TITLE,
1091                         _("Assume equal variances"));
1092
1093               tab_text (t,  1, (v * lines_per_variable) + i + 1 + n_contrasts,
1094                         TAB_LEFT | TAT_TITLE,
1095                         _("Does not assume equal"));
1096             }
1097
1098           tab_text_format (t,  2, (v * lines_per_variable) + i + 1,
1099                            TAB_CENTER | TAT_TITLE, "%d", i + 1);
1100
1101
1102           tab_text_format (t,  2,
1103                            (v * lines_per_variable) + i + 1 + n_contrasts,
1104                            TAB_CENTER | TAT_TITLE, "%d", i + 1);
1105
1106           if (cn->bad_count)
1107             continue;
1108
1109           group_stat_array = hsh_sort (group_hash);
1110
1111           for (ci = 0;
1112                coeffi != ll_null (&cn->coefficient_list) && 
1113                  ci < hsh_count (group_hash);
1114                ++ci, coeffi = ll_next (coeffi))
1115             {
1116               struct coeff_node *cn = ll_data (coeffi, struct coeff_node, ll);
1117               const double coef = cn->coeff; 
1118               struct group_statistics *gs = group_stat_array[ci];
1119
1120               const double winv = pow2 (gs->std_dev) / gs->n;
1121
1122               contrast_value += coef * gs->mean;
1123
1124               coef_msq += (coef * coef) / gs->n;
1125
1126               sec_vneq += (coef * coef) * pow2 (gs->std_dev) /gs->n;
1127
1128               df_numerator += (coef * coef) * winv;
1129               df_denominator += pow2((coef * coef) * winv) / (gs->n - 1);
1130             }
1131
1132           sec_vneq = sqrt (sec_vneq);
1133
1134           df_numerator = pow2 (df_numerator);
1135
1136           tab_double (t,  3, (v * lines_per_variable) + i + 1,
1137                       TAB_RIGHT, contrast_value, NULL);
1138
1139           tab_double (t,  3, (v * lines_per_variable) + i + 1 +
1140                       n_contrasts,
1141                       TAB_RIGHT, contrast_value, NULL);
1142
1143           std_error_contrast = sqrt (grp_data->mse * coef_msq);
1144
1145           /* Std. Error */
1146           tab_double (t,  4, (v * lines_per_variable) + i + 1,
1147                       TAB_RIGHT, std_error_contrast,
1148                       NULL);
1149
1150           T = fabs (contrast_value / std_error_contrast);
1151
1152           /* T Statistic */
1153
1154           tab_double (t,  5, (v * lines_per_variable) + i + 1,
1155                       TAB_RIGHT, T,
1156                       NULL);
1157
1158           df = grp_data->ugs.n - grp_data->n_groups;
1159
1160           /* Degrees of Freedom */
1161           tab_fixed (t,  6, (v * lines_per_variable) + i + 1,
1162                      TAB_RIGHT,  df,
1163                      8, 0);
1164
1165
1166           /* Significance TWO TAILED !!*/
1167           tab_double (t,  7, (v * lines_per_variable) + i + 1,
1168                       TAB_RIGHT,  2 * gsl_cdf_tdist_Q (T, df),
1169                       NULL);
1170
1171           /* Now for the Variances NOT Equal case */
1172
1173           /* Std. Error */
1174           tab_double (t,  4,
1175                       (v * lines_per_variable) + i + 1 + n_contrasts,
1176                       TAB_RIGHT, sec_vneq,
1177                       NULL);
1178
1179           T = contrast_value / sec_vneq;
1180           tab_double (t,  5,
1181                       (v * lines_per_variable) + i + 1 + n_contrasts,
1182                       TAB_RIGHT, T,
1183                       NULL);
1184
1185           df = df_numerator / df_denominator;
1186
1187           tab_double (t,  6,
1188                       (v * lines_per_variable) + i + 1 + n_contrasts,
1189                       TAB_RIGHT, df,
1190                       NULL);
1191
1192           /* The Significance */
1193           tab_double (t, 7, (v * lines_per_variable) + i + 1 + n_contrasts,
1194                       TAB_RIGHT,  2 * gsl_cdf_tdist_Q (T,df),
1195                       NULL);
1196         }
1197
1198       if ( v > 0 )
1199         tab_hline (t, TAL_1, 0, n_cols - 1, (v * lines_per_variable) + 1);
1200     }
1201
1202   tab_submit (t);
1203 }
1204
1205